此说明介绍了常规的 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 程序使用。