当码农的第一篇博客
2014-05-13 14:23
176 查看
工作的时间越长越感受到时光的飞逝,越感受到自己的不足,时刻保持严谨的态度非常重要。
最近做一个项目需要使用对方用c++写的类库,本人本职c#,因为惧怕类型转换,回调之类的问题,于是把问题推给一个c++高手,让他帮忙写一个调用程序,调试轻松通过。但是最后在项目部署的时候还是出了大事,32位c++调用程序连接64位oracle数据库出现问题,折腾一番无解,找了个折中方案暂时应付。
空闲时,细细想来,用c#调用c++类库或许没有那么难,于是baidu,google向各位大牛学习,取经。
调用程序
VT_DSP_SERVER_HANDLE 为服务句柄 ,启动时获得,关闭时消除。
tagDspData 传数据时的结构体。
typedef void (__stdcall *CALLBACK_recv_front_data)(DspData data,void* pData); 回调函数,
VT_DSP_start_service 启动函数
VT_DSP_stop_service 关闭函数。
接下来开始类型转换, 最终转换结果为:
调试要点: 注意回调原型中的 “stdcall” [ typedef void (__stdcall *CALLBACK_recv_front_data)(DspData data,void* pData); ] 由于c++中默认是cdecl方式,很容易习惯性把c#上的委托也设置成cdecl方式,但是此处原型中明确要求用stdcall方式。
由于未深入理解网上大神们的说法, 把回调函数的第一个参数定义为ref tagDspData dsinfo , 或者定义为 IntPtr, 在根据获得的引用或者地址去组建结构, 结果一直被一个错误困扰" 尝试读取或写入受保护的内存。这通常指示其他内存已损坏",此处磕头谢罪,, 真是太不应该了。
深深觉得,要想成为高级码农,不能只靠baidu google,不能做无头苍蝇,还要加入自己的思想,有自己的判断,基础知识永远是自己的指路明灯,而不是人云亦云,否则,连人家说什么都无法听明白。
最近做一个项目需要使用对方用c++写的类库,本人本职c#,因为惧怕类型转换,回调之类的问题,于是把问题推给一个c++高手,让他帮忙写一个调用程序,调试轻松通过。但是最后在项目部署的时候还是出了大事,32位c++调用程序连接64位oracle数据库出现问题,折腾一番无解,找了个折中方案暂时应付。
空闲时,细细想来,用c#调用c++类库或许没有那么难,于是baidu,google向各位大牛学习,取经。
调用程序
typedef void * VT_DSP_SERVER_HANDLE ; typedef struct tagDspData { char szIP[20]; //DSP设备IP地址 char szTime[25];//获取到数人结果的时间 int nChannel; //DSP设备的通道号 int nIn; //进人数 int nOut; //出人数 float fInSpeed; //进速度 float fOutSpeed; //出速度 int nZoneNum; //区域人数 }DspData; typedef void (__stdcall *CALLBACK_recv_front_data)(DspData data,void* pData); VT_DSP_SERVER_API VT_DSP_SERVER_HANDLE VT_DSP_start_service(CALLBACK_recv_front_data callback,long lPort); VT_DSP_SERVER_API void VT_DSP_stop_service(VT_DSP_SERVER_HANDLE handle);
VT_DSP_SERVER_HANDLE 为服务句柄 ,启动时获得,关闭时消除。
tagDspData 传数据时的结构体。
typedef void (__stdcall *CALLBACK_recv_front_data)(DspData data,void* pData); 回调函数,
VT_DSP_start_service 启动函数
VT_DSP_stop_service 关闭函数。
接下来开始类型转换, 最终转换结果为:
public static IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero; [StructLayout(LayoutKind.Sequential,CharSet = CharSet.Ansi)] public struct tagDspData { [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)] public string szIP; //DSP设备IP地址 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 25)] public string szTime;//获取到数人结果的时间 [MarshalAs(UnmanagedType.I4)] public Int32 nChannel; //DSP设备的通道号 [MarshalAs(UnmanagedType.I4)] public Int32 nIn; //进人数 [MarshalAs(UnmanagedType.I4)] public Int32 nOut; //出人数 [MarshalAs(UnmanagedType.R4)] public Single fInSpeed; //进速度 [MarshalAs(UnmanagedType.R4)] public Single fOutSpeed; [MarshalAs(UnmanagedType.I4)] //出速度 public Int32 nZoneNum; //区域人数 }; [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)] public delegate IntPtr VT_DSP_start_service(ProcessDelegate callback, int lPort); [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)] public delegate void VT_DSP_stop_service(IntPtr handle); [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)] public unsafe delegate void ProcessDelegate(tagDspData dsinfo, IntPtr pData); public static ProcessDelegate callback; //载入dll,且获取函数指针 hLib = LoadLibrary("DspProxySDK.dll"); IntPtr api = GetProcAddress(hLib, "VT_DSP_start_service"); Type t = typeof(VT_DSP_start_service); vt_start = (VT_DSP_start_service)Marshal.GetDelegateForFunctionPointer(api, t); api = GetProcAddress(hLib, "VT_DSP_stop_service"); t = typeof(VT_DSP_stop_service); vt_stop = (VT_DSP_stop_service)Marshal.GetDelegateForFunctionPointer(api, t); ProcessDelegate pdele = new ProcessDelegate(lpSnapFunc); //启动函数, 将回调函数指针传给dll WTS_CURRENT_SERVER_HANDLE = vts(pdele, (int)8560); public unsafe void lpSnapFunc(tagDspData dsinfo, IntPtr pData){.....}
调试要点: 注意回调原型中的 “stdcall” [ typedef void (__stdcall *CALLBACK_recv_front_data)(DspData data,void* pData); ] 由于c++中默认是cdecl方式,很容易习惯性把c#上的委托也设置成cdecl方式,但是此处原型中明确要求用stdcall方式。
由于未深入理解网上大神们的说法, 把回调函数的第一个参数定义为ref tagDspData dsinfo , 或者定义为 IntPtr, 在根据获得的引用或者地址去组建结构, 结果一直被一个错误困扰" 尝试读取或写入受保护的内存。这通常指示其他内存已损坏",此处磕头谢罪,, 真是太不应该了。
深深觉得,要想成为高级码农,不能只靠baidu google,不能做无头苍蝇,还要加入自己的思想,有自己的判断,基础知识永远是自己的指路明灯,而不是人云亦云,否则,连人家说什么都无法听明白。