第 13 章 动态链接库

31
第 13 第 第第第第第

Upload: lotte

Post on 19-Jan-2016

107 views

Category:

Documents


7 download

DESCRIPTION

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

TRANSCRIPT

Page 1: 第 13 章   动态链接库

第 13 章 动态链接库

Page 2: 第 13 章   动态链接库

13.1 DLL 基础知识

本节讲述 DLL 的基础知识。

Page 3: 第 13 章   动态链接库

13.1.1 DLL 概述

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

Page 4: 第 13 章   动态链接库

13.1.2 认清 DLL 与 LIB

DLL 与 LIB 的相似之处是它们都是将一部分可执行代码以及数据放在库中供用户程序使用,而且在使用时,这些代码就象是用户程序本身的一部分。

LIB 文件是静态链接库文件,在其中放置了许多的函数和变量。其中一部分函数和变量是供内部使用的,在接口中不可见,即没有输出;另一部分是供接口使用的(当然内部函数也可以使用),外部可见,从而应用程序可以调用这些输出的函数和变量。

在 DLL 中同样包括许多变量和函数,也分为内部变量、函数和外部接口函数、变量。对于内部的函数和变量,供 DLL 自己调用,因此并不包含名称,只使用地址;对于供外部调用的函数和变量,在内部的时候也使用地址,只是在头文件中包含了输出变量和函数的名称,而且包含了这些名称所对应的地址,即将名称和地址对应起来。通过这些名称就可以查找对应变量和函数的地址(函数的地址是指函数入口点的地址)。

Page 5: 第 13 章   动态链接库

13.1.3 认清 DLL 与 EXE

DLL 和 EXE 都是 Windows 下的可执行模块,在对应的文件结构上,它们也类似的:具有文件头,重定位信息表,导入动态库表等,另外, DLL 作为供程序调用的服务者,文件中还包含导出的函数表和变量表。

DLL 是服务的提供者,主要用来提供输出变量和函数供别的程序调用,在 DLL 被装入的时候,以及进程中创建线程的时候, Windows 都会以不同的参数调用入口点函数,然后该函数进行某些初始化工作后返回,DLL 的执行就停止了。 Windows 并不为 DLL 创建单独的进程空间,而是将其装入共享地址,然后将其映射到不同的进程供进程调用,从而达到代码共享的目的。

EXE 是 DLL 所提供服务的使用者,调用 DLL 中的输出的函数和变量,每一个 EXE 在运行的时候, Windows 均为它创建单独的进程环境,包括进程地址空间, EXE 就在它的地址空间中运行,对别的进程是不透明的,因此也就无法为别的进程调用,因此在许多情况下只能使用 DLL来实现某些功能。

Page 6: 第 13 章   动态链接库

13.1.4 DLL 的两种动态链接方法

加载时动态链接由编译系统完成对 DLL 的加载和应用程序结束时 DLL卸载的编码(如还有其它程序使用该 DLL ,则 Windows 对 DLL 的应用记录减 1 ,直到所有相关程序都结束对该 DLL 的使用时才释放它),简单实用,但不够灵活,只能满足一般要求。

运行时动态链接是由编程者用 API 函数加载和卸载 DLL 来达到调用 DLL 的目的,使用上较复杂,但能更加有效地使用内存,是编制大型应用程序时的重要方式。

Page 7: 第 13 章   动态链接库

13.2 DLL 入 / 出口函数

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

Page 8: 第 13 章   动态链接库

13.2.1 DllMain 函数

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

Page 9: 第 13 章   动态链接库

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 函数。所需要完成的初始化和终止的工作需要在这两个函数中完成。

Page 10: 第 13 章   动态链接库

13.3 从 DLL 中导出函数

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

Page 11: 第 13 章   动态链接库

13.3.1 使用 DEF 文件导出函数

模块定义文件( .DEF )是一个或多个用于描述 DLL属性的模块语句组成的文本文件,每个 DEF文件至少必须包含以下模块定义语句:

第一个语句必须是 LIBRARY语句,指出 DLL 的名字; EXPORTS语句列出被导出函数的名字;将要输出的

函数修饰名罗列在 EXPORTS 之下,这个名字必须与定义函数的名字完全一致,如此就得到一个没有任何修饰的函数名了。

可以使用 DESCRIPTION语句描述 DLL 的用途(此句可选);

“;”对一行进行注释(可选)。

Page 12: 第 13 章   动态链接库

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代码里调用。

Page 13: 第 13 章   动态链接库

13.3.3 使用 AFX_EXT_CLASS 导出

MFC扩展 DLL 使用 AFX_EXT_CLASS来导出类,链接这种 DLL 的应用程序或其他DLL 使用这个宏来导入类。

如果用于 DLL 应用程序的实现中,则表示输出;如果用于使用 DLL 的应用程序中,则表示输入。要输出整个的类,对类使用 _declspec ( _DLLexpot );要输出类的成员函数,则对该函数使用 _declspec ( _DLLexport )。

Page 14: 第 13 章   动态链接库

13.4 DLL 中的数据和内存

Page 15: 第 13 章   动态链接库

13.4.1 DLL 多进程间的数据共享

DLL 被多个进程调用,它的代码从而被映射到不同的进程内存空间之中, DLL 共享数据也尽量只在系统中有一个拷贝,这是通过COPY-ON-WRITE技术实现的。

使用共享数据段,首先需要把欲放入数据段的数据定义在特定的数据段中,然后在说明这个数据段是共享数据段就可以了。

Page 16: 第 13 章   动态链接库

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根据线程变量的索引号存取各自的数据指针,并存取到各自的数据。

Page 17: 第 13 章   动态链接库

13.5 几种常用的 DLL

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

Page 18: 第 13 章   动态链接库

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 类型了。

Page 19: 第 13 章   动态链接库

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 动态链接类库的存在,但占用磁盘空间比较大;

Page 20: 第 13 章   动态链接库

13.5.3 Regular using the shared MFC DLL

第二种 DLL 的特点是:在运行时,动态链接到MFC 类库,因此减少了空间的占用,但是在运行时却依赖于 MFC 动态链接类库。动态链接到 MFC 的规则DLL 应用程序里头的输出函数可以被任意Win32 程序使用,包括使用 MFC 的应用程序。但是,所有从 DLL 输出的函数应该以如下语句开始:

AFX_MANAGE_STATE(AfxGetStaticModuleState( ))

此语句用来正确地切换MFC 模块状态。

Page 21: 第 13 章   动态链接库

13.5.4 MFC Extension DLL

第三种 DLL 的特点类似于第二种,作为 MFC类库的扩展,只能被 MFC 程序使用。

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

Page 22: 第 13 章   动态链接库

13.6 DLL 的调用和调试

Page 23: 第 13 章   动态链接库

13.6.1 VC 对 DLL 的调用

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

的执行,

Page 24: 第 13 章   动态链接库

13.6.2 VB 对 DLL 的调用

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

Page 25: 第 13 章   动态链接库

13.6.3 DLL 的调试

DLL 的调试有很多中方法,但都需要把DLL 工程生成的后缀名为 .dll 的文件放在执行它的应用程序可以找到的目录中。这可以有很多中方法,手工也行的。

应用 VC 可以很容易地调试 DLL ,只要从 DLL 工程中运行调试程序即可。当第一次调试 DLL 的时候,调试程序会跳出如下对话框图 13.5 。

Page 26: 第 13 章   动态链接库

13.7 DLL例程

Page 27: 第 13 章   动态链接库

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页

Page 28: 第 13 章   动态链接库

13.7.2 资源 DLL

本例程将建立两个 DLL 文件以实现自己的双语菜单。首先创建工程 Eg13_2 : 1.在 VC++集成开发环境中,通过菜单 File | New ,弹出 New

对话框; 2.在 Projects选项卡中选择MFC App Wizard ( exe ),在 P

roject name 中输入“ Eg13_2”Location读者可以自己选择; 3.按下 OK按钮,在弹出的 MFC App Wizard Step-1 对话框

中选择程序框架为单文档框架,即选中 Single Document ; 4.按下 Finish按钮,在弹出的 New Project Information 对话框

中按下 OK按钮后等待创建完相应的工程参考课本 262-265页完成 Eg13_2 。

Page 29: 第 13 章   动态链接库

13.7.2 资源 DLL (续)

下面建立英文菜单 DLL 。英文菜单资源可以通过系统资源获得,首先建立一个英文单文档应用程序 Eg13_3 ,英文菜单资源将通过它获得。建立 Eg13_3 工程:

1.在 VC++集成开发环境中,通过菜单 File | New ,弹出 New 对话框; 2.在 Projects选项卡中选择MFC App Wizard ( exe ),在 Project n

ame 中输入“ 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 文件。

Page 30: 第 13 章   动态链接库

13.7.3 使用自己的 DLL

本例程将建立一个的导出函数 DLL ,并建立应用程序调用这个 DLL 中的函数 。首先建立 DLL 模块。

参考课本 269-272页完成 Eg13_6 , Eg13_7 。

Page 31: 第 13 章   动态链接库

13.8 小结

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