zoukankan      html  css  js  c++  java
  • 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);
      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

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

      

  • 相关阅读:
    WCF 第八章 安全 确定替代身份(中)使用AzMan认证
    WCF 第八章 安全 总结
    WCF 第八章 安全 因特网上的安全服务(下) 其他认证模式
    WCF Membership Provider
    WCF 第八章 安全 确定替代身份(下)模仿用户
    WCF 第八章 安全 因特网上的安全服务(上)
    WCF 第九章 诊断
    HTTPS的七个误解(转载)
    WCF 第八章 安全 日志和审计
    基于比较的排序算法集
  • 原文地址:https://www.cnblogs.com/wdysunflower/p/1813947.html
Copyright © 2011-2022 走看看