C#調用行情接口API
很早就已經通過了C#調用行情接口API的實現(見以前的日志),但是還是有人沒有搞明白,在這里就講講如何實現的。
首先,根據提供的stockdrv.h的信息,將數據結構移植到C#代碼中。
1.數據結構定義
// <summary>
// stockdrv.dll 的摘要說明。
// </summary>
//
// stockdrv.dll 的摘要說明。
// </summary>
//
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct tagSTOCK_STRUCTEx
{
public byte m_type; // stock's type, see enum StockType
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public char[] m_code; // stock code
}
public struct tagSTOCK_STRUCTEx
{
public byte m_type; // stock's type, see enum StockType
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public char[] m_code; // stock code
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct tagRCV_REPORT_STRUCTEx
{
public UInt16 m_cbSize; // 結構大小
public Int32 m_time; // 交易時間
public UInt16 m_wMarket; // 股票市場類型
//m_szLabel,m_szName 的定義根據自己喜好可以定義成
public struct tagRCV_REPORT_STRUCTEx
{
public UInt16 m_cbSize; // 結構大小
public Int32 m_time; // 交易時間
public UInt16 m_wMarket; // 股票市場類型
//m_szLabel,m_szName 的定義根據自己喜好可以定義成
[MarshalAs( UnmanagedType.ByValArray, SizeConst=10)]
public char[] m_szLabel; // 代碼,以'\0'結尾 數組大小為STKLABEL_LEN,在c++描述中為char[10]
[MarshalAs( UnmanagedType.ByValArray, SizeConst=32)]
public char[] m_szName; // 名稱,以'\0'結尾 數組大小為STKNAME_LEN,在c++描述中為char[32]
/* 也可以定義成
public char[] m_szLabel; // 代碼,以'\0'結尾 數組大小為STKLABEL_LEN,在c++描述中為char[10]
[MarshalAs( UnmanagedType.ByValArray, SizeConst=32)]
public char[] m_szName; // 名稱,以'\0'結尾 數組大小為STKNAME_LEN,在c++描述中為char[32]
/* 也可以定義成
[MarshalAs(UnmanagedType.ByValTStr,SizeConst = 10)]
public string m_szLabel; // 代碼,以'\0'結尾 數組大小為STKLABEL_LEN,在c++描述中為char[10]
[MarshalAs(UnmanagedType.ByValTStr,SizeConst = 32)]
public string m_szName; // 名稱,以'\0'結尾 組大小為STKNAME_LEN,在c++描述中為char[32]
public string m_szLabel; // 代碼,以'\0'結尾 數組大小為STKLABEL_LEN,在c++描述中為char[10]
[MarshalAs(UnmanagedType.ByValTStr,SizeConst = 32)]
public string m_szName; // 名稱,以'\0'結尾 組大小為STKNAME_LEN,在c++描述中為char[32]
*/
public Single m_fLastClose; // 昨收
public Single m_fOpen; // 今開
public Single m_fHigh; // 最高
public Single m_fLow; // 最低
public Single m_fNewPrice; // 最新
public Single m_fVolume; // 成交量
public Single m_fAmount; // 成交額
public Single m_fOpen; // 今開
public Single m_fHigh; // 最高
public Single m_fLow; // 最低
public Single m_fNewPrice; // 最新
public Single m_fVolume; // 成交量
public Single m_fAmount; // 成交額
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public Single[] m_fBuyPrice; // 申買價1,2,3
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public Single[] m_fBuyVolume; // 申買量1,2,3
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public Single[] m_fSellPrice; // 申賣價1,2,3
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public Single[] m_fSellVolume; // 申賣量1,2,3
public Single[] m_fBuyPrice; // 申買價1,2,3
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public Single[] m_fBuyVolume; // 申買量1,2,3
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public Single[] m_fSellPrice; // 申賣價1,2,3
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public Single[] m_fSellVolume; // 申賣量1,2,3
public Single m_fBuyPrice4; // 申買價4
public Single m_fBuyVolume4; // 申買量4
public Single m_fSellPrice4; // 申賣價4
public Single m_fSellVolume4; // 申賣量4
public Single m_fBuyVolume4; // 申買量4
public Single m_fSellPrice4; // 申賣價4
public Single m_fSellVolume4; // 申賣量4
public Single m_fBuyPrice5; // 申買價5
public Single m_fBuyVolume5; // 申買量5
public Single m_fSellPrice5; // 申賣價5
public Single m_fSellVolume5; // 申賣量5
public Single m_fBuyVolume5; // 申買量5
public Single m_fSellPrice5; // 申賣價5
public Single m_fSellVolume5; // 申賣量5
...... 其他項的定義
};
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct tagRCV_FILE_HEADEx
{
public int m_dwAttrib; // 文件子類型
public int m_dwLen; // 文件長度
public int m_dwSerialNoorTime; //文件序列號或時間.
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 260)]
char[] m_szFileName; // 文件名 or URL
}
public struct tagRCV_FILE_HEADEx
{
public int m_dwAttrib; // 文件子類型
public int m_dwLen; // 文件長度
public int m_dwSerialNoorTime; //文件序列號或時間.
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 260)]
char[] m_szFileName; // 文件名 or URL
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi , Pack=1)]
public struct tagRCV_DATA
{
public int m_wDataType; // 文件類型
public int m_nPacketNum; // 記錄數,參見注一
[MarshalAs(UnmanagedType.Struct)]
public tagRCV_FILE_HEADEx m_File; // 文件接口
public int m_bDISK; // 文件是否已存盤的文件
public IntPtr m_pData;
} ;
public struct tagRCV_DATA
{
public int m_wDataType; // 文件類型
public int m_nPacketNum; // 記錄數,參見注一
[MarshalAs(UnmanagedType.Struct)]
public tagRCV_FILE_HEADEx m_File; // 文件接口
public int m_bDISK; // 文件是否已存盤的文件
public IntPtr m_pData;
} ;
以上的定義關鍵點之一,是說明Pack,如果不說明,在后面的數據處理時,會遇到點問題。
如在C#里定義的數據結構,取得結構長度,與C++的結構長度不一致。
究其原因,就是沒有對齊處理或二者對齊處理方式不一致。
2.API借口定義
public class Stockdrv
{
{
// 宏定義直接根據C++的改寫過來即可
public const int RCV_WORK_SENDMSG = 4; // 工作方式類型定義-窗口消息方式
public const int RCV_MSG_STKDATA = 0x8001; //指定使用的消息
public const int RCV_WORK_SENDMSG = 4; // 工作方式類型定義-窗口消息方式
public const int RCV_MSG_STKDATA = 0x8001; //指定使用的消息
public const int RCV_REPORT = 0x3f001234; //股票行情
public const UInt32 EKE_HEAD_TAG = 0xffffffff; //數據頭結構標記
public const UInt32 EKE_HEAD_TAG = 0xffffffff; //數據頭結構標記
。。。。。。
// 接口函數的定義,根據提供stockdrv.h 按照如下改寫即可
// 偶提供的是UNICODE版本的DLL,如果不是CharSet 改為 CharSet.Ansi 即可
// 顯式說明,可以避免一些不必要的麻煩
[DllImport("stockdrv.dll")]
public static extern int Stock_Init(IntPtr nHwnd, int nMsg, int nWorkMode);
[DllImport(@"stockdrv.dll",CharSet = CharSet.Unicode) ]
public static extern int GetStockByCodeEx (string strCode,int nMarket,ref tagRCV_REPORT_STRUCTEx sRcvReort);
[DllImport("stockdrv.dll")]
public static extern int SetupReceiver(bool bSetup);
[DllImport("stockdrv.dll")]
public static extern int GetTotalNumber();
[DllImport("stockdrv.dll")]
public static extern int Stock_Quit(int hWnd);
[DllImport(@"stockdrv.dll" ,CharSet = CharSet.Unicode)]
public static extern int Stock_Init_Nodialog(IntPtr hWnd, int Msg, int nWorkMode, string szAddress, int nPort, string szUser, string szPasswd,string szSecAddress, int nSecPort,int nAuth );
[DllImport("stockdrv.dll")]
public static extern int IsEngineWorking( );
[DllImport("stockdrv.dll")]
public static extern int SetAutoReport( int bAutoReport );
[DllImport("stockdrv.dll")]
public static extern int RequestStockData(int nDataType, tagSTOCK_STRUCTEx [] pStocks, int nSize, int nKType, int nDataCount);
public static extern int Stock_Init(IntPtr nHwnd, int nMsg, int nWorkMode);
[DllImport(@"stockdrv.dll",CharSet = CharSet.Unicode) ]
public static extern int GetStockByCodeEx (string strCode,int nMarket,ref tagRCV_REPORT_STRUCTEx sRcvReort);
[DllImport("stockdrv.dll")]
public static extern int SetupReceiver(bool bSetup);
[DllImport("stockdrv.dll")]
public static extern int GetTotalNumber();
[DllImport("stockdrv.dll")]
public static extern int Stock_Quit(int hWnd);
[DllImport(@"stockdrv.dll" ,CharSet = CharSet.Unicode)]
public static extern int Stock_Init_Nodialog(IntPtr hWnd, int Msg, int nWorkMode, string szAddress, int nPort, string szUser, string szPasswd,string szSecAddress, int nSecPort,int nAuth );
[DllImport("stockdrv.dll")]
public static extern int IsEngineWorking( );
[DllImport("stockdrv.dll")]
public static extern int SetAutoReport( int bAutoReport );
[DllImport("stockdrv.dll")]
public static extern int RequestStockData(int nDataType, tagSTOCK_STRUCTEx [] pStocks, int nSize, int nKType, int nDataCount);
。。。。。。。。 // 余下的按此方式寫就是了,用不到的也可以不寫哦!
public Stockdrv()
{
//
// TODO: 在此處添加構造函數邏輯
//
}
}
///---------------------------------------------------------------------------------------
{
//
// TODO: 在此處添加構造函數邏輯
//
}
}
///---------------------------------------------------------------------------------------
}
3. 調用API
采用數據共享引用時
// 數據共享引用,調用 C++ 中的接口函數
Stockdrv.Stock_Init(this.Handle, Stockdrv.RCV_MSG_STKDATA, Stockdrv.RCV_WORK_SENDMSG);
Stockdrv.SetupReceiver(false);
這種方式一般不使用了,提供該方式主要為了兼容的緣故
網絡直接連接
int lResult = Stockdrv.Stock_Init_Nodialog(this.Handle, Stockdrv.RCV_MSG_STKDATA, Stockdrv.RCV_WORK_SENDMSG,.......);
Stockdrv.SetupReceiver(false);
接下來請求數據
tagSTOCK_STRUCTEx[] pStocks = new tagSTOCK_STRUCTEx[1];
string s = "999999;
pStocks[0].m_code = s.ToCharArray(0, s.Length);
string s = "999999;
pStocks[0].m_code = s.ToCharArray(0, s.Length);
pStocks[0].m_type = 0x10;
Stockdrv.RequestStockData(Stockdrv.RCV_REPORT, pStocks, 1, 0, 0);
Stockdrv.RequestStockData(Stockdrv.RCV_REPORT, pStocks, 1, 0, 0);
4.數據接收
只要對數據接收,重載 WndProc 即可,代碼如下:
/// //////////////////////////////////////////////////////////
/// 重載虛擬 WndProc 接收消息的窗口的消息處理過程,以截獲任何消息
/// /////////////////////////////////////////////////////////
protected override void WndProc(ref Message m)
{
if ((int)m.Msg == Stockdrv.RCV_MSG_STKDATA)
{
tagRCV_DATA recvData = (tagRCV_DATA)Marshal.PtrToStructure(m.LParam, typeof(tagRCV_DATA));
/// 重載虛擬 WndProc 接收消息的窗口的消息處理過程,以截獲任何消息
/// /////////////////////////////////////////////////////////
protected override void WndProc(ref Message m)
{
if ((int)m.Msg == Stockdrv.RCV_MSG_STKDATA)
{
tagRCV_DATA recvData = (tagRCV_DATA)Marshal.PtrToStructure(m.LParam, typeof(tagRCV_DATA));
if ((int)m.WParam == Stockdrv.RCV_REPORT) //行情數據
{
m_bInitOK = true;
tagRCV_REPORT_STRUCTEx ReprotData;
int recLength ;
recLength = Marshal.SizeOf(typeof(tagRCV_REPORT_STRUCTEx)) // 需要實際長度
for (int i = 0; i < recvData.m_nPacketNum; i++)
{
{
m_bInitOK = true;
tagRCV_REPORT_STRUCTEx ReprotData;
int recLength ;
recLength = Marshal.SizeOf(typeof(tagRCV_REPORT_STRUCTEx)) // 需要實際長度
for (int i = 0; i < recvData.m_nPacketNum; i++)
{
//結構中如果沒有說明 Pack =1,需要前移 -2
// 這也是許多人感覺為啥差-2原因, 在 VB6.0 也是如此,VB.NET如果沒有說明Pack =1,也是同樣的問題
//ReprotData = (tagRCV_REPORT_STRUCTEx)Marshal.PtrToStructure(new IntPtr((int)recvData.m_pData + i * recLength-2), typeof(tagRCV_REPORT_STRUCTEx));
//ReprotData = (tagRCV_REPORT_STRUCTEx)Marshal.PtrToStructure(new IntPtr((int)recvData.m_pData + i * recLength-2), typeof(tagRCV_REPORT_STRUCTEx));
//結構中需要說明 Pack =1
ReprotData = (tagRCV_REPORT_STRUCTEx)Marshal.PtrToStructure(new IntPtr((int)recvData.m_pData + i * recLength ), typeof(tagRCV_REPORT_STRUCTEx));
// 數據計算與分析 省略
。。。。。。。。。
}
}
}
return;
}
base.WndProc(ref m); // 調用基類成員函數!
}
至此,已經完成說明了如何在C#中調用API,相信大家可以開發出適合自己的股軟分析軟件!
如有不對之處,請大家多指教!