VC Dll的函数或变量导出常用的有两种方式:一是通过.DEF文件,再者是通过函数或变量修饰符__declspec (dllexport). 这两种是有区别的:
1. 使用__declspec (dllexport)导出、__declspec (dllimport)导入.
各种编译器以及调用方式
这种导入方式是MS专门为VC实现的。如果生成的DLL只在VC的应用程序里面被调用,那这种方式完全是可以满足要求的。但是如果我们的程序可能会运行在VB、Dephi等其它厂商的工具下面的时候,名字的改变将会产生链接问题,这是为什么呢?
C++编译器,不管函数以什么方式调用,__cdecl/__stdcall/__fastcall等,编译器会将函数的名字变成?GetMyValue@@...这样的格式。跟用其它厂商的开发工具开发的应用程序链接时,可能会导致找不到函数地址。
使用C++编译时,生成的DLL的函数名字,看下面的例子:
注意:GetMyValue是用的__stdcall调用,是跟@@YG加参数表,SetMyValue是__cdecl调用,跟@@YA加参数表,两者是不一样的。详细可以阅读关于函数名字修饰规则的文章。
在开发DLL的时候,一般不让编译器改变函数的名字,所以通常是以C编译,及在函数前加extern “C”说明,但是经过实验:用C编译时,只有C默认调用方式__cdecl编译出来的DLL不会改变函数的名字,Windows常用的__stdcall及__fastcall都会,摘自网上的说明:
这种方式的DLL编写时注意事项:
1. DLL中的全局函数、变量、类都可以用__declspec (dllexport)导出,.DEF方式则不能导出类
2. 对于对DLL中变量的调用的时候,在调用的应用程序里面,对于全局变量,不能直接通过include DLL的头文件,编译时,链接.lib,运行时再加载.dll这种方式,这样会造成DLL的变量是你调用程序里的全局变量,而不是被调DLL中使用的全局变量。
这种情况下,还值得我深入研究,DLL以及应用程序都在同一块地址空间,DLL和应用程序都有同一个全局变量,是不是编译器,就自动把DLL中的全局变量当成了应用程序中的了呢?那DLL内部对变量值的改变,以及应用程序中对应用内部的值的改变,怎样同步?
3. 在调用的应用程序里面,如果是对函数的调用,其实声明为__declspec (dllimport)或者__declspec (dllexport)都没有关系,都可以正常调用;但是如果是对变量的调用,则必须声明为__declspec (dllimport),不然不能正常使用变量的值。
4. 对于调用方式,调用应用程序中,以及被调用的DLL中,函数的调用方式__cdecl/stdcall等必须定义一致,不然,会造成调用应用程序链接时,不能找到函数的地址。
2. .DEF方式导出函数
VC的.DEF文件可以定义导出的变量或函数,不可以导出类,格式就不提了,每个VC的动态库工程中都可以看到。不管采用什么方式的函数调用方式,函数名字都不会改变,编译出来的动态库如图所示:
注意:
1. 函数的序号ordinal是编译器自动指定的,如果没有手动指定的话。需要手动指定的格式为:
LIBRARY "ExptDll"
DESCRIPTION 'ExptDll Windows Dynamic Link Library'
EXPORTS
; Explicit exports can go here
SetMyValue @1通过@1指定序号。
2. 对于变量的导出,需用DATA指出,以前是CONSTANT,编译时,会提示过已经不再用,请用DATA代替。格式:
EXPORTS
; Explicit exports can go here
iValue DATA实际上在测试过程中,发现即使没有指定DATA,访问也是可以正常的。
3. 可以在导出函数时,隐藏掉函数名字,这样,用dependency等工具查看导出函数时,就不能显示,以保护一些比较敏感的信息。如:
EXPORTS
; Explicit exports can go here
iValue DATA
szValue
SetMyValue @1
GetMyValue @2 NONAME注意,当使用NONAME时,必须跟@2指定序号同时使用,不然编译时报错,也不方便调用了。这样编译出来是:
这就是为什么我们试图查看一些动态库时,总是显示N/A的原因了。
对于隐藏名字的函数,我们可以根据序号来调用,方法是:
typedef int (* AddFunc)(int, int);
HMODULE hModule = LoadLibrary("dll.dll");
AddFunc add = (AddFunc)GetProcAddress(hModule, MAKEINTRESOURCE(1));
















