1 / 126

第九章 动态链接库

第九章 动态链接库. 库 是 C/C++ 的一种重要软件形式,它的作用表现在: · 软件开发中 系统功能 、 通用功能 、 特殊功能 的提供者; · 提供 商品化 的 特定功能 或 通用功能集 的软件形式; · 组织开发 大型软件工程 中不可缺少的重要 软件构件 。 库 以 库文件 的形式提供, 库文件 包括 静态链接库 和 动态链接 库 两种, 库 所提供的功能是 不能独立运行 的,而必须通过将 库 静态 或 动态链接 到 可执行程序 中进行 加载 才能执行。 在 Visual C++ 开发环境中,不仅提供了开发软件所需要的大

Télécharger la présentation

第九章 动态链接库

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 第九章 动态链接库

  2. 库是C/C++ 的一种重要软件形式,它的作用表现在: ·软件开发中系统功能、通用功能、特殊功能的提供者; ·提供商品化的特定功能或通用功能集的软件形式; ·组织开发大型软件工程中不可缺少的重要软件构件。 库以库文件的形式提供,库文件包括静态链接库和动态链接 库两种,库所提供的功能是不能独立运行的,而必须通过将库 静态或动态链接到可执行程序中进行加载才能执行。 在 Visual C++ 开发环境中,不仅提供了开发软件所需要的大 部分功能库,同时为制作用户自定义功能库提供了方便,有效 的方法。对于 Windows 程序设计来说,动态链接库的制作和使 用,是至关重要的,它是一种常用的软件打包技术。本章将对 这一主题进行较为系统的讨论。

  3. 9.1 动态链接库介绍 动态链接库是具有某些特定功能的函数和类的目标代码集 合,它可以由用户自己开发,并被最终用户使用。使用动态链 接库与能提供相同功能的源代码相比有以下优点: ·省去了用户管理源代码的烦恼(许多情况下用户并不关心模 块的内部实现细节); ·使功能提供者达到只提供功能的使用而保护源代码的目的; ·可以减少模块的函数名、变量名与最终用户的程序相冲突。

  4. 9.1.1 动态链接库和静态库 ·静态链接库:提供功能函数的目标代码,如果应用程序调用 库中的函数,则在程序的编译链接阶段,就将库中被调函数的 目标代码复制链接到运行文件中。 ·动态链接库:提供功能函数的目标代码,如果应用程序调用 库中的函数,则在程序的编译链接阶段,只是记录被调函数的 位置信息(即位于哪个动态链接库的什么位置),而不将库中 被调函数的目标代码复制链接到运行文件。直到程序执行时, 才依据被调函数的位置信息找到函数的目标代码,完成真正的 链接,因此称为动态链接。被调函数的位置信息被放在动态链 接库的导入库文件中(与动态链接库文件同时创建)。

  5. 与静态链接库相比,动态链接库具有以下不同之处和优点: ·当两个以上程序调用同一库中的函数时,使用静态链接库,则将随着 所运行的程序在内存中出现该函数的多份拷贝;使用动态链接库,则 无论有多少运行的程序,被调用函数在内存中只有一份拷贝,更适合 多任务环境。 ·将共用的资源制作成动态链接库可以实现资源共享,Windows下的串 行口、并行口等外设的驱动程序,字体库等都是通过动态链接库实现 资源共享的。 ·由于静态链接库是将函数的目标代码复制到程序的运行文件中,所以 应用程序可以脱离库独立运行;而动态链接库是在程序运行时才发生 真正的链接,所以提供可执行程序的运行文件的同时,还必须提供所 调用的动态链接库文件。

  6. 动态链接库与应用程序都是目标代码,它们协同工作完成进动态链接库与应用程序都是目标代码,它们协同工作完成进 程的操作。在这种协同工作中它们的区别在于:应用程序总是 主动地调用动态链接库,而动态链接库总是被动地提供服务, 有些情况下(如中断驱动)也主动完成一些功能。

  7. 9.1.2 动态链接库与进程的关系 进程与被加载的动态链接库之间的关系: Windows NT进程内存映象 0xFFFFFFFF 2G保护区 0x80000000 Windows NT内核 设备驱动程序等 64K隔离区 0x7FFFFFFF 2G私有区 0x00000000 Windows DLL 应用程序DLL 内存映射文件 EXE 64K隔离区

  8. 进程: 进程中的线程可以调用动态链接库中任意一个函进程: 进程中的线程可以调用动态链接库中任意一个函 数和使用动态链接库提供的类、变量,通过堆栈 为被调用的动态链接库中的函数提供参数。 动态链接库:库中的所有资源(函数、类、变量)都可被进程 中的所有线程使用,被调用的库函数通过进程中 线程的堆栈获取被调用的参数、分配任何它所需 要的局部变量和返回调用结果,函数所创建的任 何对象都归调用它的线程和进程所有,动态链接 库在进程中一无所有。

  9. 9.1.3 动态链接库与静态链接库的加载原理 1静态链接库 ⑴提供形式: ·库文件*.LIB 库函数和其他资源的目标代码,用于静态 链接。 ·头文件*.h库函数和其他资源的声明,用于编译。 ⑵加载方法: 静态联编,即在编译链接阶段,就把应用程序所调用的库 函数目标代码复制连接进应用程序的目标程序中,所以随 后应用程序的运行将可以独立于静态库的存在。

  10. 2 动态链接库 ⑴提供形式: ①库文件*.dll库函数和其他资源的目标代码,用于动态 链接。 ②导入文件*.lib库函数和其他资源的位置索引信息,用于 静态链接。位置索引信息有两种形式: ·符号索引—— 按照函数名、类名和资源 名寻找目标代码; ·序号索引—— 按照函数、类、资源在库 中的序号寻找目标代码。 ③头文件*.h库函数和其他资源的声明,用于编译。

  11. ⑵加载方法: ①静态联编阶段,通过*.lib 和*.h 文件在应用程序的目标代 码中确定被调用库函数的位置。 ②动态链接阶段,即在应用程序加载后,首先根据程序中 由 *.lib 提供的 dll 文件名,搜寻 dll 文件。系统搜寻 dll文 件按照如下顺序: ·包含 EXE 文件的目录; ·进程执行的当前目录; ·Windows目录; ·Windows\system 目录; ·PATH 环境变量列出的目录。

  12. 一般情况下,dll 文件放在应用程序的执行文件所在的目 录下,而系统所用的 dll 文件则放在 Windows\system 目录 下。系统找到对应的 dll 文件后,把它加载到进程中的一 个相对固定的地址。在 Win32,该地址为 0x10000000, 而进程的加载地址为 0x400000。如果加载多个 dll,则顺 序下移。这个地址由实例句柄来描述,它唯一地标志了 被加载动态链接库在进程中的位置。根据该句柄和由 lib 文件提供的位置索引信息很容易就找到了对应的目标代 码,从而实现了对库函数等的动态链接调用。

  13. 9.1.4 实例1:静态库的制作和使用 动态链接库可以视为是静态链接库的延伸,在大多数情况 下,使用动态链接库具有更多的优点,但有些情况使用静态链接库更合适,二者并非可以相互替代。学习编写和使用动态链 接库之前,学习如何编写和使用静态链接库也是十分必要的。 1 生成静态链接库框架 在 VC 环境中,选择 File -> New 菜单项,弹出 New 对话框。 选择 Projects标签,在项目类别中选择 Win32 Static Library,在 Name文本框中输入项目名,即建立一个与项目名同名的静态 链接库。本例中建立一个 “mymath.lib”。

  14. 在生成项目过程中的 Win32 Static Library – Step 1 of 1 对话框中使用默认选择,即不做任何新的选择。

  15. 2 添加库源文件 ⑴ 添加库头文件 mymath.h #ifndef _MYMATH_H // 防止重定义 #define _MYMATH_H extern "C"// 标准c风格代码 { int Summary( int n ); int Factorial( int n ); } #endif 其中:extern “C” 关键字表明被修饰的函数是 C 风格的外部函 数。这样做的目的是避免 C++ 编译器把函数名改为带修饰名 的名称,因为带有修饰名的函数名不能被非 C++ 编译器(如 VB、Delphi 等)正确地编译。

  16. ⑵ 添加库源代码文件 mymath.cpp #include "mymath.h" int Summary( int n ) { int sum = 0; int i; for( i = 1; i < n; i++ ) { sum += i; } return sum; }

  17. int Factorial( int n ) { int Fact = 1; int i; for( i = 1; i <= n; i++ ) { Fact *= i; } return Fact; }

  18. 3 编译库文件 成功编译后,会在项目的 mymath\debug\ 子目录中建立一个 名为 mymath.lib 的静态链接库文件。

  19. 4 测试静态链接库 由于库文件中的函数是不能独立运行的,因此必须建立一个 可执行程序,并在此程序中测试所建静态链接库中的函数。 ⑴ 使用 MFC AppWizard(exe) 在当前工作区中生成一个名为 “test” 的 Dialog Based类型的测试项目。 ⑵ 修改对话框资源 在 IDD_TEST_DIALOG对话框中添加两个按钮控件:

  20. ⑶ 使用 ClassWizard 为控件 IDC_SUM 和 IDC_FACTORIAL 的消息 BN_CLICKED 添加映射和响应函数 OnSum 和 OnFactorial。 在 CTestDlg定义文件中添加的响应函数声明: afx_msg void OnSum(); afx_msg void OnFactorial(); 在 CTestDlg实现文件中添加的消息映射项和响应函数定义: … ON_BN_CLICKED( IDC_SUM, OnSum ) ON_BN_CLICKED( IDC_FACTORIAL, OnFactorial ) …

  21. void CTestDlg::OnSum() { int nSum = Summary(10) ; CString sResult; sResult.Format("Sum(10) = %d", nSum); AfxMessageBox(sResult); } void CTestDlg::OnFactorial() { int nFact = Factorial(10) ; CString sResult; sResult.Format("10! = %d", nFact); AfxMessageBox(sResult); }

  22. ⑷ 添加编译链接静态库的有关代码 ·首先将 mymath.lib 和 mymath.h 复制到 test 目录下; ·使用 Project -> Add to Project -> Files 菜单命令,或在 Workspace 的属性页 FileView 中使用环境菜单项Add Files to Project 命令, 将 mymath.lib 加入到 test 项目中; ·在 testdlg.cpp 文件的头部加入包含 mymath.h 的代码: … #include "mymath.h“ … static char THIS_FILE[] = __FILE__; #endif … 5 编译运行测试项目“test”

  23. 9.1.5 实例2:Win32 动态链接库的创建和使用 1 生成 dll 项目 ⑴ 在 VC 环境中,选择 File -> New 菜单项,弹出 New 对话框。 在 Project 标签页下,选择 Win32 Dynamic-Link Library 项目生 成向导。 ⑵ 输入项目名 “mymaths”,进入 Win32 Dynamic-Link Library – Step 1 of 1 对话框。选择 A DLL that’exports some symbols 。 ⑶ 单击 Finish 按钮,创建动态链接库所需要的项目文件框架。

  24. 2 修改框架文件 项目目录中包括 4个文件(以本例中的文件为例): ·源文件 mymaths.cpp:该文件定义了 DllMain 函数和要导出的 变量、函数和类的定义; ·头文件 mymaths.h:该文件定义了要导出的变量、函数和类的 原型及导出标识符; ·预编译头文件 StdAfx.h 和源文件 StdAfx.cpp。

  25. ⑴ 在头文件 mymaths.h 中添加导出函数的原型 #ifdef MYMATHS_EXPORTS #define MYMATHS_API __declspec(dllexport) #else #define MYMATHS_API __declspec(dllimport) #endif // This class is exported from the mymaths.dll class MYMATHS_API CMymaths { public: CMymaths(void); // TODO: add your methods here. };

  26. extern MYMATHS_API int nMymaths; MYMATHS_API int fnMymaths(void); MYMATHS_API int Summary(int); MYMATHS_API int Factorial(int); 其中 ·MYMATHS_API创建动态链接库时为 _declspec(dllexport) 导 出标志;在使用动态库时为 _declspec(dllimport)导入标志。 ·MYMATHS_EXPORTS创建动态链接库时,框架自动定义 了该宏常量;使用动态库时,则不定义它。 显然,通过上述宏的作用,使得该头文件 mymaths.h 即可以 作为导出头文件,又可以作为导入头文件。

  27. ⑵ 在源文件 mymaths.cpp 中加入导出函数定义 #include "stdafx.h" #include "mymaths.h“ BOOL APIENTRY DllMain( HANDLE 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; }

  28. MYMATHS_API int Summary(int n) { int sum = 0; int i; for(i = 1; i < n; i++) sum += i; return sum; } MYMATHS_API int Factorial(int n) { int Fact = 1; int i; for(i = 1; i <= n; i++) Fact *= i; return Fact; }

  29. 其中: ·#include “mymaths.h”声明了动态链接库所包含的两个导 出函数。 ·DllMain动态链接库入口函数,应用程序通过该入口函数 访问动态链接库所提供的服务。该函数的主体是一个 switch/case结构。在各个 case 分支中允许加入你所需要的 特定代码,例如 在 DLL_PROCESS_ATTACH分支中加入动态链接库执行时的 初始化代码; 在 DLL_PROCESS_DETACH分支中加入动态链接库卸载时 的清理代码。 详细信息参见 MSDN中的相关部分。 ·库功能函数 Summary 和 Factorial 的定义代码。

  30. 3 编译动态链接库项目 成功编译后,会在项目的 mymaths\debug\ 子目录中建立名为 mymaths.dll 的动态链接库文件和一个名为 mymaths.lib 的导入库 文件。

  31. 4 测试 mymaths.dll 使用动态链接库有两种方式:隐式连接和显式连接。 ⑴ 通过引入库的隐式连接 ① 把实例1中的 “test”项目加入 mymaths 工作区: 可以重新建立一个与实例1中的测试项目相同的项目 test。 也可以按照以下方法将实例1中的测试项目复制加入到本 例中: ·将实例1 的 test 目录及其目录下的文件复制到 mymaths 目录下;

  32. ·在 mymaths 工作区的文件视图区 File View 中,右击 Workspace ‘mymaths’ 选择 “Insert project into workspace… ” 命令, 在随后的对话框中选择 test.dsp把项目 test 加入 mymaths工作区。

  33. ·在文件视图区 FileView 中删除 test 项目下的 mymath.h 和 mymath.lib 条目以及 test 目录中的 mymath.h 和 mymath.lib 文件。

  34. 把动态链接库相关的文件复制到指定的目录下: ·将动态链接库头文件 mymaths.h 从 ..\mymaths\ 目录中复 制到 ..\mymaths\test\ 目录中,以便 test 应用程序编译时使 用; ·将导入库 mymaths.lib 从 ..\mymaths\debug\ 目录中复制到 ..\mymaths\test\中,以便 test 应用程序静态链接时使用; ·将动态链接库 mymaths.dll 从 ..\mymaths\debug\ 目录中复 制到 ..\mymaths\test\debug\ 目录中或复制到应用程序运行 时可以找到的磁盘目录中,便于 test 应用程序运行时与 mymaths.dll动态链接。

  35. ③使用 Project->Add to project->Files 菜单命令,将 mymaths.lib 加入到 test 项目中,或通过 Project->Settings…->Link 在 Project Settings 对话框中将 mymaths.lib 加入 Link 属性的 Object / library modules 中。

  36. ④在 testdlg.cpp 中加入包含动态链接库头文件 mymaths.h 的 代码: #include "mymaths.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ⑤ 编译运行 “test”

  37. ⑵ 直接指定库和函数地址的显式连接 ① 使用 MFC AppWizard(exe) 在 mymaths 工作区中再添加一个 名为 “test1” 的 Dialog Based 类型的应用程序项目。 ② 在对话框 IDD_TEST1_DIALOG 中加入按钮倥件 IDC_SUM 和 IDC_FACTORIAL, ③ 使用 ClassWizard 为按钮 IDC_SUM 和 IDC_FACTORIAL 的通 知消息 BN_CLICKED 添加消息映射和响应函数 OnSum 和 OnFactorial。函数的定义代码可以从 test项目的 testdlg.cpp 中复制相应的代码。

  38. ④ 在 CTest1Dlg 类中增加用于显式连接动态链接库的函数 LoadDLL,并编写操作代码。 在test1dlg.h中增加了: class CTest1Dlg : public CDialog { … protected: BOOL LoadDLL(); … };

  39. 在 test1dlg.cpp 中增加了: BOOL CTest1Dlg::LoadDLL() { // It return if DLL library has been loaded if(ghMathsDLL != NULL) return TRUE; // To load Mymaths.DLL ghMathsDLL = ::LoadLibrary("mymaths.DLL"); // To display indicating information if loading fails if(ghMathsDLL == NULL) { AfxMessageBox("Cannot load DLL file!"); return FALSE; }

  40. // To get the address of function Summary in DLL. Summary = (MYPROC)::GetProcAddress( ghMathsDLL, LPCSTR 2 ); // ordinal value of Summary is 2 // To get the address of function Factorial in DLL. Factorial = (MYPROC)::GetProcAddress( ghMathsDLL, LPCSTR 1 ); // ordinal value of Factorial is 1 return TRUE; } 其中: ·ghMathsDLL是加载后的动态链接库的句柄。 ·Summary和Factorial是获取的动态链接库中的导出函数 Summary(…) 和 Factorial(…) 的调用地址。

  41. 上述三个变量是全程的,它们被定义在 test1dlg.cpp 中: … #endif // The instance of the Mymaths.DLL library HINSTANCE ghMathsDLL = NULL; typedef int (*MYPROC) (int); // declare the Summary() function from the Mymaths.DLL library MYPROC Summary; // declare the Factorial() function from the Mymaths.DLL library MYPROC Factorial;

  42. ⑤ 在 OnSum 和 OnFactorial 的中加入调用 LoadDLL 的代码: void CTest1Dlg::OnSum() { // TODO: Add your control notification handler code here if(!LoadDLL()) return; int nSum = Summary(10); CString sResult; sResult.Format("Sum(10) = %d", nSum); AfxMessageBox(sResult); }

  43. void CTest1Dlg::OnFactorial() { // TODO: Add your control notification handler code here if(!LoadDLL()) return; int nFact = Factorial(10); CString sResult; sResult.Format("10! = %d", nFact); AfxMessageBox(sResult); } ⑥ 为了能显式连接动态链接库,将 mymaths.dll 复制到 ..\mymaths\test1\debug\ 目录中。

  44. ⑦ 编译运行 “test1” 显式连接动态链接库的方式经常用在随时加载或者有选择地 加载动态链接库的场合。例如,一个程序带有多个动态链接 库,分别用于访问 JPEG、BMP、GIF 等多种图象文件格式, 这些动态链接库提供了相同的库函数接口。此时,无法使用 引入库隐式连接动态链接库,可以用下面的显式连接的方法 解决: HANDLE nLibrary; FARPROC lpFunc; int nFormat;

  45. hLibrary = NULL; if(nFormat == JPEG) // 如果是JPEG格式,装入JPEG动态链接库 hLibrary = LoadLibrary(“JPEG.DLL”); else if(nFormat == BMP) // 如果是BMP格式,装入BMP动态链接库 hLibrary = LoadLibrary(“BMP.DLL”); else // 如果是GIF格式,装入GIF动态链接库 hLibrary = LoadLibrary(“GIF.DLL”); if(hLibrary) { lpFunc = GetProcAddress(hLibrary, “ReadImage”); if(lpFunc != (FARPROC)NULL) (*lpFunc)((LPCTSTR)strFileName); // 装载图象 FreeLibrary(hLibrary) // 释放动态链接库 }

  46. 其中: ·LoadLibrary 函数装入所需要的动态链接库,并返回句柄。 ·GetProcAddress 函数使用函数名取得函数的地址,利用函 数地址,就可以访问动态链接库的函数了。注意,用于获 取函数地址的“函数名字串”有可能与函数名有所不同(可 以在导入库 *.lib 中查找)。 ·FreeLibrary 函数通过检查动态链接库的引用计数器,判断 是否还有别的程序正在使用这个动态链接库。如果没有, 从内存中移去该动态链接库;如果有,将动态链接库的使 用计数器减1,LoadLibrary 则将引用计数器加1。

  47. 9.2 MFC动态链接库 虽然从实现功能的角度看,静态链接库和动态链接库都一样 可以将用户所需要的库函数打包在一个用户模块中,使得重用 这些函数功能的用户都能告别源代码,但两者的用途各有其 所。但在一些情况下,必须使用动态链接库,这些场合包括: ⑴ 多个应用程序共享代码和资源。 ⑵ 在钩子程序<HOOK> 过滤系统消息时必须使用动态链接库。 ⑶ 设备驱动程序必须是动态链接库。 ⑷ 如果要在对话框编辑器中使用自定义控件,也必须使用动态 链接库或 ActiveX 控件。

  48. 使用动态链接库可以以一种自然的方式将一个大规模应用程 序划分为若干小模块,有利于软件开发人员的分工和合作。 ⑹ 为了实现应用程序的国际化,可以将针对不同国家的语言等 信息作为资源存放在不同版本的动态链接库中,对于针对不 同国家的应用程序只要连接不同的动态链接库即可。 在前一节所讲述的动态链接库的编制中只能使用使用 Win32 API 函数。在VC 集成开发环境中还提供了可以使用 MFC 类库编 制动态链接库的方法,使我们能更方便地编制功能强大的 MFC 动态链接库。AppWizard 支持创建的 MFC 动态链接库有两类: ·MFC 正规动态链接库 ·MFC 扩展动态链接库

  49. 9.2.1 MFC 正规动态链接库与 MFC 扩展动态链接库

More Related