c++ dll的特殊回调处理

本文关键字:回调 处理 dll c++ | 更新日期: 2023-09-27 18:18:07

我有一个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函数之后,所有回调函数都声明为转发函数,然后将getcbaddr的结果赋值给函数指针。之后,所有的函数都在dll中调用,使用delphi实现。

在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. 如何获取指针(tPtr^):= pAdrPtr;用c#工作?
  2. c#中不支持前向声明,所以我使用委托。

现在到我测试的c#部分(由google搜索指导):

首先我定义了一个委托函数和这个类型的成员。

[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是getcbaddr的c#附属项:

所有编译和运行都很好,但回调不起作用。我想c#方面缺少了一个简单的步骤。到目前为止,我尝试使用托管代码,但可能是我必须使用不安全的代码

c++ dll的特殊回调处理

简单地用新的函数指针赋值tOutputCBPtr变量是行不通的,你必须将新的函数指针值写入由drvGetCBAddr返回的"sOutputCB"的地址

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