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

时间:2013-06-12 14:51:05

标签: c# c++ callback marshalling

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

    现在我测试的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 个答案:

答案 0 :(得分:0)

简单地使用新函数指针分配tOutputCBPtr变量将不起作用,您必须将新函数指针值写入drvGetCBAddr返回的“sOutputCB”地址。

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