如何正确地将.NET字符串封送为本机代码的std::wstrings
How to correctly marshal .NET Strings to std::wstrings for native code?
我有一个第三方库,它有一个构造函数接受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
。
我错过了什么或做得不对?
EDIT当我在createSomething
函数中仅使用return 0
时,当使用String
时,我会得到StackImbalanceException
。
我不相信现成的.Net整理器支持C++ABI。
您需要将.Net字符串封送为wchar_t*
,然后在本机端创建std::wstring
。
或者,您可以使用C++/CLI(假设为msvc)为您调解两者(通过marshal_as
),它理解.Net字符串,并让marshaller将其封送为std::wstring
。Microsoft提供了几种标准封送拆收器,请参阅此处的概述。
我的经验通常是,在这些情况下,C++/CLI存根更整洁、更容易(您的里程数在这里会有所不同),否则您可以尝试为第三方库提供一个简单的C样式API。
您的示例代码提示您可能已经控制了一段Bridge
代码。考虑保持桥接中的接口尽可能简单(内置类型、POD等),它应该简化第三方库的集成。
同样值得注意的是,如果要链接到第三方的C++库,则需要使用相同的编译器、设置、调用约定和运行时等。与它们一样,否则仍会遇到ABI问题。为外部库提供导出C++接口(包括STL)并不总是一个好主意。
基本上有几种方法可以将这些代码链连接在一起,您必须选择一种适合正在使用的工具链的方法。
在DLL接口边界上拥有STL类是非常脆弱的和具有高度约束性的,例如,您必须注意DLL及其客户端都是使用相同的C++编译器版本、使用同一开关、与风格相同的CRT链接等构建的。此外,它在本机C++和C#之间并不能"开箱即用"。
对于C++和.NET互操作,我建议您使用C++/CLI在本机C++代码组件和.NET C#代码之间构建桥接层。
如果您选择这样做,要在本机C++代码(例如使用std::wstring
)和.NET代码(使用.NET的托管String
)之间封送字符串,您可以使用Microsoft构建的包装器,如下所示:
C++中的编组概述
例如,要从.NET托管字符串转换为本机C++std::wstring
,您可能需要使用以下代码:
#include <string>
#include <msclrmarshal_cppstd.h>
System::String^ managedString = "Connie";
std::wstring nativeString
= msclr::interop::marshal_as<std::wstring>(managedString);
解决方案非常简单!我只是忘记了__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);
}
现在我可以通过String
s和StringBuilder
s。
- 在nodejs中使用本机代码的最佳方法是什么?
- Winapi - SetWindowLongPtr in ShutdownBlockReason创建/销毁JNI本机代码
- 可以从本机代码(如C++/Delphi)中使用.netCore DLL
- 使用 x86_64-w64-mingw32-g++ 编译 JNI C++本机代码
- 终止调用本机代码的 .Net 线程
- 从本机代码返回到托管代码会损坏返回的对象
- 我能否通过 JNI 调试在本机代码中创建的 JVM?
- Android 无法从本机代码调用 Java 方法 JNI
- 将自定义 Java 数据模型传递给我的本机代码
- 用于 JUnit 测试的本机代码未使用 'nar-maven-plugin' 编译
- 将本机C 代码转换为Java
- 有什么方法可以用本机C 代码替换Windows API计时器(settimer)
- 使用 Android Studio 使用本机代码编译 apk 时,如何在链接处删除 libgnustl_static.
- 如何在 Linux 上调试本机代码时自动附加到多个子进程
- 如何将 WebAssembly 编译成常规汇编/本机代码(或 Cpp 等)
- Android- AAR库的调试本机代码
- 附加到 C++/CLI dll 的性能探查器无法访问本机C++代码
- AccessViolationException从托管C (ASP.NET的包装器)调用本机C 代码
- Excel 2016 VSTO 加载项与本机代码 DLL
- 如何正确地将.NET字符串封送为本机代码的std::wstrings