DLL 问题 - 相关总结 一个网友的 wltg2001

来源:互联网 发布:淘宝店铺宝贝图片尺寸 编辑:程序博客网 时间:2024/05/01 02:40

有关DLL的问题现在资料很多,但是很多人写DLL时经常出现调用程序无法找到相关的导出函数的问题,这里主要的原因是DLL在声明时出的问题。 
在这里主要有两个问题,一个是调用约定的问题,一个是函数名修饰的问题,而这两个问题又是相互影响的。 
一:声明为:extern "C" int __declspec(dllexport)add(int x, int y); 
这种声明是强制用C语言方式进行修饰,且用C的默认约定,即__cdecl方式。这种方式编译产生的DLL中有一个导出函数:add,不加任何修饰。 
二:声明为:extern "C" int __declspec(dllexport) __stdcall add(int x, int y); 
这种声明是强制用C语言方式进行修饰,且用stdcall约定,这种方式编译产生的DLL中有一个导出函数:_add@8,即前面有“_”,后面加了参数长。 
三:声明为:int __declspec(dllexport) __stdcall add(int x, int y); 
这种声明不强制用C语言方式进行修饰,但是用stdcall约定,这种方式编译产生的DLL中有一个导出函数:?add@@YGHHH@Z。这个名字很怪,后面的不好理解。 
四:声明为:int __declspec(dllexport) __cdecl add(int x, int y); 
这种声明是不强制用C语言修饰,且用cdecl约定,这种方式编译产生的DLL中有一个导出函数:?add@@YAHHH@Z,注意看,和第三种方有一点不同。 

实验一:显式调用方式调用DLL中的add函数。 
#include <stdio.h> 
#include <windows.h> 
typedef  int(_stdcall *lpAddFun)(int, int); //宏定义函数指针类型 
int main(int argc, char *argv[]) 

HINSTANCE hDll; //DLL句柄 
lpAddFun addFun; //函数指针 
hDll = LoadLibrary("1.dll"); 
if (hDll != NULL) 

addFun = (lpAddFun)GetProcAddress(hDll, "add"); 
if (addFun != NULL) 

int result = addFun(2, 3); 
printf("%d", result); 

else 
printf("No Function"); 

else 
printf("NO DLL"); 
FreeLibrary(hDll); 
return 0; 

方式一:调用成功。另外三种方式全部出错 
实验二:隐式调用DLL中的add函数 
#include <stdio.h> 
#include <windows.h> 
#pragma comment(lib,"1.lib") 
extern "C" int __declspec(dllimport) add(int x, int y);//声明方式随着DLL中的声明方式改变 
int main(int argc, char *argv[]) 

int result = add(2, 3); 
printf("%d", result); 
return 0; 

方式一:调用成功。另外发现一个奇怪现象:在调用程序中 
声明函数时extern "C" int __declspec(dllimport) add(int x, int y); 
写作:extern "C" int __declspec(dllecprot) add(int x, int y);同样成功,将__declspec(…)去掉也同样成功。换句话说,在调用DLL的程序中,导入是没有必要加的。 
方式二:调用成功。同样出现上面导入标识可以不加的现象。 
方式三:调用成功,同样也出现上面导入标识可以不加的现象。 
方式四:调用成功,同样也出现上面导入标识可以不加的现象。 
总结:对于DLL导出函数声明的四种写法,在动态调用时, 
声明成这样:extern "C" int __declspec(dllimport) add(int x, int y);是最好的,其它声明方式调用都没有成功。但是众所周知,windows默认的调用约定是stdcall方式,如果想别的语言能用DLL的话,最好是将调用约定写成stdcall方式,但是这种方式又不能动态调用。 
在隐式调用时,四种声明方式都是可以的,只要调用者的声明方式和DLL声明时的方式一致即可。另外,在调用程序中对于导入的声明是可以去掉的,大量书籍中关于导入、导出的问题都是利用宏来处理的,如:在头文件中写作: 
#ifdef DLL_FILE 
extern "C" int __declspec(dllexport) add(int x, int y); 
#else 
extern "C" int __declspec(dlleximport) add(int x, int y); 
这样这个头文件既可以用在DLL工程中,又可以用在调用程序中,但是经过实验发现,这个根本就没有必要,在调用者程序中不管是写作__declspce(dllexport)还是写作__declspec(dllimport)或者不写都能成功调用。 
关于DEF文件 
在DLL工程中引用DEF文件,内容如下: 
LIBRARY 1 
EXPORTS 
add @ 1 
通过depends查看导出函数全是add,但是隐式方式调用时,还是要求调用者的声明方式和DLL中声明方式相同。 
对于动态调用实验结果: 
方式一:成功。方式二:不成功,但是将函数指针改为typedef int(_stdcall *lpAddFun)(int, int);成功,即调用者要声明约定方式与DLL中声明的调用约定方式相同,否则报错。 
方式三:同方式二,同样要将函数指针改为typedef int(_stdcall *lpAddFun)(int, int);才成功完成调用。 
方式四:成功。 
总结:通过DEF文件来导出函数,调用者同样也要声明相同的调用约定,即_stdcall或是_cdecl必须要相同,其中_cdecl是C语言默认方式。 

原创粉丝点击