自定义封了的PInvoke与标准::字符串 [英] Custom Marshaler for PInvoke with std::string
问题描述
免责声明:C ++ / CLI noob问题
我尝试使用的PInvoke在C有一个std ++ DLL ::串中的签名。目前,我只是测试:我的目标是一个字符串传递给本机DLL,并将其返回
I'm attempting to use a PInvoke on a C++ DLL that has a std::string in the signature. At the moment I just testing: my goal is to pass a string to the native DLL, and return it.
本机的出口是这样的:
#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以正常的方式,具有自定义封
I'm attempting to PInvoke it in the normal way, with a custom marshaler:
[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
,是一个 ICustomMarshaler
在C ++ / CLI项目,并拟采取系统::字符串
输入,并将其转换为本地的std ::字符串
。我的 MarshalManagedToNative
的实施是一个在黑暗中刺伤。我已经尝试了一些事情,但是这是我最好的猜测:
The custom marshaler specified here, clr_wrapper.string_marshaler
, is an ICustomMarshaler
in a C++/CLI project, and is intended to take the System::String
input and convert it to the native std::string
. My MarshalManagedToNative
implementation is a stab in the dark. I've tried a few things, but this is my best guess:
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
。
我在做什么错了,或者是这整个创业不周?
What am I doing wrong, or is this entire venture ill-conceived?
先编辑,完整清单
的 1 。 C#控制台应用程序的
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 <string>
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 <string>
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;
}
末首先修改
推荐答案
如果你可以用相同的编译器版本构建C ++ / CLI DLL,包装类成员对齐,调用约定,CRT联动,库选项像_ITERATOR_DEBUG_LEVEL,调试/发布配置等与本地DLL,那么你可以的传递一个STL类在DLL边界。像这样的包装功能可以工作:
If you can build the C++/CLI dll with the exact same compiler version, packing, class member alignment, calling convention, CRT linkage, library options like _ITERATOR_DEBUG_LEVEL, debug/release configuration etc with the native DLL, then you can pass an STL class over the DLL boundary. and a wrapper function like this may work:
public ref class Wrapper
{
void hello_std_managed(String^ inp, array<Byte>^ buffer)
{
IntPtr inpCopy = Marshal::StringToHGlobalAnsi(inp);
std::string inpNative(static_cast<const char*>(inpCopy.ToPointer()));
pin_ptr<BYTE> nativeBuffer = &buffer[0];
hello_std(inpNative,nativeBuffer);
Marshal::FreeHGlobal(inpCopy);
}
}
不过,因为这是一个很大的中频,您可能希望问DLL的作者的方法的签名更改为原始的C / COM类型,如字符*或BSTR。它实际上是更好的这种方式,DLL是目前耗材的语言,而不考虑或建立配置。
However since this is a big IF, you may want to ask the DLL's author to change the method's signature to primitive C/COM types like char* or BSTR. It is actually better this way, the DLL is now consumable regardless of language or build configuration.
这篇关于自定义封了的PInvoke与标准::字符串的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!