C#使用C++动态库2

用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();
        }
    }
}
相关推荐
敲上瘾26 分钟前
动静态库的制作与使用(Linux操作系统)
linux·运维·服务器·c++·系统架构·库文件·动静态库
Uitwaaien5428 分钟前
51单片机——按键控制LED流水灯
c++·单片机·嵌入式硬件·51单片机
步、步、为营2 小时前
C# 与.NET 日志变革:JSON 让程序“开口说清话”
c#·json·.net
漫漫进阶路4 小时前
VS C++ 配置OPENCV环境
开发语言·c++·opencv
code_shenbing6 小时前
基于 WPF 平台使用纯 C# 制作流体动画
开发语言·c#·wpf
code_shenbing6 小时前
基于 WPF 平台实现成语游戏
游戏·c#·wpf
hefaxiang8 小时前
【C++】函数重载
开发语言·c++·算法
花生树什么树8 小时前
下载Visual Studio Community 2019
c++·visual studio·vs2019·community
exp_add39 小时前
Codeforces Round 1000 (Div. 2) A-C
c++·算法
练小杰9 小时前
Linux系统 C/C++编程基础——基于Qt的图形用户界面编程
linux·c语言·c++·经验分享·qt·学习·编辑器