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

时间:2014-10-10 13:32:44

标签: c# c++ pinvoke

我声明了以下结构(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" };
}

我的期望是从C#通过P / Invoke调用ReturnFailureWithMessage(一种你想知道的测试方法)。在NativeOperationResult构造函数中,它需要“Test”的副本并将其放在ErrorMessage中。

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

坦率地说,我对删除内存的位置有点模糊。我认为C ++编译器会复制我的结构(或者只是移动它),然后CLR将使用该副本......这意味着我应该使用Marshal.FreeHGlobal从.NET中删除本机内存。

这是对的吗?

2 个答案:

答案 0 :(得分:1)

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

1)您没有在C ++端进行任何分配。 您正在讨论的情况

2)你确实在C ++方面进行了分配,你需要处理重新分配。

这样回答你的问题:不,你的例子不需要任何“删除”内存,因为没有人明确地分配了内存。

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

您需要向NativeOperationResult添加新标记,例如DeletionRequired,并从非托管端导出新功能:FreeNativeOperationResultIfNeeded(..)There is longer discussion here

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

struct NativeOperationResult {
    const INTEROP_BOOL Success; // INTEROP_BOOL = char
    const string const ErrorMessage;

    NativeOperationResult(const NativeOperationResult& c);
    /* various constructors, omitted for brevity */
};

答案 1 :(得分:0)

您可以将其设置为out参数,并期望调用方法向其传递适量的内存,而不是返回NativeOperationResult。这样,.Net可以分配内存,然后在pinvoke完成后清理它。但是你必须找到一种方法来通知.Net预期分配的内存量。

extern" C" __declspec(dllexport)void ReturnFailureWithMessage(NativeOperationResult * result)