使用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中删除本机内存。
这是正确的吗?
不,这是不正确的您需要区分两种情况:
1) 您没有在C++方面进行任何分配这就是您现在所说的情况。
2) 您确实在C++端进行了分配,您需要处理释放。
因此,为了回答您的问题:不,您的示例不需要任何内存的"删除",因为没有人明确分配内存。
第二种情况有点棘手。如果使用new char16_t[blah]
在C++端进行内存分配,则需要使用delete[] nativeOperationResult.ErrorMessage
释放内存。这在C#端是不可能做到的。可以使用不同的分配器(例如;malloc
、new
)来分配内存,而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*结果)