2013-06-12 140 views
0

我有一个C++ DLL,它定义了一组回调函数的处理。这个函数在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 funcs中,所有的回调函数声明为转发的功能,然后我们分配给函数指针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; 

我的问题是:

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

+0

我完全不理解你的逻辑,但看起来这两个字符串不一样:''fOutputCB“'和''OutputCB”'。这可能是一个原因吗? –

+0

“不工作”是一种无望的诊断,神秘的“FString”以及“FInt”如何变成“GOutputLevel”也无济于事。您需要执行测试来调试代码。在您的C#项目中启用非托管调试,以便您可以在本机代码中设置断点以找出发生了什么问题。 –

+0

好吧,我忘了typedefs,我编辑了原始帖子。 –

回答

0

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

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