MsgBox with a timeout
- John
- Jun 11
- 4 min read
Showing a message box to a user is great to inform them of information or to get input. But it can be a problem if the user isn't paying attention and doesn't notice the message box. Maybe they've gone for lunch. Or gone home for the day. Or the weekend. Or quit their job. The message box will just sit there waiting indefinitely.
But a message box with a timeout ... using the Windows API function "MessageBoxTimeout" ... won't wait indefinitely ... it'll wait as long as you want it to wait.
So, as with any Windows API function, you need first of all to declare it ... in the Module you want to use it in, in the declaration lines (i.e. before the first Sub or Function), add:
I think it always makes sense to make your Windows API function declarations Private (as they are here) and have a separate VBA 'helper' function that calls them for you. This lets you carry out any necessary sanity-checks on the values before they are passed to the Windows API function and to do the same on the value returned by it. So the following code is the VBA 'helper' function which you should put into the same Module as the Windows API function declarations:
The 'helper' Function has comments to explain each parameter and the return value - what values that calling code should pass to it and what values that calling code will get back from it. The key take-away is that:
If you show the message box with 2 or more buttons, you will get information back about whether the message box timed-out without a button being clicked, or (if a button was clicked) which button
If you show the message box with only 1 button, you will only know that the message box closed ... not whether it timed-out or if it was closed via a click of a button
Note also that the 'helper' Function not only returns a Boolean, but that it also "returns" a VbMsgBoxResult via its outlResult parameter. This parameter is explicitly declared as ByRef (so allowing the Function to update the value passed to it by the calling code ... ByRef is the default but it makes the code clearer to be explicit when a parameter is being used to "return" a value like this) and, by (my) convention uses the 'out' prefix to indicate that a value is being "returned".
So here's a couple of examples of using this, first an example that shows the message box with 2 buttons (as it passes-in vbOKCancel) ... we can then determine whether the message box timed-out or whether a button was clicked:

And another example but this time showing the message box with only 1 button (as it passes-in vbOKOnly) ... so all we know is that the message box closed:
In both of these examples, the message box will time-out and disappear after 2 seconds (= 2000 milliseconds).
While the Windows API function declarations and the 'helper' VBA Function are independent of which host application they are being used in, the two examples are 'Excel-centric' due to the use of Application.hWnd. Note that you can pass 0 for the hWnd parameter (or omit it) if desired ... the impact of doing that is that the message box will have no owning window. Another alternative is to use the GetHwnd function from my Get the application window handle in any app (well ... in Excel, Word, Access, PowerPoint or Outlook) post. Or:
If using this code in Word, replace Application.hWnd with Application.ActiveDocument.ActiveWindow.hWnd
If using this code in Access, replace Application.hWnd with Application.hWndAccessApp
This is all great but any eagle-eyed readers will have noticed that the Windows API declarations are the ANSI versions ("MessageBoxTimeoutA"). In other words, they don't handle Unicode characters. So here's the Unicode equivalent Windows API function declarations using "MessageBoxTimeoutW" (where "W" means wide i.e. Unicode whereas the "A" in "MessageBoxTimeoutA" means "ANSI"):
And to work with the updated parameters, here's an updated 'helper' function:
Note that this uses the VBA StrPtr function to pass pointers to the 'prompt' and 'title' Strings rather than the Strings themselves. The reason for passing pointers rather than Strings is to avoid VBA's automatic conversion of all Strings into ANSI when passing them to Windows API functions (even when that function is a "W" function). And in order to accept those pointers, the Windows API functions that previously had String parameters now have Long/Ptr parameters.
Finally, here's a (very basic!) example of using the Unicode version that shows a smiley-face in the message box title bar and body ... again, in the following code, update Application.hWnd depending on the host application you are using.
This is the dialog:

A caveat: I no longer have a pre-VBA7 version of Office (i.e. 2007 or earlier) with with to test the Windows API function declarations in the #Else blocks. If you are reading this and you do have Office 2007 or earlier and can test this code, please leave a comment to confirm if it works or, if not, what problems it has.
Normally, to round off, I'd include a link to the office MS Docs for the subject of my blog post. In this case, though, I can't do that ... because the "MessageBoxTimeout" function is undocumented. There are MS Docs for the "MessageBox" function with which this shares some similarities here https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-messagebox. But the actual source of my knowledge for the "MessageBoxTimeout" function is here https://www.delphibasics.info/home/delphibasicssnippets/undocumentedmessageboxtimeoutfunction ... this site in turn references another site but, sad to say, that site is no longer functioning.
You said, "Note that you can pass 0 for the hWnd parameter (or omit it) if desired ... the impact of doing that is that the message box will have no owning window."
What happens when the message box has no owning window? Is it modeless instead of modal?