从PInvoke返回一个字符串?

Jam*_*ame 6 c# c++ pinvoke

我使用PInvoke实现本机代码(C++)和托管代码(C#)之间的互操作性.我只写了一个简单的函数,它从C++代码中获取一个字符串.我的代码看起来像

C#代码:

[DllImport("MyDll.dll")]
private static extern string GetSomeText();
public static string GetAllValidProjects() {
    string s = GetSomeText();
    return s;
}
Run Code Online (Sandbox Code Playgroud)

C++代码

char* GetSomeText() {
    std::string stri= "Some Text Here";
    char * pchr = (char *)stri.c_str();
    return pchr;
}
Run Code Online (Sandbox Code Playgroud)

所有在C++端都可以正常工作,即变量pchr包含"Some Text Here",但在C#中,字符串s包含注释.我不知道我做错了什么.任何帮助,将不胜感激

Dav*_*nan 16

首先,正如其他人所指出的那样,即使在尝试互操作之前,您的C++也已被破坏.您正在返回指向stri缓冲区的指针.但是因为stri函数返回后会立即销毁,返回值无效.

更重要的是,即使你修复了这个问题,你也需要做更多.在C++代码中分配内存将无法使用C#代码来解除分配.

有几个选项可以正确完成.

您的C#代码可以询问C++代码字符串的长度.然后创建一个C#StringBuilder并将其分配给适当的大小.接下来,StringBuilder对象将传递给C++代码,其默认编组将作为LPWSTR.在这种方法中,C#代码分配字符串,并且您的C++代码接收必须复制缓冲区的C字符串.

或者,您可以从C++返回BSTR,它允许在本机C++代码中分配和在C#代码中释放.

BSTR方法可能就是我的方法.它看起来像这样:

C++

#include <comutil.h>
BSTR GetSomeText()
{
    return ::SysAllocString(L"Greetings from the native world!");
}
Run Code Online (Sandbox Code Playgroud)

C#

[DllImport(@"test.dll", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.BStr)]
private static extern string GetSomeText();
Run Code Online (Sandbox Code Playgroud)

更新

Hans Passant在评论中补充了几个有用的观察结果.首先,大多数P/Invoke互操作是针对现有的无法更改的界面完成的,并且您无法选择首选的互操作接口方法.看来情况并非如此,应选择哪种方法?

选项1是在首次询问本机代码需要多少空间之后,在托管代码中分配缓冲区.也许使用双方都同意的固定大小的缓冲区就足够了.

选项1落下的地方是组装字符串是昂贵的并且你不想做两次(例如一次返回它的长度,再一次用于内容).这是选项2,BSTR它发挥作用.

汉斯指出了一个缺点BSTR,即它带有一个UTF-16有效载荷,但你的源数据很可能char*,这是一个"有点麻烦".

为了克服麻烦,你可以用了转换,从char*BSTR这样的:

BSTR ANSItoBSTR(char* input)
{
    BSTR result = NULL;
    int lenA = lstrlenA(input);
    int lenW = ::MultiByteToWideChar(CP_ACP, 0, input, lenA, NULL, 0);
    if (lenW > 0)
    {
        result = ::SysAllocStringLen(0, lenW);
        ::MultiByteToWideChar(CP_ACP, 0, input, lenA, result, lenW);
    } 
    return result;
}
Run Code Online (Sandbox Code Playgroud)

这是方式的最难的了,现在可以很容易地添加其他包装转换到BSTRLPWSTR,std::string,std::wrstring等.