windows中静态库lib和动态dll的区别及使用方法
网上有很多关于这方面的说明,但是比较乱,感觉都说不清楚的样子。根据自己的理解以及查询资料,有些内容参考于网络,做一下总结:
1. 静态库lib和动态dll的区别
1.1 项目类型
VS在建Win32项目时,有以下选项:
- windows应用程序
- 控制台应用程序
- DLL
- 静态库
最后两个类型:DLL和静态库,这两种项目类型是不可以单独运行的,必须在Windows应用程序调用他们执行,是提供的库函数而已。
1.2 两种lib的区别:
(1)静态库(.lib)
函数和数据被编译进一个二进制文件(通常扩展名为.LIB)。在使用静态库的情况下,在编译链接可执行文件时,链接器从库中复制这些函数和数据并把它们和应用程序的其他模块组合起来创建最终的可执行文件(.EXE文件)。当发布产品时,只需要发布这个可执行文件,并不需要发布被使用的静态库。
(2)动态库(.lib文件和.dll文件)
在使用动态库的时候,编译后往往提供两个文件:一个引入库(.lib)文件(也称“导入库文件”)和一个DLL(.dll)文件。当然到了后面会告诉你如果只提供一个DLL文件,使用显示连接的方式也可以调用,只是稍加麻烦而已。
虽然引入库的后缀名也是“lib”,但是,动态库的引入库文件和静态库文件有着本质的区别。对一个DLL文件来说,其引入库文件(.lib)包含该DLL导出的函数和变量的符号名,而.dll文件包含该DLL实际的函数和数据。在使用动态库的情况下,在编译链接可执行文件时,只需要链接该DLL的引入库文件,该DLL中的函数代码和数据并不可复制到可执行文件,直到可执行程序运行时,才去加载所需的DLL,将该DLL映射到进程的地址空间中,然后访问DLL中导出的函数。这时,在发布产品时,除了发布可执行文件以外,同时还需要发布该程序将要调用的动态链接库。
只有当EXE程序确实要调用这些DLL模块的情况下,系统才会将它们装载到内存空间中。这种方式不仅减少了EXE文件的大小和对内存空间的需求,而且使这些DLL模块可以同时被多个应用程序使用。如果DLL不在内存中,系统就将其加载到内存中。当链接Windows程序以产生一个可执行文件时,你必须链接由编程环境提供的专门的 “引入库(import library)”。这些引入库包含了动态链接库名称和所有Windows函数调用的引用信息。链接程序使用该信息在.EXE文件中构造一个表,当加载程序时,Windows使用它将调用转换为Windows函数。
引入库LIb和静态库Lib的区别:
引入库和静态库的区别很大,他们实质是不一样的东西。静态库本身就包含了实际执行代码、符号表等等,而对于引入库而言,其实际的执行代码位于动态库中,引入库只包含了地址符号表等,确保程序找到对应函数的一些基本地址信息。但是引入库文件的引入方式和静态库一样,要在链接路径上添加找到这些.lib的路径。
1.3 动态库(Lib和DLL)和静态库Lib的区别:
其实上面已经提到了区别,这里再总结一下:
(1)静态链接库与动态链接库都是共享代码的方式,如果采用静态链接库,lib 中的指令都全部被直接包含在最终生成的 EXE 文件中了,最终的可执行文件exe会比较大。但是若使用 DLL,该 DLL 不必被包含在最终 EXE 文件中,EXE 文件执行时可以“动态”地引用和卸载这个与 EXE 独立的 DLL 文件。
(2)静态链接库和动态链接库的另外一个区别在于静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接 库。静态链接库与静态链接库调用规则总体比较如下。
静态链接库运行之前就加载了,而且一直存在直到关闭程序,动态DLL实在运行时再加载,不用一直占内存,dll模块内部更改了,是要替换Dll即可,方便维护。优点明显,但是dll如果丢失或被误删,就无法运行了
2. 静态库lib和动态dll的使用
2. 1 动态dll的使用
动态链接库的使用需要库的开发者提供生成的.lib文件和.dll文件。或者只提供dll文件。使用时只能使用dll中导出的函数,未导出的函数只能在dll内部使用。Dll的调用有显示连接和隐式连接两种:隐式连接需要三个东西,分别是*.h头文件,lib库(动态的),DLL库;显示连接只需要.dll文件即可。
2.1.1 隐式连接
隐式链接需要三个东西,分别是*.h头文件,lib库(动态的),DLL库,而这里的lib库仅是编译的时候用,运行时候不用,运行时只用Dll
2.1.1.1 添加Lib
方法1: 通过设置工程配置来添加lib库.
A、添加工程的头文件目录:工程->属性->配置属性->c/c++->常规->附加包含目录:加上头文件存放目录。添加头文件参考2.2.1.2
B、添加文件引用的lib静态库路径:工程->属性->配置属性->链接器->常规->附加库目录:加上lib文件存放目录。
C 然后添加工程引用的lib文件名:工程->属性->配置属性->链接器->输入->附加依赖项:加上lib文件名。
这种方法比较繁琐,且不直观,而且还可能要争对debug版本和release版本作不同的配置,因为我们生成的两个版本的库可能放在不同的目录中的.
方法2: 使用编译语句:
#ifdef _DEBUG
#pragma comment(lib,"..\\debug\\LedCtrlBoard.lib")
#else
#pragma comment(lib,"..\\release\\LedCtrlBoard.lib")
#endif
这种方法直观,方便,且可以按如上直接区分出Debug版本和Release版本的不同目录.当然,通过宏,还可以区分更多版本.但是在指定目录时,不小心容易出错.
方法3: 直接添加库文件到工程中.
就像你添加.h和.cpp文件一样,把lib文件添加到工程文件列表中去.
VC中,切换到”解决方案视图”—>选中要添加lib的工程–>点击右键–>”添加”–>”现有项”–>选择lib文件–>确定.
这个方法适用于在我的工程的debug版本和Release版本中都使用同一个lib库文件时.这样就省去了你1方法配置环境的繁琐,也省去了方法2种语句的可能性错误发生.。
2.1.1.2添加头文件
在调用DLL中导出的函数之前要include对应的头文件,可以写绝对路径,也可以拷贝到工程调用源文件的同一目录下,也可以通过VS添加(include)头文件目录,VS中配置方法:
(1)VS项目->右击属性->配置属性->VC++目录->包含目录
(2)VS项目->右击属性->配置属性->C/C++->常规->附加包含目录
2.1.1.3 添加dll
一般将dll拷贝到运行时目录即可,与调用者exe文件在同一目录,当然有其他方法添加环境变量PATH,
(1)VS项目->右击属性->配置属性->VC++目录->可执行目录
(2)设定DLL目录的位置,具体方法为:项目右击->属性 -> 配置属性 -> 调试 ->工作目录,在这里设置dll的路径就可以了
注1:release版本和debug版本的区分,每种版本的设置都是独立的,要分别设置
注2:单纯添加lib目录的方法有以下几种方法
(1):把.lib文件放在当前使用它的工程目录下;(如:.exe所在目录,或者工程代码所在的目录)
(2):对某一个项目:项目”->“属性”->“配置属性”->“VC++目录”->“常规”->“附加库目录”
(3):在vs中,“项目”->“属性”->“配置属性”->“链接器”->“常规”->“附加库目录”
(4):放在开发环境IDE的lib库目录下,例如:“C:\Program Files\Microsoft Visual Studio 8\VC\lib”,这是vs2005的vc开发的lib库目录。
注:在VS属性中配置路径时可以用绝对路径,也可以用相对路径,其中./表示当前目录,而../表示上一级目录
上面仅仅对单个项目有效,我们配置过opencv库的都知道,有一种是全局配置lib和include头文件,对所有的项目有效,以Debug版为例,步骤如下:
(1) 点击“视图”→“其他窗口”→“属性管理器”
(2) 从左侧项目中打开“Debug| Win32”→“Microsoft.Cpp.Win32.user”
(3) 双击“Microsoft.Cpp.Win32.user”,在弹出的窗口,点击左侧VC++目录,编辑右侧的可执行文件目录、包含目录与库目录,分别添加对应的路径
(4) 附加依赖项,单击“链接器”→“输入”→“附加依赖项”,填入依赖项.lib后缀的文件名。
2.1.2 显示连接
隐式链接虽然实现较简单,但除了必须的.dll文件外还需要DLL的.h文件和.lib文件,在那些只提供.dll文件的场合就无法使用,而只能采用显式链接的方式。这种方式通过调用API函数来完成对DLL的加载与卸载,能更加有效地使用内存,在编写大型应用程序时往往采用此方式。这种方法编程具体实现步骤如下:
①使用Windows API函数Load Library或者MFC提供的AfxLoadLibrary将DLL模块映像到进程的内存空间,对DLL模块进行动态加载。
②使用GetProcAddress函数得到要调用DLL中的函数的指针。
③不用DLL时,用Free Library函数或者AfxFreeLibrary函数从进程的地址空间显式卸载DLL。
使用LoadLibrary显式链接,那么在函数的参数中可以指定DLL文件的完整路径;如果不指定路径,或者进行隐式链接,Windows将遵循下面的搜索顺序来定位搜索DLL:
- 包含EXE文件的目录
- 工程目录
- Windows系统目录
- Windows目录
- 列在Path环境变量中的一系列目录
2.2 静态库lib的使用
静态lib中,一个lib文件实际上是任意个obj文件的集合,obj文件是cpp文件编译生成的。静态库的.lib文件包含了链接库的所有信息(函数代码和接口信息)。所以我们在调用静态库.lib时,只需要包含头文件目录(../include. .h),以及附加库目录即可。因此,静态链接库的使用需要库的开发者提供生成库的.h头文件和.lib文件
在VC中新建一个static library类型的工程TestLib,加入test.cpp文件和test.h文件(头文件内包括函数声明),然后编译,就生成了TestLib.lib文件。
别的工程要使用这个lib方式:
(1)添加lib
方法1):直接用”项目右击”->”添加”–>”现有项”–>选择lib文件–>确定,通过这种方式将.lib加入工程
方法2):工程属性-> 配置属性->链接器->输入->附加依赖项中添加要使用的Lib库的名字;在工程属性-> 配置属性->链接器->输入->附加库目录中输入.lib文件所在路径(相对或绝对路径)
方法3):或者在源代码中加入指令#pragma comment(lib, “TestLib.lib”),也可以指定完整路径(绝对路径或相对路径)#pragma comment(lib, “..\Debug\TestLib.lib”)。可以通过宏#if defined(_DEBUG)来区分用release或debug版本的lib。另外这里如果不指定完整路径,也要像方法2一样添加附加库目录。
如果不在工程属性中添加附加lib库目录,也可以将静态里边库比如TestLib.lib拷贝到工程所在目录,或者拷贝到执行文件生成的目录,或者拷贝到系统Lib目录中。
(2). 添加头文件
加入相应的头文件test.h。#include “test.h”
include file path可以为绝对路径,也可以为相对于工程所在目录的相对路径,如果头文件比较多,可以在project>settings>c/c++>preprocessor的Additional include directories中填入你的头文件所在目录,编译时会自动在此目录查找头文件。