动态链接库:我们经常把常用的代码制作成一个可执行模块供其他可执行文件调用,这样的模块称为链接库,分为动态链接库和静态链接库。
对于静态链接库,LIB包含具体实现代码且会被包含进EXE中,导致文件过大,浪费磁盘和内存;
对于动态链接库,DLL不必被包含在最终的EXE中,EXE执行时可以动态地装载和卸载DLL文件。
导出函数
将函数声明为导出函数有两种方式:
1、 在函数声明上加上_declspec(dllexport):
如:extern "C" int__declspec(dllexport)add(int x, int y);
2、 在.def文件中声明:如:
; lib.def : 导出DLL函数
LIBRARY dllTest
EXPORTS
Add @ 1
以”;”开始的为注释行,上述例子表示:生成名为“dllTest”的动态链接库,导出其中的add函数,序号为1。该序号在函数调用时起作用:GetProcAddress ( hDll, MAKEINTRESOURCE ( n ))返回 def定义的第n个函数地址。
DLL调用
使用动态链接库的时,往往提供两个文件:引入库文件(LIB)和动态链接库(DLL)。引入库文件(LIB)包含动态连接库(DLL)所有导出的函数和变量的符号名和地址,动态连接库(DLL)包含实际的函数和数据。调用方式分为动态调用(显式链接)和静态调用(隐式链接)。
1、 静态调用(隐式链接)
由编译系统完成对DLL的加载和卸载。共需两步操作:
告诉编译器与DLL相对应的LIB文件所在的路径及文件名,如#pragma comment (lib,"dllTest.lib") ;
声明导入函数,如:extern "C" __declspec(dllimport)add(int x,int y)(若有导出函数声明的头文件可直接包含该.h文件);
2、 动态调用(显示链接)
使用Win32 API 函数LoadLibrary、GetProcAddress、FreeLibrary实现"DLL加载-DLL函数地址获取-DLL释放”。这种方法不需要LIB文件,只需要DLL文件即可,显式链接速度更快且更加灵活。
关于DLL放置路径:
使用显式链接,在函数LoadLibrary的参数中可以指定DLL文件的完整路径;如果不指定路径,或者进行隐式链接,Windows将遵循下面的搜索顺序来定位DLL:
(1)包含EXE文件的目录
(2)工程目录
(3)Windows系统目录
(4)Windows目录
(5)列在Path环境变量中的一系列目录
例:
1、 生成DLL
创建Win32控制台的DLL项目MyDll,新建mydll.h和mydll.cpp,源码如下:
//mydll.h文件 #ifndef _MYDLL_H #define _MYDLL_H extern "C" int __declspec(dllexport)add(int x, int y); #endif
//mydll.cpp文件 #include "mydll.h" int add(int x, int y) { return x + y; }
编译(F7)生成MyDll.lib和MyDll.dll。(extern "C"指定导出函数为c链接,避免C/C++命名规则不同导致的函数名不一致)
2、 调用DLL
创建Win32控制台项目,新建test.cpp文件,源码如下:
动态调用:(将MyDll.dll拷贝至工程目录)
#include <stdio.h> #include <windows.h> typedef int(*lpAddFun)(int, int); //宏定义函数指针类型 int main(int argc, char *argv[]) { HINSTANCE hDll = LoadLibrary("MyDll.dll");//加载DLL if (hDll != NULL) { lpAddFun addFun = (lpAddFun)GetProcAddress(hDll, "add");//获取函数地址 if (addFun != NULL) { int result = addFun(2, 3); printf("%d", result); } FreeLibrary(hDll);//卸载DLL } return 0; }
静态调用:(将MyDll.dll和MyDll.lib拷贝至工程目录)
#pragma comment(lib,"MyDll.lib") extern "C" __declspec(dllimport) int add(int x,int y); #include <stdio.h> int main(int argc, char* argv[]) { int result = add(2,3); printf("%d",result); return 0; }
有时DLL导出函数若很多,在静态调用时,为了避免逐个写出extern "C"__declspec(dllimport) function(),可以直接包含DLL头文件(若有的话)。对于DLL制作者来说,需要实现头文件中函数是导入还是导出的自动转换,这时可通过定义宏实现。在DLL工程中头文件作用为导出声明,在应用工程中该头文件为导出声明。这时,DLL工程MyDll源码如下:
//mydll.h文件 #ifndef _MYDLL_H #define _MYDLL_H #ifdef MYDLL_EXPORT #define MYDLL_API __declspec(dllexport) #else #define MYDLL_API __declspec(dllimport) #endif extern "C" MYDLL_API int add(int x, int y); #endif
// mydll.cpp文件 #define MYDLL_EXPORT //只在DLL实现中定义此符号 #include "mydll.h" int add(int x, int y) { return x + y; }
静态调用:(将MyDll.dll,MyDll.lib, mydll.h拷贝至工程目录)
#pragma comment(lib,"MyDll.lib") #include"mydll.h" #include <stdio.h> int main(int argc, char* argv[]) { int result = add(2,3); printf("%d",result); return 0; }
参考:
http://www.cnblogs.com/chio/archive/2007/11/03/948480.html#undefined
http://www.cnblogs.com/fangyukuan/archive/2010/06/20/1761464.html
http://www.cppblog.com/amazon/archive/2009/09/04/95318.html