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

发布于 2024-08-11 21:07:09 字数 328 浏览 8 评论 0原文

函数签名:

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.

我做错了什么?感谢您的任何帮助。

Function signature:

char * errMessage(int err);

My code:

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

This returns an error:

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

What am I doing wrong? Thanks for any help.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

煮酒 2024-08-18 21:07:09

一种简单而可靠的方法是在 C# 中以 StringBuilder 的形式分配一个缓冲区,将其传递给非托管代码并在那里填充。

示例:

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

A simple and robust way is to allocate a buffer in C# in the form if a StringBuilder, pass it to the unmanaged code and fill it there.

Example:

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());
}

Test:

Hello World
苦笑流年记忆 2024-08-18 21:07:09

请参阅此问题。总而言之,该函数应该返回一个 IntPtr,并且您必须使用 Marshal.PtrToString* 将其转换为托管 String 对象。

See this question. To summary, the function should return an IntPtr and you have to use Marshal.PtrToString* to convert it to a managed String object.

梦旅人picnic 2024-08-18 21:07:09

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

您甚至无法在非托管程序中可靠地调用该函数。您使用正确版本的 free() 的可能性非常小。您所能做的就是将其声明为 IntPtr 并使用 Marshal.PtrToStringAnsi() 自行封送返回值。确保编写一个测试程序,当您在 Taskmgr.exe 中观察它时,它会执行一百万次。如果程序的虚拟机大小无限制地增长,则会出现无法修复的内存泄漏。

It is a horrible function signature, there's no way to guess how the string was allocated. Nor can you deallocate the memory for the string. If you declare the return type as "string" in the declaration then the P/Invoke marshaller will call CoTaskMemFree() on the pointer. That is very unlikely to be appropriate. It will silently fail in XP but crash your program in Vista and Win7.

You can't even reliably call the function in an unmanaged program. The odds that you'd use the correct version of free() are pretty slim. All you can do is declare it as IntPtr and marshal the return value yourself with Marshal.PtrToStringAnsi(). Be sure to write a test program that does so a million times while you observe it in Taskmgr.exe. If the VM size for the program grows without bound, you have a memory leak you cannot plug.

与风相奔跑 2024-08-18 21:07:09

试试这个:

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

我相信 C# 足够聪明,可以处理指针并返回一个字符串。

编辑:添加了 MarshalAs 属性

try this:

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

I believe C# is smart enough to handle the pointer and return you a string.

Edit: Added the MarshalAs attribute

浅忆 2024-08-18 21:07:09

尝试在托管端使用 String。你不妨将 CharSet 设置为 Ansi

Try using String in the managed side. you might as well set the CharSet to Ansi

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文