使用P/Invoke跨C++/C#边界管理非托管字符串

本文关键字:管理 字符串 边界 Invoke C++ 使用 | 更新日期: 2023-09-27 18:26:07

我声明了以下结构(C++):

struct NativeOperationResult {
    const INTEROP_BOOL Success; // INTEROP_BOOL = char
    const char16_t* const ErrorMessage;
    NativeOperationResult(const NativeOperationResult& c);
    /* various constructors, omitted for brevity */
};

现在,我在其他地方有一个导出的函数定义:

extern "C" __declspec(dllexport) NativeOperationResult ReturnFailureWithMessage() {
    return { INTEROP_BOOL_FALSE, "Test" };
}

我的期望是通过p/Invoke从C#调用ReturnFailureWithMessage(如果您想知道的话,这是一个测试方法)。在NativeOperationResult构造函数中,它获取"Test"的副本并将其放入ErrorMessage中。

NativeOperationResult拥有char16_t*的所有权,因此我需要在结构被销毁时将其删除。这没问题,但我不想在.NET CLR有机会将字符串复制到托管堆之前删除内存。

坦率地说,我有点不清楚在哪里删除那段记忆。我认为C++编译器会复制我的结构(或者只是移动它),然后CLR会使用该副本。。。这意味着我应该使用Marshal.FreeHGlobal从.NET中删除本机内存。

这是正确的吗?

使用P/Invoke跨C++/C#边界管理非托管字符串

不,这是不正确的您需要区分两种情况:

1) 您没有在C++方面进行任何分配这就是您现在所说的情况

2) 您确实在C++端进行了分配,您需要处理释放。

因此,为了回答您的问题:不,您的示例不需要任何内存的"删除",因为没有人明确分配内存。

第二种情况有点棘手。如果使用new char16_t[blah]在C++端进行内存分配,则需要使用delete[] nativeOperationResult.ErrorMessage释放内存。这在C#端是不可能做到的。可以使用不同的分配器(例如;mallocnew)来分配内存,而C#不知道如何处理这些指针。

您需要向NativeOperationResult添加一个新标志,例如DeletionRequired,并从非托管端导出新函数:FreeNativeOperationResultIfNeeded(..)。这里还有更长的讨论。

使用C++strings可以避免所有这些无意义的操作。它们神奇地工作,不需要删除。

struct NativeOperationResult {
    const INTEROP_BOOL Success; // INTEROP_BOOL = char
    const string const ErrorMessage;
    NativeOperationResult(const NativeOperationResult& c);
    /* various constructors, omitted for brevity */
};

您可以将其设为out参数,并期望调用方法向其传递适当数量的内存,而不是返回NativeOperationResult。通过这种方式,.Net可以分配内存,然后在pinvoke完成后进行清理。但是,您必须想办法通知.Net它预计要分配的内存量。

extern"C"__declspec(dllexport)void ReturnFailureWithMessage(NativeOperationResult*结果)