【发布时间】:2013-06-10 13:53:44
【问题描述】:
免责声明:C++/CLI 新手问题
我正在尝试在签名中有 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# 控制台应用程序
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# 人,但看起来你正在将一个指向字符串的指针传递给一个函数,该函数按值接受
std::string。另外,C# 中的std:string可能与 C++ 中的std::string布局不同。 -
这是一项英勇的努力,没有任何错误。但是与 std::string 和朋友的互操作是出了名的困难,只有当您的 C++/CLI 程序集和您的 C++ DLL 使用完全相同的共享 CRT 版本时,它才能工作。这里没有足够的代码来自己尝试。
-
你能修改 C++ 代码还是给你的?根据@Hans Passant 的评论,使用字符数组可能更容易?
-
第一步:拍摄DLL的作者
-
@HansPassant 谢谢 - 我已经用完整的代码清单更新了我的问题。