Click here to Skip to main content
15,885,546 members
Articles / Programming Languages / C
Tip/Trick

A pure-C Centered MessageBox Replacement

Rate me:
Please Sign up or sign in to vote.
5.00/5 (5 votes)
23 Oct 2015CPOL2 min read 11.6K   299   9   2
A C-replacement for MessageBox

Image 1

Introduction

The most annoying feature of the Windows API MessageBox is that the dialog box is displayed at the middle of the main monitor, no matter which program triggered the API. It is often more user-friendly to create the message boxes overlapping the parent application window.

The trouble is that there is no native way to do it with the MessageBox API.

Hopefully, Microsoft provides a hack based on the SetWindowsHook API, which inserts a hook between the window creation and its display. This method is extensively documentated (see for instance here). But unfortunately, most antivirus do not trust this SetWindowsHook API since it can be used to do nasty thinks like, for instance, put a keylogger in place.

Background

The code presented is a replacement of the MessageBox API which automatically centers the windows. It also allows a printf-like syntax for arguments processing, but the top parameters are exactly the same ones, so you can replace all MessageBox calls with a define instruction:

C
#define MessageBox myMsgBox

In order to be antivirus-friendly, it is written in C and needs only the standard windows DLLs kernel, user32 and GDI.

Using the Code

Since we intend to put this function in a library and include it in most programs, we don't want to use the common resource file of the project and ask to the user to add some code in this file.

The hack is to create the dialog box template directly in RAM using the structure:

C
#pragma pack(push, 4)                 
const static struct { // dltt 
    DWORD  style; 
    DWORD  dwExtendedStyle; 
    WORD   ccontrols; 
    short  x; 
    short  y; 
    short  cx; 
    short  cy; 
    WORD   menu;         		// name or ordinal of a menu resource
    WORD   windowClass;  		// name or ordinal of a window class
    WCHAR  wszTitle[sizeof DLGTITLE]; 	// title string of the dialog box
    short  pointsize;       		// only if DS_SETFONT flag is set
    WCHAR  wszFont[sizeof DLGFONT];   	// typeface name, if DS_SETFONT is set
} 
sEmptyDialogBox = 
{
   // only style and font are used
   WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU  | DS_MODALFRAME  | DS_SETFONT ,
   0x0,					// exStyle;
   0,					// # of ccontrols
   0, 0, 10, 10,			// position --> don't care
   0,                       		// menu: none
   0,                       		// window class: none
   DLGTITLE,                		// temporary Window caption
   8,                       		// font pointsize
   DLGFONT,				// font 
 };
#pragma pack(pop)

This template defines an empty dialog box (ccontrols, the number of controls included in the template, is set to 0). It is called with the DialogBoxIndirectParam API:

C
Rc = DialogBoxIndirectParam ( hInstance,
                              (LPCDLGTEMPLATEW) & sEmptyDialogBox,
                              hParentWnd,
                              (DLGPROC) myMsgBoxCbk,
                              (LPARAM) & cParam);

Then the buttons and an edittext control are created in the WM_INITDIALOG message handler using CreateWindowEx. Of course, we have to keep track of each window created in static variables, since they will not be automatically destroyed.

C
res->hwndText = CreateWindow  ( "Edit",  // Predefined class; Unicode assumed 
			"", 
			WS_VISIBLE | WS_CHILD | ES_READONLY | ES_MULTILINE | WS_DISABLED,  // Styles 
			TEXT_X_POSITION, 
			TEXT_Y_POSITION,
			Size.cx, 
			Size.cy,
			hwndDlg,     	// Parent windo
			NULL,       	// No menu.
			GetWindowInstance (hwndDlg), 
			NULL);      	// Pointer not needed.

The main difference with standard dialog box procedures is that, due to manual control creation, the callback has no ControlID wParam to manage. Instead of that, the callback has to use the lParam which identifies the control window's handler.

This process can be found during the WM_COMMAND handler to evaluate the control which has triggered the WM_COMMAND message. Here we want to retrieve the value to be returned to the caller: we have to match the lParam argument (which is copied into hButtonWnd) with all the controls we have created.

C
for (Ark=0 ;   Ark < tMapping[myMsgBoxResources.style].nButtons  ;  Ark++)
{
    if (res->hwndButton[Ark] == hButtonWnd)
        Rc = tMapping[res->style].tButtons[Ark];
    ...
}

Displaying the Icon was really easy. Remember MessageBox uses only standard Icons, so do we. We just have to load them first during DialogBox initialization and display them in response to the WM_PAINT event.

C
switch (message)
{
    case WM_INITDIALOG :
       myMsgBoxResources.hIcon = LoadIcon (NULL, tIcon[nIconIdx]);
       ...
       break;

    case WM_PAINT :
        // text is already managed by the control,
        // but icon has to be drawn manually
        hDC = GetDC (hwndDlg);
        Rc = DrawIcon (hDC, ICON_X_POSITION, ICON_Y_POSITION, myMsgBoxResources.hIcon);
        ReleaseDC (hwndDlg, hDC);
        return FALSE;
    ...
}

Points of Interest

I wrote this API as an exercise of how to create a dialog box without resource file.

History

  • Release 1.0: 23<sup>rd</sup> October, 2015

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
France France
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionVery useful but cannot change the gray color of the text in the dialog box Pin
toddma27-Dec-23 11:44
toddma27-Dec-23 11:44 
Suggestionperhaps not use a #define ? Pin
John Torjo26-Oct-15 11:25
professionalJohn Torjo26-Oct-15 11:25 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.