“不再询问”消息框






4.59/5 (21投票s)
为您的应用程序添加“请勿再次询问”消息框的两种简单方法。
引言
"请勿再次询问" 消息框现在出现在许多应用程序中。为每个消息框创建单独的对话框结果证明是一场噩梦,我自己甚至没有尝试过!本文提出了一种实现这些对话框的非常直接的方法:使用 Windows 钩子。其思想是捕获消息框的创建(下面将介绍如何做到这一点),然后在调整消息框大小后添加必要的控件(分隔线和复选框)。正如 Shog9 所建议的,我将以两种不同的方式来实现。
第一种方法:钩子
钩取消息框
Windows 钩子是 WIN32 提供的一项很棒的功能:它允许我们拦截许多系统调用,尤其是在窗口管理方面。特别是,我们可以捕获我们应用程序打开的每个对话框的 WM_INITDIALOG
消息:我们自己的对话框类,但更重要的是通用控件对话框或其他任何东西。当我们捕获新对话框的 WM_INITDIALOG
时,我们可以决定是否对其进行子类化。当我们对其进行子类化时,当我们自己的一个函数在每次对话框收到消息(例如 WM_INITDIALOG
或 WM_COMMAND
)时都会被调用。
检测新消息框并非易事:一旦创建,它就无法与其他对话框区分开来。这里使用的方法是简单地检查对话框的所有子窗口。对话框成为消息框的标准是:1 个静态图标、1 个静态文本、1 到 3 个按钮,以及没有其他类型的控件。我们使用 EnumChildWindows
函数来完成此操作。此外,我们还检查静态文本是否是我们打开消息框之前记录的文本。
一旦检测到消息框,我们就会对其进行子类化。我们的子类化在 WM_INITDIALOG
上执行以下操作:
- 调整窗口大小以容纳新控件(分隔线和复选框)
- 如果窗口的宽度发生变化,则移动按钮,使其仍然居中显示
- 创建新控件
它还捕获 WM_COMMAND
以处理复选框上的鼠标单击。清理(一些 delete
)在 WM_NCDESTROY
上完成,这是我们收到的关于对话框的最后一个消息。
用法
在使用钩子之前,您必须先安装它。安装它的最佳位置是在应用程序的 InitInstance
中。同样,钩子也应在应用程序的 ExitInstance
中卸载。提供了两个实用方法来执行此操作:InstallMessageBoxHook()
和 UninstallMessageBoxHook()
。安装函数接受两个可选参数,即复选框的标签:这便于本地化。
使用非常简单,通过新的函数 NabMessageBox()
替换 AfxMessageBox()
,但保持相同的接口。每当您希望对话框具有“请勿再次询问”复选框时,只需使用 NabMessageBox()
而不是 AfxMessageBox()
。返回值将完全相同,只是如果选中了“请勿...”框,则返回值会相反(即负数)(例如,-MB_OK
而不是 MB_OK
)。
第二种方法:MFC 子类化
子类化消息框
Shog9 建议的方法是使用 MFC 子类化,并让一个类封装整个子类化过程。这使得代码更加清晰。这依赖于通过 AfxHookWindowCreate
函数进行的 MFC 钩子。通过这种方式,我们将消息框消息重定向给我们,因此能够(再次)捕获 WM_INITDIALOG
。在这里,我们调整窗口大小,添加控件等...
最大的优点是我们类在某种程度上就是消息框本身。因此,我们不必通过检查子窗口或第一种方法中使用的任何花哨技巧来检测我们是否是消息框。此外,按钮的单击只需通过消息映射中的普通 ON_COMMAND
条目来处理。
用法
此方法无需安装或卸载钩子。只需实例化一个 CnbMessageBox
并传递父窗口,然后调用 MessageBox()
并传递与 AfxMessageBox()
相同的参数。要了解是否选中了“请勿...再次询问”框,只需调用对话框上的 GetChecked()
即可。
实现
在两种情况下,您都有责任唯一地标识应用程序的每个提示,并将“请勿再次询问”的答案序列化(存储在注册表或文件中),以便确保不再重复相同的消息!
您可能需要一个持久的唯一对象(单例),该对象将在应用程序启动时加载答案,并在退出时保存它们。每次要显示消息框时,都应调用类似以下内容(这是针对钩子方式的)
int CMessagePrompter::DoMessageBox(CString strMessage, int nType, int nUniqueId) { if (HasStoredReply(nUniqueId)) { return GetStoredReply(nUniqueId); } else { int rc = NabMessageBox(strMessage, nType); if (rc<0) StoreReply(nUniqueId, -rc); return abs(rc); } }
比较
钩子存在一个很大的限制:您不能同时显示两个具有完全相同文本的消息框。但是,您可以(从不同的线程)毫无问题地显示两个消息框。子类化例程使用此文本来了解它是否应该修改对话框。如果同时显示两个具有完全相同文本的消息框,那么我们将遇到问题。除此之外,一切似乎都运行正常!
MFC 子类化没有显示任何问题,所以它可能更好 :-)
结论
我们已经看到了在应用程序中实现此类消息框的两种简单方法。MFC 子类化方法具有代码更简单、更清晰的巨大优势。所以,这取决于您的选择!