1 |
extern "C" __declspec ( dllexport ) int __stdcall testfunc( char * astr, int * a); |
extern 」C」html
一般來講,C++編譯器可能會改變函數和變量的名字,從而致使嚴重的連接程序問題。例如,假設使用C++編寫一個DLL,當建立DLL 時,Microsoft的編譯器就會改變函數的名字。函數名將被設置一個前導下劃線,再加上一個@符號的前綴,後隨一個數字,表示做爲參數傳遞給函數的字 節數。例如,下面的函數是做爲DLL的輸出節中的_MyFunc@8輸出的:ios
1 |
__declspec ( dllexport ) LONG __stdcall MyFunc( int a, int b); |
若是用另外一個供應商的工具建立了一個可執行模塊,它將設法連接到一個名叫MyFunc的函數,該函數在Microsoft編譯器已有的DLL中 並不存在,所以連接將失敗。數組
使用extern 「C」關鍵字能夠使編譯器按照C語言的方式編譯DLL文件,即編譯時不改變函數名。app
__declspec(dllexport)函數
在 32 位編譯器版本中,能夠使用__declspec(dllexport) 關鍵字從DLL導出數據、函數、類或類成員函數。__declspec(dllexport) 會將導出指令添加到對象文件中,所以不須要使用.def文件。工具
若要導出函數,__declspec(dllexport) 關鍵字必須出如今調用約定關鍵字的左邊(若是指定了關鍵字)。例如:ui
1 |
__declspec ( dllexport ) void __cdecl Function1( void ); |
__stdcallspa
代表被調用方清理堆棧。debug
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;code
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 |
} |