天天看點

(轉)C#封裝CTP

一、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:[email protected]

當然,如果有朋友願意和我一起把剩下的部分建立起來的話,那就更好了:D