DLL

来源:互联网 发布:js test方法 编辑:程序博客网 时间:2024/05/10 13:24

一、定义

动态链接库 (DLL) 是作为共享函数库的可执行文件。动态链接提供了一种方法,使进程可以调用不属于其可执行代码的函数。函数的可执行代码位于一个 DLL 中,该 DLL 包含一个或多个已被编译、链接并与使用它们的进程分开存储的函数。DLL 还有助于共享数据和资源。多个应用程序可同时访问内存中单个 DLL 副本的内容。

动态链接与静态链接的不同之处在于:动态链接允许可执行模块(.dll 文件或 .exe 文件)仅包含在运行时定位 DLL 函数的可执行代码所需的信息。在静态链接中,链接器从静态链接库获取所有被引用的函数,并将库同代码一起放到可执行文件中。

使用动态链接代替静态链接有若干优点。DLL 节省内存,减少交换操作,节省磁盘空间,更易于升级,提供售后支持,提供扩展 MFC 库类的机制,支持多语言程序,并使国际版本的创建轻松完成。

 

二、应用程序和 DLL 之间的区别

尽管 DLL 和应用程序都是可执行的程序模块,但它们之间有若干不同之处。对于最终用户来说,最明显的差异在于 DLL 不是可直接执行的程序。从系统角度讲,应用程序和 DLL 之间有两个基本差异:

  • 应用程序可有多个同时在系统上运行的实例,而 DLL 只能有一个实例。

  • 应用程序可以拥有堆栈、共用内存、文件句柄、消息队列这样的事物,而 DLL 不能

三、从 DLL 导出

.DLL 文件的布局与 .exe 文件非常相似,但有一个重要的差异:DLL 文件包含导出表导出表包含 DLL 导出到其他可执行文件的每个函数的名称。这些函数是 DLL 中的入口点;只有导出表中的函数可由其他可执行文件访问。DLL 中的任何其他函数都是 DLL 私有的。通过使用带 /EXPORTS 选项的 Dumpbin 工具,可以查看 DLL 的导出表。

 

四、将可执行文件链接到 DLL

可执行文件以下列两种方式之一链接到(或加载)DLL:

 

五、初始化 DLL

DLL 通常具有在 DLL 加载时必须执行的初始化代码(如分配内存)。使用 Visual C++ 时,在何处添加初始化 DLL 的代码取决于生成的 DLL 类型。如果不需要添加初始化代码或终止代码,则在生成 DLL 时没有什么特别的事情要做。如果需要初始化 DLL,则下表描述了应在何处添加代码。

 

六、使用 DLL 的优点

动态链接具有下列优点:

 

七、非 MFC DLL:概述

非 MFC DLL 是内部不使用 MFC 的 DLL,这类 DLL 中的导出函数可由 MFC 或非 MFC 可执行文件调用。函数通常是通过标准 C 接口从非 MFC DLL 导出的。

八、静态链接到 MFC 的规则 DLL

静态链接到 MFC 的规则 DLL 是在内部使用 MFC 的 DLL,这类 DLL 中的导出函数可由 MFC 或非 MFC 可执行文件调用。正如名称所描述的,这类 DLL 是使用 MFC 静态链接库版本生成的。函数通常是通过标准 C 接口从规则 DLL 导出的。有关如何编写、生成和使用规则 DLL 的示例,请参见示例 DLLScreenCap

 

九、动态链接到 MFC 的规则 DLL

动态链接到 MFC 的规则 DLL 是在内部使用 MFC 的 DLL,这类 DLL 中的导出函数可由 MFC 或非 MFC 可执行文件调用。正如名称所体现的,这类 DLL 是使用 MFC 动态链接库版本(也称作 MFC 共享版本)生成的。函数通常是通过标准 C 接口从规则 DLL 导出的。

AFX_MANAGE_STATE(AfxGetStaticModuleState( ))

动态链接到 MFC 的规则 DLL 具有下列功能:

 

 

 

 

 

 

 

 

 

 

 

  • 它是由 Visual C++ 4.0 引入的一种新的 DLL 类型。

  • 客户端可执行文件可以用任何支持使用 DLL 的语言(C、C++、Pascal、Visual Basic 等)编写;它不必是 MFC 应用程序。

  • 与静态链接的规则 DLL 不同,这类 DLL 动态链接到 MFC DLL(也称作共享 MFC DLL)。

  • 链接到这类 DLL 的 MFC 导入库与用于使用 MFC DLL 的扩展 DLL 或应用程序的 MFC 导入库相同:都是 MFCxx(D).lib。

动态链接到 MFC 的规则 DLL 具有下列要求:

  • 与动态链接到 MFC DLL 的可执行文件相同,这些 DLL 也是通过定义的 _AFXDLL 进行编译的。但同时也像静态链接到 MFC 的规则 DLL 那样,定义了 _USRDLL

  • 这类 DLL 必须实例化 CWinApp 派生类。

  • 此类型的 DLL 使用 MFC 提供的 DllMain与在标准 MFC 应用程序中一样,将所有 DLL 特定的初始化代码放到 InitInstance 成员函数中,将终止代码放到 ExitInstance 中。

由于这类 DLL 使用 MFC 动态链接库版本,因此必须将当前模块的状态显式设置为 DLL 的状态。为此,请在从 DLL 导出的每个函数的开始处使用 AFX_MANAGE_STATE 宏。

与 MFC 应用程序相同,规则 DLL 必须有一个 CWinApp 派生的类和此应用程序类的单个对象。然而,与应用程序的 CWinApp 对象不同,DLL 的 CWinApp 对象没有主消息泵。

请注意,CWinApp::Run 机制不适用于 DLL,因为应用程序拥有主消息泵。如果 DLL 生成无模式对话框或有自己的主框架窗口,则应用程序的主消息泵必须调用从 DLL 导出的例程来调用 CWinApp::PreTranslateMessage

与在标准 MFC 应用程序中一样,将所有 DLL 特定的初始化放到 CWinApp::InitInstance 成员函数中。卸载 DLL 之前,将从 MFC 提供的 DllMain 函数调用 CWinApp 派生类的 CWinApp::ExitInstance 成员函数。

必须随应用程序一起发布共享 DLL:MFCx0.dll 和 Msvcr*0.dll(或类似的文件)。

动态链接到 MFC 的 DLL 无法同时静态链接到 MFC。像任何其他 DLL 一样,应用程序链接到动态链接到 MFC 的规则 DLL。

符号通常是通过标准 C 接口从规则 DLL 导出的。从规则 DLL 导出的函数的声明类似下面这样:

extern "C" __declspec(dllexport) MyExportedFunction( );

规则 DLL 内的所有内存分配都应在该 DLL 内进行;DLL 不应向调用可执行文件传递或从调用可执行文件接收下列任何指针:

  • 指向 MFC 对象的指针

  • 指向由 MFC 分配的内存的指针

如果需要执行上述任一操作,或者如果需要在调用可执行文件和 DLL 之间传递 MFC 派生的对象,则必须生成扩展 DLL。

仅当创建了数据副本后,在应用程序和 DLL 之间传递指向 C 运行库所分配的内存的指针才是安全的。一定不要删除这些指针或调整它们的大小,也不要在没有创建内存副本的情况下使用这些指针。

生成动态链接到 MFC 的规则 DLL 时,需要使用 AFX_MANAGE_STATE 宏正确切换 MFC 模块状态。为此,需将下列代码行添加到从 DLL 导出的函数的开始处:

AFX_MANAGE_STATE(AfxGetStaticModuleState( ))

AFX_MANAGE_STATE 宏不应当用于静态链接到 MFC 的规则 DLL 中,也不应当用于扩展 DLL 中。有关更多信息,请参见管理 MFC 模块的状态数据

有关如何编写、生成和使用规则 DLL 的示例,请参见示例 DLLScreenCap有关动态链接到 MFC 的规则 DLL 的更多信息,请参见示例摘要中标题为“转换 DLLScreenCap 以与 MFC DLL 动态链接”的部分。

 

十、扩展 DLL:概述

MFC 扩展 DLL 是通常实现从现有 Microsoft 基础类库类派生的可重用类的 DLL。扩展 DLL 是使用 MFC 动态链接库版本(也称作共享 MFC 版本)生成的。只有用共享 MFC 版本生成的 MFC 可执行文件(应用程序或规则 DLL)才能使用扩展 DLL。使用扩展 DLL,可以从 MFC 派生新的自定义类,然后将此“扩展”版本的 MFC 提供给调用 DLL 的应用程序。

扩展 DLL 也可用于在应用程序和 DLL 之间传递 MFC 派生的对象。与已传递的对象关联的成员函数存在于创建对象所在的模块中。由于在使用 MFC 的共享 DLL 版本时正确导出了这些函数,因此可以在应用程序和它加载的扩展 DLL 之间随意传递 MFC 或 MFC 派生的对象指针。

有关满足扩展 DLL 基本要求的 DLL 示例,请参见 MFC 示例 DLLHUSK特别要查看 Testdll1.cpp 和 Testdll2.cpp 文件。

请注意,Visual C++ 文档中不再使用 AFXDLL 一词。扩展 DLL 具有与原来的 AFXDLL 相同的特性。

在动态链接到 MFC 的规则 DLL 中,必须在所有导出函数的开始处添加 AFX_MANAGE_STATE 宏,以将当前模块的状态设置为 DLL 的状态。为此,需将下列代码行添加到从 DLL 导出的函数的开始处:

请注意,Visual C++ 文档中不再使用 USRDLL 一词。静态链接到 MFC 的规则 DLL 具有与原来的 USRDLL 相同的特性。

静态链接到 MFC 的规则 DLL 具有下列功能:

  • 客户端可执行文件可以用任何支持使用 DLL 的语言(C、C++、Pascal、Visual Basic 等)编写;它不必是 MFC 应用程序。

  • DLL 可以链接到由应用程序使用的同一 MFC 静态链接库。已不再有单独用于 DLL 的静态链接库版本。

  • 在 MFC 4.0 版之前,USRDLL 与静态链接到 MFC 的规则 DLL 提供相同的功能类型。自 Visual C++ 4.0 版起,“USRDLL”一词已过时。

静态链接到 MFC 的规则 DLL 具有下列要求:

  • 这类 DLL 必须实例化从 CWinApp 派生的类。

  • 此类型的 DLL 使用 MFC 提供的 DllMain与在标准 MFC 应用程序中一样,将所有 DLL 特定的初始化代码放到 InitInstance 成员函数中,将终止代码放到 ExitInstance 中。

  • 尽管“USRDLL”一词已过时,但仍必须在编译器命令行上定义“_USRDLL”。此定义确定从 MFC 头文件中复制的声明。

与 MFC 应用程序相同,规则 DLL 必须有一个 CWinApp 派生的类和此应用程序类的单个对象。然而,与应用程序的 CWinApp 对象不同,DLL 的 CWinApp 对象没有主消息泵。

请注意,CWinApp::Run 机制不适用于 DLL,因为应用程序拥有主消息泵。如果 DLL 打开无模式对话框或有自己的主框架窗口,则应用程序的主消息泵必须调用由 DLL 导出的例程,而该例程应会反过来调用 DLL 应用程序对象的 CWinApp::PreTranslateMessage 成员函数。

有关此函数的示例,请参见 DLLScreenCap 示例。

符号通常是通过标准 C 接口从规则 DLL 导出的。从规则 DLL 导出的函数的声明形如:

extern "C" __declspec(dllexport) MyExportedFunction( );

规则 DLL 内的所有内存分配都应在该 DLL 内进行;DLL 不应向调用可执行文件传递或从调用可执行文件接收下列任何指针:

  • 指向 MFC 对象的指针

  • 指向由 MFC 分配的内存的指针

如果需要执行上述任一操作,或者需要在调用可执行文件和 DLL 之间传递 MFC 派生的对象,则必须生成扩展 DLL。

仅当创建了数据副本后,在应用程序和 DLL 之间传递指向 C 运行库所分配的内存的指针才是安全的。一定不要删除这些指针或调整它们的大小,也不要在没有创建内存副本的情况下使用这些指针。

静态链接到 MFC 的 DLL 无法同时动态链接到共享 MFC DLL。静态链接到 MFC 的 DLL 像任何其他 DLL 一样动态绑定到应用程序;而应用程序像任何其他 DLL 一样链接到静态链接到 MFC 的 DLL。

标准 MFC 静态链接库依据 MFC DLL 命名约定中描述的约定进行命名。但是,在 MFC 3.0 版及更高版本中,不再需要向链接器手动指定希望链接的 MFC 库版本,而是由 MFC 头文件基于预处理器定义(如 _DEBUG_UNICODE)自动确定要链接到的 MFC 库的正确版本。MFC 头文件添加 /DEFAULTLIB 指令来指示链接器链接到特定的 MFC 库版本中。

  • 节省内存和减少交换操作。很多进程可以同时使用一个 DLL,在内存中共享该 DLL 的一个副本。相反,对于每个用静态链接库生成的应用程序,Windows 必须在内存中加载库代码的一个副本。

  • 节省磁盘空间。许多应用程序可在磁盘上共享 DLL 的一个副本。相反,每个用静态链接库生成的应用程序均具有作为单独的副本链接到其可执行图像中的库代码。

  • 升级到 DLL 更为容易。当 DLL 中的函数发生更改时,只要函数的参数和返回值没有更改,就不需重新编译或重新链接使用它们的应用程序。相反,静态链接的对象代码要求在函数更改时重新链接应用程序。

  • 提供售后支持。例如,可修改显示器驱动程序 DLL 以支持当初交付应用程序时不可用的显示器。

  • 支持多语言程序。只要程序遵循函数的调用约定,用不同编程语言编写的程序就可以调用相同的 DLL 函数。程序与 DLL 函数在下列方面必须是兼容的:函数期望其参数被推送到堆栈上的顺序,是函数还是应用程序负责清理堆栈,以及寄存器中是否传递了任何参数。

  • 提供了扩展 MFC 库类的机制。可以从现有 MFC 类派生类,并将它们放到 MFC 扩展 DLL 中供 MFC 应用程序使用。

  • 使国际版本的创建轻松完成。通过将资源放到 DLL 中,创建应用程序的国际版本变得容易得多。可将用于应用程序的每个语言版本的字符串放到单独的 DLL 资源文件中,并使不同的语言版本加载合适的资源。

使用 DLL 的一个潜在缺点是应用程序不是独立的;它取决于是否存在单独的 DLL 模块。

DLL 类型

添加初始化代码和终止代码的位置

规则 DLL

在 DLL 的 CWinApp 对象的 InitInstanceExitInstance 中。

扩展 DLL

在“MFC DLL 向导”生成的 DllMain 函数中。

非 MFC DLL

在您提供的称为 DllMain 的函数中。

在 Win32 中,所有 DLL 都可能包含一个可选入口点函数(通常称为 DllMain),初始化和终止时都要调用此函数。这使您有机会在需要时分配或释放其他资源。Windows 在四种情况下调用入口点函数:进程附加、进程分离、线程附加和线程分离。

C 运行库提供了一个名为 _DllMainCRTStartup 的入口点函数,并调用 DllMain根据 DLL 类型的不同,应在源代码中包含一个名为 DllMain 的函数,或应用 MFC 库中提供的 DllMain

  • 隐式链接

  • 显式链接

隐式链接有时称为静态加载或加载时动态链接。显式链接有时称为动态加载或运行时动态链接。

在隐式链接下,使用 DLL 的可执行文件链接到该 DLL 的创建者所提供的导入库(.lib 文件)。使用 DLL 的可执行文件加载时,操作系统加载此 DLL。客户端可执行文件调用 DLL 的导出函数,就好像这些函数包含在可执行文件内一样。

在显式链接下,使用 DLL 的可执行文件必须进行函数调用以显式加载和卸载该 DLL,并访问该 DLL 的导出函数。客户端可执行文件必须通过函数指针调用导出函数。

可执行文件对两种链接方法可以使用同一个 DLL。另外,由于一个可执行文件可隐式链接到某个 DLL,而另一个可显式附加到此 DLL,故这些机制不是互斥的

有两种从 DLL 导出函数的方法:

  • 在生成 DLL 时,创建一个模块定义 (.def) 文件并使用该 .def 文件。如果希望按序号而不是按名称从 DLL 导出函数,则请使用此方法。

  • 在函数的定义中使用 __declspec(dllexport) 关键字。

用上述任何方法导出函数时,确保使用 __stdcall 调用约定。