1 / 31

第 13 章 动态链接库

第 13 章 动态链接库. 13.1 DLL 基础知识. 本节讲述 DLL 的基础知识。. 13.1.1 DLL 概述.

lotte
Télécharger la présentation

第 13 章 动态链接库

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. 第13章 动态链接库

  2. 13.1 DLL基础知识 本节讲述DLL的基础知识。

  3. 13.1.1 DLL概述 Windows系统平台上提供了一种完全不同的有效的编程和运行环境,可以将独立的程序模块创建为较小的DLL(Dynamic Linkable Library)文件,并可对它们单独编译和测试。在运行时,只有当EXE程序确实要调用这些DLL模块的情况下,系统才会将它们装载到内存空间中。这种方式不仅减少了EXE文件的大小和对内存空间的需求,而且使这些DLL模块可以同时被多个应用程序使用。Windows自己就将一些主要的系统功能以DLL模块的形式实现。

  4. 13.1.2 认清DLL与LIB DLL与LIB的相似之处是它们都是将一部分可执行代码以及数据放在库中供用户程序使用,而且在使用时,这些代码就象是用户程序本身的一部分。 • LIB文件是静态链接库文件,在其中放置了许多的函数和变量。其中一部分函数和变量是供内部使用的,在接口中不可见,即没有输出;另一部分是供接口使用的(当然内部函数也可以使用),外部可见,从而应用程序可以调用这些输出的函数和变量。 • 在DLL中同样包括许多变量和函数,也分为内部变量、函数和外部接口函数、变量。对于内部的函数和变量,供DLL自己调用,因此并不包含名称,只使用地址;对于供外部调用的函数和变量,在内部的时候也使用地址,只是在头文件中包含了输出变量和函数的名称,而且包含了这些名称所对应的地址,即将名称和地址对应起来。通过这些名称就可以查找对应变量和函数的地址(函数的地址是指函数入口点的地址)。

  5. 13.1.3 认清DLL与EXE • DLL和EXE都是Windows下的可执行模块,在对应的文件结构上,它们也类似的:具有文件头,重定位信息表,导入动态库表等,另外,DLL作为供程序调用的服务者,文件中还包含导出的函数表和变量表。 • DLL是服务的提供者,主要用来提供输出变量和函数供别的程序调用,在DLL被装入的时候,以及进程中创建线程的时候,Windows都会以不同的参数调用入口点函数,然后该函数进行某些初始化工作后返回,DLL的执行就停止了。Windows并不为DLL创建单独的进程空间,而是将其装入共享地址,然后将其映射到不同的进程供进程调用,从而达到代码共享的目的。 • EXE是DLL所提供服务的使用者,调用DLL中的输出的函数和变量,每一个EXE在运行的时候,Windows均为它创建单独的进程环境,包括进程地址空间,EXE就在它的地址空间中运行,对别的进程是不透明的,因此也就无法为别的进程调用,因此在许多情况下只能使用DLL来实现某些功能。

  6. 13.1.4 DLL的两种动态链接方法 • 加载时动态链接 由编译系统完成对DLL的加载和应用程序结束时DLL卸载的编码(如还有其它程序使用该DLL,则Windows对DLL的应用记录减1,直到所有相关程序都结束对该DLL的使用时才释放它),简单实用,但不够灵活,只能满足一般要求。 • 运行时动态链接 是由编程者用API函数加载和卸载DLL来达到调用DLL的目的,使用上较复杂,但能更加有效地使用内存,是编制大型应用程序时的重要方式。

  7. 13.2 DLL入/出口函数 Win32 DLL与 Win16 DLL有很大的区别,这主要是由操作系统的设计思想决定的。一方面,在Win16 DLL中程序入口点函数和出口点函数(LibMain和WEP)是分别实现的;而在Win32 DLL中却由同一函数DLLMain来实现。无论何时,当一个进程或线程载入和卸载DLL时,都要调用该函数。

  8. 13.2.1 DllMain函数 每一个DLL必须有一个入口点,DLLMain是一个缺省的入口函数。DLLMain负责初始化(Initialization)和结束(Termination)工作,每当一个新的进程或者该进程的新的线程访问DLL时,或者访问DLL的每一个进程或者线程不再使用DLL或者结束时,都会调用DLLMain。但是,使用TerminateProcess或TerminateThread结束进程或者线程,不会调用DLLMain。

  9. 13.2.2 MFC AppWizard 生成的Regular DLL入/出口 MFC AppWizard 生成的Regular DLL在后面的章节会有介绍,这里只讨论它的入/出口点问题。 每个Regular DLL都有MFC AppWizard 自动生成的CWinApp派生类的对象,与其它MFC应用程序一样,它是在CWinApp派生类的成员函数InitInstance和ExitInstance完成初始化和终止的工作。 实际上,MFC提供了一个基本的DllMain函数,在这种DLL中不必自己编写DllMain函数,由MFC提供的这个函数在装载DLL的时候调用InitInstance函数,而在DLL退出的时候调用ExitInstance函数。所需要完成的初始化和终止的工作需要在这两个函数中完成。

  10. 13.3 从DLL中导出函数 关于DLL的函数动态链接库中定义有两种函数:导出函数(export function)和内部函数(internal function)。导出函数可以被其它模块调用,内部函数在定义它们的DLL程序内部使用。

  11. 13.3.1 使用DEF文件导出函数 模块定义文件(.DEF)是一个或多个用于描述DLL属性的模块语句组成的文本文件,每个DEF文件至少必须包含以下模块定义语句: • 第一个语句必须是LIBRARY语句,指出DLL的名字; • EXPORTS语句列出被导出函数的名字;将要输出的函数修饰名罗列在EXPORTS之下,这个名字必须与定义函数的名字完全一致,如此就得到一个没有任何修饰的函数名了。 • 可以使用DESCRIPTION语句描述DLL的用途(此句可选); • “;”对一行进行注释(可选)。

  12. 13.3.2 使用关键字_declspec(dllexport) 使用MFC提供的修饰符号_declspec(DLLexport)在要输出的函数、类、数据的声明前加上_declspec(DLLexport)的修饰符,表示输出。__declspec(DLLexport)在C调用约定、C编译情况下可以去掉输出函数名的下划线前缀。extern "C"使得在C++中使用C编译方式成为可能。在C++下定义C函数,需要加extern "C"关键词。用extern "C"来指明该函数使用C编译方式。输出的C函数可以从C代码里调用。

  13. 13.3.3 使用AFX_EXT_CLASS导出 MFC扩展DLL使用AFX_EXT_CLASS来导出类,链接这种DLL的应用程序或其他DLL使用这个宏来导入类。 如果用于DLL应用程序的实现中,则表示输出;如果用于使用DLL的应用程序中,则表示输入。要输出整个的类,对类使用_declspec(_DLLexpot);要输出类的成员函数,则对该函数使用_declspec(_DLLexport)。

  14. 13.4 DLL中的数据和内存

  15. 13.4.1 DLL多进程间的数据共享 DLL被多个进程调用,它的代码从而被映射到不同的进程内存空间之中,DLL共享数据也尽量只在系统中有一个拷贝,这是通过COPY-ON-WRITE技术实现的。 使用共享数据段,首先需要把欲放入数据段的数据定义在特定的数据段中,然后在说明这个数据段是共享数据段就可以了。

  16. 13.4.2 DLL进程中多线程间的数据隔离 Windows API提供了进程中多线程数据分离的一系列TLS(Thread Local Storage)函数,这是进程内线程隔离的解决方法,TLS函数集共有四个函数: • DWORD TlsAlloc (VOID); • BOOL TlsFree(DWORD dwTlsIndex); • LPVOID TlsGetValue (DWORD dwTlsIndex); • BOOL TlsSetValue(DWORD dwTlsIndex,LPVOID lpTlsValue); 函数TlsAlloc用来分配线程局部变量索引,它的返回值是后面三个函数的参数dwTlsIndex。而函数TlsFree负责释放线程变量索引。所以这两个函数应该在副线程创建之前的主线程中调用。在DLL模块中,就是在入口点函数被参数调用时候使用。 线程运行过程中,可以使用函数TlsGetValue和TlsSetValue根据线程变量的索引号存取各自的数据指针,并存取到各自的数据。

  17. 13.5 几种常用的DLL MFC中的DLLa、Non-MFC DLL:指的是不用MFC的类库结构,直接用C语言写的DLL,其输出的函数一般用的是标准C接口,并能被非MFC的应用程序所调用。

  18. 13.5.1 Win32 DLL VC6支持自动生成的Win32 DLL和MFC AppWizard DLL,其中自动生成的Win32 DLL共包括三种DLL工程。从FILE|NEW菜单项,选择对话框中的Projects选项卡,图13.1选中Win32 Dynamic-Link Liabrary,输入工程名,点击OK按钮后,弹出如下对话框图13.2所示:选择所要生成的Win32 DLL类型了。

  19. 13.5.2 Regular statically linked to MFC DLL 在VC6中有三种形式的MFC DLL(在该DLL中可以使用和继承已有的MFC类)可供选择,即Regular statically linked to MFC DLL(标准静态链接MFC DLL)和Regular using the shared MFC DLL(标准动态链接MFC DLL)以及Extension MFC DLL(扩展MFC DLL)。 第一种DLL的特点是,在编译时把使用的MFC代码加入到DLL中,因此,在使用该程序时不需要其他MFC动态链接类库的存在,但占用磁盘空间比较大;

  20. 13.5.3 Regular using the shared MFC DLL 第二种DLL的特点是:在运行时,动态链接到MFC类库,因此减少了空间的占用,但是在运行时却依赖于MFC动态链接类库。动态链接到MFC的规则DLL应用程序里头的输出函数可以被任意Win32程序使用,包括使用MFC的应用程序。但是,所有从DLL输出的函数应该以如下语句开始: AFX_MANAGE_STATE(AfxGetStaticModuleState( )) 此语句用来正确地切换MFC模块状态。

  21. 13.5.4 MFC Extension DLL 第三种DLL的特点类似于第二种,作为MFC类库的扩展,只能被MFC程序使用。 Extension DLL用来实现从MFC所继承下来的类的重新利用,就是说,用这种类型的动态连接库,可以用来输出一个从MFC所继承下来的类。它输出的函数仅可以被使用MFC且动态链接到MFC的应用程序使用。可以从MFC继承所想要的,更适于用户自己用的类,并把它提供给应用程序。也可随意的给应用程序提供MFC或MFC继承类的对象指针。Extension DLL使用MFC的动态连接版本所创建的,并且它只被用MFC类库所编写的应用程序所调用。

  22. 13.6 DLL的调用和调试

  23. 13.6.1 VC对DLL的调用 系统运行一个调用DLL的程序时,将在以下位置查找 该DLL: • 1.包含EXE文件的目录 • 2.进程的当前工作目录 • 3.Windows系统目录 • 4.Windows目录 • 5.列在Path环境变量中的一系列目录 若找不到该文件,系统将显示对话框提示并终止程序 的执行,

  24. 13.6.2 VB对DLL 的调用 在VB中调用Windows DLL之前,必须在Standard(代码)模块的Declarations段中加一个特殊的声明。对于一个DLL或API函数,Declare语句是Windows所必需的,这一点很重要。并且在32位版的VB中动态链接库中的函数对条件是很敏感的。

  25. 13.6.3 DLL的调试 DLL的调试有很多中方法,但都需要把DLL工程生成的后缀名为.dll的文件放在执行它的应用程序可以找到的目录中。这可以有很多中方法,手工也行的。 应用VC可以很容易地调试DLL,只要从DLL工程中运行调试程序即可。当第一次调试DLL的时候,调试程序会跳出如下对话框图13.5。

  26. 13.7 DLL例程

  27. 13.7.1 使用已有的DLL 本例程将通过调用DLL来实现程序的隐藏,即不显示程序窗口,程序不显示在任务栏上,在按下Ctrl+Alt+Del键后出现的任务列表中也不显示。 首先创建工程: • 1.在VC++集成开发环境中,通过菜单File | New,弹出New对话框; • 2.在Projects选项卡中选择MFC App Wizard(exe),在Project name中输入“Eg13_1”Location读者可以自己选择; • 3.按下OK按钮,在弹出的MFC App Wizard Step-1对话框中选择程序框架为单文档框架,即选中Single Document; • 4.按下OK按钮,在弹出的New Project Information对话框中按下OK按钮后等待创建完相应的工程。 参考课本260页

  28. 13.7.2 资源DLL 本例程将建立两个DLL文件以实现自己的双语菜单。 首先创建工程Eg13_2: • 1.在VC++集成开发环境中,通过菜单File | New,弹出New对话框; • 2.在Projects选项卡中选择MFC App Wizard(exe),在Project name中输入“Eg13_2”Location读者可以自己选择; • 3.按下OK按钮,在弹出的MFC App Wizard Step-1对话框中选择程序框架为单文档框架,即选中Single Document; • 4.按下Finish按钮,在弹出的New Project Information对话框中按下OK按钮后等待创建完相应的工程 参考课本262-265页完成Eg13_2。

  29. 13.7.2 资源DLL(续) 下面建立英文菜单DLL。英文菜单资源可以通过系统资源获得,首先建立一个英文单文档应用程序Eg13_3,英文菜单资源将通过它获得。建立Eg13_3工程: • 1.在VC++集成开发环境中,通过菜单File | New,弹出New对话框; • 2.在Projects选项卡中选择MFC App Wizard(exe),在Project name中输入“Eg13_3”Location读者可以自己选择; • 3.按下OK按钮,在弹出的MFC App Wizard Step-1对话框中选择程序框架为单文档框架,即选中Single Document,另外需要选中英语为资源使用的语言; • 4.按下Finish按钮,在弹出的New Project Information对话框中按下OK按钮后等待创建完相应的工程。 参考课本262-265页完成Eg13_3。 参考课本267-268页完成英文DLL资源Eg13_4文件。

  30. 13.7.3 使用自己的DLL 本例程将建立一个的导出函数DLL,并建立应用程序调用这个DLL中的函数 。首先建立DLL模块。 参考课本269-272页完成Eg13_6 , Eg13_7 。

  31. 13.8 小结 本章主要讲述了什么是动态链接库及其相关知识,并举例说明了如何用Visual C++ 6.0建立和调用动态链接库方法。

More Related