【问题标题】:How do I properly return a char * from an Unmanaged DLL to C#?如何正确地将 char * 从非托管 DLL 返回到 C#?
【发布时间】:2010-12-17 23:09:53
【问题描述】:

函数签名:

char * errMessage(int err);

我的代码:

[DllImport("api.dll")] 内部静态 extern char[] errMessage(int err); ... char[] 消息 = errMessage(err);

这会返回一个错误:

无法封送“返回值”:托管/非托管类型组合无效。

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

【问题讨论】:

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


    【解决方案1】:

    试试这个:

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

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

    编辑:添加 MarshalAs 属性

    【讨论】:

    • 即使是marshal as,如果改成string,也会失去寻找入口点的能力。
    • 这是我想要的解决方案。我们知道我们可以做些什么来完成这项工作吗?
    • 也许尝试将原型声明为返回 int?
    • 这不应该被赞成和接受。 p/invoke marshaler 将尝试销毁调用CoTaskMemFree 返回的C 字符串。除非字符串是通过调用 CoTaskMemAlloc 分配的,否则这通常是泄漏或运行时错误,或两者兼而有之。
    【解决方案2】:

    尝试在托管端使用字符串。您不妨将 CharSet 设置为 Ansi

    【讨论】:

    • CharSet 可能是 Ansi,但希望 IronicMuffin 知道它是什么。
    【解决方案3】:

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

    【讨论】:

    • 使用此解决方案释放为字符串分配的空间的最佳做法是什么?
    • 这取决于内存是如何分配的。如果它是使用 CoTaskMemAlloc 分配的,则应使用 Marshal.FreeCoTaskMem。否则,不要释放它,因为它肯定是在不属于您的程序集的堆中使用 new/malloc 分配的。
    【解决方案4】:

    一个简单而健壮的方法是在 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());
    }
    

    测试:

    你好世界

    【讨论】:

    • 这很好,但我无权访问非托管源。
    【解决方案5】:

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

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

    【讨论】:

      猜你喜欢
      • 2017-01-05
      • 2018-03-08
      • 1970-01-01
      • 2019-07-19
      • 1970-01-01
      • 1970-01-01
      • 2023-04-02
      • 2020-05-09
      • 1970-01-01
      相关资源
      最近更新 更多