C#调用C++Dll封装时遇到的一系列问题【转】
最近帮底层开发的同时用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")]
public static extern void SCCleanup(ref SipClient psip);
其中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);
将导出函数修改为:
extern "C" _declspec(dllexport) bool _stdcall Test(char* fileName, CiCiCallBack callback)
一开始的时候就只修改了定义那,却忘记了导出时的修改,差点就放弃了这条解决思路了,不过还好,所谓坚持就是胜利!
5、后来封装好拿到用户那里用,却总是提示说找不到C++那些dll.
网上一查,初步定位是开发环境引起的,跟环境部署有关系。我们的开发环境是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
C#调用C++Dll封装时遇到的一系列问题【转】的更多相关文章
- C#调用C++Dll封装时遇到的一系列问题
最近帮底层开发的同时用C#重新封装一下dll,也就是用C#类来封装C++Dll里的方法,以供用户使用. 之前也用到过类似的应用,大多数问题都出在类型转换上,但是这次的应用层出不穷,所以在这里总结一下, ...
- [转]C#调用C++dll
本文转载至http://www.cnblogs.com/ysharp/archive/2012/05/25/2517803.html 在合作开发时,C#时常需要调用C++DLL,当传递参数时时常遇到问 ...
- C#时常需要调用C++DLL
在合作开发时,C#时常需要调用C++DLL,当传递参数时时常遇到问题,尤其是传递和返回字符串是,现总结一下,分享给大家: VC++中主要字符串类型为:LPSTR,LPCSTR, LPCTSTR, st ...
- 简单实现python调用c#dll动态链接库
在python调用c#dll库时要先安装库clr,即安装pythonnet,参考文章:https://www.cnblogs.com/kevin-Y/p/10235125.html(为在python中 ...
- Delphi 中的DLL 封装和调用对象技术(刘艺,有截图)
Delphi 中的DLL 封装和调用对象技术本文刊登2003 年10 月份出版的Dr.Dobb's 软件研发第3 期刘 艺摘 要DLL 是一种应用最为广泛的动态链接技术但是由于在DLL 中封装和调用对 ...
- 多个类的DLL封装及调用
#define FaceLIBDLL #include "stdafx.h" #include "facedll.h" #include <opencv2 ...
- C# 调用C++ CLR dll类库时,实现从 string 到 sbyte* 的转换
问题描述 今天在做项目的时候碰到一个问题,就是用C++编写CLR类库dll的时候,C++的函数参数列表中包含一个char*的输出型参数,然而在C#调用该dll时候,会自动将函数的中的char*参数“翻 ...
- c#调用c++ dll的几种类型(转)
http://www.sosuo8.com/article-2012/dllleixingzhuanhuan.htm 在合作开发时,C#时常需要调用C++DLL,当传递参数时时常遇到问题,尤其是传 ...
- c#调用c++ dll(二)
当对c++几种调用方式有了解以后我们可以试着写个c++动态连接库了,我们现在来写个简单的c++求和函数并把它封装成dll,供以后的c#调用 我们写dll的时候,个人认为,要写就要把dll写好,写标准, ...
随机推荐
- Python+Selenium练习篇之4-利用link text定位元素
本文介绍如何通过link text 来定位页面元素,我们打开网页,一些可以点击的链接跳转上面的文字,就是link text,用百度首页举例来看: 在上面图中,这一排上面的文字都是link text,例 ...
- Windows下Redis3.2.10及图像化工具redis-desktop-manager安装教程
1.下载地址: GitHub地址:https://github-production-release-asset-2e65be.s3.amazonaws.com/3402186/bb1d10fc-3f ...
- Linux之ubuntu系统操作学习笔记
1,swp分区:当内存不够时用swp分区顶替内存 2,语言环境检查 locale –a:可以明白系统支持什么语言 3,安装软件: apt-cache search(软件):搜索软件 apt-cach ...
- Python学习-day15-JavaScript
JavaScript是一门编程语言,浏览器内置了JavaScript语言的解释器,所以在浏览器上按照JavaScript语言的规则编写相应代码之,浏览器可以解释并做出相应的处理. 一.如何编写 1.J ...
- Leetcode 491.递增子序列
递增子序列 给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是2. 示例: 输入: [4, 6, 7, 7] 输出: [[4, 6], [4, 7], [4, 6, 7] ...
- Summary—【base】(HTML)
Html知识点: 1. 建议开发人员计算机基本配置 a) 显示所有文件的后缀名* b) 文件的排列方式改为详细信息,并且名称一定要能够全部显示出来 c) 使用小的任务栏 d) 将常用的工具锁定到任务栏 ...
- SOCK5代理服务器
SOCK5代理服务器 简单介绍下比较好用的代理服务器,在部署过程中在内外网访问,需要切网比较麻烦,所以可以在互联网区部署sock5代理,通过配置代理切换服务器, 比较方便配置,节省时间. 官网: ht ...
- 四则运算出题系统,java
程序设计思想: 首先通过判断选择计算的范围,然后用随机数生成两个随机数,定义另一个数,将两个随机数计算得到的值赋给定义的数 程序代码: package Kaos1; import java.util. ...
- python(1)-- 变量类型
常规: Python有五个标准的数据类型: Numbers(数字):数字数据类型用于存储数值.他们是不可改变的数据类型,这意味着改变数字数据类型会分配一个新的对象.String(字符串):由数字.字母 ...
- 开发人员为组件添加自定义的className
在开发过程当中需要给组件写上自己的样式,这个时候怎么办呢? 直接给组件添加className这样是无效的 当给组件添加className之后 在写组件的时候需要对使用你的组件的开发人员提供自定义cla ...