The computer is a machine that only follows
instructions. It almost doesn't know anything. Because of this, the
computer cannot predict what a user wants to do with the computer. In
fact, a great deal of the responsibility is left to the programmer who
must decide what can and cannot or should not be done on an application. To help
the users with computer interaction, the operating system provides a
series of objects called Windows controls. The programmer decides what
objects are necessary for a given application.
Each computer application is equipped with Windows
controls that allow the user to interact with the computer. Because the
computer cannot and would not predict what the user wants to do when using
the computer, the operating system lets each object tell it when it needs
something from Windows. To do this, a control sends a message to
the operating system every time something is new. Because there can be so many
messages a control can send and because many controls can send various
messages, there is a formula each message or almost every one of them must
follow, just like there are rules the post office wants you to follow in
order to send a letter.
A message to Windows must provide four pieces of
information:
WHO sent the message? Every object you will need in your program,
just like everything in the computer, must have a name. The operating
system needs this name to identify every object, for any reason. An
object in Microsoft Windows is identified as a Handle. For Windows
controls, the handle is called HWND WHAT message? The object that sends a message must let the operating
system know what message it is sending. As we will learn, there are
various types of messages for different circumstances. Nevertheless,
to make matters a little easier, each message is a constant positive
natural number (unsigned int) identified with a particular name.
Therefore, the message identifier is passed as UINT Accompanying items: Because there are so many types of messages, you
must provide two additional pieces of information to help process the
message. These two items depend on the type of message and could be
anything. The first accompanying item is a 32-bit type (unsigned int)
called WPARAM (stands for WORD Parameter; in other
words, it is a WORD (unsigned int) argument). The second accompanying item is a 32-bit type
of value (long) calle LPARAM (stands for LONG Parameter; in
other words, it is a LONG (long in C/C++) argument). Remember that these two can be different things
for different messages.
To manage the messages sent to Windows, they are
communicated through a function pointer called a Windows procedure. The
name of the function is not important but it must return a 32-bit integer,
in fact a C/C++ long or Win32 LONG.
Therefore, it is declared as LRESULT (LONG Result). Because this is a function pointer,
it must be declared and defined as CALLBACK . The messages can
be carried in a function defined as follows:
LRESULT CALLBACK MessageProcedure (HWND hWnd , UINT uMsg , WPARAM wParam , LPARAM lParam );
To process the messages, and because there can be so
many of them, this function typically uses a switch control to list
all necessary messages and process each one in turn. After processing a
message, its case must return a value indicating that the message was
successfully processed or not.
No matter how many messages you processed, there will
still be messages that you did not deal with. It could be because they
were not sent even though they are part of the Windows controls used on an
application. If you didn't process some messages, you should/must let the
operating system know so it can take over. What happens is that the
operating system is aware of all messages and it has a default behavior or
processing for each one of them. Therefore, you should/must return a value
for this to happen. The value returned can be placed in the default
section of the switch condition and must simply be a DefWindowProc()
function. Its syntax is:
LRESULT DefWindowProc(HWND hWnd , UINT uMsg , WPARAM wParam , LPARAM lParam );
This function is returned to Windows, saying
"There are messages I couldn't process. Do what you want with
them". The operating system would simply apply a default processing
to them. The values returned by the DefWindowProc() function should be the
same passed to the procedure.
The most basic message you can process is to make sure
a user can close a window after using it. This can be done with a function
called PostQuitMessage() . Its syntax is:
VOID PostQuitMessage(int nExitCode )
This function takes one argument which is the value of
the LPARAM argument. To close a window, you can pass the argument as
WM_QUIT .
Based on this, a simple Windows procedure can be
defined as follows:
LRESULT CALLBACK WndProcedure(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
switch(Msg)
{
case WM_DESTROY:
PostQuitMessage(WM_QUIT);
break;
default:
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
return 0;
}
<img>
A basic program with one message can be written as
follows:
//---------------------------------------------------------------------------
#include <windows.h>
//---------------------------------------------------------------------------
HWND hWnd;
const char ClsName[] = "WndMsg";
const char WindowCaption[] = "Windows and Controls Messages";
LRESULT CALLBACK WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
//---------------------------------------------------------------------------
INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
MSG Msg;
WNDCLASSEX WndClsEx;
WndClsEx.cbSize = sizeof(WNDCLASSEX);
WndClsEx.style = CS_HREDRAW | CS_VREDRAW;
WndClsEx.lpfnWndProc = WndProc;
WndClsEx.cbClsExtra = NULL;
WndClsEx.cbWndExtra = NULL;
WndClsEx.hInstance = hInstance;
WndClsEx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
WndClsEx.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClsEx.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
WndClsEx.lpszMenuName = NULL;
WndClsEx.lpszClassName = ClsName;
WndClsEx.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
RegisterClassEx(&WndClsEx);
hWnd = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,
ClsName,
WindowCaption,
WS_OVERLAPPEDWINDOW,
100,
120,
640,
480,
NULL,
NULL,
hInstance,
NULL);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
while( GetMessage(&Msg, NULL, 0, 0) )
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}
//---------------------------------------------------------------------------
LRESULT CALLBACK WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
switch(Msg)
{
case WM_DESTROY:
PostQuitMessage(WM_QUIT);
break;
default:
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
return 0;
}