0

一連のコールバック関数を定義する C++ dll があります。この関数は、C++ dll のどこかで呼び出されます。このコールバックを処理するには、反対側でこれらの関数を上書きする必要があります。そのため、C++ dll はエクスポートされた関数を実装し、すべてのコールバック関数の関数ポインタを返します。

C++ コード (その一部)

C++ コードは次のようになります。

// typedefs
typedef int FInt;
typedef const char* FString;

// Pointers to CB functions.
void  (CALLINGCONV *sOutputCB)(FInt pMode, FString pMsg, FString pSys);

一部の関数では、C++ dll はこれを次のように使用します (GOutputLevel も int です)。

void DWindowsOutput::output(GOutputLevel pLevel, const string &pSys, 
  const char *pMsg) 
{
   if (sOutputCB != 0)
    sOutputCB(pLevel, pSys.c_str(), pMsg);
}

アプリケーションの呼び出しでこのコールバックを実装するために、C++ dll は次のように定義された関数をエクスポートします。

long CALLINGCONV dGetCBAddr(const char *pCBName)
{
    ...
    if (!strcmp(pCBName, "fOutputCB"))
      return (long)&sOutputCB;    
}

基本的なこと

呼び出し側では、dll 関数をロードしてマッピングした後、すべてのコールバックを転送関数として宣言し、dGetCBAddr の結果を関数ポインタに割り当てます。その後、Delphi 実装を使用して、すべての関数が dll で呼び出されます。

Delphi (元のコード) では、これは次のようになります。

// type defs
type
  FString = PAnsiChar;
  FInt = Integer;
// callback forward
procedure fOutputCB(pMode: FInt; pSys, pMsg: FString); stdcall; forward;
// initialize GF CallBacks
// NOTE: the dll is loaded and dGetCBAddr is assigned with GetProcAdress!
procedure GF_CB_Initialize;

  procedure loadCB(pAdrPtr: Pointer; const pAdrName: String);
  var
    tPtr: Pointer;
  begin
    tPtr := IFAPI.dGetCBAddr(FString(AnsiString(pAdrName)));
    if Assigned(tPtr) then Pointer(tPtr^) := pAdrPtr;
  end;

begin
  loadCB(@fOutputCB,'fOutputCB');
  ...
end;

// callbacks
procedure fOutputCB(pMode: FInt; pSys, pMsg: FString);
begin
  // do something in delphi with the dll callback
end;

私の問題は次のとおりです。

  1. Pointer(tPtr^) の取得方法 := pAdrPtr; C# で作業するには?
  2. 前方宣言は C# ではサポートされていないので、デリゲートを使用しました。

c# の試行

今私がテストした(そしてグーグル検索によって指示された)C#部分に:

まず、デリゲート関数とこの型のメンバーを定義しました。

[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public delegate void fOutputCB(int pMode, string pSys, string pMsg);
public static fOutputCB mOutputCB; // member to avoid GC cleansup

呼び出す必要があるメソッドは次のとおりです(テストしてください):

private void OutputCB(int pMode, string pSys, string pMsg)
        {
            string tSys = pSys;
            string tMsg = pMsg;
            int tMode = pMode;
        }  

次に、メソッドにロードのものを実装しました。C++ Dll の場合、WinAPI LoadLibrary などを使用しました。ここでは、メンバーを作成し、必要な呼び出しメソッドをパラメーターとして指定し、C++ DLL から関数ポインターの割り当てを割り当てようとします。

mOutputCB = new fOutputCB(OutputCB);
IntPtr tOutputCBPtr = drvGetCBAddr("OutputCB");
if (tOutputCBPtr != null)
  tOutputCBPtr = Marshal.GetFunctionPointerForDelegate(mOutputCB);

drvGetCBAddr は、dGetCBAddr の C# ペンダントです。

すべてが正常にコンパイルおよび実行されますが、コールバックは機能していません。C# 側で 1 つの簡単なステップが欠落していると思います。これまでマネージ コードを使用しようとしましたが、安全でないコードを使用する必要がある可能性があります。

4

1 に答える 1