如何正确编组.NET字符串到本地代码的std :: wstrings?

时间:2015-06-23 09:21:44

标签: c# c++ string interop marshalling

我有一个第三方库,其中有一个类,其中构造函数采用std::wstring

构造函数由头文件中的第三方定义:

Something(const std::wstring &theString);

我的头文件包含:

extern "C" __declspec(dllexport) ThirdParty::Something* createSomething(const std::wstring &theString);

我的实现是这样的:

ThirdParty::Something* Bridge::createSomething(const std::wstring &theString) {
    return new ThirdParty::Something(theString);
}

现在我的C#示例程序中有:

[DllImport("Bridge.dll", CallingConvention=CallingConvention.StdCall, CharSet=CharSet.Unicode)]
public static extern IntPtr createSomething(StringBuilder theString);

当我现在尝试将其称为:

IntPtr ip = createSomething(new StringBuilder("foo"));

我得到AccessViolationException。当我使用String代替StringBuilder时,我会获得SEHException

我错过了什么或做错了什么?

编辑当我在return 0函数中createSomething时,我在使用StackImbalanceException时得到String

3 个答案:

答案 0 :(得分:2)

我不相信.Net marshaller开箱即可支持C ++ ABI。

您需要将.Net字符串封送到wchar_t*,然后在本机端创建std::wstring

或者你可以使用C ++ / CLI(假设msvc)为你调解两个(通过marshal_as),它理解.Net字符串并有编组来编组std::wstring。 Microsoft提供了几个标准编组程序,请参阅他们的overview here

我的经验一般是在这些情况下C ++ / CLI存根更整洁,更容易(这里你的里程会有所不同),否则你可以尝试为第三方库提供一个简单的C风格的API你。

您的示例代码提示可能已由您控制的Bridge段代码。考虑尽可能简单地保持网桥中的接口(内置类型,POD等),它应该简化第三方库的集成。

值得注意的是,如果你要链接第三方的C ++库,那就是使用相同的编译器,设置,调用约定和运行时等。做,否则你仍然会遇到ABI问题。为外部库提供导出C ++接口(包括STL)并不总是一个好主意。

基本上有几种方法可以将这些代码链接在一起,你必须选择一个适合所用工具链的方法。

答案 1 :(得分:2)

在DLL接口边界处拥有STL类是非常脆弱高度约束,例如,您必须注意DLL及其客户端都是使用相同的 C ++编译器版本,带有相同的开关,与CRT的相同的风格链接等。 而且,它在本机C ++和C#之间“开箱即用”。

对于C ++和.NET互操作,我建议您使用 C ++ / CLI 在本机C ++代码组件和.NET C#代码之间构建桥接层。

如果您选择这样做,要在本机C ++代码(例如使用std::wstring)和.NET代码(使用.NET的托管String)之间编组字符串,您可以使用由微软并在此处列出:

  

Overview of Marshaling in C++

例如,要从.NET托管字符串转换为本机C ++ std::wstring,您可能希望使用以下代码:

#include <string>
#include <msclr\marshal_cppstd.h>

System::String^ managedString = "Connie";
std::wstring nativeString 
    = msclr::interop::marshal_as<std::wstring>(managedString);

答案 2 :(得分:-1)

解决方案更简单,更简单!我刚忘了__stdcall,需要使用wchar_t

我的标题条目现在看起来像:

extern "C" __declspec(dllexport) ThirdParty::Something* __stdcall createSomething(wchar_t* theString);

实施也是如此:

ThirdParty::Something* __stdcall Bridge::createSomething(wchar_t* theString) {
    std::wstring theWString = std::wstring(theString);
    return new ThirdParty::Something(theWString);
}

现在我可以传递StringStringBuilder s。