0

免責事項: 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_marshalerICustomMarshaler、C++/CLI プロジェクト内にあり、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;
}

最初の編集を終了

4

1 に答える 1

1

ネイティブ DLL とまったく同じコンパイラ バージョン、パッキング、クラス メンバーの配置、呼び出し規約、CRT リンケージ、_ITERATOR_DEBUG_LEVEL などのライブラリ オプション、デバッグ/リリース構成などを使用して C++/CLI dll をビルドできる場合は、STL クラスを渡すことができます。 DLL 境界を越えて。そして、このようなラッパー関数が機能する可能性があります:

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

ただし、これは大きな IF であるため、DLL の作成者にメソッドのシグネチャを char* や BSTR などのプリミティブな C/COM 型に変更するよう依頼することをお勧めします。実際にはこの方法の方が優れており、言語やビルド構成に関係なく DLL を使用できるようになりました。

于 2013-06-14T03:04:38.190 に答える