本节将介绍如何编写动态链接库,并更全面地探讨动态链接库的使用方法,包括以不同的方法装入动态链接库和以不同的方法调用其中的函数等。
本节必须掌握的知识点:
动态链接库的概念
第157练:编写动态链接库DLL
第158练:共享链接库共享内存
第159练:资源库
20.1.1动态链接库的概念
动态链接库(Dynamic Link Library,DLL)是一种在 Windows 操作系统中常见的可执行文件格式。与静态链接库(Static Link Library)不同,DLL 是在运行时加载和链接的,而不是在编译时。
DLL 文件包含可重用的代码和数据,可以被多个应用程序同时使用。它们提供了一种在应用程序之间共享代码和资源的方式,以减少重复开发和减小可执行文件的大小。
■以下是一些关于动态链接库的重要特点:
●动态加载:DLL 是在应用程序运行时动态加载的,而不是编译时静态链接的。这意味着应用程序可以根据需要在运行时加载 DLL,而无需将其包含在应用程序的可执行文件中。
●共享代码和资源:多个应用程序可以共享同一个 DLL 文件。这样可以减少重复的代码和数据,提高代码的复用性和维护性。
●动态更新:由于 DLL 是在运行时加载的,因此可以通过更新 DLL 文件来修复和升级应用程序的功能,而无需重新编译和发布整个应用程序。
●动态链接:应用程序通过动态链接来使用 DLL 中的函数和资源。这意味着应用程序只在需要时才加载和链接 DLL 中的代码,减小了可执行文件的大小。
●导出函数:DLL 中的函数需要通过导出表(Export Table)来公开给应用程序使用。导出表中列出了 DLL 中的可供外部调用的函数和数据。
●运行时依赖:应用程序在运行时需要正确的 DLL 文件才能正常工作。如果应用程序需要的 DLL 文件不存在或版本不匹配,可能会导致运行错误。
在 Windows 平台上,可以使用编程语言(如C/C++)和开发工具(如Visual Studio)来创建和使用动态链接库。在编写 DLL 时,开发人员需要定义导出函数,并在编译时生成 DLL 文件。在应用程序中,可以使用动态链接库的函数和资源,通过加载 DLL 文件并调用其中的导出函数来实现。
■动态链接库与静态链接库的区别
动态链接库(Dynamic Link Library,DLL)和静态链接库(Static Link Library)是两种可执行文件格式和链接方式的不同概念。它们在链接时和运行时的行为和特点有所区别。
以下是动态链接库(DLL)和静态链接库(静态库)之间的主要区别:
●链接时刻:
静态链接库:在编译时将静态库的代码和数据与应用程序的代码一起链接到一个可执行文件中。链接是在编译时完成的。
动态链接库:在运行时将 DLL 的代码和数据动态加载到内存中,然后与应用程序进行链接。链接是在运行时完成的。
●内存占用:
静态链接库:静态库的代码和数据被完全复制到应用程序的可执行文件中,因此每个应用程序都包含一份静态库的拷贝。这意味着每个应用程序独立占用内存,可能会导致可执行文件较大。
动态链接库:DLL 的代码和数据被共享,多个应用程序可以共用同一个 DLL 文件。这样可以减少内存占用,多个应用程序共享同一个 DLL 文件的一份拷贝。
●更新和维护:
静态链接库:每当静态库的代码或数据发生变化时,需要重新编译和重新分发整个应用程序。这可能会导致更复杂的更新和维护过程。
动态链接库:通过更新 DLL 文件,可以独立地修复和升级应用程序的功能,而无需重新编译和重新分发整个应用程序。这样可以简化更新和维护过程。
●运行时依赖:
静态链接库:应用程序在编译时静态链接到静态库,因此不需要额外的运行时依赖项。可执行文件包含了静态库的完整实现。
动态链接库:应用程序在运行时需要正确的 DLL 文件才能正常工作。如果所需的 DLL 文件不存在或版本不匹配,可能会导致运行错误。
●文件大小:
静态链接库:静态库的代码和数据被完全复制到应用程序的可执行文件中,这可能会增加可执行文件的大小。
动态链接库:DLL 文件只包含代码和数据的一份拷贝,并在运行时被多个应用程序共享。这可以减小每个应用程序的体积。
选择动态链接库还是静态链接库取决于具体的需求和应用场景。静态库适用于希望将所有代码和数据包含在单个可执行文件中的情况,而动态库适用于需要代码共享和运行时更新的情况。
■静态链接库和动态链接库的特点
名称 |
特点 |
|
静态链接库 |
缺点 |
①多个程序使用相同库函数时,要存多份相同的代码到各个exe中,显然浪费空间。 ②如果某个函数有错或更新算法,则所有用到此函数的exe要重新编译一遍,升级麻烦。 ③多个exe运行时,要载入相同的代码,浪费内存。 |
优点 |
①仅在链接时使用,链接完后,可执行文件可脱离库文件单独存在 ②代码的访问速度快 |
|
动态链接库 |
特点 |
①程序运行时被载入,且内存中只保留一份代码,这份代码是通过分页机制被映射到不同进程的地址空间。但数据段仍是多份的,会被映射到不同的物理内存中,有多少个程序,就会产生多少份的数据段 ②动态链接库与可执行文件的不同,仅在文件头的属性位不同而己,exe文件的一些特征,动态链接库中也有。如动态链接库也可以使用各种资源。 ③动态链接库是被映射到其他应用程序的地址空间的,与应用程序是“一体”的。它所拥有的资源也可被应用程序使用。它的任何操作都代表应用程序进行,当在库中打开文件、分配内存和创建窗口,这些都为应用程序所拥的。 ④不能独立于应用程序而单独运行。 |
■动态链接库的入口点和退出点
int WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved)
fdwReason |
含义 |
DLL_PROCESS_ATTACH |
①当动态链接链被映射到程序进程的地址空间时,相当于初始化信号 ②在进程的整个生命周期内,只会用这个参数调用一次。如果以后的线程通过调用LoadLibraryEx函数,只会递增DLL的使用计数,而不再用这个参数去调用DllMain。 ③返回值TRUE,表明初始化成功。返回FALSE,系统会终止整个进程的运行。 ④hInstance为动态链接库的模块实例句柄(注意不是“宿主”的实例句柄),获得该句柄的唯一途径是在入口函数被调用时,保存这个参数,否则运行时没有其他方法可获取了。 |
DLL_PROCESS_DETACH |
①表示动态链接库将被卸载,给库提供一个自清理的机会 ②同样,进程的整个生命期内,只会用这个参数调用函数一次。 ③注意,DLL能够阻止进程的终止。例如,当收到DLL_PROCESS_DETACH时,让程序进入一个无限循环。只有当每个DLL都处理完该通知后,操作系统才会中止进程。 |
DLL_THREAD_ATTACH |
①表示应用程序创建了一个新的线程,系统会向当前进程中的所有DLL发送该通知。只有系统处理后这个通知,系统才允许新线程开始执行它的线程函数。 ②如果程序频繁的创建创建和结束线程,会以该参数和DLL_THREAD_DETACH频繁地调用该函数。 ③系统只会新的线程调用该函数,如果系统己经存的线程则不会再次收到该通知。 ④主线程比较特殊,他不会调用以该参数去调用DllMain。 |
DLL_THREAD_DETACH |
①线程将要结束时,会以该参数调用DllMain。注意,此时的线程还没结束,甚到还可以发送线程消息。但不应该再使用PostMessage,因为该线程可能在消息被收取取之前就消失了。 ②如果调用TerminateThread终止线程时,那么不会收到该通知。 ③如果DLL被撤消时,仍有线程在运行,那么就不为任何线程调用该函数。 ④该通知能阻止线程的中止 |
■动态链接库导出函数名称问题
图20-1 动态链接库导出函数名
动态链接库中的导出函数采用C语言编译后的函数名格式,而不是繁复的C++函数名,目的是为了方便调用函数时在动态链接库中准确快速的找到导出函数。
.def(Module Definition)文件是一种文本文件,用于在Windows平台上定义动态链接库(Dynamic Link Library,D