如何正确地将char *从非托管DLL返回到C#?

时间:2009-11-18 16:56:38

标签: c# .net c++ interop unmanaged

功能签名:

char * errMessage(int err);

我的代码:

[DllImport("api.dll")]       
internal static extern char[] errMessage(int err);
...
char[] message = errMessage(err);

这会返回错误:

Cannot marshal 'return value': Invalid managed/unmanaged type combination.

我做错了什么?谢谢你的帮助。

5 个答案:

答案 0 :(得分:7)

一种简单而强大的方法是在表单中使用StringBuilder在C#中分配缓冲区,将其传递给非托管代码并将其填充到那里。

示例:

<强> C

#include <string.h>

int foo(char *buf, int n) {
   strncpy(buf, "Hello World", n);
   return 0;
}

<强> C#

[DllImport("libfoo", EntryPoint = "foo")]
static extern int Foo(StringBuilder buffer, int capacity);

static void Main()
{
    StringBuilder sb = new StringBuilder(100);
    Foo(sb, sb.Capacity);
    Console.WriteLine(sb.ToString());
}

测试:

Hello World

答案 1 :(得分:5)

this question。总而言之,该函数应返回一个IntPtr,您必须使用Marshal.PtrToString *将其转换为托管String对象。

答案 2 :(得分:5)

这是一个可怕的函数签名,没有办法猜测字符串是如何分配的。你也不能为字符串释放内存。如果在声明中将返回类型声明为“string”,那么P / Invoke编组器将在指针上调用CoTaskMemFree()。这不太合适。它会在XP中无声地失败,但会在Vista和Win7中崩溃你的程序。

您甚至无法在非托管程序中可靠地调用该函数。你使用正确版本的free()的几率非常小。您所能做的就是将其声明为IntPtr并使用Marshal.PtrToStringAnsi()自行编组返回值。当你在Taskmgr.exe中观察它时,一定要编写一个测试程序,这样做了一百万次。如果程序的VM大小无限制地增长,则会出现无法插入的内存泄漏。

答案 3 :(得分:2)

试试这个:

[DllImport("api.dll")]
[return : MarshalAs(UnmanagedType.LPStr)]
internal static extern string errMessage(int err);
...
string message = errMessage(err);

我相信C#足够智能来处理指针并返回一个字符串。

编辑:添加了MarshalAs属性

答案 4 :(得分:0)

尝试在托管端使用String。你也可以将CharSet设置为Ansi