使用 C++ dll 进行特殊回调处理

Posted

技术标签:

【中文标题】使用 C++ dll 进行特殊回调处理【英文标题】:Special callback handling with C++ dll 【发布时间】:2013-06-12 14:51:05 【问题描述】:

我有一个 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 的结果分配给函数指针。 之后,在 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;

我的问题是:

    如何获得指针(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# 方面缺少一个简单的步骤。到目前为止,我尝试使用托管代码,但可能是 我必须使用不安全的代码。

【问题讨论】:

我不完全理解你的逻辑,但看起来这两个字符串不一样:"fOutputCB""OutputCB"。这可能是一个原因吗? “不工作”是一个绝望的诊断,神秘的“FString”以及“FInt”如何变成“GOutputLevel”也无济于事。您需要做一些工作来调试您的代码。在您的 C# 项目中启用非托管调试,以便您可以在本机代码中设置断点以找出问题所在。 好吧,我忘记了 typedef,我编辑了原帖。 【参考方案1】:

简单地将新函数指针赋值给tOutputCBPtr变量是行不通的,必须将新函数指针值写入drvGetCBAddr返回的“sOutputCB”地址。

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

【讨论】:

以上是关于使用 C++ dll 进行特殊回调处理的主要内容,如果未能解决你的问题,请参考以下文章

C++ 到 C# 回调结构编组

将带有参数的 c# 回调方法传递给 c++ dll 会导致 System.ExecutionEngineException

C++ dll 回调C# 程序总结

将 python + numpy 代码嵌入到 C++ dll 回调中

从 C++ COM DLL 回调到 C# 应用程序

python ctypes,回调函数增广类型为C++参考