7

C dll へのラッパーを作成しようとしていますが、コールバック関数を受け取り、オブジェクトをポインターとして受け取り、返される関数を呼び出そうとしています。

.h ファイルのデラレス

extern int SetErrorHandler(void (*handler) (int, const char*, void*),
                              void *data_ptr);

ハンドラーは、エラーが発生したときに呼び出されるコールバック関数であり、data_ptr は、これ (現在のオブジェクト) になる私のアプリの場合、あなたに返される任意のオブジェクト (状態) です。

単純な型の文字列、int などのマーシャリングされた定数型を使用する dll で関数を呼び出すことができます。しかし、状態である C# オブジェクトへのポインターをマーシャリングする方法がわかりません。

ここで検索して見つけたものからオブジェクト参照をC関数に渡すには、関数にマーシャリングできるように構造体型が必要なようですので、オブジェクトを保持する構造体を作成しました:

[StructLayout(LayoutKind.Sequential)]
struct MyObjectState
{
   public object state;
}

[MarshalAs(UnmanagedType.Struct, SizeConst = 4)]編集: プロパティに attribute:を配置しようとしましたpublic object stateが、これにより同じエラーが発生するため、削除しましたが、とにかく機能しないようです。

構造体には、コールバック関数の任意のオブジェクトを保持する単一のオブジェクト プロパティが含まれています。

次のように C# でデリゲートを宣言しました。

delegate void ErrorHandler(int errorCode, IntPtr name, IntPtr data);

次に、C# でインポート関数を次のように宣言しました。

[DllImport("somedll.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int SetErrorHandler handler, IntPtr data);

次に、C# コードでコールバック関数を作成しました。

void MyErrorHandler(int errorCode, IntPtr name, IntPtr data)
{
    var strName = Marshal.PtrToStringAnsi(name);
    var state = new MyObjectState();
    Marshal.PtrToStructure(data, state);
    Console.WriteLine(strName);
}

次のようにライブラリ関数を呼び出すことができます。

var state = new MyObjectState()
{
    state = this
};
IntPtr pStruct = Marshal.AllocHGlobal(Marshal.SizeOf(state));
Marshal.StructureToPtr(state, pStruct, true);
int ret = SetErrorHandler(MyErrorHandler, pStruct);

呼び出しは機能し、コールバック関数が呼び出されますが、コールバック関数のデータにアクセスできず、試行Marshal.PtrToStructureするとエラーが発生します:

構造体は値クラスであってはなりません。

私はここで多くの検索を行い、マーシャルとボイド*でさまざまなものを見つけましたが、これを機能させるのに役立つものは何もありませんでした

ありがとう。

4

2 に答える 2

3

これを必要以上に複雑にしています。C# デリゲートには既にポインターdata_ptrを維持するためのメカニズムが組み込まれているため、C # クライアントはパラメーターを使用する必要はありません。this

IntPtr.Zeroしたがって、単にデリゲートに渡すことができます。エラー ハンドラー デリゲート内では、使用可能になるdata_ptrため、値を無視するだけthisです。

この説明に従わない場合のために、ここに私の意味を説明する短いプログラムを示します。MyErrorHandlerエラー ハンドラーとして機能し、インスタンス データを変更できるインスタンス メソッドに注意してください。

class Wrapper
{
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    delegate void ErrorHandler(int errorCode, string name, IntPtr data);

    [DllImport("somedll.dll", CallingConvention = CallingConvention.Cdecl)]
    static extern int SetErrorHandler(ErrorHandler handler, IntPtr data);

    void MyErrorHandler(int errorCode, string name, IntPtr data)
    {
        lastError = errorCode;
        lastErrorName = name;
    }

    public Wrapper()
    {
        SetErrorHandler(MyErrorHandler, IntPtr.Zero);
    }            

    public int lastError { get; set; }
    public string lastErrorName { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        Wrapper wrapper = new Wrapper();
    }
}
于 2012-04-12T07:33:57.993 に答える