用C#完整实现了一下对C++动态库的调用,还是有一些问题待解决。
C++回调函数里面的class的指针类型参数,在C#中还是用class来对应,在用struct的ref形式时遇到的一个问题是,C++的指针为空时,在回调时候会遇到 System.NullReferenceException 异常。
C#的CharSet目前只有Ansi跟Unicode可选,对于Utf8格式的处理还是有问题。目前采用的方案是在C++库里面把Utf8格式转为GBK。
目前在封装class类型参数的字符串类型字段时,仅尝试 UnmanagedType.ByValTStr 类型成功,使用UnmanagedType.LPUTF8Str时会遇到 System.AccessViolationException 异常。
C++ TradeCApi.h
cpp
#pragma once
#include "StepStructs.h"
#ifdef WIN32
#define TRADE_CAPI_CALL _stdcall
#ifdef LIB_TRADE_CAPI_EXPORT
#define TRADE_CAPI_EXPORT __declspec(dllexport)
#else
#define TRADE_CAPI_EXPORT __declspec(dllimport)
#endif
#else
#define TRADE_CAPI_CALL
#define TRADE_CAPI_EXPORT
#endif
extern "C"
{
typedef TRADE_CAPI_EXPORT void (TRADE_CAPI_CALL *OnConnected)();
typedef TRADE_CAPI_EXPORT void (TRADE_CAPI_CALL *OnDisConnected)();
typedef TRADE_CAPI_EXPORT void (TRADE_CAPI_CALL *OnRspAccountLogin)(step::StepRspAccountLogin* rspAccountLogin, step::StepRspInfo* rspInfo, int requestID, bool isLast);
typedef TRADE_CAPI_EXPORT void (TRADE_CAPI_CALL *OnRspAccountLogout)(step::StepRspAccountLogout* rspAccountLogout, step::StepRspInfo* rspInfo, int requestID, bool isLast);
typedef TRADE_CAPI_EXPORT void (TRADE_CAPI_CALL *OnRspInsertOrder)(step::StepOrder* order, step::StepRspInfo* rspInfo, int requestID, bool isLast);
typedef TRADE_CAPI_EXPORT void (TRADE_CAPI_CALL *OnRspCancelOrder)(step::StepRspCancelOrder* rspCancelOrder, step::StepRspInfo* rspInfo, int requestID, bool isLast);
typedef TRADE_CAPI_EXPORT void (TRADE_CAPI_CALL *OnRspQryCapital)(step::StepCapital* capital, step::StepRspInfo* rspInfo, int requestID, bool isLast);
typedef TRADE_CAPI_EXPORT void (TRADE_CAPI_CALL *OnRspQryPosition)(step::StepPosition* position, step::StepRspInfo* rspInfo, int requestID, bool isLast);
typedef TRADE_CAPI_EXPORT void (TRADE_CAPI_CALL *OnRspQryOrder)(step::StepOrder* order, step::StepRspInfo* rspInfo, int requestID, bool isLast);
typedef TRADE_CAPI_EXPORT void (TRADE_CAPI_CALL *OnRspQryTrade)(step::StepTrade* trade, step::StepRspInfo* rspInfo, int requestID, bool isLast);
typedef TRADE_CAPI_EXPORT void (TRADE_CAPI_CALL *OnRspQryInstrument)(step::StepInstrument* instrument, step::StepRspInfo* rspInfo, int requestID, bool isLast);
typedef TRADE_CAPI_EXPORT void (TRADE_CAPI_CALL *OnRspQryCommissionRate)(step::StepCommissionRate* commissionRate, step::StepRspInfo* rspInfo, int requestID, bool isLast);
typedef TRADE_CAPI_EXPORT void (TRADE_CAPI_CALL *OnRtnOrder)(step::StepOrder* order);
typedef TRADE_CAPI_EXPORT void (TRADE_CAPI_CALL *OnRtnTrade)(step::StepTrade* trade);
class TRADE_CAPI_EXPORT TradeCSpi
{
public:
OnConnected OnConnected;
OnDisConnected OnDisConnected;
OnRspAccountLogin OnRspAccountLogin;
OnRspAccountLogout OnRspAccountLogout;
OnRspInsertOrder OnRspInsertOrder;
OnRspCancelOrder OnRspCancelOrder;
OnRspQryCapital OnRspQryCapital;
OnRspQryPosition OnRspQryPosition;
OnRspQryOrder OnRspQryOrder;
OnRspQryTrade OnRspQryTrade;
OnRspQryInstrument OnRspQryInstrument;
OnRspQryCommissionRate OnRspQryCommissionRate;
OnRtnOrder OnRtnOrder;
OnRtnTrade OnRtnTrade;
};
TRADE_CAPI_EXPORT void TRADE_CAPI_CALL CreateTradeApi();
TRADE_CAPI_EXPORT const char* TRADE_CAPI_CALL GetApiVersion();
TRADE_CAPI_EXPORT void TRADE_CAPI_CALL Init();
TRADE_CAPI_EXPORT void TRADE_CAPI_CALL Join();
TRADE_CAPI_EXPORT void TRADE_CAPI_CALL Release();
TRADE_CAPI_EXPORT void TRADE_CAPI_CALL RegisterFront(const char* ip, unsigned short port);
TRADE_CAPI_EXPORT void TRADE_CAPI_CALL RegisterSpi(TradeCSpi* spi);
TRADE_CAPI_EXPORT int TRADE_CAPI_CALL ReqAccountLogin(step::StepReqAccountLogin* reqAccountLogin, int requestID);
TRADE_CAPI_EXPORT int TRADE_CAPI_CALL ReqAccountLogout(step::StepReqAccountLogout* reqAccountLogout, int requestID);
TRADE_CAPI_EXPORT int TRADE_CAPI_CALL ReqInsertOrder(step::StepReqInsertOrder* reqInsertOrder, int requestID);
TRADE_CAPI_EXPORT int TRADE_CAPI_CALL ReqCancelOrder(step::StepReqCancelOrder* reqCancelOrder, int requestID);
TRADE_CAPI_EXPORT int TRADE_CAPI_CALL ReqQryCapital(step::StepReqQryCapital* reqQryCapital, int requestID);
TRADE_CAPI_EXPORT int TRADE_CAPI_CALL ReqQryPosition(step::StepReqQryPosition* reqQryPosition, int requestID);
TRADE_CAPI_EXPORT int TRADE_CAPI_CALL ReqQryOrder(step::StepReqQryOrder* reqQryOrder, int requestID);
TRADE_CAPI_EXPORT int TRADE_CAPI_CALL ReqQryTrade(step::StepReqQryTrade* reqQryTrade, int requestID);
TRADE_CAPI_EXPORT int TRADE_CAPI_CALL ReqQryInstrument(step::StepReqQryInstrument* reqQryInstrument, int requestID);
TRADE_CAPI_EXPORT int TRADE_CAPI_CALL ReqQryCommissionRate(step::StepReqQryCommissionRate* reqQryCommissionRate, int requestID);
}
这里定义的数据结构太多,仅展示个例子
cs
[StructLayout(LayoutKind.Sequential)]
public class StepReqQryOrder
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string? AccountID;
}
[StructLayout(LayoutKind.Sequential)]
public class StepOrder
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 9)]
public string? TradingDay;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string? AccountID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
public string? ExchangeID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string? InstrumentID;
public int OrderID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string? OrderSysID;
public DirectionType Direction;
public StockOrderPriceTypeType StockOrderPriceType;
public double Price;
public int Volume;
public int VolumeTotal;
public int VolumeTraded;
public OrderStatusType OrderStatus;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string? StatusMsg;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 9)]
public string? OrderDate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 9)]
public string? OrderTime;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 9)]
public string? CancelDate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 9)]
public string? CancelTime;
public long SessionID;
public int ClientOrderID;
public int RequestID;
}
[StructLayout(LayoutKind.Sequential)]
public class StepReqQryTrade
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string? AccountID;
}
[StructLayout(LayoutKind.Sequential)]
public class StepTrade
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 9)]
public string? TradingDay;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string? AccountID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
public string? ExchangeID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string? InstrumentID;
public int OrderID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string? OrderSysID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string? TradeID;
public DirectionType Direction;
public double Price;
public int Volume;
public double TradeAmount;
public double Commission;
public double StampTax;
public double TransferFee;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 9)]
public string? TradeDate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 9)]
public string? TradeTime;
}
C# 导入C++ API的函数 TradeApi.cs
cs
using System;
using System.Runtime.InteropServices;
namespace TradeClient;
public partial class TradeApi
{
const string LibName = "TradeCApi";
[DllImport(LibName, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern void CreateTradeApi();
[DllImport(LibName, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern string GetApiVersion();
[DllImport(LibName, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern void Init();
[DllImport(LibName, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern void Join();
[DllImport(LibName, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern void Release();
[DllImport(LibName, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern void RegisterFront(string ip, int port);
[DllImport(LibName, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern void RegisterSpi(IntPtr spi);
[DllImport(LibName, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern int ReqAccountLogin(StepReqAccountLogin reqAccountLogin, int requestID);
[DllImport(LibName, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern int ReqAccountLogout(StepReqAccountLogout reqAccountLogout, int requestID);
[DllImport(LibName, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern int ReqInsertOrder(StepReqInsertOrder reqInsertOrder, int requestID);
[DllImport(LibName, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern int ReqCancelOrder(StepReqCancelOrder reqCancelOrder, int requestID);
[DllImport(LibName, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern int ReqQryCapital(StepReqQryCapital reqQryCapital, int requestID);
[DllImport(LibName, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern int ReqQryPosition(StepReqQryPosition reqQryPosition, int requestID);
[DllImport(LibName, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern int ReqQryOrder(StepReqQryOrder reqQryOrder, int requestID);
[DllImport(LibName, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern int ReqQryTrade(StepReqQryTrade reqQryTrade, int requestID);
[DllImport(LibName, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern int ReqQryInstrument(StepReqQryInstrument reqQryInstrument, int requestID);
[DllImport(LibName, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern int ReqQryCommissionRate(StepReqQryCommissionRate reqQryCommissionRate, int requestID);
}
C#回调委托、结构的定义 TradeSpi.cs
cs
using System.Runtime.InteropServices;
namespace TradeClient;
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public delegate void OnConnected();
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public delegate void OnDisConnected();
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public delegate void OnRspAccountLogin(StepRspAccountLogin? rspAccountLogin, StepRspInfo? rspInfo, int requestID, bool isLast);
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public delegate void OnRspAccountLogout(StepRspAccountLogout? rspAccountLogout, StepRspInfo? rspInfo, int requestID, bool isLast);
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public delegate void OnRspInsertOrder(StepOrder? order, StepRspInfo? rspInfo, int requestID, bool isLast);
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public delegate void OnRspCancelOrder(StepRspCancelOrder? rspCancelOrder, StepRspInfo? rspInfo, int requestID, bool isLast);
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public delegate void OnRspQryCapital(StepCapital? capital, StepRspInfo? rspInfo, int requestID, bool isLast);
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public delegate void OnRspQryPosition(StepPosition? position, StepRspInfo? rspInfo, int requestID, bool isLast);
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public delegate void OnRspQryOrder(StepOrder? order, StepRspInfo? rspInfo, int requestID, bool isLast);
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public delegate void OnRspQryTrade(StepTrade? trade, StepRspInfo? rspInfo, int requestID, bool isLast);
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public delegate void OnRspQryInstrument(StepInstrument? instrument, StepRspInfo? rspInfo, int requestID, bool isLast);
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public delegate void OnRspQryCommissionRate(StepCommissionRate? commissionRate, StepRspInfo? rspInfo, int requestID, bool isLast);
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public delegate void OnRtnOrder(StepOrder? order);
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public delegate void OnRtnTrade(StepTrade? trade);
public struct TradeSpi
{
public OnConnected OnConnected;
public OnDisConnected OnDisConnected;
public OnRspAccountLogin OnRspAccountLogin;
public OnRspAccountLogout OnRspAccountLogout;
public OnRspInsertOrder OnRspInsertOrder;
public OnRspCancelOrder OnRspCancelOrder;
public OnRspQryCapital OnRspQryCapital;
public OnRspQryPosition OnRspQryPosition;
public OnRspQryOrder OnRspQryOrder;
public OnRspQryTrade OnRspQryTrade;
public OnRspQryInstrument OnRspQryInstrument;
public OnRspQryCommissionRate OnRspQryCommissionRate;
public OnRtnOrder OnRtnOrder;
public OnRtnTrade OnRtnTrade;
}
C#回调函数的实现 TradeSpiMiddle.cs
cs
using Microsoft.Extensions.Logging;
namespace TradeClient;
public class TradeSpiMiddle
{
public TradeSpiMiddle(ILogger<TradeSpiMiddle> logger, TradeSpiImpl spiImpl)
{
Logger = logger;
TradeSpiImpl = spiImpl;
}
private readonly ILogger<TradeSpiMiddle> Logger;
private readonly TradeSpiImpl TradeSpiImpl;
public void InitTradeSpi(ref TradeSpi spi)
{
spi.OnConnected = new OnConnected(OnConnected);
spi.OnDisConnected = new OnDisConnected(OnDisConnected);
spi.OnRspAccountLogin = new OnRspAccountLogin(OnRspAccountLogin);
spi.OnRspAccountLogout = new OnRspAccountLogout(OnRspAccountLogout);
spi.OnRspInsertOrder = new OnRspInsertOrder(OnRspInsertOrder);
spi.OnRspCancelOrder = new OnRspCancelOrder(OnRspCancelOrder);
spi.OnRspQryCapital = new OnRspQryCapital(OnRspQryCapital);
spi.OnRspQryPosition = new OnRspQryPosition(OnRspQryPosition);
spi.OnRspQryOrder = new OnRspQryOrder(OnRspQryOrder);
spi.OnRspQryTrade = new OnRspQryTrade(OnRspQryTrade);
spi.OnRspQryInstrument = new OnRspQryInstrument(OnRspQryInstrument);
spi.OnRspQryCommissionRate = new OnRspQryCommissionRate(OnRspQryCommissionRate);
spi.OnRtnOrder = new OnRtnOrder(OnRtnOrder);
spi.OnRtnTrade = new OnRtnTrade(OnRtnTrade);
}
public void OnConnected()
{
Logger.LogInformation("OnConnected");
TradeSpiImpl.OnConnected();
}
public void OnDisConnected()
{
Logger.LogInformation("OnDisConnected");
TradeSpiImpl.OnDisConnected();
}
public void OnRspAccountLogin(StepRspAccountLogin? rspAccountLogin, StepRspInfo? rspInfo, int requestID, bool isLast)
{
Logger.LogInformation($"OnRspAccountLogin: requestID:{requestID}, isLast:{isLast}");
if (rspAccountLogin != null)
{
Logger.LogInformation($"StepRspAccountLogin: AccountID:{rspAccountLogin.AccountID}, LoginDate:{rspAccountLogin.LoginDate}, LoginTime:{rspAccountLogin.LoginTime}, SessionID:{rspAccountLogin.SessionID}");
}
if (rspInfo != null)
{
Logger.LogInformation($"StepRspInfo: ErrorID:{rspInfo.ErrorID}, ErrorMsg:{rspInfo.ErrorMsg}");
}
TradeSpiImpl.OnRspAccountLogin(rspAccountLogin, rspInfo, requestID, isLast);
}
public void OnRspAccountLogout(StepRspAccountLogout? rspAccountLogout, StepRspInfo? rspInfo, int requestID, bool isLast)
{
Logger.LogInformation($"OnRspAccountLogout: requestID:{requestID}, isLast:{isLast}");
if (rspAccountLogout != null)
{
Logger.LogInformation($"StepRspAccountLogout: AccountID:{rspAccountLogout.AccountID}");
}
if (rspInfo != null)
{
Logger.LogInformation($"StepRspInfo: ErrorID:{rspInfo.ErrorID}, ErrorMsg:{rspInfo.ErrorMsg}");
}
TradeSpiImpl.OnRspAccountLogout(rspAccountLogout, rspInfo, requestID, isLast);
}
public void OnRspInsertOrder(StepOrder? order, StepRspInfo? rspInfo, int requestID, bool isLast)
{
Logger.LogInformation($"OnRspInsertOrder: requestID:{requestID}, isLast:{isLast}");
if (order != null)
{
Logger.LogInformation($"StepOrder: TradingDay:{order.TradingDay}, AccountID:{order.AccountID}, ExchangeID:{order.ExchangeID}, InstrumentID:{order.InstrumentID}, OrderID:{order.OrderID}, OrderSysID:{order.OrderSysID}, Direction:{order.Direction}, StockOrderPriceType:{order.StockOrderPriceType}, Price:{order.Price}, Volume:{order.Volume}, VolumeTotal:{order.VolumeTotal}, VolumeTraded:{order.VolumeTraded}, OrderStatus:{order.OrderStatus}, StatusMsg:{order.StatusMsg}, OrderDate:{order.OrderDate}, OrderTime:{order.OrderTime}, CancelDate:{order.CancelDate}, CancelTime:{order.CancelTime}, SessionID:{order.SessionID}, ClientOrderID:{order.ClientOrderID}, RequestID:{order.RequestID}");
}
if (rspInfo != null)
{
Logger.LogInformation($"StepRspInfo: ErrorID:{rspInfo.ErrorID}, ErrorMsg:{rspInfo.ErrorMsg}");
}
TradeSpiImpl.OnRspInsertOrder(order, rspInfo, requestID, isLast);
}
public void OnRspCancelOrder(StepRspCancelOrder? rspCancelOrder, StepRspInfo? rspInfo, int requestID, bool isLast)
{
Logger.LogInformation($"OnRspCancelOrder: requestID:{requestID}, isLast:{isLast}");
if (rspCancelOrder != null)
{
Logger.LogInformation($"StepRspCancelOrder: AccountID:{rspCancelOrder.AccountID}, ExchangeID:{rspCancelOrder.ExchangeID}, InstrumentID:{rspCancelOrder.InstrumentID}, ClientCancelOrderID:{rspCancelOrder.ClientCancelOrderID}, OrderID:{rspCancelOrder.OrderID}, OrderSysID:{rspCancelOrder.OrderSysID}, ClientOrderID:{rspCancelOrder.ClientOrderID}");
}
if (rspInfo != null)
{
Logger.LogInformation($"StepRspInfo: ErrorID:{rspInfo.ErrorID}, ErrorMsg:{rspInfo.ErrorMsg}");
}
TradeSpiImpl.OnRspCancelOrder(rspCancelOrder, rspInfo, requestID, isLast);
}
public void OnRspQryCapital(StepCapital? capital, StepRspInfo? rspInfo, int requestID, bool isLast)
{
Logger.LogInformation($"OnRspQryCapital: requestID:{requestID}, isLast:{isLast}");
if (capital != null)
{
Logger.LogInformation($"StepCapital: TradingDay:{capital.TradingDay}, AccountID:{capital.AccountID}, AccountType:{capital.AccountType}, Balance:{capital.Balance}, PreBalance:{capital.PreBalance}, CashAsset:{capital.CashAsset}, PreCashAsset:{capital.PreCashAsset}, Available:{capital.Available}, CashIn:{capital.CashIn}, CashOut:{capital.CashOut}, Commission:{capital.Commission}, StampTax:{capital.StampTax}, TransferFee:{capital.TransferFee}, FrozenCash:{capital.FrozenCash}, FrozenCommission:{capital.FrozenCommission}, FrozenStampTax:{capital.FrozenStampTax}, FrozenTransferFee:{capital.FrozenTransferFee}, MarketValue:{capital.MarketValue}, TotalProfit:{capital.TotalProfit}, TodayProfit:{capital.TodayProfit}, Deposit:{capital.Deposit}, Withdraw:{capital.Withdraw}");
}
if (rspInfo != null)
{
Logger.LogInformation($"StepRspInfo: ErrorID:{rspInfo.ErrorID}, ErrorMsg:{rspInfo.ErrorMsg}");
}
TradeSpiImpl.OnRspQryCapital(capital, rspInfo, requestID, isLast);
}
public void OnRspQryPosition(StepPosition? position, StepRspInfo? rspInfo, int requestID, bool isLast)
{
Logger.LogInformation($"OnRspQryPosition: requestID:{requestID}, isLast:{isLast}");
if (position != null)
{
Logger.LogInformation($"StepPosition: TradingDay:{position.TradingDay}, AccountID:{position.AccountID}, AccountType:{position.AccountType}, ExchangeID:{position.ExchangeID}, InstrumentID:{position.InstrumentID}, TotalPosition:{position.TotalPosition}, PositionFrozen:{position.PositionFrozen}, TodayPosition:{position.TodayPosition}, TotalCostPrice:{position.TotalCostPrice}, TodayCostPrice:{position.TodayCostPrice}, CashIn:{position.CashIn}, CashOut:{position.CashOut}, Commission:{position.Commission}, StampTax:{position.StampTax}, TransferFee:{position.TransferFee}, MarketValue:{position.MarketValue}, TotalCost:{position.TotalCost}, TodayCost:{position.TodayCost}, TotalProfit:{position.TotalProfit}, TodayProfit:{position.TodayProfit}, LastPrice:{position.LastPrice}, PreClosePrice:{position.PreClosePrice}");
}
if (rspInfo != null)
{
Logger.LogInformation($"StepRspInfo: ErrorID:{rspInfo.ErrorID}, ErrorMsg:{rspInfo.ErrorMsg}");
}
TradeSpiImpl.OnRspQryPosition(position, rspInfo, requestID, isLast);
}
public void OnRspQryOrder(StepOrder? order, StepRspInfo? rspInfo, int requestID, bool isLast)
{
Logger.LogInformation($"OnRspQryOrder: requestID:{requestID}, isLast:{isLast}");
if (order != null)
{
Logger.LogInformation($"StepOrder: TradingDay:{order.TradingDay}, AccountID:{order.AccountID}, ExchangeID:{order.ExchangeID}, InstrumentID:{order.InstrumentID}, OrderID:{order.OrderID}, OrderSysID:{order.OrderSysID}, Direction:{order.Direction}, StockOrderPriceType:{order.StockOrderPriceType}, Price:{order.Price}, Volume:{order.Volume}, VolumeTotal:{order.VolumeTotal}, VolumeTraded:{order.VolumeTraded}, OrderStatus:{order.OrderStatus}, StatusMsg:{order.StatusMsg}, OrderDate:{order.OrderDate}, OrderTime:{order.OrderTime}, CancelDate:{order.CancelDate}, CancelTime:{order.CancelTime}, SessionID:{order.SessionID}, ClientOrderID:{order.ClientOrderID}, RequestID:{order.RequestID}");
}
if (rspInfo != null)
{
Logger.LogInformation($"StepRspInfo: ErrorID:{rspInfo.ErrorID}, ErrorMsg:{rspInfo.ErrorMsg}");
}
TradeSpiImpl.OnRspQryOrder(order, rspInfo, requestID, isLast);
}
public void OnRspQryTrade(StepTrade? trade, StepRspInfo? rspInfo, int requestID, bool isLast)
{
Logger.LogInformation($"OnRspQryTrade: requestID:{requestID}, isLast:{isLast}");
if (trade != null)
{
Logger.LogInformation($"StepTrade: TradingDay:{trade.TradingDay}, AccountID:{trade.AccountID}, ExchangeID:{trade.ExchangeID}, InstrumentID:{trade.InstrumentID}, OrderID:{trade.OrderID}, OrderSysID:{trade.OrderSysID}, TradeID:{trade.TradeID}, Direction:{trade.Direction}, Price:{trade.Price}, Volume:{trade.Volume}, TradeAmount:{trade.TradeAmount}, Commission:{trade.Commission}, StampTax:{trade.StampTax}, TransferFee:{trade.TransferFee}, TradeDate:{trade.TradeDate}, TradeTime:{trade.TradeTime}");
}
if (rspInfo != null)
{
Logger.LogInformation($"StepRspInfo: ErrorID:{rspInfo.ErrorID}, ErrorMsg:{rspInfo.ErrorMsg}");
}
TradeSpiImpl.OnRspQryTrade(trade, rspInfo, requestID, isLast);
}
public void OnRspQryInstrument(StepInstrument? instrument, StepRspInfo? rspInfo, int requestID, bool isLast)
{
Logger.LogInformation($"OnRspQryInstrument: requestID:{requestID}, isLast:{isLast}");
if (instrument != null)
{
Logger.LogInformation($"StepInstrument: ExchangeID:{instrument.ExchangeID}, InstrumentID:{instrument.InstrumentID}, InstrumentName:{instrument.InstrumentName}, SecurityType:{instrument.SecurityType}");
}
if (rspInfo != null)
{
Logger.LogInformation($"StepRspInfo: ErrorID:{rspInfo.ErrorID}, ErrorMsg:{rspInfo.ErrorMsg}");
}
TradeSpiImpl.OnRspQryInstrument(instrument, rspInfo, requestID, isLast);
}
public void OnRspQryCommissionRate(StepCommissionRate? commissionRate, StepRspInfo? rspInfo, int requestID, bool isLast)
{
Logger.LogInformation($"OnRspQryCommissionRate: requestID:{requestID}, isLast:{isLast}");
if (commissionRate != null)
{
Logger.LogInformation($"StepCommissionRate: AccountID:{commissionRate.AccountID}, ExchangeID:{commissionRate.ExchangeID}, OpenByVolume:{commissionRate.OpenByVolume}, CloseByVolume:{commissionRate.CloseByVolume}, OpenByMoney:{commissionRate.OpenByMoney}, CloseByMoney:{commissionRate.CloseByMoney}, MinCommission:{commissionRate.MinCommission}, MaxCommission:{commissionRate.MaxCommission}");
}
if (rspInfo != null)
{
Logger.LogInformation($"StepRspInfo: ErrorID:{rspInfo.ErrorID}, ErrorMsg:{rspInfo.ErrorMsg}");
}
TradeSpiImpl.OnRspQryCommissionRate(commissionRate, rspInfo, requestID, isLast);
}
public void OnRtnOrder(StepOrder? order)
{
Logger.LogInformation($"OnRtnOrder");
if (order != null)
{
Logger.LogInformation($"StepOrder: TradingDay:{order.TradingDay}, AccountID:{order.AccountID}, ExchangeID:{order.ExchangeID}, InstrumentID:{order.InstrumentID}, OrderID:{order.OrderID}, OrderSysID:{order.OrderSysID}, Direction:{order.Direction}, StockOrderPriceType:{order.StockOrderPriceType}, Price:{order.Price}, Volume:{order.Volume}, VolumeTotal:{order.VolumeTotal}, VolumeTraded:{order.VolumeTraded}, OrderStatus:{order.OrderStatus}, StatusMsg:{order.StatusMsg}, OrderDate:{order.OrderDate}, OrderTime:{order.OrderTime}, CancelDate:{order.CancelDate}, CancelTime:{order.CancelTime}, SessionID:{order.SessionID}, ClientOrderID:{order.ClientOrderID}, RequestID:{order.RequestID}");
}
TradeSpiImpl.OnRtnOrder(order);
}
public void OnRtnTrade(StepTrade? trade)
{
Logger.LogInformation($"OnRtnTrade");
if (trade != null)
{
Logger.LogInformation($"StepTrade: TradingDay:{trade.TradingDay}, AccountID:{trade.AccountID}, ExchangeID:{trade.ExchangeID}, InstrumentID:{trade.InstrumentID}, OrderID:{trade.OrderID}, OrderSysID:{trade.OrderSysID}, TradeID:{trade.TradeID}, Direction:{trade.Direction}, Price:{trade.Price}, Volume:{trade.Volume}, TradeAmount:{trade.TradeAmount}, Commission:{trade.Commission}, StampTax:{trade.StampTax}, TransferFee:{trade.TransferFee}, TradeDate:{trade.TradeDate}, TradeTime:{trade.TradeTime}");
}
TradeSpiImpl.OnRtnTrade(trade);
}
}
C#将回调类注册给C++相关的代码 Program.cs
cs
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using NLog;
using NLog.Extensions.Logging;
using System.Runtime.InteropServices;
namespace TestTradeApi
{
internal class Program
{
static void Main(string[] args)
{
LogManager.Configuration.Variables["ProgramName"] = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name;
LogManager.Configuration.Variables["StartTime"] = DateTime.Now.ToString("yyyyMMdd-HHmmss");
var config = new ConfigurationBuilder().Build();
var servicesCollection = new ServiceCollection();
servicesCollection.AddLogging(loggingBuilder => loggingBuilder.AddNLog(config));
servicesCollection.AddSingleton<TradeSpiImpl>();
servicesCollection.AddSingleton<TradeSpiMiddle>();
var serviceProvider = servicesCollection.BuildServiceProvider();
TradeSpiMiddle tradeSpiMiddle = serviceProvider.GetRequiredService<TradeSpiMiddle>();
TradeSpi tradeSpi = new();
tradeSpiMiddle.InitTradeSpi(ref tradeSpi);
int size = Marshal.SizeOf(tradeSpi);
IntPtr tradeSpiPoint = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(tradeSpi, tradeSpiPoint, true);
TradeApi.CreateTradeApi();
TradeApi.RegisterSpi(tradeSpiPoint);
TradeApi.RegisterFront("127.0.0.1", 10001);
TradeApi.Init();
Thread.Sleep(30000);
TradeApi.Release();
}
}
}