【好文章要分享】C#程序调用非托管C++ DLL文件的方法

__stdcall

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送:

Chase的技术博客

http://www.cnblogs.com/Chase/archive/2010/05/31/1748596.html



C#程序调用非托管C++ DLL文件的方法

2010-05-31 22:15 by Caizhi, 12196 阅读, 14 评论, 收藏编辑

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

 

using System.Runtime.InteropServices;

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

 

[DllImport(“dllfile path”)]

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

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

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

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

 

数据传递方法

1.基本数据类型的传递

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
using  System;
using  System.Text;
using  System.Runtime.InteropServices;
 
class  Program
{
     [DllImport( @"E:\Projects\testdll\debug\testdll.dll" )]
     public  static  extern  int  testfunc( int  a, float  b, double  c, char  d);
 
     static  void  Main( string [] args)
     {
         int  a = 1;
         float  b = 12;
         double  c = 12.34;
         char  d = 'A' ;
         testfunc(a,b,c,d);
         Console.ReadKey();
     }
}

  C++代码:

1
2
3
4
5
6
7
8
9
10
11
12
<pre class = "brush:cpp" >#include <iostream>
using  namespace  std;
 
extern  "C"
{
  _declspec( dllexport ) int  __stdcall testfunc( int  a, float  b, double  c, char  d)
  {
   cout<<a<< ", " <<b<< ", " <<c<< ", " <<d<<endl;
   return  0;
  }
}
</pre>

2.向DLL传入字符串

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using  System;
using  System.Text;
using  System.Runtime.InteropServices;
 
class  Program
{
     [DllImport( @"E:\Projects\testdll\debug\testdll.dll" )]
     public  static  extern  int  testfunc( string  a);
 
     static  void  Main( string [] args)
     {
         string  a= "Hello World!" ;
         testfunc(a);
         Console.ReadKey();
     }
}

  C++代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
using  namespace  std;
 
extern  "C"
{
  _declspec( dllexport ) int  __stdcall testfunc( char * astr)
  {
   cout<<astr<<endl;
   *astr= 'A' ; //更改字符串的数据
   cout<<astr<<endl;
   return  0;
  }
}

3.DLL传出字符串

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using  System;
using  System.Text;
using  System.Runtime.InteropServices;
 
class  Program
{
     [DllImport( @"E:\Projects\testdll\debug\testdll.dll" )]
     public  static  extern  int  testfunc(StringBuilder abuf);
 
     static  void  Main( string [] args)
     {
         StringBuilder abuf= new  StringBuilder();
         abuf.Capacity = 100; //设置字符串最大长度
         testfunc(abuf);
         Console.ReadKey();
     }
     
}

  C++代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
using  namespace  std;
 
extern  "C"
{
  _declspec( dllexport ) int  __stdcall testfunc( char * astr)
  {
   *astr++= 'a' ;
   *astr++= 'b' ; //C#中abuf随astr改变
   *astr= '\0' ;
 
   return  0;
  }
}

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
using  System;
using  System.Text;
using  System.Runtime.InteropServices;
 
[StructLayout(LayoutKind.Sequential)]
public  struct  Point
{
     public  double  x;
     public  double  y;
}
 
class  Program
{
     [DllImport( @"E:\Projects\testdll\debug\testdll.dll" )]
     public  static  extern  int  testfunc(Point p);
 
     static  void  Main( string [] args)
     {
         Point p;
         p.x = 12.34;
         p.y = 43.21;
         testfunc(p);
         Console.ReadKey();
     }   
}

C++代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
using  namespace  std;
 
struct  Point
{
     double  x;
     double  y;
};
 
extern  "C"
{
  _declspec( dllexport ) int  __stdcall testfunc(Point p)
  {
   cout<<p.x<< ", " <<p.y<<endl;
   return  0;
  }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值