.lib .dll文件都是程序可直接引用的文件,前者就是所谓的库文件,后者是动态链接库(Dynamic Link Library)也是一个库文件。而.pdb则可以理解为符号表文件。DLL(Dynamic Link Library)文件为动态链接库文件,又称为“应用程序扩展”,是一种软件文件类型。在Windows中,许多应用程序并不是一个完整的可执行文件,它们被分割成一些相对独立的动态链接库,即dll文件,放置于系统中。
关于lib和dll的区别
(1)lib是编译时用到的,dll是运行时用到的。如果要完成源代码的编译,只需要lib;如果要使动态链接的程序运行起来,只需要dll。
(2)如果有dll文件,那么lib一般是一些索引信息,记录了dll中函数的入口和位置,dll中是函数的具体内容;如果只有lib文件,那么这个lib文件是静态编译出来的,索引和实现都在其中。使用静态编译的lib文件,在运行程序时不需要再挂动态库,缺点是导致应用程序比较大,而且失去了动态库的灵活性,发布新版本时要发布新的应用程序才行。
(3)动态链接的情况下,有两个文件:一个是LIB文件,一个是DLL文件。LIB包含被DLL导出的函数名称和位置,DLL包含实际的函数和数据,应用程序使用LIB文件链接到DLL文件。在应用程序的可执行文件中,存放的不是被调用的函数代码,而是DLL中相应函数代码的地址,从而节省了内存资源。如果不想用lib文件或者没有lib文件,可以用WIN32 API函数LoadLibrary、GetProcAddress装载。
需要的文件
静态链接库(Static Link Library)使用lib需注意两个文件:
- h头文件,包含lib中说明输出的类或符号原型或数据结构。应用程序调用lib时,需要将该文件包含入应用程序的源文件中。
- LIB文件,这种 lib 中有函数的实现代码,它是将 lib 中的代码加入目标模块(.exe 或者 .dll)文件中,所以链接好了之后,lib 文件就没有用了。这种 lib文件实际上是任意个 obj 文件的集合。obj 文件则是 cpp 文件编译生成的,如果有多个cpp 文件则会编译生成多个 obj 文件,从而生成的 lib 文件中也包含了多个 obj。
动态链接库(Dynamic Link Library)的导入库(Import Library)使用dll需注意三个文件:
- h头文件,包含dll中说明输出的类或符号原型或数据结构的.h文件。应用程序调用dll时,需要将该文件包含入应用程序的源文件中。
- LIB文件,是dll在编译、链接成功之后生成的文件,作用是当其他应用程序调用dll时,需要将该文件引入应用 程序,否则产生错误。如果不想用lib文件或者没有lib文件,可以用WIN32 API函数LoadLibrary、GetProcAddress装载。
- dll文件,真正的可执行文件,开发成功后的应用程序在发布时,只需要有.exe文件和.dll文件,并不需要.lib文件和.h头文件。
这种 lib 是和 dll 配合使用的,里面没有代码,代码在 dll 中,这种 lib 是用在静态调用 dll 上的,所以起的作用也是链接作用,链接完成了, lib 也没用了。至于动态调用 dll 的话,根本用不上 lib 文件。目标模块(exe 或者 dll)文件生成之后,就用不着 lib 文件了。
引入加载文件
加载lib/头文件(若不添加,连接报错:无法解析的外部符号)
静态链接只需要lib文件,当然头文件也是需要的。这种方式时候lib文件中有两个部分,可以将文件后缀改为rar解压可以看到。第一部分就是和第一种方式中的key,第二部分是.obj文件存储在obj文件夹下,他相当与dll中的机器码,只不过这个机器码是在链接的时候放入程序的,而不是在程序运行时拿进来的。如果这时候我们也有源代码,并且希望IED可以调试源码,这很容易因为如果lib文件生成的时候模式是NDEBUG(好像不是也可以),obj文件夹下是有一个xx.pdb这个pdb文件中的东西会被IED放入程序的pdb中,所以直接指定源代码位置使用就可以了。
1 第一步:项目->属性->C/C++->常规->附加包含目录(浏览.h文件的路径) 添加包含文件
2 第二步:项目->属性->C/C++->链接器->输入->附加依赖项(写入lib的名称) 添加用到的lib (这一步也可以在代码中显示调用 #pragma comment(lib, “***.lib”) )
项目->属性->C/C++->链接器->常规->附加库目录 添加库文件路径
4 第三步:在要使用cpp文件前加入 #include
上述方法,在每次创建工程时都要重新进行设置,而且debug和release都要进行设置,同时注意win32和win64平台。
加载dll(如果不添加,编译链接不报错,运行报错:无法找到***.dll)
这种方式的基本原理是lib文件中包扩了某一段程序(函数)的入口或者说是地址,而他真正的机器码是在dll文件中,IDE链接的时候将.lib文件(程序地址)链接到源代码中,程序运行时到相应位置(环境变量path,当前目录等)寻找dll文件并执行其中的机器码。所以这种引用方式一般需要的文件一般有三个:.h,.lib,.dll,生成的源程序也会比较小,因为他只保存了函数地址,但是这种方式总是会出现找不到xxx.dll这种问题。如果这时候我们还有dll的源代码,并且希望IED可以调试源码,那么就需要.pdb文件了,pdb文件中保存了dll的符号表,所谓符号表可以理解为机器码(这里是dll中的)中插入的key与源代码文件的映射,这样只要指定源码存放的路径,IDE就会自动去找源码。需要注意的是,pdb文件和dll文件是配套的,也就是说一旦dll文件有改动(比如说重新生成)pdb文件就必须做相应改变。pdb文件也比较大,程序运行时也会因为要完成映射而比较慢,这也是release版与debug的区别。
对于dll文件的使用,将dll文件拷贝到工程debug文件下,或者在系统环境变量中加入dll文件的路径(...\bin)。
(1)附加依赖项配置
类似静态库的使用,直接在vs中进行配置。(其实本质还是隐式链接,只是配置方式不同)
(2)隐式链接
第一种方法是:通过project->link->Object/LibraryModule中加入.lib文件(或者在源代码中加入指令#pragma comment(lib, “Lib.lib”)),并将.dll文件置入工程所在目录,然后添加对应的.h头文件。
1 #include "stdafx.h" 2 #include "DLLSample.h" 3 4 #pragma comment(lib, "DLLSample.lib") //你也可以在项目属性中设置库的链接 5 6 int main() 7 { 8 TestDLL(123); //dll中的函数,在DllSample.h中声明 9 return(1); 10 }
(3)显式链接
还有一种方式是调用windows的api LoadLibrary来加载dll,并根据头文件调用GetProcAddress 加载dll中的函数,最后使用FreeLibrary释放,这种方式显然不可以调试。需要函数指针和WIN32 API函数LoadLibrary、GetProcAddress装载,使用这种载入方法,不需要.lib文件和.h头文件,只需要.dll文件即可(将.dll文件置入工程目录中)。
1 #include2 #include //使用函数和某些特殊变量 3 typedef void (*DLLFunc)(int); 4 int main() 5 { 6 DLLFunc dllFunc; 7 HINSTANCE hInstLibrary = LoadLibrary("DLLSample.dll"); 8 9 if (hInstLibrary == NULL) 10 { 11 FreeLibrary(hInstLibrary); 12 } 13 dllFunc = (DLLFunc)GetProcAddress(hInstLibrary, "TestDLL"); 14 if (dllFunc == NULL) 15 { 16 FreeLibrary(hInstLibrary); 17 } 18 dllFunc(123); 19 std::cin.get(); 20 FreeLibrary(hInstLibrary); 21 return(1); 22 }
LoadLibrary函数利用一个名称作为参数,获得DLL的实例(HINSTANCE类型是实例的句柄),通常调用该函数后需要查看一下函数返回是否成功,如果不成功则返回NULL(句柄无效),此时调用函数FreeLibrary释放DLL获得的内存。
GetProcAddress函数利用DLL的句柄和函数的名称作为参数,返回相应的函数指针,同时必须使用强转;判断函数指针是否为NULL,如果是则调用函数FreeLibrary释放DLL获得的内存。此后,可以使用函数指针来调用实际的函数。
最后要记得使用FreeLibrary函数释放内存。
注意:应用程序如何找到DLL文件?
使用LoadLibrary显式链接,那么在函数的参数中可以指定DLL文件的完整路径;如果不指定路径,或者进行隐式链接,Windows将遵循下面的搜索顺序来定位DLL:
(1)包含EXE文件的目录
(2)工程目录
(3)Windows系统目录
(4)Windows目录
(5)列在Path环境变量中的一系列目录