使用DLL进行不同语言之间的调用
生活随笔
收集整理的這篇文章主要介紹了
使用DLL进行不同语言之间的调用
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
?
__declspec(dllexport)是告訴編譯器用來導(dǎo)出函數(shù)的,在代碼中不另作說明了extern "C"的意思就是用C的方式來導(dǎo)出函數(shù),為什么要用C的方式來導(dǎo)出呢.
因?yàn)镃++中有重載,編譯器會(huì)對(duì)函數(shù)名進(jìn)行更改,修飾成唯一的函數(shù)名.
__stdcall告訴編譯器函數(shù)調(diào)用方式.這點(diǎn)可以參考其他文章,
我預(yù)計(jì)也會(huì)在blog中寫上一篇關(guān)于函數(shù)調(diào)用方式. 復(fù)制內(nèi)容到剪貼板
代碼:
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; }?
這是一段代碼,使用參數(shù)和返回值為int和double是有目的的在VC8下int是32位的double是64位.
使用重載也是有目的的.
編譯命令如下
cl /c dlltest.cpp
link /DLL dlltest.obj
編譯后使用Depends查看dll中的內(nèi)容.能看到dll中有3個(gè)函數(shù).
?Min@@YANNN@Z
?Min@@YGHHH@Z
Max
其中的?Min@@YANNN@Z和?Min@@YGHHH@Z就是重載兩個(gè)Min函數(shù).
可以使用運(yùn)行庫中未公開函數(shù)__unDNameEx看到相對(duì)應(yīng)的函數(shù)聲明.
這兩個(gè)名字就是C++函數(shù)和二進(jìn)制文件中的函數(shù)名相對(duì)應(yīng)的.
重載的時(shí)候根據(jù)參數(shù)不同鏈接到不同的函數(shù)名字
C如果使用的話得動(dòng)態(tài)加載函數(shù)
接下來看如何傳遞指針類型.在以下的部分都使用C可以使用函數(shù)
于是將extern "C" __declspec(dllexport)定義為一個(gè)宏 復(fù)制內(nèi)容到剪貼板
代碼:
#define DLLEXPORT extern "C" __declspec(dllexport) DLLEXPORT int swap(int* x,int& y) {int z = *x;*x = y ;y = z;return 0; } /* 這和前面的例子重復(fù)了,主要用于調(diào)用的例子 */ DLLEXPORT double __stdcall Max_d(double x,double y) {return x>y?x:y; }?
接下來是使用結(jié)構(gòu)體的,由于結(jié)構(gòu)體會(huì)對(duì)成員進(jìn)行對(duì)齊,所以在調(diào)用的時(shí)候需要注意和這里的結(jié)構(gòu)體有相同的內(nèi)存布局 復(fù)制內(nèi)容到剪貼板
代碼:
#include<string.h> struct testStruct {char a;int b;double c;char sz[5]; }; DLLEXPORT int __stdcall UseStruct(testStruct* p) {p->a = 'a';p->b = 20;p->c = 1.234;strcpy( p->sz , "abcd" );return sizeof(testStruct); } /*這是修改了內(nèi)存對(duì)齊的結(jié)構(gòu)體使用,主要在調(diào)用的時(shí)候有區(qū)別*/ #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 = 'a';p->b = 20;p->c = 1.234;strcpy( p->sz , "abcd" );return sizeof(testStruct2); }?
這是使用回調(diào)函數(shù)的例子,這里想成功調(diào)用主要還是要看如何調(diào)用. 復(fù)制內(nèi)容到剪貼板代碼:
?
DLLEXPORT int __stdcall UserCallBackFunc( const char* lp, int (__stdcall *p)(const char*) ) {return p( lp ); }
EXPORTS
? ? Max
? ? swap
? ? Max_d
? ? UseStruct
? ? UseStruct2
? ? UserCallBackFunc
這里的def文件不是必須的,如果不使用def文件編譯,則dll中的一些函數(shù)名前面會(huì)被加上_后面加上參數(shù)大小
?Min@@YANNN@Z ?Min@@YGHHH@Z Max _Max_d@16 _UseStruct2@4 _UseStruct@4 _UserCallBackFunc@8 swap
當(dāng)然如果只是C使用的話不使用def文件也是可以的,但要是其他語言的話就有一些不方便了也得使用_Max_d@16
這樣的函數(shù)名字,有些不美觀.在這里增加def文件也就是改名的意思
編譯方法如下
cl /c dlltest.cpp
link /DLL /DEF:dlltest.def dlltest.obj
使用了def文件后dll中所導(dǎo)出的函數(shù)如下
?Min@@YANNN@Z ?Min@@YGHHH@Z Max Max_d UseStruct2 UseStruct UserCallBackFunc swap
除去重載的兩個(gè)函數(shù),凡是使用extern "C"的函數(shù)都可以正常現(xiàn)實(shí)
C/C++的使用方法
分別使用.c和.cpp的擴(kuò)展名編譯,就是C的調(diào)用方法和C++的調(diào)用方法了 復(fù)制內(nèi)容到剪貼板
代碼:
#ifdef __cplusplusextern "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) #endifDLLIMPORT 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 __cplusplusprintf("%d ",Min( 2,3 ) );printf("%f ",Min( 2.0,3.0 ) ); #elseint(__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 ) ); #endifswap( &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的使用方法 復(fù)制內(nèi)容到剪貼板代碼:
program test; {$APPtype CONSOLE} usesSysUtils,Classes,Math,Windows;typetestStruct = recorda:Char;b:Integer;c:Double;sz:array[0..4] of char;end; {$A1}{定義record使之和 修改了對(duì)齊的結(jié)構(gòu)體有相同的內(nèi)存布局 }testStruct2 = recorda: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;beginwriteln( 'CallFunc=' , lp );result := 0 ; end; {這里是使用重載函數(shù) }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;beginwriteln( 'Min(1,2)=' , Min( 1,2 ) );writeln( 'Min_d(1.0,2.1)=' , Min_d( 1.0,2.1 ) );end;varx,y:Integer;a_struct:testStruct;b_struct:testStruct2; beginwriteln( '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方式的函數(shù),所以只有部分函數(shù)能被VB6所調(diào)用。 復(fù)制內(nèi)容到剪貼板
代碼:
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 Long) As LongPublic Type testStructa As Byteb As Longc As Doublesz As String * 5 End Type Public Type Tempsz As String * 5 End Type Public Declare Function Max_d Lib "dlltest" (ByVal a As Double, ByVal b As Double) As Double Public Declare Function Min Lib "dlltest" Alias "?Min@@YGHHH@Z" (ByVal a As Long, ByVal b As Long) As 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 String, ByVal f As Long) As LongFunction CallBack(ByVal lp As Long) As LongDim L As LongL = lstrlen(lp)Dim s As Strings = String$(L + 1, vbNullChar)CopyMemory s, lp, LMsgBox s & " , " & Str$(L)Debug.Print "CallBack", sCallBack = 3 End FunctionSub Main()Debug.Print Max_d(4, 5), Min(4, 6)Dim a As testStructDebug.Print UseStruct(a)Debug.Print Chr(a.a), a.b, a.c, a.szDim buf(18) As ByteDebug.Print "----------------"Debug.Print UseStruct2(buf(0))Dim t As ByteCopyMemory t, buf(0), 1Dim L As LongCopyMemory L, buf(1), 4Dim d As DoubleCopyMemory d, buf(5), 8Dim s As TempCopyMemory s, buf(13), 5Debug.Print Chr(t), L, d, s.szDebug.Print UserCallBackFunc("_測試asdasd中文sdfasdf", AddressOf CallBack) End Sub?
VB版本需要注意的是lstrlen 的聲明 參數(shù)不是String而是Long類型,這是因?yàn)槿绻荢tring的話VB會(huì)對(duì)參數(shù)進(jìn)行改造,將字符串指針轉(zhuǎn)化為String類型,而我這里不需要改變,就需要一個(gè)原始的Long類型的指針.所以就更改了API的函數(shù)聲明.以適應(yīng)我的需求。總結(jié)
以上是生活随笔為你收集整理的使用DLL进行不同语言之间的调用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java调用c dll,指针参数和结构体
- 下一篇: 字节对齐的作用