c#调用c++ 回调

场景:怎么封装C++的中的回调函数供C#调用

求助:如何封装C++的中的回调函数供C#调用
我现在有一个C++写的dll文件,以及和它相关的.h文件,没有.cpp文件。我的问题描述如下:

.h中定义了一个unmanaged class


class CThostFtdcMdSpi
{
  virtual void OnRspError(CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast) {};
}


其中CThostFtdcRspInfoField定义为:

struct CThostFtdcRspInfoField
{
  int ErrorID;
  char ErrorMsg[81];
};


还有个专门的方法注册派生自回调接口类的实例

virtual void RegisterSpi(CThostFtdcMdSpi *pSpi) = 0;


上面那个CThostFtdcMdSpi是所谓回调接口类,OnRspError是回调函数。调用这个回调函数的函数指针在dll内部实现,我没有源代码。

现在我想用C++/CLI把这个dll封装一下,供C#程序调用。但是不知道如何处理这个回调函数。我想最好是能把这个回调函数转换成.net中的事件。还望各位大神指导,谢谢。

我本人不是专门搞程序的,为了方便自己的工作才想这样搞一下,请见谅。



------解决方案--------------------
参考: C#调用C++动态库一些重点问题
引用
C#中上面的回调函数只支持stdcall,C++库默认是cdecl,
一般简单处理办法是将C++库的回调接口修饰为_stdcall

具体内容,包括C#回掉函数的处理,见上面链接的文章
------解决方案--------------------
有点奇怪,如果单纯的要注册回调的话,用函数指针就可以了,特地用一个接口类来实现回调不像是C/C++的实现方式,反而更接近Java。

抛开疑问,要使用这样的回调需要:
1.继承这个接口类,重写OnRspError方法。
    由于在C++/CLI中托管类不能继承非托管类,必须由一个非托管类(假设为CThostFtdcMdSpiImpl)继承CThostFtdcMdSpi。
2.注册回调时,传递一个CThostFtdcMdSpiImpl实例。
    注意对象的生存期管理。

非托管类中不能储存托管句柄,但可以调用静态托管函数,或者使用Marshal类提供的GetFunctionPointerForDelegate获取并储存一个委托对应的函数指针。在OnRspError方法的实现中,你可以通过调用静态托管函数来间接引发托管事件,或者使用函数指针。

参考实现(需要dll对应的lib文件方能通过链接,假定RegisterSpi是一个全局函数):
#include "dll.h"

using namespace System;
using namespace System::Runtime::InteropServices;

typedef void (*RspErrorCallback)(CThostFtdcRspInfoField*, int, bool);

namespace CppInvoke
{
public ref class RspErrorEventArgs : public EventArgs
{
public:
int ErrorID;
String^ ErrorMsg;
int RequestID;
bool IsLast;
RspErrorEventArgs(CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
{
this->ErrorID = pRspInfo->ErrorID;
this->ErrorMsg = gcnew String(pRspInfo->ErrorMsg);
this->RequestID = nRequestID;
this->IsLast = bIsLast;
}
};

class CThostFtdcMdSpiImpl : public CThostFtdcMdSpi
{
public:
CThostFtdcMdSpiImpl(RspErrorCallback ecb)
{
this->RaiseRspError = ecb;
}
virtual void OnRspError(CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
{
this->RaiseRspError(pRspInfo, nRequestID, bIsLast);
}
void Test(void)
{
Console::WriteLine("Test");
}
private:
RspErrorCallback RaiseRspError;
};

delegate void RspErrorDel(CThostFtdcRspInfoField*, int, bool);

public ref class CThostFtdcMdSpiWrapper
{
private:
CThostFtdcMdSpiImpl* nestImplClass;
public:
CThostFtdcMdSpiWrapper()
{
this->nestImplClass = new CThostFtdcMdSpiImpl(
(RspErrorCallback)Marshal::GetFunctionPointerForDelegate(
gcnew RspErrorDel
(
this,
&CppInvoke::CThostFtdcMdSpiWrapper::RaiseRspError
)
).ToPointer()
);
}
~CThostFtdcMdSpiWrapper()
{
delete nestImplClass;
}
event EventHandler<RspErrorEventArgs^>^ RspError;
void RaiseRspError(CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
{
RspError(this, gcnew RspErrorEventArgs(pRspInfo, nRequestID, bIsLast));
}
static void RegisterSpi(CThostFtdcMdSpiWrapper^ spi)
{
::RegisterSpi(spi->nestImplClass);
}
};
}

C#调用:
using System;
using CppInvoke;

class Program
{
static void Main(string[] args)
{
using (var c = new CThostFtdcMdSpiWrapper())
{
CThostFtdcMdSpiWrapper.RegisterSpi(c);
c.RspError += (_, e) =>
{
Console.WriteLine("ErrorId = {0}, ErrorMsg = \"{1}\", RequestId = {2}, IsLast = {3}", e.ErrorID, e.ErrorMsg, e.RequestID, e.IsLast);
};
//其他操作
}
}
}