动态链接库
静态库:一个二进制文件(通常拓展名为.LIB)。在程序编译时,链接器将库中的函数和数据复制创建最终的可执行文件(.EXE文件)。因此发布时不需要使用静态链接库。
动态库:包含一个引入库(.lib)文件和一个DLL(.dll)文件。与静态库不同,引入库包含DLL导出的函数和变量符号,而.dll文件包含DLL实际的函数和数据。是程序编译时,链接器只复制引入库的内容生成可执行文件。直到可执行程序运行时,才回去加载所需的DLL,将该DLL映射到进程的地址空间中,然后访问DLL中导出的函数。因此,发布时需要同时发布动态链接库。(DLL可以包含对话框模板、字符串、图标和位图等资源)
动态链接库优势
- 实现多种语言编写
- 增强产品功能(插件、DLL规范拓展等)
- 方便二次开发
- 简化项目管理
- 节省磁盘空间
- 有助于资源共享
- 有助于实现程序语言国际化
动态链接库的创建
1. Win32 DLL创建
1)VS创建一个Win32 DLL空项目;
2)添加CPP文件,如需h文件也可以添加。编写正常C++/C代码
3) 对于全局函数而言,在需要导出函数前添加_declspec(dllexport)。对于类而言,若想导出类的所有成员函数,在类名前添加_declspec(dllexport);若想导出部分成员函数,则在相应函数前添加_declspec(dllexport)。
4)编译执行,获得.lib文件和.dll文件
2. MFC DLL创建
与Win32 DLL过程类似,只是增加了MFC函数的支持。包含三种,a)常规静态MFC链接 b)常规共享MFC链接 c)拓展共享MFC链接。 后两者的区别,前者不能导出MFC类,后者可以。
3. “名字改编”问题的解决
说明:编译器在导出DLL时,会改编函数的名称,不同编译器、不同语言的改变规则不一样。因此在发布和客户端两者的规则不一样时,会导致客户端无法使用DLL里面的函数。
法1: 在_declspec(dllexport)和_declspec(dllimport)前均加上extern “C”,注意C一定要大写。这种方法的缺点是不能用于导出一个类的成员函数,只能用于全局函数。此外,若函数调用约定改变,这种方式也会失效,如在_declspec(dllexport)和_declspec(dllimport)后,函数名前加入_stdcall关键字。
法2: 不使用_declspec(dllexport)方式导出,在函数前不加任何修饰符,在项目中添加一个.def后缀名的文件,并在里面添加如下代码:
LIBRARY DLL文件名 //可有可无
EXPORTS
函数名1
函数名2
新函数名=函数名3
4.隐式/显示加载DLL
隐式加载
- 将.lib和.dll文件复制到工程目录,其中dll可以放在DEBUG目录下。(注意:编译时dll搜索顺序:项目执行目录,当前项目目录,系统目录,path环境变量目录)
使用extern声明dll的函数。如下,
extern int add(int a, int b)
不用extern,利用_declspec(dllimport)声明外部函数【推荐这种】。如下,
_declspec(dllimport) int add(int a, int b)
显示加载
HINSTANCE hInst;
hInst=LoadLibrary("XXX.dll");//加载DLL
typedef int (*XXX)(int a, int b);//定义函数指针,与DLL里函数形式要一致
XXX x=(XXX)GetProcAddress(hInst, "dll里函数名");
if(!x)
{
MessageBox("获取函数地址失败");
return;
}
//接下来就可以调用x函数指针了
//需要注意的是,在不使用DLL后,可以释放
FreeLibrary(hInst);