• 【转】 编写C#调用的C++DLL


    最近一段时间,经常遇到这些问题,前一阵子研究了一下,没有记下来,没想到最近研究又有些不记得了,今天把它写下来以备忘。
    
    一般我们提供给其他语言调用的DLL,都是用C或者C++编写,然后封装。我这边也是采用的C++。
    
    首先有几个注意点:
    
    1、如果功能很简单,或者不使用第三方库(如MFC自带的库),建立一个win32的控制台程序就可以了,然后把项目生成改为DLL。值得一提的是,代码生成里面
    
              运行时库分四种:
    
                        (1)多线程MTD(静态库,编译之后,你的lib带有调试功能)——> debug时用
    
                        (2)多线程MT(静态库,没有调试功能)                           ——> release时用
    
                        (3)多线程DLL MTD(动态库,带有调试功能)                  ——> debug时用
    
                        (4)多线程DLL MT(动态库,没有有调试功能)。              ——> release时用s
    
              既然封装DLL,那调试的时候用(3),发布的时候用(4)。
    
    2、设置为导出函数,并采用C风格。函数前加extern "C" __declspec(dllexport)。定义函数在退出前自己清空堆栈,在函数前加__stdcall。
    
          如extern "C" __declspec(dllexport) int __stdcall add(int x,int y);
    
     
    
          具备上述条件时,生成的DLL就含有导出函数的功能了,不过此时DLL中的函数名称不是规则的,使用编译器自定义的,可能是这样一个名字_add@20,具体的可以用VS的Depends工具查看一下。
    
     
    
    3、把导出函数名称变为标准名称,需加模块定义文件,就是.def文件。
    
    内容如下:(需要注释,前面加分号就可以了,注释需要单独行)
    
    LIBRARY "TEST"
    
         EXPORTS
    
                    ;add函数
    
                    adds
    
     
    
    LIBRARY 库名称
    
    EXPORTS 需要导出的各个函数名称
    
     
    
         重新编译之后,再用Depends工具看一下,函数已经变成标准add,而不是_add@20。这个在动态加载时很有用,特别是在GetProcAddress函数寻找入库函数的时候。
    
     
    
    4、C#调用C++ DLL,介绍两种方法
    
         (1)静态加载
    
                 [DllImport("TEST.dll", EntryPoint = "add")]
    
                 public int add(int x,int y);//与dll中一致 
    
     
    
                 注意如果需要返回字符串可以这样
    
                 C++int getString(const char* source,char* dest);
    
                 C#中
    
                 int getString(string source,StringBuilder sbr);
    
     
    
                 切记调用的时候给StringBuilder 分配空间,否则会报错。
    
                 如dest 长度为10,可以这样。
    
                 StringBuilder sbr=new StringBuilder(10);
    
                 getString("hello",sbr);
    
     
    
                 如果你希望C++的dll还能被VB等语言调用,建议将字串写成com的形式
    
                 如
    
                 C++int getString(BSTR source,BSTR dest);//BSTR就是一个com形式的字符数组,相当于字符串
    
                 C#中
    
                 int getString(string source,StringBuilder sbr);
    
                 VB中
    
                 Declare Function getString Lib "TEST.dll" (ByVal source As String, ByVal dest As String) As Integer;         
    
                
    
         (2)动态加载
    
                 [DllImport("kernel32.dll")]
                 private extern static IntPtr LoadLibrary(String path);//path 就是dll路径 返回结果为0表示失败。
                 [DllImport("kernel32.dll")]
                 private extern static IntPtr GetProcAddress(IntPtr lib, String funcName);//lib是LoadLibrary返回的句柄,funcName 是函数名称 返回结果为0标识失败。
                 [DllImport("kernel32.dll")]
                 private extern static bool FreeLibrary(IntPtr lib);
    
     
    
                 //声明委托
    
                 delegate int ADD(int x,int y);
    
                  
    
                 //使用动态加载
    
                 IntPtr hLib = LoadLibrary(dllPath);//加载函数
    
                 IntPtr apiFunction = GetProcAddress(hLib, apiName);//获取函数地址
    
                 int i = Marshal.GetLastWin32Error();
                 if (apiFunction.ToInt32() == 0)//0表示函数没找到
                     return null;
    
                 //获取函数接口,相当于函数指针
                 ADD add = (Delegate)Marshal.GetDelegateForFunctionPointer(apiFunction, typeof(ADD)) as ADD;          
    
                 //调用函数
    
                 add(1,2);
    
                 //释放句柄
    
                 FreeLibrary(hLib );
    
        最后,
    
                1)C++在返回字符串时,切记最后添加/0,不然在C#等中调用,会显示部分乱码。    
    
                2)C++动态申请的内存,需在出函数之前就必须释放,否则会报意想不到的错误。比如内存写入错误等等。
  • 相关阅读:
    php的cURL库介绍
    php函数ob_start()、ob_end_clean()、ob_get_contents()
    php中curl、fsockopen的应用
    App架构设计经验谈:服务端接口的设计
    图解正向代理与反向代理
    三种数据库连接池的配置
    数据库连接池在Tomcat中的几种配置方法
    Java四种线程池的使用
    JVM调优总结(一)-- 一些概念
    JVM调优总结(十)-调优方法
  • 原文地址:https://www.cnblogs.com/xyzhuzhou/p/3977454.html
Copyright © 2020-2023  润新知