1 动态库和静态库的定义
1.1 库的定义
库是写好的现有的,成熟的,可以复用的代码。所谓静态、动态是指链接。
静态库在链接阶段,会将汇编生成的目标文件.o与引用到的库一起链接打包到可执行文件中。因此对应的链接方式称为静态链接。
动态库在程序编译时并不会被链接到目标代码中,而是在程序运行是才被载入。动态库链接的程序运行速度比静态库链接的慢。
为什么还需要动态链接?
1) 空间浪费是静态库的一个问题。
2) 另一个问题是静态库对程序的更新、部署带来麻烦。如果静态库liba.lib更新了,所有使用它的应用程序都需要重新编译、发布给用户(对于玩家来说,可能是一个很小的改动,却导致整个程序重新下载,全量更新)。
3) 动态库在程序运行时才会载入。不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例,规避了空间浪费问题。同时也解决了静态库对程序的更新、部署带来麻烦。用户只需要更新动态库即可,增量更新。
1.2 将一个程序编译成可执行程序的步骤
1.3 文件之间的关系
1).h头文件是编译时必须的,.lib是链接时需要的,dll是运行时需要的。
2)附加依赖项添加的是.lib而不是.dll,若生成了DLL,则肯定也生成了LIB文件。
3)H文件的作用:声明函数接口。
DLL文件作用:函数可执行代码。
LIB文件作用:当我们在自己的程序中引用了一个H文件里的函数,链接器怎么知道该调用哪个DLL文件呢?这就是LIB文件的作用了。它告诉链接器调用的函数在哪个DLL中,函数执行代码在DLL中的什么位置,这也就是为什么需要附加依赖项.LIB文件,它起到桥梁的作用。
4)如果是生成静态库文件,则没有DLL,只有lib,这时函数可执行代码部分也在lib文件中。
2 动态库的建立与使用
创建动态库关键是导出函数,DLL中导出函数的声明有两种方式
2.1 建立方法1
在函数声明中加上 __declspec(dllexport)。
Step1:新建一个动态库项目。 Win32项目->DLL
Step2:编写项目内容:xxx.h xxx.cpp;项目添加新建项 xxx.def文件。添加就好,不需要修改任何东西。添加->代码->模块定义文件 .def。同时需要在xxx.h中添加如下内容:
//具体的宏定义名称需要到 属性->C/C++->命令行 查看,否则随便定义的话会有警告
//这里面用到了两个编译指令 __declspec(dllexport) 和__declspec(dllimport)
#ifdef SEQLIST_DLL_EXPORTS
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __declspec(dllimport)
#endif
// 注意:上述宏定义后,需要在导出的函数声明前添加“DLL_API”,在需要导出的 结构体/类 的struct/class 和变量名中间添加“DLL_API”。
Step3:生成 xxx.lib 文件。直接点击生成解决方案。生成的lib文件在项目目录的Debug文件夹中。
Step4:如何使用看下面介绍,有两种使用方式。
2.2 建立方法2
采用模块定义(.def)文件声明,(.def)文件为链接器提供了有关被链接程序的导出、属性及其他方面的信息。
Step1:新建一个动态库项目。 Win32项目->DLL
Step2:编写项目内容:xxx.h xxx.cpp dllmain.cpp;项目添加新建项 xxx.def文件。dll.main文件的内容:
#include <windows.h>
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
xxx.def文件的内容:
LIBRARY "动态库项目名称"
EXPORTS
函数名1 @1
函数名2 @2
Step3:生成 xxx.lib 文件。直接点击生成解决方案。生成的lib文件在项目目录的Debug文件夹中。
Step4:如何使用看下面介绍,有两种使用方式。
2.3 动态库的加载
动态加载是指在生成可执行文件时不将所有程序用到的函数链接到一个文件,因为有许多函数在操作系统带的dll文件中,当程序运行时直接从操作系统中找。
而静态加载就是把所有用到的函数全部链接到exe文件中。动态加载是只建立一个引用的接口,而真正的代码和数据存放在另外的可执行模块中,在运行时再装入;而静态加载是把所有的代码和数据都复制到本模块中,运行时就不再需要库了,但是需要DLL文件。
2.3.1 静态加载 (经常使用)
Step1: 新建项目,将刚刚生成的xxx.dll、xxx.lib、xxx.h文件拷贝到项目文件下 ( 如果xxx.lib、xxx.h 文件不拷入,需要包含它们的路径)工程调用dll时首先在工程文件目录中查找dll,找不到后在C:\Windows\System32 中找。所以我们自己项目简单调用dll时就把生成的dll文件复制到工程目录中,如果经常用可以把dll文件放到C:\Windows\System32中。
Step2: 右键“目录”“属性”选择“链接器”->“输入”,在“附加依赖项”这里添加要调用的lib文件的名字:xxx.lib。 如果不添加,需要在程序中使用 #pragma comment(lib, "xxx.lib") //编译器指令
Step3:运行得到实验结果
2.3.2 动态加载(缺乏lib文件的时候可以用这种方式)
例如:
#include "stdafx.h"
#include "xxxdll.h"
#include "windows.h"
int _tmain(int argc, CHAR* argv[])
{
printf("Hello World!\n");
HMODULE hmod = LoadLibrary("TestDll1.dll"); //用于加载dll
typedef int(*LoadProc)(int x, int y);
//GetProcAddress()用于获得函数地址
LoadProc Load_proc = (LoadProc)GetProcAddress(hmod, "函数名");
int iRet = Load_proc(3, 5); //得到地址后调用该函数,返回较大值
printf("the Add the value is:%x\n", iRet);
return 0;
}
需要理解调用动态库主要用到三个函数,加载LoadLibrary、调用GetProcAddress、释放FreeLibrary!别忘了在右键“目录”“属性”选择“链接器”->“输入”,在“附加依赖项”这里添加要调用的lib文件的名字:xxx.lib
3 静态库的建立与使用
第一步:新建一个静态库(static library)项目。 Win32项目->静态库
第二步:编写项目内容 xxx.h xxx.cpp
第三步:生成 xxx.lib 文件。直接点击生成解决方案。生成的lib文件在项目目录的Debug文件夹中。
第四步:如何使用。
- 新建工程项目,将头文件xxx.h和静态库xxx.lib拷到项目目录下。也可以不同拷贝,但是需要在项目属性->配置属性->vc++目录->包含目录/库目录分别添加 xxx.h和xxx.lib文件的绝对路径。
- 右键“目录”“属性”选择“链接器”->“输入”,在“附加依赖项”这里添加要调用的lib文件的名字:TestLib1.lib。如果不添加,需要在程序中使用: #pragma comment(lib, "xxx.lib") //编译器指令
注意:
目前以lib后缀的库有两种,一种为静态链接库(Static Libary,以下简称“静态库”),另一种为动态链接库(DLL,以下简称“动态库”)的导入库(Import Libary,以下简称“导入库”)。
静态库是一个或者多个obj文件的打包,所以有人干脆把从obj文件生成lib的过程称为Archive,即合并到一起。比如你链接一个静态库,如果其中有错,它会准确的找到是哪个obj有错,即静态lib只是壳子。
动态库一般会有对应的导入库,方便程序静态载入动态链接库,否则你可能就需要自己LoadLibary调入DLL文件,然后再手工GetProcAddress获得对应函数了。有了导入库,你只需要链接导入库后按照头文件函数接口的声明调用函数就可以了。
导入库和静态库的区别很大,他们实质是不一样的东西。静态库本身就包含了实际执行代码、符号表等等,而对于导入库而言,其实际的执行代码位于动态库中,导入库只包含了地址符号表等,确保程序找到对应函数的一些基本地址信息。
一般的动态库程序有lib文件和dll文件。lib文件是必须在编译期就连接到应用程序中的,而dll文件是运行期才会被调用的。如果有dll文件,那么对应的lib文件一般是一些索引信息,具体的实现在dll文件中。如果只有lib文件,那么这个lib文件是静态编译出来的,索引和实现都在其中。