TN011:将 MFC 作为 DLL 的一部分使用
此说明描述了规则 MFC DLL,其允许使用 MFC 库作为 Windows 动态链接库 (DLL) 的一部分。 此说明假定您已熟悉 Windows DLL 及其生成方式。 有关 MFC 扩展 DLL(可以使用其来创建 MFC 库的扩展)的信息,请参阅 MFC 的 DLL 版本。
DLL 接口
规则 MFC DLL 假定应用程序和 DLL 之间的接口是在类似 C 的函数或显式导出的类中指定的。 MFC 类接口无法导出。
如果 DLL 和应用程序要使用 MFC,则请选择是使用共享版本的 MFC 库还是静态链接到这些库的副本。 应用程序和 DLL 可能都会使用 MFC 库的标准版本之一。
规则 MFC DLL 有几个优点:
使用 DLL 的应用程序不必使用 MFC,并且不必是 Visual C++ 应用程序。
对于静态链接到 MFC 的规则 MFC DLL,DLL 的大小只取决于使用和链接的 MFC 以及 C 运行时例程。
对于动态链接到 MFC 的规则 MFC DLL,使用共享版本的 MFC 节省的内存可能很明显。 但是,必须将共享 DLL、Mfc<version>.dll 和 Msvvcrt<version>.dll 与 DLL 一起分发。
DLL 设计独立于类的实现方式之外。 您的 DLL 设计仅导出到需要的 API。 因此,如果实现发生更改,则规则 MFC DLL 仍然有效。
对于静态链接到 MFC 的规则 MFC DLL,如果 DLL 和应用程序都使用 MFC,则需要不同于 DLL 的 MFC 版本的应用程序无任何问题,反之亦然。 由于 MFC 库将静态链接到每个 DLL 或 EXE,因此您具有哪一版本没有任何问题。
API 限制
一些 MFC 功能不适用于 DLL 版本,原因是技术方面的限制或者这些服务一般都是应用程序提供的。 利用当前版本的 MFC,唯一不适用的功能是 CWinApp::SetDialogBkColor
。
生成您的 DLL
编译静态链接到 MFC 的规则 MFC DLL 时,必须定义符号 _USRDLL
和 _WINDLL
。 你的 DLL 代码还必须使用下列编辑器开关进行编译:
/D_WINDLL 表示针对 DLL 的编译
/D_USRDLL 指定正在生成规则 MFC DLL
编译动态链接到 MFC 的规则 MFC DLL 时,还必须定义这些符号并使用这些编译器开关。 此外,必须定义 _AFXDLL
符号,并且必须使用以下内容编译 DLL 代码:
- /D_AFXDLL 指定正在生成一个动态链接到 MFC 的规则 MFC DLL
必须显式导出应用程序和 DLL 之间的接口 (API)。 建议您将接口定义为低带宽,并且如果可以只使用 C 接口。 直接 C 接口更易于维护更复杂的 C++ 类。
将 API 放在 C 文件和 C++ 文件均可包含的单独标头中。 有关示例,请参阅 MFC 高级概念示例 DLLScreenCap 中的 ScreenCap.h 标头。 若要导出函数,请在模块定义文件 (.DEF) 的 EXPORTS
部分中输入它们,或将 __declspec(dllexport)
包含在您的函数定义中。 使用 __declspec(dllimport)
将这些函数导入客户端可执行文件中。
必须在动态链接到 MFC 的规则 MFC DLL 中所有导出函数的开头添加 AFX_MANAGE_STATE 宏。 此宏会将当前模块状态设置为 DLL 的模块状态。 若要使用该宏,请将以下代码行添加到从 DLL 导出的函数的开始处:
AFX_MANAGE_STATE(AfxGetStaticModuleState( ))
WinMain -> DllMain
MFC 库按照在典型 MFC 应用程序中一样定义初始化 CWinApp 派生对象的标准 Win32 DllMain
入口点。 按照在典型 MFC 应用程序中一样将所有 DLL 特定初始化放入 InitInstance 方法。
请注意,CWinApp::Run 机制不适用于 DLL,因为应用程序拥有主消息泵。 如果 DLL 显示无模式对话框或有自己的主框架窗口,则应用程序的主消息泵必须调用 DLL 导出的例程,该例程调用 CWinApp::PreTranslateMessage。
有关此函数的使用,请参阅 DLLScreenCap 示例。
在卸载 DLL 之前,MFC 提供的 DllMain
函数将调用派生自 CWinApp
的类的 CWinApp::ExitInstance 方法。
链接您的 DLL
对于静态链接到 MFC 的规则 MFC DLL,必须将 DLL 与 Nafxcwd.lib 或 Nafxcw.lib 以及与名为 Libcmt.lib 的 C 运行时的版本链接在一起。 这些库之前是预先生成的,并且可能是通过在运行 Visual C++ 安装程序时指定它们安装的。
代码示例
有关完整示例,请参阅 MFC 高级概念示例程序 DLLScreenCap。 以下是此示例中要注意的一些有趣事项:
DLL 的编译器标志与应用程序的不同。
DLL 的链接线和 .DEF 文件与应用程序的不同。
使用 DLL 的应用程序不必使用 C++。
应用程序和 DLL 之间的接口是 C 或 C++ 可使用以及使用 DLLScreenCap.def 导出的 API。
以下示例阐释了在静态链接到 MFC 的规则 MFC DLL 中定义的 API。 在此示例中,声明将封闭在 C++ 用户的 extern "C" { }
块中。 这有许多好处。 首先,它使您的 DLL API 可由非 C++ 客户端应用程序使用。 其次,它减少了 DLL 开销,原因是 C++ 名称重整不会应用于导出的名称。 最后,它使显式添加到 .DEF 文件(以便按顺序导出)更方便,不必担心名称重整。
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
struct TracerData
{
BOOL bEnabled;
UINT flags;
};
BOOL PromptTraceFlags(TracerData FAR* lpData);
#ifdef __cplusplus
}
#endif
API 使用的结构不是从 MFC 类派生的,是在 API 标头中定义的。 这将降低 DLL 和应用程序之间接口的复杂性并使 DLL 可由 C 程序使用。