在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
最近帮底层开发的同时用C#重新封装一下dll,也就是用C#类来封装C++Dll里的方法,以供用户使用。 之前也用到过类似的应用,大多数问题都出在类型转换上,但是这次的应用层出不穷,所以在这里总结一下,以供自己以后查阅,也希望对大家能够有所帮助。
首先,重复一下一些基本使用方法。具体的那些方式在这里就不重复讲了,网上很多的。比如http://blog.csdn.net/sunboyljp/archive/2009/12/31/5110639.aspx c++ 头文件中的定义: NPD_API int NP_Init(); C#中定义函数 [DllImport("npd_api.dll")] public static extern int NP_Init();
基本类型转换见下表(我用到过的): BSTR——StringBuilder LPCTSTR ——StringBuilder LPCWSTR ——IntPtr handle ——IntPtr hwnd ——IntPtr char * ——string int * ——ref int int & ——ref int void * ——IntPtrs unsigned char * ——ref byte BOOL ——bool DWORD ——uint或int(我用的是uint,没出过什么问题)
我的问题来了,长期的经验教训我知道了: 1、指针做参数时在C#中一定要使用ref 或out关键字,尤其是结构体指针,要不会报内存读取错误,即使不报错数据也是不太对的。呵呵 SIPCLIENT_API void WINAPI SCCleanup(SipClient * psip); [DllImport("sipclient.dll")] 其中SipClient是一个结构体。
2、重写结构体的时候,之前有指明类型长度或数组长度的地方,也要进行相应的标注,要不也会导致内存错误。 typedef struct {
char sDVRIP[16]; /* DVR IP地址 */ char sDVRIPMask[16]; /* DVR IP地址掩码 */ DWORD dwNetInterface; /* 10M/100M自适应,索引 */ WORD wDVRPort; /* 端口号 */ BYTE byMACAddr[MACADDR_LEN]; /* 服务器的物理地址 */ }NET_POSA_ETHERNET; public struct NET_POSA_ETHERNET { [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)] public string sDVRIP; //DVR IP地址 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)] public string sDVRIPMask; // DVR IP地址掩码 public uint dwNetInterface; //网络接口 1-10MBase-T 2-10MBase-T全双工 3-100MBase-TX 4-100M全双工 5-10M/100M自适应 public uint wDVRPort; //端口号 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] public byte[] byMACAddr; //[MACADDR_LEN]; //PPPoE用户名//服务器的物理地址 }
3、遇到这样一个问题,折腾了大半天时间——http://space.cnblogs.com/q/16616/。 最后是在C++那边做了修改解决的,通过制定模块定义 (.def) 文件,统一制定导出函数对应的名称。返回值为结构体指针的函数用IntPtr也能使用了。 SIPCLIENT_API SipClient* SCInit(const char * reaml,
const char * from_ip, int from_port,
const char * to_ip, int to_port, const char * server_id,
const char * user_id, const char * user_name, void * user_obj_param);
[DllImport("sipclient.dll")] public static extern IntPtr SCInit(string reaml, string from_ip, int from_port, string to_ip, int to_port, string server_id, string user_id, string user_name, IntPtr user_obj_param);
4、后来还遇到个回调函数导致的崩溃问题,又耽误了大半天时间,下班了还耽搁了会终于找的解决发办法了。 刚开始同事分析出了崩溃的原因,都是回收方式惹的祸,可参见http://www.hudong.com/wiki/WINAPI,尝试使用__stdcall,但是还是没有解决问题 后来实践证明,程序是很严谨的,半点差错都不能出才不会导致错误,思路还是__stdcall,只不过少改了东西,有两个地方需要改,才能保证不出错。 参考http://hi.baidu.com/tease/blog/item/1fe7213802780f22b9998f5a.html。 关键就是这两句话 typedef void (_stdcall *CiCiCallBack) (bool started, void* client,char *message); 一开始的时候就只修改了定义那,却忘记了导出时的修改,差点就放弃了这条解决思路了,不过还好,所谓坚持就是胜利! 网上一查,初步定位是开发环境引起的,跟环境部署有关系。我们的开发环境是vs2008,而客户使用的vs2010,通过几次尝试,问题终于了。 首先考虑是缺少某些C++必备的运行库,存在相互依赖关系,所以导致找不到dll。用查看Dependency Walker查看才发现真的是客户机子上少了一些东西。 但是此路不通,将缺少的那些东西拷贝到可执行程序目录下,问题依旧没有解决。但是依旧坚持这条路~ 尝试安装vcredist_x86.exe,以排除是否还是缺少了某些运行库的可能,问题依然存在。 后来我想起来之前搜索问题的时候,看到好像跟dll的Releas\Debug版本还有关系,所有又尝试提议让同事将他们的c++dll改为Release版的。 因为项目是多个人一起做了,编译Release版还花了不少时间,不过好歹问题终于解决了! 总结:直接安装vcredist_x86.exe,所有dll必须使用Release版的。如果使用Debug版的就必须保证可执行程序目录下的dll是完整的,缺一不可! 网上详细的讲解也很多,感觉这个总结的很好http://hi.baidu.com/fairysky/blog/item/e7a8366dbaa735f3431694c8.html。
做程序就怕出现问题,出现问题就怕不知道原因,知道原因了就好找解决的办法啦!
原文 : http://www.cnblogs.com/wdysunflower/archive/2010/09/01/1813947.html |
请发表评论