#声明一个导出的以c为连接方式的函数
DLl中导出函数的声明有两种方式
(一)使用__declspec(dllexport);
extern "c" int __declspec(dllexport)add(int x,int y)
__declspec(dllexport)的含义是声明函数add为DLL的导出函数。
dll内的函数分为两种
(1)DLL导出函数,可供应用程序调用;
(2)DLL内部函数,只能在DLL程序内使用,应用程序无法调用它们。
(二)使用(.def)文件声明
.def文件为连接器提供了有关被连接程序的导出,属性及其他方面的信息。
将add函数声明为DLL导出函数
LIBRARY dllTest
EXPORTS
add @ 1
.def文件的规则
(1)LIBRARY语句说明.def文件相应的DLL;
(2)EXPORTS 语句后列出要导出函数的名称。可以在.def文件中的导出函数名后加@n 表示要导出函数的需要为n(在进行函数调用时,这个序号将发挥其作用)。
(3).def 文件中的注释由每个注释行开始出的分号(;)指定,并且注释不能与语句共享一行。
#对DLL的调用
(一)动态加载
"LoadLibrary-GetProcAddress-FreeLibrary"
DLL加载-DLl函数地址获取-DLL释放
(1)语句typedef int (*lpAddFun)(int,int)定义一个与add函数接受参数类型和返回值相同的函数指针类型。随后,在main函数中定义了lpAddFun的实力addFun;
(2)在函数main中定义了一个DLL HINSTANCE 句柄实例hDll,通过Win32 Api 函数 LoadLibrary动态加载了DLL模块并将DLL模块句柄付给了hDll
HINSTANCE hDll;
hDll = LoadLibrary("..//Debug//dllTest.dll");
(3)在函数main中通过Win32 Api函数GetProcAddress得到了所加载DLL模块中函数add的地址并付给了addFun。经由函数指针addFun进行了对DLL中add函数的调用;
addFUn = (lpAddFun)GetProcAddress(hDll,"add");
(4)应用工程师用完DLL后,在函数main中通过Win32 Api函数FreeLibrary是犯了已经加载的DLl模块。
FreeLibrary(hDll);
(二)静态加载
静态调用方法的顺利进行需要完成两个动作:
(1)告诉编译器与DLL相对应的.lib文件所在的路径及文件名,#pragma comment (lib,"dllTest.lib")就是这个作用
程序员在建立一个DLL文件时,链接器会自动为其上传一个对于的.lib文件,该文件包含了DLL导出函数的符号名及序号(并不含有实际的代码)。在应用程序里,.lib文件将作为DLL的替代文件参与编译。
(2)声明导入函数,
extern "C" __declspec(dllimport) add(int x,int y)
语句中的__declspec(dllimport)发挥这个作用。
静态调用方法不用使用系统API来加载,卸载DLL已经获取DLL中到处函数的地址。这是因为,当程序员通过静态链接方式编译生成应用程序时,应用程序中调用的与.lib文件中到处符号相匹配的函数符号将进入到生成的EXE文件,.lib文件中所包含的阈值对应的DLL文件的文件名也被编译器存储在EXE文件内部。当应用程序运行过程中需要加载DLL文件时,Windows将根据这些信息发现并加载DLL,然后通过符号名实现对DLL函数的动态链接。
这样,EXE将能直接通过函数名调用DLL的输出函数,就像调用程序内部的其他函数一样。
#DllMain函数
Windows在加载DLL的时候,需要一个入口函数,就如同控制台或DOS程序需要main函数一样,Windows在找不到DllMain的时候,系统会从其他运行库中引入一个不做任何操作的缺省DllMain函数版本,并不意味着DLL可以放弃DllMain函数。
它是DLL内部函数不能直接被应用工程引用。
BOOL APIENTRY DllMain(HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved)
DllMain在DLL加载和卸载时被调用,在线程启动终止时也调用。
ul_reason_for_call指明被调用的原因。
PROCESS_ATTACH
PROCESS_DETACH
THREAD_ATTACH
THREAD_DETACH
HINSTANCE和HMODULE一样,标识了一个全局唯一的句柄。
GetProcAddress(hDll,MAKEINTRESOURCE(1))中的MAKEINTRESOURCE是一个以函数指定顺序访问号来标识函数的宏。
#__stdcall约定
如果通过vc++编写的DLL欲被其他语言编写的程序调用,应将函数的调用方式声明为 __stdcall方式,WINAPI都采用这种方式,而C/C++缺省的调用方式却是__cdecl,
若采用C 编译方式(在C++ 中需将函数声明为extern "C") ,__stdcall 调用约定在输出函数名前面加下划线,后面加“@”符号和参数的字节数,形如_functionname@number;而__cdecl 调用约定仅在输出函数名前面加下划线,形如_functionname 。
在lib.h中,这样声明一个add函数
int __stdcall add(int x,int y);
在应用工程中函数指针类型应定义为:
typedef int(__stdcall *apAddFun)(int,int);
DLL导出变量
DLL定义的全局变量可以被调用进程访问;DLL也可以访问调用进程的全局数据。
DLL中的全局变量可以在DllMain中赋值
lib.def
LIBRARY "dllTest"
EXPORTS
dllGlobalVar DATA
GetGlobalVar
若要到处某全局变量,我们需要在.def文件的EXPORTS后添加:
变量名 CONSTANT //旧
变量名 DATA //新
在函数中引用DLL中定义的全局变量
extern int dllGlobalVar
声明所导入的并不是DLL 中全局变量本身,而是其地址,
应用程序必须通过强制指针转换来使用DLL 中的全局变量e
xtern int _declspec(dllimport) dllGlobalVar
通过_declspec(dllimport) 方式导入的就是DLL 中全局变量本身而不再是其地址
类的导出
可以用宏
#ifdef DLL_FILE
来确定在DLL内
DLL内的.h文件声明时用class _declspec(dllexport) class_name
在应用程序的.h声明文件中使用class _declspec(dllimport) class_name