C# 中的字符 * 编组

Posted

技术标签:

【中文标题】C# 中的字符 * 编组【英文标题】:Char * marshalling in C# 【发布时间】:2009-11-27 12:47:31 【问题描述】:

我在 Visual C++ DLL 中有这个函数

char * __stdcall GetMessage(int id) 
char buffer[250];
.
.
.
strcpy(buffer, entry); // entry is a char * that has allocated 250 chars
return (char *)buffer;

我正在尝试使用以下代码从 C# 导入此函数

[DllImport("mydll.dll", CharSet=CharSet.Ansi)]
public static extern string GetMessage(int id);

我试图在 MessageBox 中显示它,它的字符串末尾总是有奇怪的符号。有什么建议可以解决这个问题吗?

【问题讨论】:

我没有提到我调试的DLL char *buffer是一个正确的字符串,最后是0。 我不知道返回值是怎样的,但是对于参数,以下规则适用:对 const char* 使用字符串,对 char* 使用 StringBuilder。另一方面,您正在返回一个指向作为局部变量的缓冲区的指针。这听起来不是一个好主意。这个问题可能是你的朋友,但是:***.com/questions/370079/… @OreghonGost,不,规则是:使用string 用于您不变异的内容,并使用StringBuilder 用于您所做的事情。 const 给出了提示,但并非严格要求。 【参考方案1】:

我将再次强调您的 C++ 代码无效这一事实。您正在返回一个指向堆栈帧上的局部变量的指针,该指针在函数返回时不再有效。它现在起作用只是一个意外。一旦你调用另一个函数,堆栈空间将被重用,破坏缓冲区内容。当您从 C# 调用此代码时,肯定会发生这种情况,P/Invoke marshaller 实现将覆盖堆栈帧并破坏缓冲区。

更糟糕的是,P/Invoke 编组器会尝试释放缓冲区。它将假定缓冲区由 CoTaskMemAlloc() 分配并调用 CoTaskMemFree() 以释放缓冲区。这将在 Windows XP 及更早版本上静默失败,但在 Vista 及更高版本上会使您的程序崩溃。

这是您的问题的一种解决方案,使用 CoTaskMemAlloc() 分配缓冲区而不是使用局部变量。全方位更好的解决方案是让函数的调用者通过缓冲区,用字符串填充。这样,调用者可以决定如何分配缓冲区并在必要时清理它。函数签名应如下所示:

extern "C" __declspec(dllexport)
void __stdcall GetMessage(int id, char* buffer, size_t bufferSize);

使用 strcpy_s() 安全地填充缓冲区并避免任何缓冲区溢出。相应的 C# 声明将是:

[DllImport("mydll.dll")]
private static extern void GetMessage(int id, StringBuilder buffer, int bufferSize);

并像这样调用:

var sb = new StringBuilder(250);
GetMessage(id, sb, sb.Capacity);
string retval = sb.ToString();

【讨论】:

【参考方案2】:

从 DLL 中,您返回一个字符数组,即在 GetMessage 函数内的堆栈上的缓冲区,当执行返回时,数据被弄乱了,因为缓冲区从堆栈中弹出并且在堆栈中是本地的此外,因此您看到的奇怪字符。有两种方法可以解决此问题,将缓冲区变量声明更改为指针并在返回之前对其进行 malloc 或将其声明为静态。

字符 * 缓冲区; if (!(buffer = (char *)malloc((250 * sizeof(char)) + 1)) // 处理内存不足的情况 // 现在缓冲区有 250 + 1 用于空终止字符。

或者

静态字符缓冲区[250];

您还可以澄清入口指针变量 - 是全局的吗?

希望这会有所帮助, 最好的祝福, 汤姆。

【讨论】:

【参考方案3】:

尝试使用 StringBuilder 作为返回类型并省略 CharSet 属性:

[DllImport("mydll.dll")]
public static extern StringBuilder GetMessage(int id);

【讨论】:

以上是关于C# 中的字符 * 编组的主要内容,如果未能解决你的问题,请参考以下文章

C#编组来自C++ DLL的无符号字符返回值[重复]

从 c# dll 调用/编组字符串到非托管代码

如何将 C# 字符串 [] 编组(理想情况下在 SWIG 中)到 C++ 字符串*?

C# 中的 C++ int & long 编组

C 到 C# 数组编组中的矛盾行为

通过非默认 AppDomain 中的 C# 函数调用编组 C++ 指针接口