使用std :: string为PInvoke定制Marshaler

免责声明:C ++ / CLI Noob问题

我正在尝试在签名中具有std :: string的C ++ DLL上使用PInvoke。 目前我只是测试:我的目标是将字符串传递给本机DLL,然后返回它。

本机导出如下所示:

#define NATIVE_CPP_API __declspec(dllexport) NATIVE_CPP_API void hello_std(std::string inp, char* buffer) { const char* data = inp.data(); strcpy(buffer, data); } 

我正在尝试使用自定义封送器以正常方式对其进行PInvoke:

 [DllImport("native_cpp.dll", EntryPoint = "?hello_std@@YAPADV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z", CallingConvention = CallingConvention.Cdecl)] private static extern void hello_std( [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(clr_wrapper.string_marshaler))] String inp, StringBuilder buffer); static void Main(string[] args) { var buffer = new StringBuilder(100); hello_std("abcdefg", buffer); Console.WriteLine(buffer); Console.ReadLine(); } 

此处指定的自定义封送程序clr_wrapper.string_marshaler是C ++ / CLI项目中的ICustomMarshaler ,用于获取System::String输入并将其转换为本机std::string 。 我的MarshalManagedToNative实施是在黑暗中刺伤。 我尝试了一些东西,但这是我最好的猜测:

 IntPtr string_marshaler::MarshalManagedToNative( Object^ ManagedObj ) { String^ val = (String^) ManagedObj; size_t size = (size_t)val->Length; char* ptr = (char*) Marshal::StringToHGlobalAnsi(val->ToString()).ToPointer(); std::string * str = new std::string(ptr, size); IntPtr retval = (IntPtr) str; return retval; } 

不幸的是,当我尝试运行它时,PInvoke调用会触发AccessViolationException

我做错了什么,或者整个风险投资是否构思错误?


首先编辑,完成清单

1. C#Console应用程序

 class Program { static void Main(string[] args) { var buffer = new StringBuilder(100); hello_std("abcdefg", buffer); Console.WriteLine(buffer); Console.ReadLine(); } [DllImport("native_cpp.dll", EntryPoint = "?hello_std@@YAXV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@PAD@Z", CallingConvention = CallingConvention.Cdecl)] private static extern void hello_std( [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(clr_wrapper.string_marshaler))] [In] String inp, StringBuilder buffer ); } 

2.原生C ++ DLL项目“native_cpp”

native_cpp.h

 #ifdef NATIVE_CPP_EXPORTS #define NATIVE_CPP_API __declspec(dllexport) #else #define NATIVE_CPP_API __declspec(dllimport) #endif #include  NATIVE_CPP_API void hello_std(std::string inp, char* buffer); 

native_cpp.cpp

 #include "native_cpp.h" void hello_std(std::string inp, char* buffer) { const char* data = inp.data(); strcpy(buffer, data); } 

3. C ++ / CLI项目“clr_wrapper”

clr_wrapper.h

 #pragma once using namespace System; using namespace System::Runtime::InteropServices; namespace clr_wrapper { public ref class string_marshaler : public ICustomMarshaler { public: string_marshaler(void); virtual Object^ MarshalNativeToManaged( IntPtr pNativeData ); virtual IntPtr MarshalManagedToNative( Object^ ManagedObj ); virtual void CleanUpNativeData( IntPtr pNativeData ); virtual void CleanUpManagedData( Object^ ManagedObj ); virtual int GetNativeDataSize(); static ICustomMarshaler ^ GetInstance(String ^ pstrCookie) { return gcnew string_marshaler(); } private: void* m_ptr; int m_size; }; } 

clr_wrapper.cpp

 #include "clr_wrapper.h" #include  using namespace clr_wrapper; using namespace System::Text; string_marshaler::string_marshaler(void) { } Object^ string_marshaler::MarshalNativeToManaged( IntPtr pNativeData ) { return Marshal::PtrToStringAnsi(pNativeData); } IntPtr string_marshaler::MarshalManagedToNative( Object^ ManagedObj ) { String^ val = (String^) ManagedObj; size_t size = (size_t) val->Length; char* ptr = (char*) Marshal::StringToHGlobalAnsi(val->ToString()).ToPointer(); std::string * str = new std::string(ptr, size); m_size = sizeof(str*); m_ptr = (void*) str; IntPtr retval = (IntPtr) str; return retval; } void string_marshaler::CleanUpNativeData( IntPtr pNativeData ) { //Marshal::FreeHGlobal(pNativeData); delete (std::string*) m_ptr; } void string_marshaler::CleanUpManagedData( Object^ ManagedObj ) { } int string_marshaler::GetNativeDataSize() { return m_size; } 

结束第一次编辑

如果您可以使用完全相同的编译器版本,打包,类成员对齐,调用约定,CRT链接,库选项(如_ITERATOR_DEBUG_LEVEL,调试/发布配置等)与本机DLL构建C ++ / CLI DLL,则可以传递STL类在DLL边界上 。 像这样的包装函数可能有效:

 public ref class Wrapper { void hello_std_managed(String^ inp, array^ buffer) { IntPtr inpCopy = Marshal::StringToHGlobalAnsi(inp); std::string inpNative(static_cast(inpCopy.ToPointer())); pin_ptr nativeBuffer = &buffer[0]; hello_std(inpNative,nativeBuffer); Marshal::FreeHGlobal(inpCopy); } } 

但是,由于这是一个很大的IF,您可能需要让DLL的作者将方法的签名更改为原始C / COM类型,如char *或BSTR。 实际上这种方式更好,无论语言或构建配置如何,DLL现在都可以使用。