Char *在C#中编组

时间:2009-11-27 12:47:32

标签: c# c++ marshalling

我在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中呈现它,它总是在字符串的末尾有奇怪的符号。有什么建议如何克服这个问题?

3 个答案:

答案 0 :(得分:8)

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

更糟糕的是,P / Invoke marshaller将尝试释放缓冲区。它将假设缓冲区由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();

答案 1 :(得分:1)

从DLL返回一个chars数组,即缓冲区,它位于GetMessage函数的堆栈中,当执行返回时,数据被搞乱,因为缓冲区从堆栈中弹出并且是堆栈本地的因此,你看到的是奇怪的人物。有两种方法可以解决这个问题,在返回之前将缓冲区变量声明更改为指针和malloc,或者将其声明为静态。

char *buffer;
if (!(buffer = (char *)malloc((250 * sizeof(char)) + 1)){
   // Handle the out of memory condition
}
// Now the buffer has 250 + 1 for null terminating character.

static char buffer[250];

你也可以澄清一下条目指针变量 - 是全局吗?

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

答案 2 :(得分:0)

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

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