TN033:MFC的DLL版本
此说明描述如何使用 MFC 应用程序和扩展 DLL 的 MFCxx.DLL 和 MFCxxD.DLL (其中 x 是 MFC 版本号) 共享动态链接库。 有关规则 DLL 的更多信息,请参见 作为 DLL 的一部分使用的 MFC。
此方法声明包含 DLL 的三个方面。 最后两个用于高级用户:
如何编译 MFC 扩展 DLL
如何生成使用 MFC 的 DLL 版本的 MFC 应用程序
MFC 共享动态链接库实现方式。
如果您对使用 MFC 的 DLL 可用于非 MFC 应用程序生成感兴趣 (称为规则 DLL)。,请参见 技术说明 11。
MFCxx.DLL 概述支持:术语和文件
Regular DLL:使用一些 MFC 类,可以使用规则 DLL 生成单个 DLL。 在 App/DLL 边界的接口为 “C”接口,因此,客户端应用程序不必是 MFC 应用程序。
这是 DLL 的版本支持支持在 MFC 1.0。 它在 技术说明 11 所述,并且 MFC 高级概念采样 DLLScreenCap。
备注
基于 Visual C++ 4.0 版中,术语 USRDLL 已过时并将该的规则 DLL 替换了与静态链接到 MFC。您还可以生成该 MFC 的规则 DLL 动态链接。
MFC 3.0 (如) 支持与任何新功能的规则 DLL 由 OLE 和数据库类。
AFXDLL:这也称为 MFC 库的共享版本。 这是新 DLL 支持添加在 MFC 2.0。 MFC 库在许多的 DLL (下述),并且客户端应用程序或 DLL 动态链接到要求的 DLL。 在 application/DLL 边界的接口是 C++/MFC 类接口。 客户端应用程序必须是 MFC 应用程序。 此支持所有 MFC 3.0 功能 (例外情况:UNICODE 没有为数据库类支持)。
备注
基于 Visual C++ 4.0 版起,这类 DLL 称为 “扩展 DLL”。
此说明将使用 MFCxx.DLL 引用整组 MFC DLL,包括:
调试:MFCxxD.DLL (合并) 和 MFCSxxD.LIB (静态)。
版本:MFCxx.DLL (合并) 和 MFCSxx.LIB (静态)。
调试:MFCxxUD.DLL (合并) 和 MFCSxxD.LIB (静态)。
Unicode 版本:MFCxxU.DLL (合并) 和 MFCSxxU.LIB (静态)。
备注
MFCSxx U [] [] D .LIB 库到 MFC 的共享 DLL 一起使用。这些库包含必须与应用程序或 DLL 静态链接的代码。
对应的导入库的应用程序链接:
调试:MFCxxD.LIB
版本:MFCxx.LIB
调试:MFCxxUD.LIB
Unicode 版本:MFCxxU.LIB
“MFC 扩展 DLL”是 DLL 已针对 MFCxx.DLL (和/或其他 MFC 共享 DLL)。 此处 MFC 组件体系结构启动。 如果从 MFC 类派生有用的类或生成另一个与 MFC 的工具套件,则在 DLL 可以将它。 DLL 使用 MFCxx.DLL,与最终客户端应用程序。 这样可重用的叶类,可重用的基类,并且,可重用视图/文档类。
优缺点
您为什么使用共享 MFC 版本?
使用共享库可能导致较小的应用程序 (使用最到 MFC 库比 10K 小于) 的最小的应用程序。
共享 MFC 版本支持 MFC 扩展 DLL 和规则 DLL。
生成使用共享 MFC 库的应用程序的生成静态链接的 MFC 应用程序快,因为链接 MFC 不必为。 这是不断变化的在链接器必须压缩调试信息 (的 调试 编译通过链接到已经包含调试信息的 DLL,具有很少的调试信息在应用程序中压缩。
为什么不使用共享 MFC 版本:
- 提供使用共享库的应用程序要求您提供具有程序的 MFCxx.DLL (和其他库)。 MFCxx.DLL 自由是可象许多 DLL,但是,您在安装程序仍然必须安装 DLL。 此外,还必须提供 MSVCRTxx.DLL,包含 C 运行库您的程序和 MFC 使用 DLL。
如何编写 MFC 扩展 DLL
MFC 扩展 DLL 是包含类和函数的 DLL 编写修饰 MFC 类的功能。 MFC 扩展 DLL 来使用共享 MFC DLL 应用程序使用它,但有其他注意事项:
生成过程类似于生成使用具有一些共享 MFC 库其他编译器和链接器选项的应用程序。
MFC 扩展 DLL 没有 CWinApp派生类。
MFC 扩展 DLL 必须提供特定 DllMain。 AppWizard 提供可以修改的一个 DllMain 功能。
,如果扩展 DLL 需要对应用程序,导出 CRuntimeClassES 或资源 MFC 扩展 DLL 通常提供初始化实例创建 CDynLinkLibrary 。 CDynLinkLibrary 一个派生类,则必须由扩展 DLL,维护每个应用程序数据可以使用。
这些注意事项如下更详细地介绍。 ,因为它说明,还应引用 MFC 高级概念的示例 DLLHUSK :
生成使用共享库的应用程序。 (DLLHUSK.EXE 与 MFC 库以及其他 DLL 动态链接。) 的 MFC 应用程序
编译 MFC 扩展 DLL。 (请注意用于生成扩展 DLL) 的特殊标志例如 _AFXEXT
MFC 扩展 DLL 的两个示例。 一个用于显示 MFC 扩展 DLL 的基本结构与限制性 TESTDLL1 导出 () 和其他的导出整个类接口 (TESTDLL2) 的显示。
客户端应用程序和所有扩展 DLL 必须使用 MFCxx.DLL 的版本相同。 您应遵循 MFC DLL 约定和的调试和您的扩展 DLL 的零售 (/release) 版本。 这允许客户端程序生成两个具有适当调试及其应用程序的零售版本并将它们链接到调试或所有 DLL 的零售版本。
备注
由于 C++ 名称重整和导出问题,从扩展 DLL 中的导出列表内容可能有所不同在不同平台的同一 DLL 的调试版本和零售版本和 DLL 之间。该零售 MFCxx.DLL 有大约 2000 个导出入口点;调试 MFCxxD.DLL 有大约 3000 个导出入口点。
有关内存管理的快速说明
此技术说明末尾附近的标题为 “内存管理,”部分,描述 MFCxx.DLL 的实现使用共享 MFC 版本的。 您需要知道实现扩展 DLL 的信息介绍此处。
MFCxx.DLL 和所有扩展 DLL 加载到客户端应用程序的地址空间将使用相同的内存分配器、资源加载和其他 MFC “全局”状态,就好像它们在同一应用程序。 这是重要的,因为与静态链接到 MFC 的非 MFC DLL 库和规则 DLL 正好相反并具有分配在其自己的内存池之外的每个 DLL。
如果扩展 DLL 分配内存,则该内存能够随意组合与其他应用程序分配的对象。 此外,,如果使用共享 MFC 库的应用程序崩溃,操作系统的保护维护共享 DLL 的其他 MFC 应用程序的完整性。
同样其他 “全局” MFC 状态,如加载资源的当前可执行文件从共享,也在客户端应用程序和所有 MFC 扩展 DLL 以及 MFCxx.DLL 之间。
生成扩展 DLL
可以使用 AppWizard 创建 MFC 扩展 DLL 项目,并且,它会自动生成相应的编译器和链接器设置。 它也是生成可修改的 DllMain 功能。
如果将现有项目转换为 MFC 扩展 DLL,使用 MFC 的共享版本,请从生成的应用程序标准规则启动,则执行以下操作:
添加 /D_AFXEXT 给编译器标志。 在项目属性对话框,请选择 C/C++ 节点。 然后选择预处理器类。 添加 _AFXEXT 到宏定义字段,分隔每一个用分号的项目。
移除 /Gy 编译器开关。 在项目属性对话框,请选择 C/C++ 节点。 然后选择代码生成类。 确保 “启用函数级链接”选项未启用。 ,因为链接器不会移除未引用的功能,这将非常容易导出类。 如果原始项目用于生成与静态链接到 MFC 的规则 DLL,请更改 /MT[d] 编译器选项。 /MD[d]。
构造导出库具有 /DLL 选项链接。 这将设置,将创建一个新的目标,指定 Win32 动态链接库作为对象类型。
更改您的头文件
扩展 DLL 的目标通常是由可以使用该函数的一个或多个应用程序导出某些通用功能。 这是因为为导出为您的客户端应用程序使用的类和全局函数。
为此必须确保每个成员函数标记为导入或导出根据需要。 这需要特殊声明: __declspec(dllexport) 和 __declspec(dllimport)。 当客户端应用程序中使用您的类,您希望它们声明为 __declspec(dllimport)。 如果扩展 DLL 进行编译时,应将它们声明为 __declspec(dllexport)。 此外,必须实际导出功能,因此,客户端程序绑定到它们在加载时。
若要导出您的整个类,请使用 AFX_EXT_CLASS 在类定义。 此宏由结构定义的转换 __declspec(dllexport) ,当 _AFXDLL 和 _AFXEXT 定义时,但是,定义为 __declspec(dllimport) ,当 _AFXEXT 未定义时。 编译,则您的扩展 DLL 时,_AFXEXT 如上所述,只能定义。 例如:
class AFX_EXT_CLASS CExampleExport : public CObject
{ ... class definition ... };
不导出整个类
有时您可能需要导出类中的各个必需的成员。 例如,如果导出 CDialog 派生类,可能只需要导出构造函数和 DoModal 调用。 可以使用 DLL 的导出 .DEF 文件中的这些成员,但是,您可以按照与还使用 AFX_EXT_CLASS 在需要导出的各个成员。
例如:
class CExampleDialog : public CDialog
{
public:
AFX_EXT_CLASS CExampleDialog();
AFX_EXT_CLASS int DoModal();
// rest of class definition
.
.
.
};
如果这样做,则可能会遇到其他问题,因为您不再导出类的所有成员。 该问题方式与 MFC 宏的工作。 几个 MFC 的 Helper 宏实际声明或定义数据成员。 因此,这些数据成员还需要从 DLL 导出。
例如,当生成扩展 DLL 时,DECLARE_DYNAMIC 宏的定义如下:
#define DECLARE_DYNAMIC(class_name) \
protected: \
static CRuntimeClass* PASCAL _GetBaseClass(); \
public: \
static AFX_DATA CRuntimeClass class##class_name; \
virtual CRuntimeClass* GetRuntimeClass() const; \
启动 “静态 AFX_DATA”的行声明类的内部静态对象。 从客户端 .EXE 若要正确导出该类和访问运行时信息,您需要导出此静态对象。 由于静态对象是用 AFX_DATA 修饰符声明的,因此只需在生成 DLL 时将 AFX_DATA 定义为 __declspec(dllexport),在生成客户端可执行文件时将其定义为 __declspec(dllimport)。
如上所述, AFX_EXT_CLASS 此类已定义。 需要重新定义 AFX_DATA 与 AFX_EXT_CLASS 同名的参考类定义。
例如:
#undef AFX_DATA
#define AFX_DATA AFX_EXT_CLASS
class CExampleView : public CView
{
DECLARE_DYNAMIC()
// ... class definition ...
};
#undef AFX_DATA
#define AFX_DATA
MFC 始终使用在宏中定义的数据项上的 AFX_DATA 符号,因此,此方法对于所有此类方案能够工作。 例如,对于 DECLARE_MESSAGE_MAP仍有效。
备注
如果导出整个类而非选定的类成员,静态数据成员将自动导出。
您可以使用同样的技术将自动导出使用 DECLARE_SERIAL 和 IMPLEMENT_SERIAL 宏的类的 CArchive 提取运算符。 通过带类声明导出存档运算符 (位于。H 文件) 使用下面的代码:
#undef AFX_API
#define AFX_API AFX_EXT_CLASS
<your class declarations here>
#undef AFX_API
#define AFX_API
_AFXEXT 的限制
,只要不具有扩展 DLL,就可以对扩展 DLL 使用 _AFXEXT 预处理器符号。 如果所具有的扩展 DLL 调用或派生自您自己的扩展 DLL 中的类,而您自己的扩展 DLL 又派生自 MFC 类,则必须使用您自己的预处理器符号以避免多义性。
问题是,在 Win32 中,必须将任何要从 DLL 导出的数据显式声明为 __declspec(dllexport),将任何要从 DLL 导入的数据显式声明为 __declspec(dllimport)。 当您定义 _AFXEXT 时,MFC 头文件确定 AFX_EXT_CLASS 的定义是正确的。
当具有多层时,一个符号 (如) AFX_EXT_CLASS 不足时,,因为扩展 DLL 也可导出新类以及导入其他类从另一个扩展 DLL。 为了处理此问题,请使用这样的一个特殊的预处理器符号来生成 DLL 使用 DLL。 例如,假设有两个扩展 DLL、 A.DLL 和 B.DLL。 根据每个导出在 A.H 和 B.H 中的一些类,它们。 B.DLL 使用从 A.DLL 的类。 头文件看上去像下面这样:
/* A.H */
#ifdef A_IMPL
#define CLASS_DECL_A __declspec(dllexport)
#else
#define CLASS_DECL_A __declspec(dllimport)
#endif
class CLASS_DECL_A CExampleA : public CObject
{ ... class definition ... };
/* B.H */
#ifdef B_IMPL
#define CLASS_DECL_B __declspec(dllexport)
#else
#define CLASS_DECL_B __declspec(dllimport)
#endif
class CLASS_DECL_B CExampleB : public CExampleA
{ ... class definition .. };
当 A.DLL 编译时,会将 /D A_IMPL 生成,所以,当 B.DLL 编译时,会将 /D B_IMPL生成。 通过对每个 DLL 不同的符号, CExampleB 导出,并导入 CExampleA,在生成 B.DLL 时。 CExampleA 导出,在生成 A.DLL 时并导入,当使用由 B.DLL (或某些其他客户端)。
,在使用内置 AFX_EXT_CLASS 和 _AFXEXT 预处理器符号时,这种分层无法完成。 ,在生成其 OLE、数据库和网络扩展时,中描述的技术在某些解决此问题的机制不同。 MFC 使用。
不导出整个类
同样,那么,当您不导出整个类,则必须特别留意。 您必须确保 MFC 宏创建的必要数据项正确导出。 这可以通过重新定义对特定类的宏的 AFX_DATA 完成。 每当不导出整个类的时候,就应进行此操作。
例如:
// A.H
#ifdef A_IMPL
#define CLASS_DECL_A _declspec(dllexport)
#else
#define CLASS_DECL_A _declspec(dllimport)
#endif
#undef AFX_DATA
#define AFX_DATA CLASS_DECL_A
class CExampleA : public CObject
{
DECLARE_DYNAMIC()
CLASS_DECL_A int SomeFunction();
//class definition
.
.
.
};
#undef AFX_DATA
#define AFX_DATA
DllMain
下面是您可以在您的扩展 DLL 的主源文件应放置的确切代码。 ,该标准包含后,它应出现。 请注意,当使用 AppWizard 创建扩展 DLL 的起始文件,它提供自己的 DllMain 。
#include "afxdllx.h"
static AFX_EXTENSION_MODULE extensionDLL;
extern "C" int APIENTRY
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
// Extension DLL one-time initialization
if (!AfxInitExtensionModule(
extensionDLL, hInstance))
return 0;
// TODO: perform other initialization tasks here
}
else if (dwReason == DLL_PROCESS_DETACH)
{
// Extension DLL per-process termination
AfxTermExtensionModule(extensionDLL);
// TODO: perform other cleanup tasks here
}
return 1; // ok
}
为 AfxInitExtensionModule 的调用获取模块运行时类 (CRuntimeClass 结构) 以及对象工厂 (COleObjectFactory 对象) 用于后期绑定时,使用的 CDynLinkLibrary 到在创建对象时。 (可选) 调用 AfxTermExtensionModule 提供 MFC 对清理扩展 DLL,如果每个处理从扩展 DLL 分离 (发生时,在进程退出时,或者,在卸载 DLL 由于 FreeLibrary 时调用)。 因为大多数扩展 DLL。动态加载 (通常,它们通过它们的导入库链接),对 AfxTermExtensionModule 的调用通常不是必需的。
如果您的应用程序动态加载和释放扩展 DLL,请务必调用 AfxTermExtensionModule 如上所示。 并确保使用 AfxLoadLibrary 和 AfxFreeLibrary (而不是 Win32 函数 LoadLibrary 和 FreeLibrary),如果应用程序使用多个线程,或者动态加载扩展 DLL。 使用 AfxLoadLibrary 和 AfxFreeLibrary 确保执行的启动和关闭代码扩展 DLL 时加载,并卸载不会损坏全局 MFC 状态。
头文件 AFXDLLX.H 包含在扩展的结构的特殊定义 DLL,如将定义用于 AFX_EXTENSION_MODULE 和 CDynLinkLibrary。
必须声明全局 extensionDLL 所示。 此时,与 16 位版本的 MFC 不同,可以分配内存和调用 MFC 函数,,因为 MFCxx.DLL 完全初始化,当您的 DllMain 调用时。
共享资源和类
只简单的 MFC 扩展 DLL 需要导出到客户端应用程序和 nothing 几个低带宽功能更多。 多个用户界面的 DLL 可对客户端应用程序导出资源和 C++ 类。
导出资源的操作是通过资源列表完成的。 在每个应用程序是单向链接表 CDynLinkLibrary 对象。 在查找资源时,大部分加载资源的标准 MFC 实现首先查看当前资源模块 (AfxGetResourceHandle),如果未找到的结构 CDynLinkLibrary 的对象列表尝试加载请求的资源。
C++ 对象的动态创建给定的 c. C++ 类名是类似的。 MFC 对象反序列化机制需要注册所有的 CRuntimeClass 对象,以便可以通过动态创建基于存储的内容所需类型的 C++ 对象重新生成。
如果您的扩展 DLL 需要客户端应用程序使用一 DECLARE_SERIAL的类,则需要导出您的类是可见的。客户端应用程序。 这是通过遍历还能完成 CDynLinkLibrary 列表。
在 MFC 高级概念的示例 DLLHUSK,此列表的形式如下:
head -> DLLHUSK.EXE - or - DLLHUSK.EXE
| |
TESTDLL2.DLL TESTDLL2.DLL
| |
TESTDLL1.DLL TESTDLL1.DLL
| |
| |
MFC90D.DLL MFC90.DLL
MFCxx.DLL 通常最后在资源中,并课程表。 MFCxx.DLL 包含所有标准 MFC 资源,包括所有标准命令 ID 的提示字符串。 放在列表的末尾使得 DLL 和客户端应用程序没有标准 MFC 资源的一个自己的副本,,但是依赖 MFCxx.DLL 中的共享资源。
将所有 DLL 的资源和类名到客户端应用程序的命名空间中的一个缺点您必须注意的 ID 或名称您选择。 当然您可以通过不会将您的资源或一 CDynLinkLibrary 对象禁用此功能到客户端应用程序。 DLLHUSK 示例通过使用多个头文件来管理共享资源命名空间。 为使用共享资源文件的更多 技术说明 35 提示参见。
初始化 DLL
如上所述,通常需要创建 CDynLinkLibrary 对象以对客户端应用程序导出您的资源和类。 您将需要提供个导出入口点初始化 DLL。 最低限度上,这是没有接受参数并返回的无效的实例,但是,它可以是您喜欢的任何名称。
若要使用 DLL 的每个客户端应用程序必须调用此初始化实例,因此,如果使用此方法。 您还可以分布在您的 DllMain 的此 CDynLinkLibrary 对象在调用 AfxInitExtensionModule之后。
初始化实例在当前应用程序内存段必须创建 CDynLinkLibrary 对象,连接到您的扩展 DLL 信息。 这可以通过以下操作:
extern "C" extern void WINAPI InitXxxDLL()
{
new CDynLinkLibrary(extensionDLL);
}
实例名称,在此示例中的 InitXxxDLL ,可以为所需的任何名称。 它不必是 extern "C",,但这使中的导出列表内容更易于维护。
备注
如果您正在使用自己的从规则 DLL 的扩展 DLL,必须导出此初始化函数。必须在使用任何扩展 DLL 类或资源之前的规则 DLL 调用此函数。
导出项
这种简单的方法导出您的类将使用 __declspec(dllimport) 和 __declspec(dllexport) 在要导出的每个类和全局函数。 这比命名每个入口点使它们很容易,但是,效率低 (下述),因为您最少控制哪些功能导出,因此您无法按序号导出函数。 TESTDLL1 和 TESTDLL2 使用此方法将的项。
一个最有效方法 (和 MFCxx.DLL 的方法) 将手动导出每个条目通过命名每项在 .DEF 文件。 因为我们是从我们的 DLL (即不是所有内容) 导出选择性的导出,必须确定哪些特定接口我们希望导出。 ,因为您必须指定已损坏的名称链接器的项的形式在 .DEF 文件,这是困难。 ,除非您非常需要具有的,一个符号字符串不要导出任何 C++ 类。
如果您尝试导出 C++ 类使用 .DEF 文件之前,您可能希望开发工具生成此自动列表。 该操作可以使用两阶段链接。 一次链接到的 DLL 不导出,并允许链接器生成 .MAP 文件。 .MAP 文件可用于生成应导出函数的列表,因此,具有某个重新排列,它可用于生成您的 .DEF 文件的导出项。 为 MFCxx.DLL 中的导出列表内容,并 OLE 和数据库扩展 DLL,数千数量上,生成了此类处理 (尽管它不是完全自动且不需要经常调整数组的手)。
CWinApp。CDynLinkLibrary
MFC 扩展 DLL 没有 CWinApp- 自己的派生对象;而必须与 CWinApp- 客户端应用程序的派生对象一起使用。 这意味着客户端应用程序拥有主消息泵,空闲循环等。
如果 MFC 扩展 DLL 需要维护额外的数据为每个应用程序,可以从 CDynLinkLibrary 派生新类,并创建它在 InitXxxDLL 实例中描述的顶部。 运行时,DLL 会检查当前应用程序的 CDynLinkLibrary 对象列表,以查找用于特定扩展 DLL 的对象。
使用在 DLL 实现的资源
如上所述,默认资源加载将遍历 CDynLinkLibrary 对象列表以查找具有所请求资源的第一个 EXE 或 DLL 的。 所有 MFC API 以及任何内部代码使用 AfxFindResourceHandle 遍历该资源列表查找所有资源,,这样不论它可以位于。
如果希望将给定只加载资源,请使用 API AfxGetResourceHandle 和 AfxSetResourceHandle 保存旧句柄和设置新句柄。 返回到客户端应用程序之前,请务必还原旧资源句柄。 该示例 TESTDLL2 为显式加载菜单使用此方法。
浏览列表的做法有一些缺点,即速度稍慢且需要管理资源 ID 范围。 它的优点在于,链接到几个扩展 DLL 的客户端应用程序可以使用 DLL 提供的任何资源,而无需指定 DLL 实例句柄。 AfxFindResourceHandle 是用于浏览资源列表以查找给定匹配的 API。 它采用资源的名称和类型,并从最先找到匹配项的位置返回资源句柄(或 NULL)。
编写使用 DLL 版本的应用程序
应用程序要求
使用共享 MFC 版本的应用程序必须服从几个简单规则:
它必须具有 CWinApp 对象,并按照消息的标准规则发送。
必须将其编译与设置所需的编译器标志 (如下所示)。
它必须与 MFCxx 导入库链接。 通过设置所需的编译器标志, MFC 头确定在库应用程序应链接到的链接时。
若要运行可执行文件, MFCxx.DLL 必须在路径中或 windows 系统目录。
用开发环境生成
如果您使用的大多数内部生成文件标准默认值,您可以轻松地将项目生成 DLL 版本。
以下步骤假定您具有 NAFXCWD.LIB 链接的正常工作的 MFC 应用程序 (" 调试 "),并 NAFXCW.LIB (对于零售版) 和要转换它使用 MFC 库的共享版本。 您运行 Visual C++ 环境并具有一个内部项目文件。
- 在 项目 菜单上,单击 属性。 在 项目默认值下的 常规 页上,将 Microsoft 基础类 (mfc) 到 Use MFC in a Shared DLL (MFCxx ()、 (.dll)。
使用 NMAKE 的生成
如果使用 Visual C++ 的外部生成文件功能或直接使用 NMAKE,则必须编辑您的生成文件支持编译器和链接器选项
所需的编译器标志:
- /D_AFXDLL /MD
/D_AFXDLL
标准 MFC 头需要定义了该符号后:
- /MD
应用程序必须使用 C 运行库的 DLL 版本
其他编译器标志遵循 MFC 默认 (例如, _DEBUG 用于调试)。
编辑链接器列表库。 更改 NAFXCWD.LIB 到 MFCxxD.LIB 并更改 NAFXCW.LIB 到 MFCxx.LIB。 在 MSVCRT.LIB 替换 LIBC.LIB。 与其他 MFC 库是重要的 MFCxxD.LIB 是放置的 before 任何 C 运行库。
可以选择添加 /D_AFXDLL 到您的发布和调试资源编译器选项 (实际生成与 /R的资源) 的脚本。 这是通过共享存在于 MFC DLL 的资源将最终可执行更小。
完全重新生成,这些更改后,需要。
生成示例
大多数 MFC 示例程序可以生成从 Visual C++ 或从命令行的共享 NMAKE 兼容生成文件。
若要将这些示例中的任何一个使用 MFCxx.DLL,可以加载 .MAK 文件添加到 Visual C++ 和上述设置项目选项。 如果使用 NMAKE 生成,则 NMAKE 命令行上指定 “AFXDLL=1”使用共享 MFC 库,并且,这将编译该示例。
MFC 高级概念的示例 DLLHUSK 使用 MFC 的 DLL 版本生成。 此示例不演示如何建立与 MFCxx.DLL 链接的应用程序,但是,它还阐释 MFC DLL 打包选项的其他功能 (如 MFC 扩展此技术声明之后描述的 DLL。
打包的说明
DLL (MFCxx [] U .DLL) 的零售版本可随便重新发布。 在应用程序的开发过程, DLL 的调试版本是不可随便重新发布,只应使用。
调试 DLL 提供调试信息。 使用 Visual C++ 调试器,可以跟踪应用程序和 DLL 的执行。 版本 DLL (MFCxx [] U .DLL) 不包含调试信息。
如果自定义或重新生成 DLL,则应将它们称为不同于 “MFCxx”以外的 MFC SRC 文件 MFCDLL.MAK 描述生成选项并包含对的 DLL 重命名逻辑。 ,因为这些 DLL 由许多 MFC 应用程序,可能会共享重命名文件是必需的。 使用共享 MFC DLL,使 MFC DLL 的自定义版本替换这些已安装在系统可以使另一个 MFC 应用程序。
MFC DLL 不建议使用重新生成。
MFCxx.DLL 实现方式。
以下各节介绍 MFC DLL (MFCxx.DLL 和 MFCxxD.DLL) 的实现方式。 了解此处详细信息也不是,如果要执行的是用来与应用程序的 MFC DLL。 此处详细信息若要理解编写 MFC 扩展 DLL,但是,了解此实现如何无关紧要可帮助您编写 DLL。
实现概述
MFC DLL 上述实际上是 MFC 扩展 DLL 的一种特例。 它有大量的类的一个非常大数量的导出。 具有我们在 MFC DLL 进行比普通扩展 DLL 更加特定的几个附加操作。
Win32 完成大部分工作
MFC 16 位版本的需要许多特殊技术包括的每个应用层有关堆栈段的数据,某个 80x86 程序集代码创建的特定段落,每个处理异常上下文和其他技术。 Win32 直接支持每个处理 DLL 中的数据,正是大多数时候您希望。 大多数情况下是 MFCxx.DLL 打包在 DLL 中 NAFXCW.LIB。 如果查看 MFC 源代码,您将找到极少数来 _AFXDLL,,因为非常需要进行少量的特殊情况。 在存在的特殊情况专门针对相关在 Windows 3.1 的 Win32 (也称为 Win32s)。 Win32s 并非支持每直接处理 DLL 数据,因此 MFC DLL 必须使用线程本地存储区 Win32 API (TLS) 获取进程内本地数据。
对库源,其他文件的影响
_AFXDLL 版本的影响。常规 MFC 类库源文件和头太小。 特定版本文件 (AFXV_DLL.H) 和一个附加头文件 (AFXDLL_.H) 包含由主 AFXWIN.H 标头。 AFXDLL_.H 头包含 CDynLinkLibrary 类和 _AFXDLL 应用程序和 MFC 扩展 DLL 其他实现详细信息。 AFXDLLX.H 标头用于生成提供 MFC 扩展 DLL (请参见上面的有关详细信息)。
到 MFC 库的常规源 MFC 的 SRC 有一些附加条件代码在 _AFXDLL #ifdef 下。 一个不同的源文件 (DLLINIT.CPP) 包含额外的 DLL 初始化代码和其他胶浆共享 MFC 版本的。
为了创建共享 MFC 版本,提供其他文件。 (请参见下文了解有关如何的详细信息生成 DLL。)
两 .DEF 文件为导出 MFC DLL 使用入口点对于调试 (MFCxxD.DEF) 并释放 (MFCxx.DEF) DLL 的版本。
.RC 文件 (MFCDLL.RC) 包含所有标准 MFC 资源和一个 VERSIONINFO 资源 DLL。
使用类向导,提供 .CLW 文件 (MFCDLL.CLW) 允许浏览 MFC 类。 注意:此功能到 MFC 的 DLL 版本不是特定的。
内存管理
使用 MFCxx.DLL 的应用程序使用 MSVCRTxx.DLL 提供的一个常见内存分配器,共享 C 运行时 DLL。 应用程序,所有扩展 DLL 和好作为 MFC DLL 使用此共享内存分配器。 通过使用内存分配的共享 DLL, MFC DLL 可以分配由应用程序后释放反之亦然的内存。 由于应用程序和 DLL 必须使用相同的分配程序,则不应该重写 C++ 全局 operator new 或 operator delete。 相同的规则适用于 C 运行时内存分配例程的其余部分 (例如 malloc、 realloc, free和其他)。
序号和类 declspec(dllexport) 和 DLL 的名称
我们不使用 C++ 编译器的 class**__declspec(dllexport)** 功能。 相反,导出列出了附带类库源 (MFCxx.DEF 和 MFCxxD.DEF)。 只选择这些设置入口点 (函数和数据) 导出。 其他符号,如 MFC 私有实现函数或类,在居民或非居民名称表中未导出任何导出序号完成没有一个字符串名称。
使用 class**__declspec(dllexport)** 可能是生成较小的 DLL 的一个可行的选择,但是,在与 MFC 的 DLL,用导出机制的默认具有性能和容量限制。
此的整个方法是我们打包在只包含约 800 KB,而不危及执行或加载速度的版本 MFCxx.DLL 的许多功能。 MFCxx.DLL 是较大 100K 此方法未使用。 这还可以添加其他入口点入口点在 .DEF 文件的最后使得简单版本,而不危及速度和效率范围导出序号。 主版本版本在 MFC 类库中更改库名。 即 MFC30.DLL 是包含 MFC 类库的 3.0 版可再发行组件 DLL。 升级此 DLL 位于假设 MFC 3.1, DLL 将命名 MFC31.DLL。 同样,因此,如果修改 MFC 源代码生成 MFC DLL 的自定义版本,请使用不同的名称 (和最好是非 “MFC”中的名称)。