为什么在调用C#DLL时不需要提供字符串缓冲区

Why do I not need to provide a string buffer when calling a C# DLL?

本文关键字:字符串 缓冲区 不需要 调用 C#DLL 为什么      更新时间:2023-10-16

我了解到,当C++调用方使用另一个C++DLL时,调用方必须分配一个具有特定最大大小的字符串,并将该指针和最大大小传递给C++被调用方/DLL。

因此,C++调用程序将执行以下操作:

MyCPPDllFunction(char *out, int maxOutSize);

现在我了解到,当C++调用程序使用C#DLL时,它可以看起来像这样:

char *output = MyCSharpDllFunction();

我不明白C#被调用者是如何分配内存的,而C++被调用者却不能?

我不明白C#被调用者是如何分配内存的,而C++被调用者不能分配内存

你找错问题了。问题不在于"C#如何分配内存"。任何人都可以分配内存。。。在很多方面:-(问题是总是"你将如何释放内存?",因为释放内存和分配内存一样重要(不释放内存会导致内存泄漏,这会使你的程序内存占用激增,而用错误的分配器释放内存至少不会释放(请参阅前一点(,并且在最坏的情况下将使程序崩溃(。

现在,操作系统(例如Windows(为您提供了一些任何人都可以使用的分配器(例如CoTaskMemAlloc(,但问题是,通常在C/C++中,您将使用malloc/new。问题就在这里:这些分配器在两个dll之间可能是"不同的"(不同的(:用C/C++编译的两个dll(例如,用不同版本的Visual C++编译,或在调试与发布模式下编译,或一个将运行时用作dll,另一个直接链接运行时,或用不同的C编译器编译(将具有不同的malloc(和C++中不同的new(。有了不同的malloc/new,它们将有不同的free/delete,因此一个dll的free无法释放另一个dll。

现在。。。使用此签名:

void MyCPPDllFunction(char *out, int maxOutSize);

调用方必须分配内存。。。所以打电话的人知道如何释放内存。。。无内存泄漏:-(

签名:

char *MyCPPDllFunction();

被调用者必须分配内存。。。现在,打电话的人该如何释放它?被叫方可以导出另一种方法:

void FreeMemoryAllocatedByMe(void *ptr)
{
free(ptr);
}

那么调用者将调用它,并且所有问题都将得到解决,因为free将是被调用者free

现在。。。通常,当C#封送对象(和字符串(时,它使用CoTaskMemAlloc来分配内存,并且它希望如果接收到内存,则必须使用CoTaskMemFree来释放内存。因此,在您的C++->C#示例中,C++应该

CoTaskMemFree(output);

我想原因如下:

当您从c++调用c#api时,您调用的代码由CLR虚拟机管理,因此内存分配由虚拟机本身自动管理,使您无需手动分配缓冲区。

返回值的方式与使用的语言无关。它仅由函数的调用签名定义。所以我可以用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#进行解除分配。

--编辑--

我也将这个问题联系起来以供进一步参考,因为它涉及对同一问题的不同看法,特别是公认的答案中有一些非常有用的信息

从C#向C++DLL传递字符串并返回--的最小示例