一連のコールバック関数を定義する 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;
私の問題は次のとおりです。
- Pointer(tPtr^) の取得方法 := pAdrPtr; C# で作業するには?
- 前方宣言は 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 つの簡単なステップが欠落していると思います。これまでマネージ コードを使用しようとしましたが、安全でないコードを使用する必要がある可能性があります。