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,相信大家可以开发出适合自己的股软分析软件!
如有不对之处,请大家多指教!