参考: C#调用C++动态库一些重点问题
具体内容,包括C#回掉函数的处理,见上面链接的文章
具体内容,包括C#回掉函数的处理,见上面链接的文章
解决方案 »
- 有没有不要dll文件就可以在Winform里添加音乐的方法
- 接分啦,指教、帮顶都有分!一个简单是问题,Mapx如何改变鼠标模式?
- 主窗体按钮操作子窗体方法
- 各位大虾,帮我看看下面那个程序里那个for循环是什么意思??
- 怎么在同一个 socket 里实现多个用户同时与 服务端 交互 C#
- 谁有用c# 利用Opengl 绘制基本图形操作的例子吗?送200
- WinFrom 求个使用Timer控件的小例子
- 麻烦兄弟们看看,这个功能怎么实现!~
- 請高手進來幫個忙!!非一般的人能做得到!!
- 批量添加uniqueidentifier类型的数据
- 关于使用MEF动态装载后,想实例化自己想要的实例问题
- 单例模式失败
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);
};
//其他操作
}
}
}
感谢你的讲解和贴心的代码demo。我看见你把RspErrorCallback作为了CThostFtdcMdSpiImpl构造函数的参数,但是我有很多个OnXxx回调函数,把函数指针都放进构造函数的参数是不是不太好啊
感谢你的讲解和贴心的代码demo。我看见你把RspErrorCallback作为了CThostFtdcMdSpiImpl构造函数的参数,但是我有很多个OnXxx回调函数,把函数指针都放进构造函数的参数是不是不太好啊那你可以换个思路,就是用静态函数。声明一个托管的静态函数,接受引发回调的非托管类的指针、事件名或者ID(比如用enum)、其他参数(可以使用void*或者...),在函数方法体中引发对应的托管事件,解析并传递对应的参数。
感谢你的讲解和贴心的代码demo。我看见你把RspErrorCallback作为了CThostFtdcMdSpiImpl构造函数的参数,但是我有很多个OnXxx回调函数,把函数指针都放进构造函数的参数是不是不太好啊那你可以换个思路,就是用静态函数。声明一个托管的静态函数,接受引发回调的非托管类的指针、事件名或者ID(比如用enum)、其他参数(可以使用void*或者...),在函数方法体中引发对应的托管事件,解析并传递对应的参数。因为我不是程序员,编程知识很有限,只能达到模仿的程度,所以可否麻烦你写出简单的示例代码,十分感谢!
感谢你的讲解和贴心的代码demo。我看见你把RspErrorCallback作为了CThostFtdcMdSpiImpl构造函数的参数,但是我有很多个OnXxx回调函数,把函数指针都放进构造函数的参数是不是不太好啊那你可以换个思路,就是用静态函数。声明一个托管的静态函数,接受引发回调的非托管类的指针、事件名或者ID(比如用enum)、其他参数(可以使用void*或者...),在函数方法体中引发对应的托管事件,解析并传递对应的参数。是不是在托管类中定义静态方法,静态方法接受来自非托管类的参数,再在静态方法中引发事件?我摸索了一下,发现这样并不行,因为在静态方法中无法使用this指针,就无法使用OnXxx(this, gcnew XxxEventArgs(...))来引发事件。
感谢你的讲解和贴心的代码demo。我看见你把RspErrorCallback作为了CThostFtdcMdSpiImpl构造函数的参数,但是我有很多个OnXxx回调函数,把函数指针都放进构造函数的参数是不是不太好啊那你可以换个思路,就是用静态函数。声明一个托管的静态函数,接受引发回调的非托管类的指针、事件名或者ID(比如用enum)、其他参数(可以使用void*或者...),在函数方法体中引发对应的托管事件,解析并传递对应的参数。是不是在托管类中定义静态方法,静态方法接受来自非托管类的参数,再在静态方法中引发事件?我摸索了一下,发现这样并不行,因为在静态方法中无法使用this指针,就无法使用OnXxx(this, gcnew XxxEventArgs(...))来引发事件。
你的静态方法应当接受一个类似 CThostFtdcMdSpi* sender 的参数,而在引发事件时应当通过这个参数找到或者创建对应的托管类包装,并将这个托管包装类作为sender参数传递给事件。
感谢回复,不过我还是没能理解。我现在采取这样的办法,不知道是否可行:定义非托管类CMdSpi继承CThostFtdcMdSpiImpl,在其中存放一个gcroot<MdSpiWrapper^>,然后在构造函数中传入MdSpiWrapper的实例。在非托管函数OnRspError中调用托管函数RaiseRspErrorEvent。class CMdSpi : public CThostFtdcMdSpi
{
private:
gcroot<MdSpiWrapper^> m_hMdSpi;public:
CMdSpi(MdSpi ^mdSpi)
{
m_hMdSpi = mdSpi;
} virtual void OnRspError(CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
{
m_hMdSpi->RaiseRspErrorEvent(pRspInfo, nRequestID, bIsLast);
}
}
定义托管类MdSpiWrapper,在其中存放CMdSpi的指针。public ref class MdSpiWrapper
{
private:
CMdSpi *m_pCMdSpi;public:
event EventHandler<RspErrorEventArgs^> ^OnRspError;
void RaiseRspErrorEvent(CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
{
OnRspError(this, gcnew RspErrorEventArgs(pRspInfo, nRequestID, bIsLast));
}
}
其中,RspErrorEventArgs定义如下:public ref class RspErrorEventArgs : public EventArgs
{
public:
ThostFtdcRspInfoField ^RspInfo;
int RequestID;
bool IsLast;
RspErrorEventArgs(CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
{
RspInfo->ErrorID = pRspInfo->ErrorID;
RspInfo->ErrorMsg = gcnew String(pRspInfo->ErrorMsg);
RequestID = nRequestID;
IsLast = bIsLast;
}
};
其中,ThostFtdcRspInfoField定义如下:public value struct ThostFtdcRspInfoField
{
int ErrorID;
[MarshalAs(UnmanagedType::ByValTStr, SizeConst = 81)]
String ^ErrorMsg;
};我现在不知道这样做到底行不行,因为我无法编译通过,而编译错误让我无法理解。编译器提示的错误是:
error C2143: syntax error : missing ';' before '^'
出错位置在RspErrorEventArgs中定义ThostFtdcRspInfoField ^RspInfo的位置。大侠能不能看出为什么编译出错?
更正一下:
定义非托管类CMdSpi继承CThostFtdcMdSpi,在其中存放一个gcroot<MdSpiWrapper^>,然后在构造函数中传入MdSpiWrapper的实例。在非托管函数OnRspError中调用托管函数RaiseRspErrorEvent。class CMdSpi : public CThostFtdcMdSpi
{
private:
gcroot<MdSpiWrapper^> m_hMdSpi;public:
CMdSpi(MdSpiWrapper ^mdSpi)
{
m_hMdSpi = mdSpi;
} virtual void OnRspError(CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
{
m_hMdSpi->RaiseRspErrorEvent(pRspInfo, nRequestID, bIsLast);
}
}