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 分发共享 DLL、Mfc<版本.dll 和 Msvvcrt<版本>>.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 。 若要导出函数,请在模块定义文件的部分中输入它们 EXPORTS (。DEF)或包含在 __declspec(dllexport) 函数定义中。 用于 __declspec(dllimport) 将这些函数导入客户端可执行文件。

必须在动态链接到 MFC 的常规 MFC DLL 中的所有导出函数的开头添加AFX_MANAGE_STATE宏。 此宏将当前模块状态设置为 DLL 的模块状态。 若要使用此宏,请将以下代码行添加到从 DLL 导出的函数的开头:

AFX_MANAGE_STATE(AfxGetStaticModuleState( ))

WinMain -> DllMain

MFC 库定义标准 Win32 DllMain 入口点,该入口点将 CWinApp 派生对象初始化为典型的 MFC 应用程序中。 将所有特定于 DLL 的初始化放置在 InitInstance 方法中,就像在典型的 MFC 应用程序中一样。

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

请参阅 DLLScreenCap 示例以使用此函数。

DllMain MFC 提供的函数将调用从 DLL 卸载之前派生CWinApp的类的 CWinApp::ExitInstance 方法。

链接 DLL

使用静态链接到 MFC 的常规 MFC DLL,必须将 DLL 与 Nafxcwd.lib 或 Nafxcw.lib 以及名为 Libcmt.lib 的 C 运行时版本链接在一起。 这些库是预构建的,可以通过在运行 Visual C++ 安装程序时指定它们进行安装。

示例代码

有关完整示例,请参阅 MFC 高级概念示例程序 DLLScreenCap。 此示例中要注意的几个有趣的事项如下:

  • DLL 的编译器标志和应用程序的标志不同。

  • 链接行和 。DLL 的 DEF 文件与应用程序的 DEF 文件不同。

  • 使用 DLL 的应用程序不必位于C++中。

  • 应用程序和 DLL 之间的接口是 C 或 C++ 可用的 API,并使用 DLLScreenCap.def 导出。

以下示例演示在静态链接到 MFC 的常规 MFC DLL 中定义的 API。 在此示例中,声明包含在C++用户的块中 extern "C" { } 。 这有几个优点。 首先,它使 DLL API 可由非C++客户端应用程序使用。 其次,它减少了 DLL 开销,因为C++名称管理不会应用于导出的名称。 最后,它使显式添加到 a.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 程序使用。

另请参阅

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