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
表明被调用方清理堆栈。
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”属性。
函数参数和返回值可以是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 =brush: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> |
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 |
} |
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 |
} |
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 |
} |