2013-06-14 2 views
0

면책 조항 : 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; 
} 

종료 먼저 편집

+0

사실 C# 사람은 아니지만 값으로 'std :: string'을 사용하는 함수에 문자열에 대한 포인터를 전달하는 것처럼 보입니다. C#의'std : string'에는 C++의'std :: string'과 같은 레이아웃이 없을 수도 있습니다. –

+0

그것은 용감한 노력이며 아무것도 잘못 소리 지르지 않습니다. 그러나 interop with std :: string 및 friends는 매우 어렵습니다. C++/CLI 어셈블리와 C++ DLL이 정확히 동일한 공유 CRT 버전을 사용하는 경우에만 작동합니다. 우리 자신을 시험해보기에 충분한 코드가 아닙니다. –

+0

C++ 코드를 변경하거나 그 코드를 사용자에게 제공 할 수 있습니까? @Hans Passant의 의견에 비추어 볼 때 문자 배열을 사용하는 것이 더 쉬울 수도 있습니다. – chessofnerd

답변

1

당신이 C++/CLI가 동일한 컴파일러 버전 DLL을 빌드 할 수있는 경우, 포장, 클래스 멤버 정렬, 호출 규칙, CRT 연계, _ITERATOR_DEBUG_LEVEL과 같은 라이브러리 옵션, 기본 DLL과 함께 디버그/릴리스 구성 등을 사용하면 pass an STL class over the DLL boundary을 사용할 수 있습니다.이 같은 래퍼 기능이 작동 할 수 있습니다

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); 
    } 
} 

을 그러나이 큰 경우이기 때문에, 당신은 숯불 * 또는 BSTR 같은 원시적 인 C/COM 유형에 메소드의 서명을 변경하려면 DLL의 저자에게 문의 할 수 있습니다. 실제로이 방법이 더 좋기 때문에 언어 또는 빌드 구성에 관계없이 DLL을 사용할 수 있습니다.

+0

답변과 유용한 링크에 감사드립니다. 이 접근법은 네이티브 라이브러리의 헤더 파일 또는 .lib 정적 라이브러리에 대해 구축해야합니다. 내 일을 위해서, 이것은 내 후퇴 일 것이고, 그것은 훌륭한 해결책이다. 하지만 내 취향은 직접 PInvoke를 통해 DLL을 사용하는 것입니다, 내 질문은 정말이 할 수 있습니다. – McGarnagle

+0

당신이 2 개의 커스텀 마셜 러 (매개 변수 당 하나의 클래스, 첫 번째는 문자열의 왕복을 처리하고 두 번째 것은 바이트 배열의 왕복을 처리)에서이 코드를 마무리한다면 명시 적으로 pinvoke를 통해 가능합니다. 이 때 컴파일러는 암시 적 pinvoke를 수행하도록 설계되었습니다? –

+0

질문에 - 빌드 복잡성 + 빌드 시간 문제입니다. C# 프로젝트의 명시 적 Pinvoke를 사용하면 프로젝트 출력에 DLL을 놓을 수 있습니다. 암시 적 Pinvoke를 사용하여, 모든 종속 항목과 함께 네이티브 C++ 소스에 대해 빌드하는 다른 래퍼 C++/CLI 프로젝트를 추가해야합니다. – McGarnagle

관련 문제