最近帮底层开发的同时用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);

  IntPtr client = IntPtr.Zero;
client = SIPCLIENT_API.SCInit(REALM, CLIENT_IP, CLIENT_PORT, SERVER_IP,
     SERVER_PORT, SERVER_ID, USER_ID, USER_ID, IntPtr.Zero);
if (client != IntPtr.Zero)
sipclient = (SipClient)Marshal.PtrToStructure(client, typeof(SipClient));
else
MessageBox.Show("SipClient初始化失败!");

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

做程序就怕出现问题,出现问题就怕不知道原因,知道原因了就好找解决的办法啦!

C#调用C++Dll封装时遇到的一系列问题的更多相关文章

  1. C#调用C++Dll封装时遇到的一系列问题【转】

      最近帮底层开发的同时用C#重新封装一下dll,也就是用C#类来封装C++Dll里的方法,以供用户使用. 之前也用到过类似的应用,大多数问题都出在类型转换上,但是这次的应用层出不穷,所以在这里总结一 ...

  2. [转]C#调用C++dll

    本文转载至http://www.cnblogs.com/ysharp/archive/2012/05/25/2517803.html 在合作开发时,C#时常需要调用C++DLL,当传递参数时时常遇到问 ...

  3. C#时常需要调用C++DLL

    在合作开发时,C#时常需要调用C++DLL,当传递参数时时常遇到问题,尤其是传递和返回字符串是,现总结一下,分享给大家: VC++中主要字符串类型为:LPSTR,LPCSTR, LPCTSTR, st ...

  4. 简单实现python调用c#dll动态链接库

    在python调用c#dll库时要先安装库clr,即安装pythonnet,参考文章:https://www.cnblogs.com/kevin-Y/p/10235125.html(为在python中 ...

  5. Delphi 中的DLL 封装和调用对象技术(刘艺,有截图)

    Delphi 中的DLL 封装和调用对象技术本文刊登2003 年10 月份出版的Dr.Dobb's 软件研发第3 期刘 艺摘 要DLL 是一种应用最为广泛的动态链接技术但是由于在DLL 中封装和调用对 ...

  6. 多个类的DLL封装及调用

    #define FaceLIBDLL #include "stdafx.h" #include "facedll.h" #include <opencv2 ...

  7. C# 调用C++ CLR dll类库时,实现从 string 到 sbyte* 的转换

    问题描述 今天在做项目的时候碰到一个问题,就是用C++编写CLR类库dll的时候,C++的函数参数列表中包含一个char*的输出型参数,然而在C#调用该dll时候,会自动将函数的中的char*参数“翻 ...

  8. c#调用c++ dll的几种类型(转)

    http://www.sosuo8.com/article-2012/dllleixingzhuanhuan.htm   在合作开发时,C#时常需要调用C++DLL,当传递参数时时常遇到问题,尤其是传 ...

  9. c#调用c++ dll(二)

    当对c++几种调用方式有了解以后我们可以试着写个c++动态连接库了,我们现在来写个简单的c++求和函数并把它封装成dll,供以后的c#调用 我们写dll的时候,个人认为,要写就要把dll写好,写标准, ...

随机推荐

  1. 20155321 2016-2017-2 《Java程序设计》第八周学习总结

    20155321 2016-2017-2 <Java程序设计>第八周学习总结 教材学习内容总结 创建Logger对象 static Logger getLogger(String name ...

  2. shell脚本常用参数

    shell 脚本 常用参数 #!/bin/sh # 在脚本第一行脚本头 # sh为当前系统默认shell,可指定为bash等shell sh -x # 执行过程 sh -n # 检查语法 (a=bbk ...

  3. [转]Restrict关键字

    0 定义 C99中新增加的用于修饰指针的关键字,用于表示该指针所指向的内存,只有通过该指针访问得到(如下ptr指向的内存单元只能通过ptr访问得到).从而可以让编译器对代码进行优化,生成更有效率的汇编 ...

  4. 编写灵活、稳定、高质量的 HTML 代码的规范

    不管有多少人共同参与同一项目,一定要确保每一行代码都像是同一个人编写的. 语法 用两个空格来代替制表符(tab) -- 这是唯一能保证在所有环境下获得一致展现的方法. 嵌套元素应当缩进一次(即两个空格 ...

  5. Nessus+Metasploit

    1.环境 受害者IP:10.0.0.6 2.攻击 首先使用Nessus进行扫描 扫描结果 我们将结果导出来,然后导入msfconsole中 使用vulns进行漏洞分析 查找对应的漏洞 使用漏洞进行攻击 ...

  6. 云计算--hdfs dfs 命令

    在hadoop安装目录下:/hadoop2/hadoop-2.7.3 1.创建目录 bin/hdfs dfs -mkdir /user bin/hdfs dfs -mkdir /user/<us ...

  7. LOJ 2249: 洛谷 P2305: 「NOI2014」购票

    题目传送门:LOJ #2249. 题意简述: 有一棵以 \(1\) 号节点为根节点的带边权的树. 除了 \(1\) 号节点的所有节点上都有人需要坐车到达 \(1\) 号节点. 除了 \(1\) 号节点 ...

  8. 为何gpio_to_irq不能静态使用?【转】

    之前在调试传感器模块的时候发现,在结构体声明的时候irq成员使用gpio_to_irq会报错,而动态使用的话就没有问题.就对gpio_to_irq为什么不能静态使用产生了疑问.恰巧最近又有朋友遇到了同 ...

  9. grep和sed匹配多个字符关键字的用法

    GNU sed和UNIX sed 写法不一样 匹配多个关键词,打印出匹配的行,效果类似于 grep grep hello\|world file > output 或者用扩展正则 grep -E ...

  10. 在阿里云申请Symantec免费SSL证书操作流程

    2016年阿里云与国内证书颁发机构天威诚信推出了基于Symantec(赛门铁克)的免费SSL证书,有需要免费SSL证书产品的可以前往阿里云进行申请. 申请地址:阿里云云盾证书服务—Symantec免费 ...