【问题标题】:Passing strings from C# to C++ DLL and back -- minimal example将字符串从 C# 传递到 C++ DLL 并返回——最小示例
【发布时间】:2014-01-12 04:06:53
【问题描述】:

我正在尝试制作一个绝对最简单的最小示例,说明如何在 C# 中将字符串传入和传出 C++ DLL。

我的 C++ 看起来像这样:

using std::string;

extern "C" {
    string concat(string a, string b){
        return a + b;
    }
}

像这样的标题

using std::string;

extern "C" {
    // Returns a + b
    __declspec(dllexport) string concat(string a, string b);
}

我的 C# 是

[DllImport("*****.dll", CallingConvention = CallingConvention.Cdecl)]
    static extern string concat(string a, string b);
}

我称它为: Console.WriteLine(concat("a", "b"));

但这会产生 System.AccessViolationException。这似乎是最微不足道的事情,但我完全坚持下去。当我尝试使用“添加”函数进行类似的实验时,该函数需要两个双精度并返回一个双精度,我没有遇到任何问题。

【问题讨论】:

    标签: c# c++ pinvoke


    【解决方案1】:

    您不能跨互操作边界传递 C++ std::string。您不能在 C# 代码中创建其中之一。所以你的代码永远无法工作。

    您需要在互操作边界使用互操作友好类型。例如,以空字符结尾的字符数组。当您在同一个模块中分配和取消分配内存时,这很有效。因此,将数据从 C# 传递到 C++ 就足够简单了。

    C++

    void foo(const char *str)
    {
        // do something with str
    }
    

    C#

    [DllImport("...", CallingConvention = CallingConvention.Cdecl)
    static extern void foo(string str);
    
    ....
    
    foo("bar");
    

    在另一个方向上,您通常希望调用者分配缓冲区,被调用者可以写入:

    C++

    void foo(char *str, int len)
    {
        // write no more than len characters into str
    }
    

    C#

    [DllImport("...", CallingConvention = CallingConvention.Cdecl)
    static extern void foo(StringBuilder str, int len);
    
    ....
    
    StringBuilder sb = new StringBuilder(10);
    foo(sb, sb.Capacity);
    

    【讨论】:

    • nm 您在回答我的问题时进行了编辑。现在试试,谢谢!
    • 您只需传递一个字符串,然后让 p/invoke marshaller 完成剩下的工作。
    • 很好的答案。干净清澈。请注意,如果您不知道要返回的字符串的长度,一种方法是创建一个附加函数来返回所需缓冲区的大小。所以首先你调用它,然后分配你的 StringBuilder。例如static extern int fooBufSize().
    • @orion 谢谢。通常你会用一个功能来做。大小参数将是 ref int。将 null 传递给 SB 以指示您想知道长度。然后再次调用。
    • @fnc12 确保没有泄漏内存
    【解决方案2】:

    这是我喜欢的最简单的方式 - 传入一个字符串,然后使用 lambda 获取响应

    C#

     public delegate void ResponseDelegate(string s);
    
     [DllImport(@"MyDLL.dll", EntryPoint ="Foo", CallingConvention = CallingConvention.StdCall)]
     public static extern void Foo(string str, ResponseDelegate response);
     ...
     
     Foo("Input", s =>
     {
        // response is returned in s - do what you want with it
     });
    

    C++

     typedef void(_stdcall *LPEXTFUNCRESPOND) (LPCSTR s);
    
     extern "C"
     {
         __declspec(dllexport) void __stdcall Foo(const char *str, LPEXTFUNCRESPOND respond) 
         {
             // Input is in str
             // Put your response in respond()
             respond("HELLO");
         }
     } 
    

    【讨论】:

    • 真的很不落俗套,也很可爱!
    • 你能解释一下这个例子中的 ResponseDelegate 是什么吗?
    • @anti - 道歉 - 添加到答案中
    猜你喜欢
    • 2013-05-03
    • 2012-02-12
    • 2013-10-27
    • 2014-02-24
    • 1970-01-01
    • 1970-01-01
    • 2011-03-02
    • 1970-01-01
    相关资源
    最近更新 更多