Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

"Do not ask again" Message Boxes

0.00/5 (No votes)
4 Feb 2003 1  
Two simple ways to add "Do not ask again" Message Boxes to your application.

Introduction

"Do not ask again" Message Boxes now appear in a lot of applications. Doing individual dialogs for each message box turns out to be a full nightmare that I did not even try myself! This article proposes a very straight way to implement these: with a Windows hook. The idea is to catch the creation of message boxes (see below how this is done) and then add the necessary controls (a separator and a checkbox) to the message box after having resized it. As Shog9 suggested I am going to do this in two different ways.

First way: Hooking

Hooking Message Boxes

Windows hooking is a nice feature WIN32 offers us: it allows us to intercept a lot of system calls especially regarding window management. In particular, we are able to catch the WM_INITDIALOG message for every dialog our application opens: our own dialog classes but more important Common Control dialogs or whatever. When we catch the WM_INITDIALOG for a new dialog, we can decide to subclass it or not. When we subclass it, a function of our own will be called each time the dialog gets a message (WM_INITDIALOG or WM_COMMAND for instance).

Detecting a new message box is not an exact science: once it is created it can't be differentiated from another dialog. The method used here is simply to inspect all the children of the dialogs. The criteria for a dialog to be a message box is: 1 static icon, 1 static text, between 1 and 3 buttons and no control of any other type. We use the EnumChildWindows function to accomplish this. Moreover we check that the static text is one we have recorded before opening the message box.

Once a message box has been detected we subclass it. Our subclassing performs the following on WM_INITDIALOG:

  • Resize the window to hold the new controls (separator line and checkbox)
  • If the width of the window was changed, then move the buttons so that they still appear centered
  • Create the new controls

It also catches WM_COMMAND to handle mouse click on the checkbox. The cleanup (some delete) is done on WM_NCDESTROY which is the very last message we get for the dialog.

Usage

Before being able to use the hook you must install it. Best place to do this is in the InitInstance of your application. In the same way, the hook should be uninstalled in the ExitInstance of your application. Two utility methods are provided to do this: InstallMessageBoxHook() and UninstallMessageBoxHook(). The install function takes two optional parameters that are the label of the checkbox: this is for easy localization.

The usage is very straightforward using the new function NabMessageBox() which replaces AfxMessageBox(), but that keeps the same interface. Whenever you want a dialog box to have a "Do Not Ask Again" checkbox, simply use NabMessageBox() instead of AfxMessageBox(). The return code will be exactly the same except that it will be opposite (negative I mean) if the "Do not..." box was checked (eg. -MB_OK instead of MB_OK).

Second way: MFC subclassing

Subclassing Message Boxes

The way suggested by Shog9 is to use the MFC subclassing and have a class encapsulate the whole subclassing. This leads to a much much cleaner code. This relies on MFC hooking through the AfxHookWindowCreate function. With this, we redirect the message box messages to us and therefore are able to catch (once more) WM_INITDIALOG. Here we resize the window, add the control and so on...

The big advantage is that our class is in a way the message box itself. Therefore we do not have to detect if we are a message box by inspecting our children, our any of the fancy tricks used in the 1st way. Also the click on the button is simply handled with a normal ON_COMMAND entry in the message map.

Usage

There is no need to install or uninstall a hook with this method. Simply instantiate a CnbMessageBox passing the parent, then call MessageBox() passing the same arguments as to AfxMessageBox(). To know if the "Do not ... again" box was checked simply call GetChecked() on the dialog.

Implementing it

In both cases it is your responsibility to uniquely identify each prompt of your application and serialize (in the registry or in a file) the "do not ask again" answers, that were made to make sure to not prompt the same message twice!

You will probably need a unique persistent object (a singleton) that will load the answer when your app starts and save them on exit. Each time you want to display a message box, then something like this should be called (this is for the hooking way):

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);
    }
}

Comparison

Hooking exhibits a big limitation: you cannot display two message boxes with the exact same text at the same time. You can display two message boxes (from different threads) without any problem though. The subclassing routine uses this text to know if it is supposed to modify the dialog or not. If two message boxes with the exact same text are displayed at the same time, then we will run into problems. Other than that, everything seems to work fine!

The MFC subclassing does not show any problem so it is probably better :-)

Conclusion

We have seen two simple ways to implement such message boxes in an application. The MFC subclassing method has the big advantage of having a much more simple and cleaner code. So it is up to you to decide!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here