使用DLL进行不同语言之间的调用

转自http://blog.csdn.net/akirya/archive/2008/02/14/2093031.aspx

__declspec(dllexport)是告诉编译器用来导出函数的,在代码中不另作说明了 extern "C"的意思就是用C的方式来导出函数,为什么要用C的方式来导出呢. 因为C++中有重载,编译器会对函数名进行更改,修饰成唯一的函数名. __stdcall告诉编译器函数调用方式.这点可以参考其他文章, 我预计也会在blog中写上一篇关于函数调用方式. C++编写的DLL

extern   " C "  __declspec(dllexport)  int   Max( int  x, int  y) {     return x>y?x:y; } __declspec(dllexport)  int  __stdcall Min( int  x, int  y) {     return x<y?x:y; } __declspec(dllexport)  double  Min( double  x, double  y) {     return x<y?x:y; }
这是一段代码,使用参数和返回值为int和double是有目的的 在VC8下int是32位的double是64位. 使用重载也是有目的的. 编译命令如下 cl /c dlltest.cpp link /DLL dlltest.obj 编译后使用Depends查看dll中的内容.能看到dll中有3个函数. ?Min@@YANNN@Z ?Min@@YGHHH@Z Max 其中的?Min@@YANNN@Z和?Min@@YGHHH@Z就是重载两个Min函数. 可以使用运行库中未公开函数__unDNameEx看到相对应的函数声明. 这两个名字就是C++函数和二进制文件中的函数名相对应的. 重载的时候根据参数不同链接到不同的函数名字 C如果使用的话得动态加载函数 接下来看如何传递指针类型.在以下的部分都使用C可以使用函数 于是将extern "C" __declspec(dllexport)定义为一个宏
#define  DLLEXPORT extern "C" __declspec(dllexport) DLLEXPORT  int  swap( int *  x, int &  y) {     int z = *x;     *= y ;     y = z;     return 0; } /* 这和前面的例子重复了,主要用于调用的例子 */ DLLEXPORT  double  __stdcall Max_d( double  x, double  y) {     return x>y?x:y; }
接下来是使用结构体的,由于结构体会对成员进行对齐, 所以在调用的时候需要注意和这里的结构体有相同的内存布局
#include < string .h > struct  testStruct {     char a;     int b;     double c;     char sz[5]; } ; DLLEXPORT  int  __stdcall UseStruct(testStruct *  p) {     p->= 'a';     p->= 20;     p->= 1.234;     strcpy( p->sz , "abcd" );     return sizeof(testStruct); } /*这是修改了内存对齐的结构体使用,主要在调用的时候有区别*/ #pragma  pack(push) #pragma  pack( 1 ) struct  testStruct2 {     char a;     int b;     double c;     char sz[5]; } ; #pragma  pack(pop) DLLEXPORT  int  __stdcall UseStruct2(testStruct2 *  p) {     p->= 'a';     p->= 20;     p->= 1.234;     strcpy( p->sz , "abcd" );     return sizeof(testStruct2); }
这是使用回调函数的例子,这里想成功调用主要还是要看如何调用.
DLLEXPORT  int  __stdcall UserCallBackFunc(  const   char *  lp,  int  (__stdcall  * p)( const   char * ) ) {     return p( lp ); }
def文件内容如下 EXPORTS     Max     swap     Max_d     UseStruct     UseStruct2     UserCallBackFunc 这里的def文件不是必须的,如果不使用def文件编译,则dll中的一些函数名前面会被加上_后面加上参数大小 ?Min@@YANNN@Z ?Min@@YGHHH@Z Max _Max_d@16 _UseStruct2@4 _UseStruct@4 _UserCallBackFunc@8 swap 当然如果只是C使用的话不使用def文件也是可以的,但要是其他语言的话就有一些不方便了也得使用 _Max_d@16 这样的函数名字,有些不美观.在这里增加def文件也就是改名的意思 编译方法如下 cl /c dlltest.cpp link /DLL /DEF:dlltest.def dlltest.obj 使用了def文件后dll中所导出的函数如下 ?Min@@YANNN@Z ?Min@@YGHHH@Z Max Max_d UseStruct2 UseStruct UserCallBackFunc swap 除去重载的两个函数,凡是使用extern "C"的函数都可以正常现实 C/C++使用方法 分别使用.c和.cpp的扩展名编译,就是C的调用方法和C++的调用方法了
#ifdef __cplusplus      extern   " C "  __declspec(dllimport)  int   Max( int  x, int  y);      int  __stdcall Min( int  x, int  y);      double  Min( double  x, double  y); #else     __declspec(dllimport)  int  Max( int  x, int  y); #endif #ifdef __cplusplus #    define DLLIMPORT  extern   " C "  __declspec(dllimport) #else #    define DLLIMPORT __declspec(dllimport) #endif DLLIMPORT  int  swap( int *  x, int *  y); DLLIMPORT  double  __stdcall Max_d( double  x, double  y); #include < string .h > typedef  struct  __testStruct {     char a;     int b;     double c;     char sz[5]; } testStruct; DLLIMPORT  int  __stdcall UseStruct(testStruct *  p); #pragma pack(push) #pragma pack( 1 ) typedef  struct  __testStruct2 {     char a;     int b;     double c;     char sz[5]; }  testStruct2; #pragma pack(pop) DLLIMPORT  int  __stdcall UseStruct2(testStruct2 *  p); DLLIMPORT  int  __stdcall UserCallBackFunc(  const   char *  lp,  int  (__stdcall  * p)( const   char * ) ); #include < stdio.h > #pragma  comment(lib,"dlltest.lib") #include < windows.h > int  __stdcall CallBackFunc( const   char * lp) {     return printf("%s ",lp); } int  main() {     int x=2,y=3;     testStruct s1;     testStruct2 s2; #ifdef __cplusplus     printf("%d ",Min( 2,3 ) );     printf("%f ",Min( 2.0,3.0 ) ); #else     int(__stdcall *pMin)(int,int)=0;     double(*pMin_d)(double,double)=0;     HMODULE hDll = GetModuleHandle("dlltest.dll");     pMin_d = (double(*)(double,double)) GetProcAddress( hDll , "?Min@@YANNN@Z" );     if( pMin_d )         printf("%f ",pMin_d(3.0,5.0 ) );     pMin = (int(__stdcall*)(int,int)) GetProcAddress( hDll , "?Min@@YGHHH@Z" );     if( pMin )         printf("%d ",pMin( 3 , 5 ) ); #endif     swap( &x,&y );     printf("swap = %d,%d ",x,y);     printf( "%d " , Max( 2,4 ) );     printf( "%f " , Max_d( 2.0,4.0 ) );     UseStruct(&s1);     UseStruct2( &s2 );     printf( "%c,%d,%f,%s ",s1.a,s1.b,s1.c,s1.sz);     printf( "%c,%d,%f,%s ",s2.a,s2.b,s2.c,s2.sz);     UserCallBackFunc("abcdef",CallBackFunc);     return 0; } ;
Delphi的使用方法.
program test; {$APPtype CONSOLE} uses   SysUtils,Classes,Math,Windows; type   testStruct  =  record     a:Char;     b:Integer;     c:Double;     sz:array[ 0 .. 4 ] of  char ;   end; {$A1} {定义record使之和 修改了对齐的结构体有相同的内存布局 }   testStruct2  =  record     a:Char;     b:Integer;     c:Double;     sz:array[ 0 .. 4 ] of  char ;   end; {$A8}   CallBackFunc type  =  function(x:PChar):Integer;stdcall;   function Max( x:Integer;y:Integer ):Integer;cdecl;external  ' dlltest.dll '  name  ' Max ' ;   function Max_d( x:Double;y:Double):Double    ;stdcall;external  ' dlltest.dll '  name  ' Max_d ' ;   function swap(var x:Integer;var y:Integer):Integer;stdcall;external  ' dlltest.dll '  name  ' swap '  ;   function UseStruct(var x:testStruct):Integer;stdcall;external  ' dlltest.dll '  name  ' UseStruct '  ;   function UseStruct2(var x:testStruct2):Integer;stdcall;external  ' dlltest.dll '  name  ' UseStruct2 '  ;   function UserCallBackFunc( lp:PChar ; F:CallBackFunctype ):Integer;stdcall;external  ' dlltest.dll '  name  ' UserCallBackFunc '  ;   function CallFunc(lp:PChar):Integer;stdcall;   begin     writeln(  ' CallFunc= '  , lp );     result : =   0  ;      end; {     这里是使用重载函数 }   function Min(x:Integer;y:Integer):Integer;stdcall;external  ' dlltest.dll '  name  ' ?Min@@YGHHH@Z ' ;       function Min_d(x:Double;y:Double):Double;cdecl;external  ' dlltest.dll '  name  ' ?Min@@YANNN@Z ' ;   procedure test_overland;   begin     writeln(  ' Min(1,2)= '  , Min(  1 , 2  ) );     writeln(  ' Min_d(1.0,2.1)= '  , Min_d(  1.0 , 2.1  ) );   end;   var   x,y:Integer;   a_struct:testStruct;   b_struct:testStruct2; begin   writeln(  ' Max(1,2)= '  , Max(  1 , 2  ) );   writeln(  ' Max(1.0,2.0)= '  , Max_d(  1.0 , 2.0  ) );   writeln(  ' x=1,y=2 '  );   x : = 1 ;y : = 2  ;   swap( x, y);   writeln(  ' swap(x,y)= '  , x ,  '   ' , y );     writeln(  ' UseStruct( a_struct ) result= '  , UseStruct( a_struct ) ,  ' ,sizeof= '  ,  sizeof (a_struct) );   writeln(  ' UseStruct= '  , a_struct.a,  '   '  , a_struct.b,  '   '   , a_struct.c ,  '   '  ,a_struct.sz );     writeln(  ' UseStruct2( b_struct ) result= '  , UseStruct2( b_struct ) ,  ' ,sizeof= '  ,  sizeof (b_struct) );   writeln(  ' UseStruct2= '  , b_struct.a,  '   '  , b_struct.b,  '   '   , b_struct.c ,  '   '  ,b_struct.sz );     UserCallBackFunc( PChar( ' abcdef ' ) , CallFunc );     test_overland;     readln;  end.
VB6的使用方法 由于VB6只能使用__stdcall方式的函数,所以只有部分函数能被VB6所调用,
Public   Declare   Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long) Public Declare Function lstrlen Lib "kernel32" Alias "lstrlenA" (ByVal lpString As LongAs Long Public Type testStruct         a As Byte         b As Long         c As Double         sz As String * 5 End Type Public Type Temp     sz As String * 5 End Type Public Declare Function Max_d Lib "dlltest" (ByVal a As DoubleByVal b As DoubleAs Double Public Declare Function Min Lib "dlltest" Alias "?Min@@YGHHH@Z" (ByVal a As LongByVal b As LongAs Long Public Declare Function UseStruct Lib "dlltest" (ByRef a As testStruct) As Long Public Declare Function UseStruct2 Lib "dlltest" (ByRef a As Any) As Long Public Declare Function UserCallBackFunc Lib "dlltest" (ByVal s As StringByVal f As LongAs Long Function CallBack(ByVal lp As LongAs Long          Dim L As Long     L = lstrlen(lp)     Dim s As String     s = String$(L + 1, vbNullChar)     CopyMemory s, lp, L     MsgBox s & " , " & Str$(L)     Debug.Print "CallBack", s     CallBack = 3 End Function Sub Main()     Debug.Print Max_d(45), Min(46)     Dim a As testStruct     Debug.Print UseStruct(a)     Debug.Print Chr(a.a), a.b, a.c, a.sz          Dim buf(18As Byte     Debug.Print "----------------"     Debug.Print UseStruct2(buf(0))          Dim t As Byte     CopyMemory t, buf(0), 1          Dim L As Long     CopyMemory L, buf(1), 4          Dim d As Double     CopyMemory d, buf(5), 8          Dim s As Temp     CopyMemory s, buf(13), 5     Debug.Print Chr(t), L, d, s.sz     Debug.Print UserCallBackFunc("_测试asdasd中文sdfasdf"AddressOf CallBack) End Sub
VB版本需要注意的是 lstrlen 的声明 参数不是String而是Long类型,这是因为如果是String的话VB会对参数进行改造,将字符串指针转化为String类型,而我这里不需要改变,就需要一个原始的Long类型的指针.所以就更改了API的函数声明.以适应我的需求 Delphi编写DLL
library dlltest; uses   SysUtils,   Classes; function Max(X, Y:  Integer):  Integer; cdecl; begin    if  X  >  Y  then Max : =  X  else  Max : =  Y; end; function Min(X, Y:  Integer):  Integer;overloadstdcall; begin    if  X  <  Y  then Min : =  X  else  Min : =  Y; end; function Min(X, Y: Double): Double;overloadcdecl; begin    if  X  <  Y  then Min : =  X  else  Min : =  Y; end; function swap( var x: Integer; var y: Integer): Integer; cdecl; var   z:integer; begin   z : =  x;   x : =  y;   y : =  z;   result : =   1 ; end; function Max_d(X, Y:Double):Double;stdcall; begin    if  X  >  Y  then Max_d : =  X  else  Max_d : =  Y; end; type   testStruct  =   record     a:Char;     b: Integer;     c:Double;     sz: array[ 0 .. 4 of  char ;    end; function UseStruct( var x:testStruct): Integer;stdcall; begin   x.a : =   ' a '  ;   x.b : =   20  ;   x.c : =   1.234  ;   StrCopy( x.sz , PChar( ' abcd ' ) );   UseStruct: = SizeOf(testStruct) ; end; {$A1} type   testStruct2  =   record     a:Char;     b: Integer;     c:Double;     sz: array[ 0 .. 4 of  char ;    end; {$A8} function UseStruct2( var x:testStruct2): Integer;stdcall; begin   x.a : =   ' a '  ;   x.b : =   20  ;   x.c : =   1.234  ;   StrCopy( x.sz , PChar( ' abcd ' ) );   UseStruct2 : =  SizeOf(testStruct2) ; end; type   CallBackFuncType  =   function(x:PChar): Integer;stdcall; function UserCallBackFunc( lp:PChar ; F:CallBackFuncType ): Integer;stdcall; begin   F( lp );   UserCallBackFunc: =  StrLen( lp ) ; end; exports   Max,   Min(X, Y:  Integer) name  ' ?Min@@YGHHH@Z ' , // 和C++导出的重载函数名字一样   Min(X, Y: Double)name  ' ?Min@@YANNN@Z ' ,   swap,   Max_d,   UseStruct,   UseStruct2,   UserCallBackFunc; begin end.
Delphi导出的时候可以自由设置输出的名字,这样就可以写出来和C++写出来的DLL有相同的导出表. 也就是说可以和C++写的dll互相替换着调用. 关于VB编写DLL 由于VB6
不容易 编写类似功能的DLL的,主要是官方未支持.虽说有人发布相应的SDK. 但毕竟没有官方支持,应用也很难推广开来,所以在这里说明一下,也就不提供VB6编写这样的DLL的. 预计我会写一篇使用COM的方式实现多语言编程,到时候就可以看到VB6版的DLL. 总结一下 说来DLL要想能够在多种语言中都可以使用,那么就需要使用__stdcall方式调用(VB只支持这中方式), 以及都有的数据类型.这样就限定了,只能使用整形(1字节char,2字节short ,4字节long,8字节的long long),浮点型(4字节的float,8字节的double),以及指针类型(包括函数指针).精确的说只有CPU所支持的类型,才容易做到无障碍的通用. 小技巧 由于C/C++程序在使用静态载入DLL的方式的时候,需要lib文件参与链接,这个时候可以自己创建一个DLL 工程,然后导出原本DLL中的函数,函数实现为空就可以了,然后用这个lib参与链接,在调用你所要使用的DLL是可以的. 欢迎各位指出不足之处,我会尽量完善.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值