5

C# 関数と同等の C ライブラリにコールバック関数を設定する必要がありますが、dlopen または kernel32 なしではそれを行う方法がわかりません。これは、windows/unix 固有のようです。これを行う方法を知っている人はいますか?

問題: C 共有ライブラリは関数ポインタを公開しており、その値は上書きして設定する必要があります。例えば

//C Code
extern void (*ptr_R_ShowMessage) (const char *)

現在の c# コードは、この署名に一致する関数へのデリゲートを作成し、マーシャル クラスを使用してそのデリゲートへのポインターを取得し、C ポインターをこの値で上書きします。

//call to libdl or kernel32.dll 
IntPtr showPointer = GetFunctionAddress(hndl,"ptr_R_ShowMessage");
IntPtr newShowPointer = Marshal.GetFunctionPointerForDelegate(matchingDelegate);
Marshal.WriteIntPtr(showPointer, newShowPointer);

libdl と kernel32.dll の要件はあらゆる種類の問題を引き起こします...そして理想的には回避されるでしょう。

Cコードを変更したり、GetFunctionAddress動的ロードを使用したりせずに、CライブラリポインターがC#コードを指すようにする方法を知っている人はいますか? これはおそらく不可能だと思いますが、可能性のように思えます。

4

3 に答える 3

5

これがあなたが探しているものかどうかは 100% わかりません。

私たちの場合、コールバック メッセージ用のいくつかのフックを提供するサード パーティの C Sdk があります。コールバック呼び出しは C コードで開始され、C 関数を呼び出すことを想定していますが、次の手順を使用して C# メソッドにフックすることができます。(このコードは古いものであり、C# の新しいイテレーションで同じことを達成するためのより簡潔で最新の方法がある可能性があります)。

最初に、C# クラス内でCallback delegateを宣言します。これは、SDK が想定している C 関数ポインターのシグネチャと互換性があります。

たとえば、C コードが次のシグネチャを持つ関数をコールバックすることを想定している場合:

void DecCallBack(int nPort, void* pBuf, int nSize, FRAME_INFO *pInfo, int nReserved1, int reserved 2);

対応する C# デリゲートの定義を次に示します。UnmanagedFunctionPointer属性のおかげで「魔法」が発生することに注意してください。

[UnmanagedFunctionPointer(CallingConvention.StdCall)]
delegate void DecCallBack(int nPort, IntPtr pBuf, int nSize, ref FRAME_INFO frameInfo, int nReserved1, int nReserved2);

これを取得したら、クラスでDecCallBack デリゲート インスタンスを保持する型のクラス メンバーを宣言します。

static DecCallBack _decodeCallBack = null;

次に、デリゲート シグネチャと互換性のあるハンドラー メソッドをコーディングします。この場合、名前を付けますHandleDecData()

private static void HandleDecData(int nPort, IntPtr pBuf, int nSize, ref FRAME_INFO frameInfo, int nReserved1, int nReserved2) {

   // Here we handle the callback,  this code is being called from an external C library

}       

次に、C# クラスのどこかで、デリゲート メンバーを初期化し、コールバック Handler をフックする必要があります (この場合は静的メソッドです)。HandleDecData()

_decodeCallBack += new DecCallBack(HandleDecData);

最後に、C への関数ポインターであるかのようにデリゲート メンバーを渡します。この場合、サード パーティの SDK にはPlayM4_SetDecCallBack()、コールバックが呼び出されたときに関数ポインターが呼び出されることを期待する C 関数があります。デリゲート メンバーを安全に渡すことができます。代わりに私たちのクラス。

 if( !PlayM4_SetDecCallBack(channel, _decodeCallBack) ) 
  throw new InvalidOperationException("Error setting DecCallBack");

これがお役に立てば幸いです。

于 2013-10-22T21:46:28.717 に答える
1

C共有ライブラリはそのように関数ポインタを公開しません。externは、この関数が別のCファイルで定義されていることを意味し、バイナリオブジェクトの作成中にリンカーは最終的な実行可能ファイルの関数から実際のコードを使用します

これを修正するには、Cコンパイラが両方をバイナリオブジェクトにリンクするように、C#ファイルをCプロジェクトに何らかの形で含める必要がありますが、それは不可能です

現在のコードがしようとしていることは「ハック」と呼ばれます。必要なハックなしで同様の機能を実現したい場合:

  1. Cコードでは、実行する関数へのポインタを受け入れる関数を提供します
  2. ptr_R_ShowMessage が呼び出される C コードでは、p1 からのポインターを使用します。
  3. C# コードでは、P1 から関数を呼び出し、引数として呼び出すメソッドを提供します

外部ライブラリから関数を実行し、C# 関数をコールバックとして提供するサンプルを次に示します。

于 2013-10-22T21:17:26.577 に答える
-2

これは C# では直接できないことを確認したので、libary の load 関数とのインターフェイスが必要です。答えてくれた両方に感謝しますが、間違いなく役に立ちます。

于 2013-10-30T17:09:29.240 に答える