(Single line input box)
(Multi-line input box)
Introduction
One day, I was writing a small Windows tool, and wanted to get some input from the user. Since my application was not an MFC, nor a console application, to my knowledge, there was no simple way to get the input from the user. The Windows API does a pretty job by giving us the MessageBox()
function but nothing like the InputBox
function. Since I wanted to keep my application slim and self-contained, I decided to investigate on how to use dialogs without using resources and without introducing too much of code.
Thus the Win32InputBox library was born.
In this article, I will illustrate how to create and use dialog boxes without creating dialog resources or using MFC. We will then use this technique to create an InputBox()
function that is similar to VB's InputBox()
.
Background
It is important for the reader to be familiar with Windows messaging, and the window/dialog procedure mechanism. Nonetheless, I will be giving a simple introduction for beginners.
Introduction to dialog boxes - the classical way
Currently, to create a dialog box driven application, you have many choices. I'll list the most popular methods:
- MFC dialog based application (can be generated by the wizard).
- Plain Win32 API - using the IDE's dialog designer.
- Plain Win32 API - without using the dialog designer, instead, using the code to create all the controls and the dialog window.
Each of the mentioned methods above have their pros and cons, but that's beyond the scope of this article. Now as promised, here's a small overview on how each of the mechanisms work.
We start with the Plain Win32 API (using the dialog designer):
- The design part:
- We start by designing our dialog with the editor.
- We create controls (buttons, textboxes, ...).
- We assign IDs to the controls.
- The coding part:
- We write our
WindowProc()
, which will handle all the events related to our dialog.
- We call the appropriate dialog creation method (
CreateDialog()
family, or DialogBox()
function family).
If you are not using the dialog designer, you will have to create the controls in the code by calling the CreateWindow()
function.
As for MFC dialog based applications, the concept is similar, however everything is wrapped into neat classes. So, all you have to do is:
- Design the dialog using the editor.
- Subclass
CDialog
, say as CMyDialog
.
- Bind your subclassed class to the desired dialog ID.
- Overwrite the needed
CDialog
's methods to handle messages and events.
Introduction to dialog templates
What is a dialog template? It is a structure that defines the styles and dimensions of a given dialog. A dialog template is defined through the DLGTEMPLATE
structure found in the Windows headers. There is an extended version of this structure that renders the latter obsolete. The DLGTEMPLATEEX
is not defined anywhere in the header files, but is described in the MSDN. The extended dialog template (DLGTEMPLATEEX
) is newer, and introduces more features.
A dialog template alone is enough to describe the dialog, but not the dialog items, thus the DLGITEMTEMPLATE/EX
structures. These DLGITEMTEMPLATE[EX]
structures will define the items inside the dialog.
When you use the dialog editor (in VC++'s IDE), you are visually constructing the dialog templates and the dialog item templates (however, in source code form). When you compile your application, the resources get compiled separately using the RC.EXE (resource compiler) tool, which will produce the .RES (binary file) out of the .RC (text file), and finally everything will be linked together to produce one executable module.
Here's how DLGTEMPLATE
and DLGITEMTEMPLATE
are described in a .RC file:
IDD_INPUTBOX DIALOGEX 22, 17, 231, 109
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS |
WS_CAPTION | WS_SYSMENU
CAPTION "Win32InputBox"
FONT 8, "MS Shell Dlg", 700, 0, 0x0
BEGIN
LTEXT "Prompt:",IDC_INPUTBOX_PROMPT,6,4,157,
33,SS_NOPREFIX
EDITTEXT IDC_INPUTBOX_DLG_EDIT1,6,37,216,14,
ES_AUTOHSCROLL
EDITTEXT IDC_INPUTBOX_DLG_EDIT2,6,55,216,49,
ES_MULTILINE | ES_AUTOHSCROLL |
ES_WANTRETURN | WS_VSCROLL
DEFPUSHBUTTON "OK",IDOK,171,4,51,14,WS_GROUP
PUSHBUTTON "CANCEL",IDCANCEL,171,21,51,14,WS_GROUP
LTEXT "",IDC_STATIC,0,39,8,8,NOT WS_VISIBLE
END
To thoroughly understand this source file, please refer to MSDN (check references below).
However, I would like you to note the following:
- The numbers which denote the dimensions and positions.
- The
IDXXXX
which denote the IDs. Those IDS are defined in the "resource.h" file.
- The commands "
LTEXT
", "EDITTEXT
", etc... that designate which control to create.
- The style constants
WS_VISIBLE
or ES_MULTILINE
, or DS_MODALFRAME
, etc...
If you're curious about how a .RES file looks like, I have included a small hex dump of this compiled structure here:
Here's how a "resource.h" (partial) looks like:
#define IDD_INPUTBOX 103
#define IDC_INPUTBOX_PROMPT 1000
#define IDC_INPUTBOX_DLG_EDIT1 1001
#define IDC_INPUTBOX_DLG_EDIT2 1002
#define IDC_STATIC -1
Please note that the RES file not only describes your dialog, but also all the items in the .RC file (icons, string table, etc...). Since the resource file holds a number of resource items, all defined by certain IDs and resource types, we need a way to select the given resource and use it. That's why we have the FindResource()
and other resource management APIs.
This small code will illustrate how to create a dialog whose ID is defined in resource.h as IDD_MYDLG
:
#include <windows.h>
#include "resource.h"
int main()
{
HMODULE hModule = ::GetModuleHandle(0);
HINSTANCE hInst = hModule;
HRSRC hrsrc = ::FindResource(hModule,
MAKEINTRESOURCE(IDD_MYDLG), RT_DIALOG);
HGLOBAL hglobal = ::LoadResource(hModule, hrsrc);
::DialogBoxIndirectParam(hInst,
(LPCDLGTEMPLATE) hglobal, 0, 0, 0);
return 0;
}
Notice how we pass the "hglobal
" as "LPCDLGTEMPLATE
". This is the pointer to the compiled dialog template as discussed earlier. This code doesn't pass a dialog procedure, thus the dialog will not respond to the Close messages or anything like that, and you will have to kill the process or write an appropriate dialog procedure.
A simple dialog procedure which we can write is:
LRESULT CALLBACK dlgProc(HWND hDlg, UINT Msg,
WPARAM wParam, LPARAM lParam)
{
switch (Msg)
{
case WM_CLOSE:
::EndDialog(hDlg, IDOK);
return TRUE;
}
return 0;
}
And then we adjust the call to:
::DialogBoxIndirectParam(hInst, (LPCDLGTEMPLATE) hglobal,
0, (DLGPROC)dlgProc, 0);
The DlgResToDlgTemplate tool
In the course of writing the Win32Inputbox library, I developed a small tool named "DlgResToDlgTemplate". This tool's sole purpose is to fetch a dialog resource from a binary (which is now in compiled form) and dump it as a C character array. After extracting the dialog template, we can employ it in our own application.
A quick example would be, employing the NOTEPAD.EXE template in our own simple application.
- Extract the template from notepad.exe using:
DlgResToDlgTemplate.exe d:\windows\system32\notepad.exe 14 n.h
DlgResToDlgTemplate v1.0 (c) <lallousx86@yahoo.com>
generated n.h (1195 bytes) successfully!
- Now that we have the "n.h" which is the dialog template of the notepad.exe "Goto line" dialog, we can modify the sample code (mentioned above) as:
static unsigned char dlg_14[] =
{
0xc0,0x20,0xc8,0x80,0x00,0x00,0x00,0x00,
0x04,0x00,0x00,0x00,0x00,0x00,0xb4,0x00,0x39,
0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x6f,
0x00,0x74,0x00,0x6f,0x00,0x20,0x00,0x6c,
0x00,0x69,0x00,0x6e,0x00,0x65,0x00,0x00,
0x00,0x08,0x00,0x4d,0x00,0x53,0x00,0x20,
0x00,0x53,0x00,0x68,0x00,0x65,0x00,0x6c,
0x00,0x6c,0x00,0x20,0x00,0x44,0x00,0x6c,
0x00,0x67,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x02,0x50,0x00,0x00,0x00,0x00,0x07,
0x00,0x09,0x00,0x32,0x00,0x0a,0x00,0xb0,
0x04,0xff,0xff,0x82,0x00,0x26,0x00,0x4c,
0x00,0x69,0x00,0x6e,0x00,0x65,0x00,0x20,
0x00,0x4e,0x00,0x75,0x00,0x6d,0x00,0x62,
0x00,0x65,0x00,0x72,0x00,0x3a,0x00,0x00,
0x00,0x00,0x00,0x80,0x00,0x81,0x50,0x00,
0x00,0x00,0x00,0x37,0x00,0x07,0x00,0x39,
0x00,0x0c,0x00,0x02,0x01,0xff,0xff,0x81,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,
0x00,0x01,0x50,0x00,0x00,0x00,0x00,0x07,
0x00,0x22,0x00,0x32,0x00,0x0e,0x00,0x01,
0x00,0xff,0xff,0x80,0x00,0x4f,0x00,0x4b,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x01,0x50,0x00,0x00,0x00,0x00,0x4e,
0x00,0x22,0x00,0x32,0x00,0x0e,0x00,0x02,
0x00,0xff,0xff,0x80,0x00,0x43,0x00,0x61,
0x00,0x6e,0x00,0x63,0x00,0x65,0x00,0x6c,
0x00,0x00,0x00,0x00,0x00
};
void show_template_dlg()
{
::DialogBoxIndirectParam((HINSTANCE) ::GetModuleHandle(0),
(LPCDLGTEMPLATE) dlg_14, 0, (DLGPROC)dlgProc, 0);
}
Hope everything's clear so far. Next, we will see how practical it is to use the dialog templates to create small (self-contained) functions.
CWin32InputBox class overview
Now we can talk about the construction of the CWin32Inputbox
class, after having introduced all the concepts needed for this task.
In a nutshell, CWin32InputBox::InputBox()
:
- Defines a proper dialog procedure to handle the OK and CANCEL buttons.
- Defines a
WM_INITDIALOG
handler to properly initialize the dialog.
- Having the dialog template needed, named as "
definputbox_dlg
", we can:
- Call
DialogBoxIndirectParam()
to show the dialog.
- Do some actions based on the return value of the modal dialog.
The class exports the two static
methods:
static INT_PTR InputBoxEx(WIN32INPUTBOX_PARAM *);
static INT_PTR InputBox(LPCTSTR szTitle,
LPCTSTR szPrompt,
LPTSTR szResult,
DWORD nResultSize,
bool bMultiLine = false,
HWND hwndParent = 0);
You may simply call the InputBox
method as:
CWin32InputBox::InputBox("hello", "what?", buf, 100, false);
to produce something like:
The extended version uses the WIN32INPUTBOX_PARAM
structure to allow you to customize the input box. So to use this class in your project, all you have to do is simply add the "Win32InputBox.cpp/.h" to your project. No need for resources or anything.
You may freely use the code under the zlib/libpng license; check the "Win32InputBox.h" file.
Points of interest
It was fun learning about dialog templates and more fun to write the reusable CWin32InputBox
class. Hope you enjoyed and learned from this article. If you give me a low rating, I'd appreciate dropping me a comment and telling me how I can improve my article.
Before closing this article, I'd like to give a small tip for those who don't know: you can use your Visual Studio IDE to peek around other binary file resources'! Just press CTRL+O, locate a Win32 PE file, and open it!
References
- MSDN -> Win32 and COM Development -> Tools -> Platform SDK Tools -> SDK Tools -> Resource Tools -> Resource Compiler -> Resource Definition Statements.
- MSDN ->
CreateWindowEx()
function.
- MSDN -> Using Resources.
- MSDN ->
DLGTEMPLATEEX
structure.