我了解到,当C ++调用者使用另一个C ++ DLL时,调用者必须分配具有一定最大大小的字符串,并将该指针和最大大小传递给C ++被调用者/ DLL。
因此C ++调用程序将执行以下操作:
MyCPPDllFunction(char *out, int maxOutSize);
现在我了解到,当C ++调用程序使用C#DLL时,它看起来可能像这样:
char *output = MyCSharpDllFunction();
我不明白C#被调用者如何分配内存,而C ++被调用者却无法分配内存?
答案 0 :(得分:2)
我不了解C#被调用者如何分配内存,而C ++被调用者却无法分配内存?
您正在寻找错误的问题。问题不在于“ C#如何分配内存”。任何人都可以分配内存...并且有很多方式:-)问题总是总是“您将如何释放内存?”,因为释放内存与分配内存一样重要(不释放内存将导致内存泄漏,这将使您的程序内存占用迅速膨胀,而使用错误的分配器释放它至少不会释放 (请参见上一点),并且将崩溃该程序最糟糕的)。
现在,操作系统(例如Windows)为您提供了一些任何人都可以使用的分配器(例如CoTaskMemAlloc
),但是问题是通常在C / C ++中,您将使用malloc
/ new
。问题就在这里:两个dll之间的分配器可能是“不同的”(不同):两个C / C ++中编译的dll(但例如使用不同版本的Visual C ++,调试或发布模式,或者一个使用运行时)作为dll,另一个直接链接运行时,或使用不同的C编译器编译的dll将具有不同的malloc
(和C ++中的不同new
)。具有不同的malloc
/ new
,它们将具有不同的free
/ delete
,因此一个DLL的free
无法释放另一个DLL的内存dll。
现在...使用此签名:
void MyCPPDllFunction(char *out, int maxOutSize);
调用方必须分配内存...因此,调用方知道如何释放内存...没有内存泄漏:-)
具有此签名:
char *MyCPPDllFunction();
被呼叫者必须分配内存...现在,呼叫者如何释放它?被调用者可以导出另一种方法:
void FreeMemoryAllocatedByMe(void *ptr)
{
free(ptr);
}
然后,呼叫者将调用它,并且所有问题都将得到解决,因为free
将成为被呼叫者 free
。
现在...通常,当C#封送对象(和字符串)时,它使用CoTaskMemAlloc
分配内存,并且期望如果接收到内存,则必须使用{{1}释放它}。因此,在您的C ++-> C#示例中,C ++应该
CoTaskMemFree
答案 1 :(得分:1)
我想原因如下:
从c ++调用c#api时,将调用CLR虚拟机管理的代码,因此内存分配由虚拟机本身自动管理,从而使您不必为手动缓冲区分配而烦恼。
答案 2 :(得分:1)
如何返回值与所使用的语言无关。它仅由函数的调用签名定义。所以我可以用C ++编写以下DLL源:
#include <windows.h>
BOOL WINAPI DllMain(
_In_ HINSTANCE hinstDLL,
_In_ DWORD fdwReason,
_In_ LPVOID lpvReserved
)
{
return TRUE;
}
__declspec(dllexport) char *function1()
{
char *foo = new char[100];
// do stuff to fill in foo
return foo;
}
__declspec(dllexport) void function2(char *out, int maxOutSize)
{
// do stuff to fill in out
}
这是完全合法的,并定义具有与OP问题中列出的两个功能完全匹配的签名的C ++入口点。
请注意,这使用C ++ new[]
为function1()
分配内存,因此有必要在某个时候使用delete[]
释放它。
同样,如果在C#情况下由CLR分配内存,则您需要将其传递回C#DLL以正确释放,或者找到在C ++中销毁它的安全方法,甚至可以进行这种操作。
我会在这里100%诚实地说,如果C ++运行时和C#CLR与内存分配配合得很好,我一点都不知道。因此,可能唯一的选择是将其传递回C#进行释放。
-编辑-
我还将链接这个问题以供进一步参考,因为它处理同一问题的观点不同,尤其是被接受的答案中包含一些非常有用的信息。
Passing strings from C# to C++ DLL and back -- minimal example