Creating a Message Box Directly using API

Download the example (C#)

.NET Framework contains holes that you can’t overcome using the managed code. Then you must dig into the API to overcome this.

Some of the overcomes in the .NET is the way that you can’t show a message box that contains a Help button!

Figure 1 shows an error message box while loading a project in Visual Basic 6.

Visual Basic File Could Not Be Loaded

To overcome this, API provides you with two functions for creating a message box.

These functions are MessageBox and MessageBoxEx.
Yeah, They are two distinct functions, but they’re the same on how they work, and also they share the same input parameters, except that the MessageBoxEx includes a parameter in the last called wLanguageId. But it’s reserved. Means that you can safely ignore this parameter.

The syntax of both functions (in C) is as follows:

int MessageBox(
    HWND hWnd,
    LPCTSTR lpText,
    LPCTSTR lpCaption,
    UINT uType);

int MessageBoxEx(
    HWND hWnd,
    LPCTSTR lpText,
    LPCTSTR lpCaption,
    UINT uType,
    WORD wLanguageId );

A handle to the owner window of the message box. If this parameter is NULL, the message box has no owner window.
A string contains the message to be displayed.
A string contains the message box caption. If this parameter is null, the Default title Error is used.
Specifies the contents and behavior of the dialog box. (we will return to discuss this later)
wLanguageId (MessageBoxEx):

This parameter specifies the contents and behavior of the message box, and can be a combination of flags from the following groups of flags:

  • To indicate the buttons displayed in the message box, specify one of the following values:
    MB_OK = 0x0 (default)
    OK button.
    MB_OKCANCEL = 0x1
    OK and Cancel buttons.
    Abort, Retry, and Ignore buttons.
    Yes, No, and Cancel buttons.
    MB_YESNO = 0x4
    Yes and No buttons.
    Retry and Cancel buttons.
    MB_HELP = 0x4000
    Help button. This flag is the only that can be used with a combination with any other buttons flag.
  • To display an icon in the message box, specify one of the following values:
    MB_ICONHAND = 0x10
    A stop-sign icon appears in the message box.
    A stop-sign icon appears in the message box.
    An exclamation-point icon appears in the message box.
    An icon consisting of a lowercase letter i in a circle appears in the message box.
  • To indicate the default button, specify one of the following values:
    MB_DEFBUTTON1 = 0x0 (default)
    The first button is the default.
    MB_DEFBUTTON2 = 0x100
    The second button is the default.
    MB_DEFBUTTON3 = 0x200
    The third button is the default.
    MB_DEFBUTTON4 = 0x300
    The fourth button is the default. (Will be the Help button if you specify MB_HELP)
  • To indicate the modality of the dialog box, specify one of the following values:
    MB_APPLMODAL = 0x0 (default)
    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.
    MB_SYSTEMMODAL = 0x1000
    Same as MB_APPLMODAL except that the message box has the is shown as top most. 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 = 0x2000
    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.
  • To specify other options, use one or more of the following values:
    The message is displayed on the default desktop.
    MB_TOPMOST = 0x40000
    The message is shown top most. (with the WS_EX_TOPMOST window style)
    MB_RIGHT = 0x80000
    The text is right-justified.
    MB_RTLREADING = 0x100000
    Displays message and caption text using right-to-left reading order on Hebrew and Arabic systems.
    The message is displayed on the active desktop, even if no user logged on to the computer.

Return Value:
Both MessageBox and MessageBoxEx return an integer.
If the function fails, the return value is zero.
If the function succeeds, the return value is one of the following value:

  • IDOK = 1
    The user clicked the OK button.
  • IDCANCEL = 2
    The user clicked the Cancel button or pressed the ESC key.
  • IDABORT = 3
    The user clicked the Abort button.
  • IDRETRY = 4
    The user clicked the Retry button.
  • IDIGNORE = 5
    The user clicked the Ignore button.
  • IDYES = 6
    The user clicked the Yes button.
  • IDNO = 7
    The user clicked the No button.
    The user clicked the Try Again button.
    The user clicked the Continue button.

But what if user clicked the Help button?
There’s no return value named for clicking the help button! That’s because clicking the Help button doesn’t close the message box! Instead it’s sending a WM_HELP message to the owner which equals pressing F1.
So, You can handle this in .NET using the HelpRequested event.

Creating a .NET Example
First, Create the flags that can be used to specify message box options.

// Flags

public static class MBIcons
    public const int MB_ICONHAND = 0x10;
    public const int MB_ICONQUESTION = 0x20;
    public const int MB_ICONEXCLAMATION = 0x30;
    public const int MB_ICONASTERISK = 0x40;

    public const int MB_ICONERROR = MB_ICONHAND;
    public const int MB_ICONSTOP = MB_ICONHAND;
public static class MBDefButton
    public const int MB_DEFBUTTON1 = 0x0;
    public const int MB_DEFBUTTON2 = 0x100;
    public const int MB_DEFBUTTON3 = 0x200;
    public const int MB_DEFBUTTON4 = 0x300;
public static class MBButton
    public const int MB_OK = 0x0;
    public const int MB_OKCANCEL = 0x1;
    public const int MB_ABORTRETRYIGNORE = 0x2;
    public const int MB_YESNOCANCEL = 0x3;
    public const int MB_YESNO = 0x4;
    public const int MB_RETRYCANCEL = 0x5;

    public const int MB_HELP = 0x4000;
public static class MBModal
    public const int MB_APPLMODAL = 0x0;
    public const int MB_SYSTEMMODAL = 0x1000;
    public const int MB_TASKMODAL = 0x2000;
public static class MBOptions
    public const int MB_DEFAULT_DESKTOP_ONLY = 0x20000;
    public const int MB_TOPMOST = 0x40000;
    public const int MB_RIGHT = 0x80000;
    public const int MB_RTLREADING = 0x100000;
    public const int MB_SERVICE_NOTIFICATION = 0x200000;

Second, Create the message box return value enumeration.

public enum MBReturn
    IDOK = 1,
    IDCANCEL = 2,
    IDABORT = 3,
    IDRETRY = 4,
    IDIGNORE = 5,
    IDYES = 6,
    IDNO = 7,
    IDTRYAGAIN = 10,
    IDCONTINUE = 11,

Third, Create the P/Invoke methods.
P/Invoke stands for Platform Invocation a way for calling unmanaged functions.

public static extern int MessageBox(
    IntPtr hWnd, string lpText,
    string lpCaption, uint uType);

public static extern int MessageBoxEx(
    IntPtr hWnd, string lpText,
    string lpCaption, uint uType,
    ushort wLanguageId);

Code Explanation:
DllImport Attribute:
This attribute is added to the P/Invoke method to indicate the DLL that expose that function. In the case of these functions the DLL is user32.dll.
static extern modifiers:
P/Invoke methods must specify the two modifiers.
extern means that the method is implemented externally, while static means that that method belongs to the type itself not the object.
IntPtr struct:
The managed type that can be used to represent a pointer or an handle in the unmanaged code.

In unmanaged code there’s some types that doesn’t exist in the managed environment, But using marshaling you can overcome this problem.
Marshaling is the process of converting a managed type into unmanaged type.
We have seen in the last code some examples of this.
An example is HWND which is a pointer to a handle in the memory. Which .NET doesn’t provide this type! But, It provides a similar type named IntPtr.
Another example is the WORD which is 16-bit unsigned integer. But, The .NET Framework includes an equivalent thats UInt16 or simply ushort (in C#).

Finally, Call the unmanaged function.

static void Main()
    // You can replace IntPtr.Zero
    // with Form.Handle if you want to
    // specify the form as an owner
    MBReturn ret = (MBReturn)MessageBoxEx(IntPtr.Zero,
        "'C:StockPrjcrviewer.dll' could not be loaded" +
        "--Continue Loading Project?", "Microsoft Visual Basic",
        MBButton.MB_YESNO | MBButton.MB_HELP |
        MBDefButton.MB_DEFBUTTON1, 0);
    Console.WriteLine("User clicked: {0}", ret.ToString());


You have completed the lesson.

Download the example (C#)