我做了一下C++的DLL,导出一个类的指针,然后根据这个类的基类函数进行操作,如上面的模式。
typedef class FTPClientAPI
{
protected:
/**
* 函数名称:构造函数
* 函数功能:主是防止用户自己创建此接口,不需要实现
* 参数列表:
* 修改记录: 20100514 赵海杰 创建
*/
FTPClientAPI()
{}
/**
* 函数名称:拷贝构造函数
* 函数功能:主是防止用户自己创建此接口,不需要实现
* 参数列表:
* 修改记录: 20100514 赵海杰 创建
*/
FTPClientAPI(FTPClientAPI&);
/**
* 函数名称:柝构函数
* 函数功能:主是防止用户自己创建此接口
* 参数列表:
* 修改记录: 20100514 赵海杰 创建
*/
virtual ~FTPClientAPI()
{}
public:
/**
* 函数名称:释放
* 函数功能:释放对象
* 参数列表:
* @Result : 返回接口版本号
* 修改记录: 20100521 赵海杰 创建
**/
virtual int __stdcall free() = 0;
/**
* 函数名称:准备
* 函数功能:申请各种资源
* 参数列表:
* @Result : Z_OK—成功,<0—失败
* 修改记录: 20100514 赵海杰 创建
**/
virtual int __stdcall Prepare() = 0;
/**
* 函数名称:解除准备
* 函数功能:释放各种资源
* 参数列表:
* @Result : Z_OK—成功,<0—失败
* 修改记录: 20100514赵海杰 创建
**/
virtual int __stdcall UnPrepare() = 0;
/**
* 函数名称:连接服务器
* 函数功能:连接上级服务器
* 参数列表:
* @szIPAddr : IP地址
* @usIPPort : IP端口
* @Result : 返回接口版本号
* 修改记录: 20100520 赵海杰 创建
**/
virtual int __stdcall Connect(char* szIPAddr, unsigned short usIPPort) = 0;
/**
* 函数名称:断开连接
* 函数功能:断开连接上级服务器
* 参数列表:
* @szIPAddr : IP地址
* @usIPPort : IP端口
* @Result : 返回接口版本号
* 修改记录: 20100520 赵海杰 创建
**/
virtual int __stdcall DisConnect() = 0;
/**
* 函数名称:设置信息到达时的回调函数
* 函数功能:释放各种资源
* 参数列表:
* @lpPubInfSPI: 回调函数
* @Result : Z_OK—成功,<0—失败
* 修改记录: 20100514赵海杰 创建
**/
virtual int __stdcall SetPubInfoSPI(LPFTPPUBINFOSPI lpPubInfSPI) = 0;
/**
* 业务处理接口
**/
// 查指令
virtual int __stdcall QueryOrder(IN const LPFTPQRYORDER lpQryOrder, OUT LPFTPQRYORDERRSP lpQryOrderRsp, INOUT int& iRowCount, char* szErrInfo) = 0;
// 查交易
virtual int __stdcall QueryTrade(IN const LPFTPQRYTRADE lpQryTrade, OUT LPFTPQRYTRADERSP lpQryTradeRsp, INOUT int& iRowCount, char* szErrInfo) = 0;
// 查组件
virtual int __stdcall QueryComp(IN const LPFTPQRYCOMP lpQryComp, OUT LPFTPQRYCOMPRSP lpQryCompRsp, INOUT int& iRowCount, char* szErrInfo) = 0;
// 查回报
virtual int __stdcall QueryInfo(IN const LPFTPQRYINFO lpQryInfo, OUT LPFTPQRYINFORSP lpQryInfoRsp, INOUT int& iRowCount, char* szErrInfo) = 0; // 添加指令
virtual int __stdcall AddOrder(IN const LPFTPADDORDER lpAddOrder, OUT LPFTPADDORDERRSP lpAddOrderRsp, char* szErrInfo) = 0;
// 删除指令
virtual int __stdcall DelOrder(IN const LPFTPDELORDER lpDelOrder, OUT LPFTPDELORDERRSP lpDelOrderRsp, char* szErrInfo) = 0;
// 操作指令
virtual int __stdcall OrderAction(IN const LPFTPORDERACT lpOrderAct, OUT LPFTPORDERACTRSP lpOrderActRsp, char* szErrInfo) = 0; // 获取参数
virtual int __stdcall GetParam(IN const LPFTPGETPARAM lpGetParam, OUT LPFTPGETPARAMRSP lpGetParamRsp, char* szErrInfo) = 0;
// 设置参数
virtual int __stdcall SetParam(IN const LPFTPSETPARAM lpSetParam, OUT LPFTPSETPARAMRSP lpSetParamRsp, char* szErrInfo) = 0; // 控制命令
virtual int __stdcall CtrlCmd(IN const LPFTPCTRLCMD lpCtrlCmd, OUT LPFTPCTRLCMDRSP lpCtrlCmdRsp, char* szErrInfo) = 0; // 订阅委托信息
virtual int __stdcall SubOrderInfo(IN const LPFTPSUBORDER lpSubOrder, OUT LPFTPSUBORDERRSP lpSubOrderRsp, char* szErrInfo) = 0;
// 订阅持仓信息
virtual int __stdcall SubHoldInfo(IN const LPFTPSUBHOLDINFO lpSubHoldInfo, OUT LPFTPSUBHOLDINFORSP lpSubHoldInfoRsp, char* szErrInfo) = 0;
// 订阅资金信息
virtual int __stdcall SubBalance(IN const LPFTPSUBBALANCE lpSubBalance, OUT LPFTPSUBBALANCERSP lpSubBalanceRsp, char* szErrInfo) = 0;
// 订阅私有信息
virtual int __stdcall SubPrivate(IN const LPFTPSUBPRIVATE lpSubPrivate, OUT LPFTPSUBPRIVATERSP lpSubPrivateRsp, char* szErrInfo) = 0;
// 订阅指令状态
virtual int __stdcall SubOrderStatus(IN const LPFTPORDERSTATUS lpSubOrderSatus, OUT LPFTPSUBORDERSTATUSRSP lpSubOrderSatusRsp, char* szErrInfo) = 0;
} FTPCLIENTAPI, *LPFTPCLIENTAPI;
此处为导出函数
LPFTPCLIENTAPI __stdcall GetFTPClientAPI();SetPubInfoSPI(LPFTPPUBINFOSPI lpPubInfSPI)函数设置一个类对象的地址,作为回调函数的处理。回调的类声明如下:
typedef class FTPPubInfoSPI
{
public:
enum COMM_STATUS
{
CS_CONNECT = 'c',
CS_DISCONNECT = 'd',
};
virtual ~FTPPubInfoSPI()
{
}
// 控制消息
virtual int __stdcall OnConnStatus(COMM_STATUS);
// 委托信息
virtual int __stdcall OnOrderInfo(IN LPFTPPUBORDERINFO lpOrderInfo, IN int iCount)
{
}
// 持仓信息
virtual int __stdcall OnHoldInfo(IN LPFTPPUBHOLDINFO lpHoldInfo, IN int iCount)
{
}
// 资金信息
virtual int __stdcall OnBalance(IN LPFTPPUBBALANCE lpBalance, IN int iCount)
{
}
// 私有信息
virtual int __stdcall OnPrivate(IN LPFTPPUBPRIVATE lpPrivate)
{
}
// 指令状态
virtual int __stdcall OnOrderStatus(IN LPFTPPUBORDERSTATUS lpOrderStatus, IN int iCount)
{
}
} FTPPUBINFOSPI, *LPFTPPUBINFOSPI;
现在我在C#中用同时声明了两个上面类相似的结构,分别存放着delegate对象,对应着上面两个类的虚函数指针,下面是操作的声明(delegate的声明就不写了):
ublic struct APIFuncEntry
{
private IntPtr FuncDestroy;
public FuncFree Free;
public FuncPrepare Prepare;
public FuncUnPrepare UnPrepare;
public FuncConnect Connect;
public FuncDisConnect DisConnect;
public FuncSetPubInfoSPI SetPubInfoSPI; public FuncQueryOrder QueryOrder;
public FuncQueryTrade QueryTrade;
public FuncQueryComp QueryComp;
public FuncQueryInfo QueryInfo; public FuncAddOrder AddOrder;
public FuncDelOrder DelOrder;
public FuncActOrder OrderAct; public FuncGetParam GetParam;
public FuncSetParam SetParam; public FuncCtrlCmd CtrlCmd; public FuncSubOrderInfo SubOrderInfo;
public FuncSubHoldInfo SubHoldInfo;
public FuncSubBalance SubBalance;
public FuncSubPrivate SubPrivate;
public FuncSubOrderStatus SubOrderStatus;
}
[StructLayout(LayoutKind.Sequential)]
public struct FTPAPIStruct
{
public IntPtr ptr;
}
回调类的相似。我用GetFTPClientAPI取到C++中对象的指针,然后根据他的内容填充上面的两个结构
this.APIPtr = GetFTPClientAPI();
FTPAPIStruct APIStruct = (FTPAPIStruct)Marshal.PtrToStructure(this.APIPtr, typeof(FTPAPIStruct));
this.FuncEntry = (APIFuncEntry)Marshal.PtrToStructure(APIStruct.ptr, typeof(APIFuncEntry));
这样就可以直接使用FuncEntry 中的函数指针进行操作了,
同样我设置好回调结构中的对象,然后把回调结构的头指向返写到C++对象中,如下:
this.SPIPtr.ptr = Marshal.AllocHGlobal(Marshal.SizeOf(this.FuncEntry));
this.SPIFunc.OnConnStatus = OnConnStatus;
this.SPIFunc.OnOrderInfo = OnOrderInfo;
this.SPIFunc.OnHoldInfo = OnHoldInfo;
this.SPIFunc.OnBalance = OnBalance;
this.SPIFunc.OnPrivate = OnPrivate;
this.SPIFunc.OnOrderStatus = OnOrderStatus; Marshal.StructureToPtr(this.SPIFunc, this.SPIPtr.ptr, false);
this.FuncEntry.SetPubInfoSPI(this.APIPtr, ref this.SPIPtr);
这样设置好后,
我开始都是可以用的,但用一段时间后,就会发现在调用回调函数的时候,就会出现访问异常的情况
我想可能是this.SPIFunc对象被提前释放了,但没有确切的证据,有哪位兄弟,对这方面比较了解,能解释一下吗?
typedef class FTPClientAPI
{
protected:
/**
* 函数名称:构造函数
* 函数功能:主是防止用户自己创建此接口,不需要实现
* 参数列表:
* 修改记录: 20100514 赵海杰 创建
*/
FTPClientAPI()
{}
/**
* 函数名称:拷贝构造函数
* 函数功能:主是防止用户自己创建此接口,不需要实现
* 参数列表:
* 修改记录: 20100514 赵海杰 创建
*/
FTPClientAPI(FTPClientAPI&);
/**
* 函数名称:柝构函数
* 函数功能:主是防止用户自己创建此接口
* 参数列表:
* 修改记录: 20100514 赵海杰 创建
*/
virtual ~FTPClientAPI()
{}
public:
/**
* 函数名称:释放
* 函数功能:释放对象
* 参数列表:
* @Result : 返回接口版本号
* 修改记录: 20100521 赵海杰 创建
**/
virtual int __stdcall free() = 0;
/**
* 函数名称:准备
* 函数功能:申请各种资源
* 参数列表:
* @Result : Z_OK—成功,<0—失败
* 修改记录: 20100514 赵海杰 创建
**/
virtual int __stdcall Prepare() = 0;
/**
* 函数名称:解除准备
* 函数功能:释放各种资源
* 参数列表:
* @Result : Z_OK—成功,<0—失败
* 修改记录: 20100514赵海杰 创建
**/
virtual int __stdcall UnPrepare() = 0;
/**
* 函数名称:连接服务器
* 函数功能:连接上级服务器
* 参数列表:
* @szIPAddr : IP地址
* @usIPPort : IP端口
* @Result : 返回接口版本号
* 修改记录: 20100520 赵海杰 创建
**/
virtual int __stdcall Connect(char* szIPAddr, unsigned short usIPPort) = 0;
/**
* 函数名称:断开连接
* 函数功能:断开连接上级服务器
* 参数列表:
* @szIPAddr : IP地址
* @usIPPort : IP端口
* @Result : 返回接口版本号
* 修改记录: 20100520 赵海杰 创建
**/
virtual int __stdcall DisConnect() = 0;
/**
* 函数名称:设置信息到达时的回调函数
* 函数功能:释放各种资源
* 参数列表:
* @lpPubInfSPI: 回调函数
* @Result : Z_OK—成功,<0—失败
* 修改记录: 20100514赵海杰 创建
**/
virtual int __stdcall SetPubInfoSPI(LPFTPPUBINFOSPI lpPubInfSPI) = 0;
/**
* 业务处理接口
**/
// 查指令
virtual int __stdcall QueryOrder(IN const LPFTPQRYORDER lpQryOrder, OUT LPFTPQRYORDERRSP lpQryOrderRsp, INOUT int& iRowCount, char* szErrInfo) = 0;
// 查交易
virtual int __stdcall QueryTrade(IN const LPFTPQRYTRADE lpQryTrade, OUT LPFTPQRYTRADERSP lpQryTradeRsp, INOUT int& iRowCount, char* szErrInfo) = 0;
// 查组件
virtual int __stdcall QueryComp(IN const LPFTPQRYCOMP lpQryComp, OUT LPFTPQRYCOMPRSP lpQryCompRsp, INOUT int& iRowCount, char* szErrInfo) = 0;
// 查回报
virtual int __stdcall QueryInfo(IN const LPFTPQRYINFO lpQryInfo, OUT LPFTPQRYINFORSP lpQryInfoRsp, INOUT int& iRowCount, char* szErrInfo) = 0; // 添加指令
virtual int __stdcall AddOrder(IN const LPFTPADDORDER lpAddOrder, OUT LPFTPADDORDERRSP lpAddOrderRsp, char* szErrInfo) = 0;
// 删除指令
virtual int __stdcall DelOrder(IN const LPFTPDELORDER lpDelOrder, OUT LPFTPDELORDERRSP lpDelOrderRsp, char* szErrInfo) = 0;
// 操作指令
virtual int __stdcall OrderAction(IN const LPFTPORDERACT lpOrderAct, OUT LPFTPORDERACTRSP lpOrderActRsp, char* szErrInfo) = 0; // 获取参数
virtual int __stdcall GetParam(IN const LPFTPGETPARAM lpGetParam, OUT LPFTPGETPARAMRSP lpGetParamRsp, char* szErrInfo) = 0;
// 设置参数
virtual int __stdcall SetParam(IN const LPFTPSETPARAM lpSetParam, OUT LPFTPSETPARAMRSP lpSetParamRsp, char* szErrInfo) = 0; // 控制命令
virtual int __stdcall CtrlCmd(IN const LPFTPCTRLCMD lpCtrlCmd, OUT LPFTPCTRLCMDRSP lpCtrlCmdRsp, char* szErrInfo) = 0; // 订阅委托信息
virtual int __stdcall SubOrderInfo(IN const LPFTPSUBORDER lpSubOrder, OUT LPFTPSUBORDERRSP lpSubOrderRsp, char* szErrInfo) = 0;
// 订阅持仓信息
virtual int __stdcall SubHoldInfo(IN const LPFTPSUBHOLDINFO lpSubHoldInfo, OUT LPFTPSUBHOLDINFORSP lpSubHoldInfoRsp, char* szErrInfo) = 0;
// 订阅资金信息
virtual int __stdcall SubBalance(IN const LPFTPSUBBALANCE lpSubBalance, OUT LPFTPSUBBALANCERSP lpSubBalanceRsp, char* szErrInfo) = 0;
// 订阅私有信息
virtual int __stdcall SubPrivate(IN const LPFTPSUBPRIVATE lpSubPrivate, OUT LPFTPSUBPRIVATERSP lpSubPrivateRsp, char* szErrInfo) = 0;
// 订阅指令状态
virtual int __stdcall SubOrderStatus(IN const LPFTPORDERSTATUS lpSubOrderSatus, OUT LPFTPSUBORDERSTATUSRSP lpSubOrderSatusRsp, char* szErrInfo) = 0;
} FTPCLIENTAPI, *LPFTPCLIENTAPI;
此处为导出函数
LPFTPCLIENTAPI __stdcall GetFTPClientAPI();SetPubInfoSPI(LPFTPPUBINFOSPI lpPubInfSPI)函数设置一个类对象的地址,作为回调函数的处理。回调的类声明如下:
typedef class FTPPubInfoSPI
{
public:
enum COMM_STATUS
{
CS_CONNECT = 'c',
CS_DISCONNECT = 'd',
};
virtual ~FTPPubInfoSPI()
{
}
// 控制消息
virtual int __stdcall OnConnStatus(COMM_STATUS);
// 委托信息
virtual int __stdcall OnOrderInfo(IN LPFTPPUBORDERINFO lpOrderInfo, IN int iCount)
{
}
// 持仓信息
virtual int __stdcall OnHoldInfo(IN LPFTPPUBHOLDINFO lpHoldInfo, IN int iCount)
{
}
// 资金信息
virtual int __stdcall OnBalance(IN LPFTPPUBBALANCE lpBalance, IN int iCount)
{
}
// 私有信息
virtual int __stdcall OnPrivate(IN LPFTPPUBPRIVATE lpPrivate)
{
}
// 指令状态
virtual int __stdcall OnOrderStatus(IN LPFTPPUBORDERSTATUS lpOrderStatus, IN int iCount)
{
}
} FTPPUBINFOSPI, *LPFTPPUBINFOSPI;
现在我在C#中用同时声明了两个上面类相似的结构,分别存放着delegate对象,对应着上面两个类的虚函数指针,下面是操作的声明(delegate的声明就不写了):
ublic struct APIFuncEntry
{
private IntPtr FuncDestroy;
public FuncFree Free;
public FuncPrepare Prepare;
public FuncUnPrepare UnPrepare;
public FuncConnect Connect;
public FuncDisConnect DisConnect;
public FuncSetPubInfoSPI SetPubInfoSPI; public FuncQueryOrder QueryOrder;
public FuncQueryTrade QueryTrade;
public FuncQueryComp QueryComp;
public FuncQueryInfo QueryInfo; public FuncAddOrder AddOrder;
public FuncDelOrder DelOrder;
public FuncActOrder OrderAct; public FuncGetParam GetParam;
public FuncSetParam SetParam; public FuncCtrlCmd CtrlCmd; public FuncSubOrderInfo SubOrderInfo;
public FuncSubHoldInfo SubHoldInfo;
public FuncSubBalance SubBalance;
public FuncSubPrivate SubPrivate;
public FuncSubOrderStatus SubOrderStatus;
}
[StructLayout(LayoutKind.Sequential)]
public struct FTPAPIStruct
{
public IntPtr ptr;
}
回调类的相似。我用GetFTPClientAPI取到C++中对象的指针,然后根据他的内容填充上面的两个结构
this.APIPtr = GetFTPClientAPI();
FTPAPIStruct APIStruct = (FTPAPIStruct)Marshal.PtrToStructure(this.APIPtr, typeof(FTPAPIStruct));
this.FuncEntry = (APIFuncEntry)Marshal.PtrToStructure(APIStruct.ptr, typeof(APIFuncEntry));
这样就可以直接使用FuncEntry 中的函数指针进行操作了,
同样我设置好回调结构中的对象,然后把回调结构的头指向返写到C++对象中,如下:
this.SPIPtr.ptr = Marshal.AllocHGlobal(Marshal.SizeOf(this.FuncEntry));
this.SPIFunc.OnConnStatus = OnConnStatus;
this.SPIFunc.OnOrderInfo = OnOrderInfo;
this.SPIFunc.OnHoldInfo = OnHoldInfo;
this.SPIFunc.OnBalance = OnBalance;
this.SPIFunc.OnPrivate = OnPrivate;
this.SPIFunc.OnOrderStatus = OnOrderStatus; Marshal.StructureToPtr(this.SPIFunc, this.SPIPtr.ptr, false);
this.FuncEntry.SetPubInfoSPI(this.APIPtr, ref this.SPIPtr);
这样设置好后,
我开始都是可以用的,但用一段时间后,就会发现在调用回调函数的时候,就会出现访问异常的情况
我想可能是this.SPIFunc对象被提前释放了,但没有确切的证据,有哪位兄弟,对这方面比较了解,能解释一下吗?
解决方案 »
- 打包的软件安装后运行出现错误,请高手解决!!!!
- C# WINFORM下面需求一个HTML编辑器。。。求类似控件
- 可否用linq进行排序?
- winform里,如果判断datagridview处于编辑状态?
- excel 一个cell里,如何设置不同的颜色
- 如何获取控制台坐标
- 点击下拉框总是清空TextMode="Password"的文件数据
- 想写个程序,通过insert,update,delete对数据库产生压力(就是让数据库负荷加大,服务器CPU,内存,IO等利用率加大),用多线程如何?
- 如何控制winform程序只能打开一个
- 生成微软提供的例子工具
- 关于Activator.CreateInstance和Assembly.Load(_PATH).CreateInstance的问题
- C#winfrom与access数据库的问题
对的,你自己的分析是正确的。所以不要传指针之类的东西,那个不靠谱。除非你确保那个对象在当前作用域有效。这个错误很多程序员都爱犯。因为这种错误有可能调试的时候发现不到,因为对象释放在.net里面是随机的。所以完全可能调试正确。你可以看微软的代码,微软返回指针的时候一般都是返回对象成员指针,因为对象只要存在,里面成员一定在,就不会错。如果不是这样情况,微软都会送一个对象进去,然后回来一个对象的指针,这样也不会错。因为回来的就是送进来的对象,当前作用域是一定存在的。
也就是:public struct APIFuncEntry
{
......
public IntPtr Connect;
......
}
......
//需要保存GCHandle对象, 在不用的时候释放,防止内存泄露
int i = 0;
this.SPIHandles[i] = GCHandle.Alloc(new ConnectDelegate(OnConnect));
this.SPIFunc.Connect = GCHandle.ToIntPtr(this.SPIHandles[i]);
i++;
......
//最后释放
foreach(GCHandle h in this.SPIHandles)
{
h.Free();
}一般pInvoke的方法有delegate类型的参数的时候,.net会自动pinning参数对象.