概述
一、CTPFutrueProvider
https://github.com/fouvy/CTPFutrueProvider
动态链接库:CTPWrapper 项目概述
应用程序向导已为您创建了此 CTPWrapper DLL。
本文件概要介绍组成 CTPWrapper 应用程序的每个文件的内容。
CTPWrapper.vcproj
这是使用应用程序向导生成的 VC++ 项目的主项目文件。
它包含生成该文件的 Visual C++ 的版本信息,以及有关使用应用程序向导选择的平台、配置和项目功能的信息。
CTPWrapper.cpp
这是主 DLL 源文件。
其他标准文件:
StdAfx.h, StdAfx.cpp
这些文件用于生成名为 CTPWrapper.pch 的预编译头 (PCH) 文件和名为 StdAfx.obj 的预编译类型文件。
其他注释:
应用程序向导使用“TODO:”指示应添加或自定义的源代码部分。
二、PInvoke的方式
今天主要讲以下的方式:
http://www.360doc.com/content/14/0211/00/3218170_351502832.shtml
从C#的托管代码中,调用C++的非托管代码,主要是使用PInvoke的方式,但CTP的API接口中存在以下两个特点,使我们无法直接调用
1、各种请求(Req函数)是Api类的成员函数,而不是静态函数
2、各种响应,是需要继承Spi类之后,才能在重写的虚函数(是叫这名字吧。。。)中得到回传数据
针对这两个特点,我使用的方法是
1、新建WIN32项目(项目名:CTPWrapper),将Api类的成员函数扩展成静态函数,如:
Code:
///创建TraderApi
///@param pszFlowPath 存贮订阅信息文件的目录,默认为当前目录
///@return 创建出的UserApi
extern "C" CTPWRAPPER_API void* CreateTraderApi(const char *pszFlowPath)
{
return CThostFtdcTraderApi::CreateFtdcTraderApi(pszFlowPath);
};
///注册前置机网络地址
///@param pszFrontAddress:前置机网络地址。
///@remark 网络地址的格式为:“protocol://ipaddress:port”,如:”tcp://127.0.0.1:17001”。
///@remark “tcp”代表传输协议,“127.0.0.1”代表服务器地址。”17001”代表服务器端口号。
extern "C" CTPWRAPPER_API void TraderRegisterFront(void *instance,char *pszFrontAddress)
{
((CThostFtdcTraderApi *)instance)->RegisterFront(pszFrontAddress);
};
其中的CTPWRAPPER_API 是
Code:
#define CTPWRAPPER_API __declspec(dllexport)
就像这样,实现了两个标准的WIN32API,将其编译成DLL,从C#中就可以直接调用了。
C#中定义PInvoke方法如下
Code:
[DllImport("CTPWrapper.dll")]
internal static extern IntPtr CreateTraderApi(string pszFlowPath);
[DllImport("CTPWrapper.dll")]
internal static extern void TraderRegisterFront(IntPtr hTrader, String address);
在C#中创建并使用TraderApi
Code:
IntPtr _instance = CreateTraderApi("");
TraderRegisterSpi(_instance, this._listener.Instance);
2、使用函数指针从C++的函数中调用C#的方法
为保证C++的函数能够正确的调用C#的方法,函数指针的构成必须一致,因此分别在C++和C#中定义以下内容:
Code:
C++
///登录请求响应
typedef void (WINAPI *OnRspUserLoginCallback)(CThostFtdcRspUserLoginField *pRspUserLogin, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast) ;
C#
Code:
public delegate void UserLoginCallback(IntPtr pRspUserLogin, IntPtr pRspInfo, int nRequestID, [MarshalAs(UnmanagedType.U1)]bool bIsLast);
(这里注意下bool类型的定义,在这里我被郁闷了好久:()
然后在创建Spi类的时候将C#的回调函数指针作为参数传到C++中
Code:
C++中Spi的构造函数
CTPTraderSpi::CTPTraderSpi(OnRspUserLoginCallback callback)
{
this->m_OnRspUserLoginCallback = callback;
}
同1的方法,将构造函数扩展为WIN32API
Code:
extern "C" CTPWRAPPER_API void* WINAPI CreateTraderSpiClass(OnRspUserLoginCallback callback)
{
CTPTraderSpi *spi = new CTPTraderSpi(callback);
return (void*)spi;
}
这样,在C++中得到响应时,就可以调用C#的方法了
Code:
///登录请求响应
void CTPTraderSpi::OnRspUserLogin(CThostFtdcRspUserLoginField *pRspUserLogin, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
{
if(this->m_OnRspUserLoginCallback)
{
(this->m_OnRspUserLoginCallback)(pRspUserLogin ,pRspInfo,nRequestID,bIsLast);
}
}
在C#中,这样使用
Code:
UserLoginCallback userLoginCallback = new UserLoginCallback(this.OnUserLogin);
_instance = CreateTraderSpiClass(userLoginCallback);
OK,到这里,最最核心的东西已经说完了,剩下的都是些技巧和体力活了
比如Spi的回调函数有很多,总不可能在C#中创建好了再一个个传到C++里,怎办呢?其实我是用了个Struct,包好了一起传的
Code:
/// <summary>
/// 回调函数指针结构
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct CTPTraderSpiCallbackStruct
{
///当客户端与交易后台建立起通信连接时(还未登录前),该方法被调用。
internal FrontConnectedCallback FrontConnectedCallback;
///当客户端与交易后台通信连接断开时,该方法被调用。当发生这个情况后,API会自动重新连接,客户端可不做处理。
///@param nReason 错误原因
///
0x1001 网络读失败
///
0x1002 网络写失败
///
0x2001 接收心跳超时
///
0x2002 发送心跳失败
///
0x2003 收到错误报文
internal FrontDisconnectedCallback FrontDisconnectedCallback;
///心跳超时警告。当长时间未收到报文时,该方法被调用。
///@param nTimeLapse 距离上次接收报文的时间
internal HeartBeatWarningCallback HeartBeatWarningCallback;
///登录请求响应
internal UserLoginCallback UserLoginCallback;
///登出请求响应
internal UserLogoutCallback UserLogoutCallback;
}
以下略
剩下还有一些细节问题,回头再慢慢说好了,整个过程被我封装成了两个工程
C++:CTPWrapper
C#:CTPInterop
由于还有很多工作没有完成,也就不发布出来了,有兴趣的朋友可以通过邮件和我联系,我把工程代码发给你
mail:lumen.xh@gmail.com
当然,如果有朋友愿意和我一起把剩下的部分建立起来的话,那就更好了:D
最后
以上就是狂野翅膀为你收集整理的(转)C#封装CTP的全部内容,希望文章能够帮你解决(转)C#封装CTP所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复