면책 조항 : C 내가 서명에 표준 : : 문자열을 가진 C++ DLL에 PInvoke를 사용하려고 해요사용자 지정 마샬 : 문자열
++/CLI 멍청한 놈 질문입니다. 지금 막 테스트하고있다. 내 목표는 네이티브 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 = "[email protected]@[email protected][email protected]@[email protected]@[email protected]@[email protected]@[email protected]@@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 = "[email protected]@[email protected][email protected]@[email protected]@[email protected]@[email protected]@[email protected]@[email protected]", 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'과 같은 레이아웃이 없을 수도 있습니다. –
그것은 용감한 노력이며 아무것도 잘못 소리 지르지 않습니다. 그러나 interop with std :: string 및 friends는 매우 어렵습니다. C++/CLI 어셈블리와 C++ DLL이 정확히 동일한 공유 CRT 버전을 사용하는 경우에만 작동합니다. 우리 자신을 시험해보기에 충분한 코드가 아닙니다. –
C++ 코드를 변경하거나 그 코드를 사용자에게 제공 할 수 있습니까? @Hans Passant의 의견에 비추어 볼 때 문자 배열을 사용하는 것이 더 쉬울 수도 있습니다. – chessofnerd