Mastering the Message Box

It’s not uncommon for programmers to build scripts that communicate with the end user. Whether the script is delivering a notification or asking the user to make a choice, most programmers will turn to the simple message box. But there’s more to the message box than meets the eye! From controlling the title bar text and icon to its appearance and behavior, the message box provides many different customizable options. Today we’re going to explore all of those options—even the ones you won’t find in the MSDN documentation!

Message boxes are displayed by using the MessageBox function of the Win32 API.  Both VBScript and Visual Basic for Applications provide a MsgBox function that allows limited use of the API MessageBox function.

The WshShell object’s Popup method, on the other hand, provides more thorough access to the MessageBox function.  The MSDN documentation for the Popup method and the MessageBox function are incomplete at best, so learning to use them takes a little trial and error.  Luckily, I’ve already done that for you.  Let’s take a look at the syntax for the Popup method.

intButton = object.Popup(strText,[nSecondsToWait],[strTitle],[nType])

The Popup method has one required parameter, which is a string value containing the prompt to be displayed in the message box.  According to the documentation, it returns an integer value that indicates the button that the user pressed.  This is incorrect. It actually returns a Variant value that can be interpreted as a Long type integer, as I’ll explain later.

The Popup method also accepts three additional optional parameters.

nSecondsToWait is an integer value indicating the number in seconds the message box should display before timing out.  Omitting this value or specifying 0 will cause the message box to remain on the user’s screen until they press a button.

strTitle accepts a string value that will be displayed in the title bar of the message box.  If this value is Null (or omitted) the title will default to “Windows Script Host” in WSH or the API’s default “Error” in VBA.

nType accepts an integer value that represents a combination of bit flags specifying the various options for the message box.  This is the most complicated part of the process, and we’ll talk about this in detail later in this article.

You can display a simple message box using the following code for WSH or VBA.

‘ WSH

Set WshShell = CreateObject("WScript.Shell")

intReturn = WshShell.Popup("Hello, world!")

 

‘ VBA

‘ Add a Reference to Windows Script Host Object Model

Dim WshShell As IWshRuntimeLibrary.WshShell

Set WshShell = New IWshRuntimeLibrary.WshShell

 

Dim lngResult As Long

lngResult = WshShell.Popup("Hello, world!")

{mospagebreak title=Controlling the appearance}

The fourth parameter of the Popup method is a bit flag that lists each of the options you would like applied to the message box.  Many of these options control the appearance or behavior of the box.  We’ll begin by examining the ones that indicate what buttons the message box should display.

A bit flag is a binary number in which each bit represents an option that can be toggled on or off.  Each bit in the sequence can be either a 1 (on) or 0 (off).  To make things a little easier, bit flags are often represented in code by their hexadecimal counterpart.  Most programmers, however, tend to avoid binary in any form and stick to decimal numbers.  To understand this a little easier, let’s look at the bit flag values that control the buttons that are displayed on the message box.

Table 1: Popup Button option flag constants

Constant

Decimal

Hex

MB_OK

0

0×00000000

MB_OKCANCEL

1

0×00000001

MB_ABORTRETRYIGNORE

2

0×00000002

MB_YESNOCANCEL

3

0×00000002

MB_YESNO

4

0×00000004

MB_RETRYCANCEL

5

0×00000005

MB_CANCELTRYCONTINUE

6

0×00000006

MB_ABORTRETRYIGNORE
The message box contains three push buttons: Abort, Retry, and Ignore.
MB_CANCELTRYCONTINUE
Microsoft Windows 2000/XP: The message box contains three push buttons: Cancel, Try Again, Continue. Use this message box type instead of MB_ABORTRETRYIGNORE.
MB_OK
The message box contains one push button: OK. This is the default.
MB_OKCANCEL
The message box contains two push buttons: OK and Cancel.
MB_RETRYCANCEL
The message box contains two push buttons: Retry and Cancel.
MB_YESNO
The message box contains two push buttons: Yes and No.
MB_YESNOCANCEL
The message box contains three push buttons: Yes, No, and Cancel.

As we continue through this article, you will learn that the various option values are combined into a single value called a mask.  Because of the way bitwise flags work, you can determine individual option values by simply examining the final mask that they create.  You’ll understand this better when you see an example later.

Did you know? Each digit in a hexadecimal number is known as a nibble. A nibble represents four bits or exactly half of one byte.

Notice how the first digit in the hex value is the only thing that changes from each option to the next.  This is because the first digit in the final mask is used to determine what buttons to display on the message box.  Next we’ll examine the option flag values that determine what, if any, icon is displayed next to the message prompt.  Then I’ll show you what a mask might look like.

Table 2: Popup Icon option flag constants

Constant

Decimal

Hex

MB_ICONNONE*

0

0×00000000

MB_ICONSTOP

16

0×00000010

MB_ICONQUESTION

32

0×00000020

MB_ICONEXCLAMATION

48

0×00000030

MB_ICONINFORMATION

64

0×00000040

MB_ICONUSER

128

0×00000080

*Table items that appear with an asterisk are not defined constants.  I’ve provided these constant names for your convenience.

MB_ICONEXCLAMATION
An exclamation-point icon appears in the message box. This is also known as the Warning icon.
MB_ICONINFORMATION
An icon consisting of a lowercase letter i in a circle appears in the message box.
MB_ICONQUESTION
A question-mark icon appears in the message box. The question-mark message icon is no longer recommended because it does not clearly represent a specific type of message and because the phrasing of a message as a question could apply to any message type. In addition, users can confuse the message symbol question mark with Help information. Therefore, do not use this question mark message symbol in your message boxes. The system continues to support its inclusion only for backward compatibility.
MB_ICONSTOP
A stop-sign icon appears in the message box. This is also known as the Error icon.

You should avoid using the Question Mark icon whenever possible as this icon is not very informative.  You should choose either the Information icon, Exclamation icon, or Stop icon instead.  The Question Mark option only exists for reverse compatibility.

As you can see from the list above, the icon that is displayed in the message box is controlled by the second digit in the flag.  A value of 0 is no icon, a value of 1 is the Stop icon, 2 is the Question Mark icon, etc.  Each of the various options flags I will present you with are offset enough that they can be combined without changing any existing flags that have been added.  To understand that, we’ll need to look a little deeper.

{mospagebreak title=Understanding Bitwise Flags}

Let’s assume for a moment that we wanted to display a message box with OK and Cancel buttons and an Information Icon.  To do this, we would need to combine their flag values into a single mask.  In VBScript this will be done for you by means of a bitwise operator that I will show you later.  But in order to understand how this works, you need to see firsthand how the process is done.

Bitmasks in VBScript are 16-bit Dword values.  In short, this simply means that each value is a 16-digit binary number.  In order to combine these flags, we’ll first need to convert them to this format.  The first of these is MB_OKCANCEL, which has a value of 1, which is the same in decimal, hex, and binary.  So we simply need to pad it with 15 leading zeros to make it a 16-digit number.

0000 0000 0000 0000 0000 0000 0000 0001 MB_OKCANCEL

The same process is done for the MB_ICONINFORMATION flag which has a value of 1,000,000 in binary.  To conserve a little space, I’m not going to go into detail about how to convert these numbers from decimal and hex into binary.  For this demonstration, I’ll give you the values so you can see how the bitmask works before I show you how to do this programmatically.  You can read my series on the binary number system if you really want to take this further.

0000 0000 0000 0000 0000 0000 0100 0000 MB_ICONINFORMATION

Now that we have our two option flag values in binary, we need to combine them into a single mask value.  This looks a whole lot like addition, but works slightly differently.

0000 0000 0000 0000 0000 0000 0000 0001 MB_OKCANCEL

0000 0000 0000 0000 0000 0000 0100 0000 MB_ICONINFORMATION

 

0000 0000 0000 0000 0000 0000 0100 0001 Mask value

To combine these values, you align them as you would for a simple addition problem.  Now move across each column in order.  If a column contains a 1 in either (or both) of the option values, then you place a 1 in that same column of the mask.  If a column does not contain a 1 in either of the option values, then a 0 is placed in that column in the mask.  This process is known as a bitwise inclusive disjunction.  The word bitwise means that the values are examined one bit at a time, and an inclusive disjunction is a logical comparison that returns True if either of the provided conditions evaluates to True.

As it turns out, this results in a mask value of 1,000,001 which just so happens to be the sum of the two option values.  For this reason, you’ll often see something like this done in VBScript.

bMask = MB_OKCANCEL + MB_ICONINFORMATION

The resulting value of bMask will be correct despite the fact that the above code uses an arithmetic operator. However, I urge you NOT to use this type of syntax because you’re not actually performing an arithmetic operation. You’re performing a logical bitwise operation. The VBScript operator for a bitwise inclusive disjunction is the Or operator. So the code above should always be rewritten as follows.

bMask = MB_OKCANCEL Or MB_ICONINFORMATION

I know this looks a little odd, but trust that it works, and it’s not prone to the same mistakes that an arithmetic operation would be.  Consider the following:

bMask = bMask + MB_OKCANCEL + MB_ICONINFORMATION

In this example, we’re seemingly appending some values to an existing bitmask, right? As it turns out, this can produce unexpected and invalid results because we’re simply adding more numbers to some existing value. There’s no logic behind this operation to ensure that a proper bitmask value is returned! The only time this addition operation works successfully every time is when the initial value of the mask is 0. (Which is why programmers get away with using code like I showed you above. They’re using addition of several values to set the initial value of the mask).

bMask = bMask Or MB_OKCANCEL Or MB_ICONINFORMATION

Contrary to the last example, using the Or operator will return a valid result in every case regardless of the initial value of the mask.  Because there is logic involved in the operation, the resulting mask will correctly contain a combination of the initial mask value and the added flag values.

The Or operator will ensure that a bit is “always on” in the final mask. Using And Not will ensure that a bit is “always off.”

{mospagebreak title=Controlling other options}

Now that you have some understanding of how to assemble a bit mask for the option flag, let’s examine the remaining option values that are available to you.  The first of these are the default button option flags, which tell the MessageBox function which button should be presented as the default option.

Table 3: Popup Default Button option flag constants

Constant

Decimal

Hex

MB_DEFAULTBUTTON1

0

0×00000000

MB_DEFAULTBUTTON2

256

0×00000100

MB_DEFAULTBUTTON3

512

0×00000200

MB_DEFAULTBUTTON4

768

0×00000300

MB_DEFBUTTON1
The first button is the default button.

MB_DEFBUTTON1 is the default unless MB_DEFBUTTON2, MB_DEFBUTTON3, or MB_DEFBUTTON4 is specified.
MB_DEFBUTTON2
The second button is the default button.
MB_DEFBUTTON3
The third button is the default button.
MB_DEFBUTTON4
The fourth button is the default button.

The next set of option flags are used to determine the modality of the message box.  Modality controls how the message box affects the programs that are currently running.

Table 4: Popup Modality option flag constants

Constant

Decimal

Hex

MB_APPLICATIONMODAL

0

0×00000000

MB_SYSTEMMODAL

4096

0×00001000

MB_TASKMODAL

8192

0×00002000

MB_APPLMODAL
The user must respond to the message box before continuing work in the window identified by the hWnd parameter. However, the user can move to the windows of other threads and work in those windows.

Depending on the hierarchy of windows in the application, the user may be able to move to other windows within the thread. All child windows of the parent of the message box are automatically disabled, but pop-up windows are not.

MB_APPLMODAL is the default if neither MB_SYSTEMMODAL nor MB_TASKMODAL is specified.
MB_SYSTEMMODAL
Same as MB_APPLMODAL except that the message box has the WS_EX_TOPMOST style. Use system-modal message boxes to notify the user of serious, potentially damaging errors that require immediate attention (for example, running out of memory). This flag has no effect on the user’s ability to interact with windows other than those associated with hWnd.
MB_TASKMODAL
Same as MB_APPLMODAL except that all the top-level windows belonging to the current thread are disabled if the hWnd parameter is NULL. Use this flag when the calling application or library does not have a window handle available but still needs to prevent input to other windows in the calling thread without suspending other threads.

Finally, there are several additional options that provide control over miscellaneous behaviors of the message box.

Table 5: Popup additional option flag constants

Constant

Decimal

Hex

MB_HELP

16384

0×00004000

MB_NOFOCUS

32768

0×00008000

MB_SETFOREGROUND

65536

0×00010000

MB_DEFAULTDESKTOPONLY

131072

0×00020000

MB_TOPMOST

262144

0×00040000

MB_RIGHT

524288

0×00080000

MB_RTLREADING

1048576

0×00100000

MB_SERVICENOTIFICATION

2097152

0×00200000

MB_HELP
Windows 95/98/Me, Windows NT 4.0 and later: Adds a Help button to the message box. When the user clicks the Help button or presses F1, the system sends a WM_HELP message to the owner.
MB_DEFAULT_DESKTOP_ONLY
Windows NT/2000/XP: Same as desktop of the interactive window station. For more information, see Window Stations.
Windows NT 4.0 and earlier: If the current input desktop is not the default desktop, MessageBox fails.

Windows 2000/XP: If the current input desktop is not the default desktop, MessageBox does not return until the user switches to the default desktop.

Windows 95/98/Me: This flag has no effect.
MB_RIGHT
The text is right-justified.
MB_RTLREADING
Displays message and caption text using right-to-left reading order on Hebrew and Arabic systems.
MB_SETFOREGROUND
The message box becomes the foreground window. Internally, the system calls the SetForegroundWindow function for the message box.
MB_TOPMOST
The message box is created with the WS_EX_TOPMOST window style.
MB_SERVICE_NOTIFICATION
Windows NT/2000/XP: The caller is a service notifying the user of an event. The function displays a message box on the current active desktop, even if there is no user logged on to the computer.
Terminal Services: If the calling thread has an impersonation token, the function directs the message box to the session specified in the impersonation token.
{mospagebreak title=Putting it all together}

Now that you’ve seen all of the different option flag values, let’s take a look at how this all fits together.  Here again is the syntax for the WshShell object’s Popup method that I described earlier.

intButton = object.Popup(strText,[nSecondsToWait],[strTitle],[nType])

Let’s take a look at a working example.

intResult = WshShell.Popup("This is my message box!",, "Hello, World!", _

   MB_OK Or MB_ICONINFORMATION)

The example above will display a message box containing the text “This is my message box!” and the title bar will display “Hello, World!”.  As you can see, I’ve applied two option flags.  The first, MB_OK, instructs the Popup method to display a message box with a single OK button.  The second, MB_ICONINFORMATION, tells the function to display the information icon next to the text prompt.  You might also notice that I’ve omitted the timeout value allowing it to use the default of 0, or do not timeout.  Let’s look at one more.

intResult = WshShell.Popup("Would you like to continue?",, "Continue", _

   MB_YESNO Or MB_ICONSTOP)

In this example I’ve asked the end user a question and given them two buttons, Yes and No, to issue a response.  That response will be assigned to the intResult variable, so let’s see what the possible return values are.

Table 6: Popup button return value constants

Constant

Decimal

Hex

MB_TIMEOUT*

-1

0xFFFFFFFF

MB_ERROR*

0

0×00000000

IDOK

1

0×00000001

IDCANCEL

2

0×00000002

IDABORT

3

0×00000003

IDRETRY

4

0×00000004

IDIGNORE

5

0×00000005

IDYES

6

0×00000006

IDNO

7

0×00000007

IDTRYAGAIN

10

0x0000000A

IDCONTINUE

11

0x0000000B

The X (close button) on the message box is not enabled unless the message box displays a Cancel button.  Closing the message box in this fashion will return IDCANCEL as if the Cancel button was pressed.

The Popup method will return a Boolean True (Long -1) in the event of a timeout or the integer value for the button that was pressed to close the message box.  In the event the message box cannot be displayed, an error value of 0 is returned instead.

{mospagebreak title=Catching bugs}

In the process of writing this article I’ve discovered some bugs in the Win32 API that you may need to be aware of.  The Popup method will incorrectly return 0 for IDTRYAGAIN and 2949165 for IDCONTINUE on Windows 2000 systems when using the MB_CANCELTRYCONTINUE option with MB_TOPMOST (either explicitly or implicitly with MB_SERVICENOTIFICATION).  The same bug exists on Windows XP systems, which incorrectly return 7471173 for IDTRYAGAIN and 7274610 for IDCONTINUE.

This presents a slight problem, but it’s manageable in most cases.  Since the erroneous values are consistent, you can check for them without worrying about coding your project for specific operating systems.  The one problem arises on Windows 2000 systems when the Try Again button will return the error code 0.

To avoid these bugs, simply avoid using the MB_TOPMOST and MB_SERVICENOTIFICATION option flags, since they have little use in the scripting environment anyway.  The following demonstrates a simple function the displays the return value from a message box button press.

Function GetButtonCode(intCode)

   Select Case intCode

       Case MB_TIMEOUT    GetButtonCode = "Timeout"

       Case MB_ERROR      GetButtonCode = "Error"

       Case IDOK          GetButtonCode = "OK"

       Case IDCANCEL      GetButtonCode = "Cancel"

       Case IDABORT       GetButtonCode = "Abort"

       Case IDRETRY       GetButtonCode = "Retry"

       Case IDIGNORE      GetButtonCode = "Ignore"

       Case IDYES         GetButtonCode = "Yes"

       Case IDNO          GetButtonCode = "No"

       Case IDTRYAGAIN    GetButtonCode = "Try Again"

       Case IDCONTINUE    GetButtonCode = "Continue"

       Case 2949165       GetButtonCode = "Continue"  ‘Windows 2000 with MB_Topmost

       Case 7471173       GetButtonCode = "Try Again" ‘Windows XP with MB_Topmost

       Case 7274610       GetButtonCode = "Continue"  ‘Windows XP with MB_Topmost

       Case Else          GetButtonCode = CStr(intCode)

   End Select

End Function

If you’ve managed to keep up this long, I applaud you.  That’s a lot of reading!  But now you understand that there’s a lot more to the message box than most would have you think.  Take advantage of this opportunity to showcase your scripting skills.  Until next time, keep coding!

One thought on “Mastering the Message Box

  1. Gained useful knowledge from this article. Having trouble getting the bMask = MB_OKCANCEL Or MB_ICONINFORMATION section etc. to work (VBA version) – using the decimal equivalent does work. Any suggestions?

[gp-comments width="770" linklove="off" ]