zoukankan      html  css  js  c++  java
  • 【转】C#程序调用非托管C++ DLL文件的方法

    08年写的一篇文章,当时项目用C#开发,但是有一些希望重用之前的C++代码,于是研究了如何在C#中调用C++的DLL。

    C++中的函数声明

    1 extern "C" __declspec(dllexport) int __stdcall testfunc(char* astr,int* a);

      

    extern ”C”

      通常来说,C++编译器可能会改变函数和变量的名字,从而导致严重的链接程序问题。例如,假设使用C++编写一个DLL,当创建DLL时,Microsoft的编译器就会改变函数的名字。函数名将被设置一个前导下划线,再加上一个@符号的前缀,后随一个数字,表示作为参数传递给函数的字节数。例如,下面的函数是作为DLL的输出节中的_MyFunc@8输出的:

    1 __declspec(dllexport) LONG __stdcall MyFunc(int a, int b);

      如果用另一个供应商的工具创建了一个可执行模块,它将设法链接到一个名叫MyFunc的函数,该函数在Microsoft编译器已有的DLL中并不存在,因此链接将失败。

      使用extern “C”关键字可以使编译器按照C语言的方式编译DLL文件,即编译时不改变函数名。

    __declspec(dllexport)

      在 32 位编译器版本中,可以使用__declspec(dllexport) 关键字从DLL导出数据、函数、类或类成员函数。__declspec(dllexport) 会将导出指令添加到对象文件中,因此不需要使用.def文件。

      若要导出函数,__declspec(dllexport) 关键字必须出现在调用约定关键字的左边(如果指定了关键字)。例如:

    1 __declspec(dllexport) void __cdecl Function1(void);

    __stdcall

      表明被调用方清理堆栈。

    C#中的函数声明

    1 using System.Runtime.InteropServices;
    2       
    3   
    4 public class Program
    5 {
    6 [DllImport(@"E:\Projects\testdll\debug\testdll.dll")]
    7 public static extern int testfunc(StringBuilder abuf,ref int a);
    8 }

    using System.Runtime.InteropServices;

      System.Runtime.InteropServices 命名空间提供各种各样支持 COM interop 及平台调用服务的成员,使程序可以与非托管代码进行交互操作。

    [DllImport(“dllfile path”)]

      代码中DllImport关键字作用是告诉编译器入口点在哪里,并将打包函数捆绑在这个类中。在声明的时候还可以添加几个属性:

    1 [DllImport("MyDLL.dll",
    2 EntryPoint="mySum",
    3 CharSet=CharSet.Auto,
    4 CallingConvention=CallingConvention.StdCall)]

      EntryPoint: 指定要调用的 DLL 入口点。默认入口点名称是托管方法的名称 。
      CharSet: 控制名称重整和封送 String 参数的方式 (默认是UNICODE)
      CallingConvention指示入口点的函数调用约定(默认WINAPI)

      注意:必须在标记为”static”和”extern”的方法上指定”DllImport”属性。

    数据传递方法

    1.基本数据类型的传递

      函数参数和返回值可以是C#和C++的各种基本数据类型,如int, float, double, char(注意不是char*)等。
      示例:
      C#代码:

    01 using System;
    02 using System.Text;
    03 using System.Runtime.InteropServices;
    04   
    05 class Program
    06 {
    07     [DllImport(@"E:\Projects\testdll\debug\testdll.dll")]
    08     public static extern int testfunc(int a,float b,double c,char d);
    09   
    10     static void Main(string[] args)
    11     {
    12         int a = 1;
    13         float b = 12;
    14         double c = 12.34;
    15         char d = 'A';
    16         testfunc(a,b,c,d);
    17         Console.ReadKey();
    18     }
    19 }

      C++代码:

    01 <PRE class=cpp>#include <iostream>
    02 using namespace std;
    03   
    04 extern "C"
    05 {
    06  _declspec(dllexport) int __stdcall testfunc(int a,float b,double c,char d)
    07  {
    08   cout<<a<<", "<<b<<", "<<c<<", "<<d<<endl;
    09   return 0;
    10  }
    11 }
    12 </PRE>

    2.向DLL传入字符串

      C#中使用string定义字符串,将字符串对象名传给DLL。
      注意:在DLL中更改字符串的值,C#中的值也会改变。
      缺点:无法改变字符串的长度,建议使用第3种方法。
      C#代码:

    01 using System;
    02 using System.Text;
    03 using System.Runtime.InteropServices;
    04   
    05 class Program
    06 {
    07     [DllImport(@"E:\Projects\testdll\debug\testdll.dll")]
    08     public static extern int testfunc(string a);
    09   
    10     static void Main(string[] args)
    11     {
    12         string a="Hello World!";
    13         testfunc(a);
    14         Console.ReadKey();
    15     }
    16 }

      C++代码:

    01 #include <iostream>
    02 using namespace std;
    03   
    04 extern "C"
    05 {
    06  _declspec(dllexport) int __stdcall testfunc(char* astr)
    07  {
    08   cout<<astr<<endl;
    09   *astr='A';//更改字符串的数据
    10   cout<<astr<<endl;
    11   return 0;
    12  }
    13 }

    3.DLL传出字符串

      C#中使用StringBuilder对象创建变长数组,并设置StringBuilder的Capacity为数组最大长度。将此对象名传递给DLL,使用char*接收。
      C#代码:

    01 using System;
    02 using System.Text;
    03 using System.Runtime.InteropServices;
    04   
    05 class Program
    06 {
    07     [DllImport(@"E:\Projects\testdll\debug\testdll.dll")]
    08     public static extern int testfunc(StringBuilder abuf);
    09   
    10     static void Main(string[] args)
    11     {
    12         StringBuilder abuf=new StringBuilder();
    13         abuf.Capacity = 100;//设置字符串最大长度
    14         testfunc(abuf);
    15         Console.ReadKey();
    16     }
    17       
    18 }

      C++代码:

    01 #include <iostream>
    02 using namespace std;
    03   
    04 extern "C"
    05 {
    06  _declspec(dllexport) int __stdcall testfunc(char* astr)
    07  {
    08   *astr++='a';
    09   *astr++='b';//C#中abuf随astr改变
    10   *astr='\0';
    11   
    12   return 0;
    13  }
    14 }

    4.DLL传递结构体(需要在C#中重新定义,不推荐使用)

      C#中使用StructLayout重新定义需要使用的结构体。
      注意:在DLL改变结构体成员的值,C#中随之改变。
      C#代码:

    01 using System;
    02 using System.Text;
    03 using System.Runtime.InteropServices;
    04   
    05 [StructLayout(LayoutKind.Sequential)]
    06 public struct Point
    07 {
    08     public double x;
    09     public double y;
    10 }
    11   
    12 class Program
    13 {
    14     [DllImport(@"E:\Projects\testdll\debug\testdll.dll")]
    15     public static extern int testfunc(Point p);
    16   
    17     static void Main(string[] args)
    18     {
    19         Point p;
    20         p.x = 12.34;
    21         p.y = 43.21;
    22         testfunc(p);
    23         Console.ReadKey();
    24     }    
    25 }

    C++代码:

    01 #include <iostream>
    02 using namespace std;
    03   
    04 struct Point
    05 {
    06     double x;
    07     double y;
    08 };
    09   
    10 extern "C"
    11 {
    12  _declspec(dllexport) int __stdcall testfunc(Point p)
    13  {
    14   cout<<p.x<<", "<<p.y<<endl;
    15   return 0;
    16  }
    17 }
  • 相关阅读:
    那一定都是你的错!- 一次FastDFS并发问题的排查经历
    程序员和烟民
    Spring Boot(十六):使用 Jenkins 部署 Spring Boot
    阿里Dubbo疯狂更新,关Spring Cloud什么事?
    从架构演进的角度聊聊Spring Cloud都做了些什么?
    FastDFS 集群 安装 配置
    中小型互联网公司微服务实践-经验和教训
    jvm系列(十一):JVM演讲PPT分享
    jvm系列(十):如何优化Java GC「译」
    Spring Boot (十五): Spring Boot + Jpa + Thymeleaf 增删改查示例
  • 原文地址:https://www.cnblogs.com/lzh_527/p/1982831.html
Copyright © 2011-2022 走看看