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 程序使用。

另请参阅

按编号列出的技术说明
按类别列出的技术说明