C#调用行情接口API 转 武胜

C#调用行情接口API

          很早就已经通过了C#调用行情接口API的实现(见以前的日志),但是还是有人没有搞明白,在这里就讲讲如何实现的。
首先,根据提供的stockdrv.h的信息,将数据结构移植到C#代码中。
1.数据结构定义
   // <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
    }
    [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 的定义根据自己喜好可以定义成
         [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]    
/*  也可以定义成
        [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 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;                              // 成交额
        [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_fBuyPrice4;                          // 申买价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
       ......          其他项的定义
    };
    [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
    }
    [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;
    } ;
 
  以上的定义关键点之一,是说明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_REPORT    = 0x3f001234; //股票行情 
        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 Stockdrv()
        {
            //
            // 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);
            pStocks[0].m_type = 0x10;
            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));
                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++)
                    {
                        //结构中如果没有说明 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));

                        //结构中需要说明 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,相信大家可以开发出适合自己的股软分析软件!
   如有不对之处,请大家多指教!
原文地址:https://www.cnblogs.com/zeroone/p/2830078.html