为什么在调用C#DLL时不需要提供字符串缓冲区
Why do I not need to provide a string buffer when calling a C# DLL?
我了解到,当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传递字符串并返回--的最小示例
- 如何使用字符串find_first_of缓冲区形式(size_t n 参数)
- Qt PL/SQL - 赋值运算符 - 字符串缓冲区太小
- 清除输入缓冲区后未提取字符串流
- 协议缓冲区字符串字段的文本编码
- 如何避免将数据缓冲区的额外副本复制到字符串?
- 在 node.js 中将缓冲区从 C++ 转换为 UTF-8 字符串
- 将指针作为缓冲区传递到第一个字符串元素
- 为什么在调用C#DLL时不需要提供字符串缓冲区
- 如何在C++中对静态缓冲区执行字符串格式化?
- 自动截断和 null 终止缓冲区溢出中的字符串缓冲区
- 使用字符串缓冲区文件名更改OFStream的目录
- 如何编写高效的字符串缓冲区
- 包装字符串缓冲区
- 字符串缓冲区如何导致运行恶意代码
- 缓冲区已满时清除字符串缓冲区.这是什么图案
- 为什么_splitpath_s()使用自定义大小的字符串缓冲区来返回驱动器号
- Cassandra cppdriver查询字符串缓冲区溢出
- 为二进制数据创建字符串缓冲区的最佳方法
- 将文件读入字符串缓冲区并检测 EOF
- C++DLL中的线程安全字符串缓冲区变量