Analyzing a Simple Windows-Based Application
In this section, you will examine the source code for a simple SDK-level Windows-based application. Since MFC applications are based on the Windows programming model, this will help you better understand how an MFC application is initialized, how windows get created, and how messages are handled.
All Windows-based applications must contain two functions, at a minimum, to provide functionality for the application as follows:
® The WinMain function
This function defines the initial entry point to the application from the operating system. It is also used to create the initial window and to start a message pump.
® A window procedure function
This function processes all messages sent from the operating system to a window. Every window, no matter how simple or how complex, has an associated window procedure.
Let’s look at a simple application so you can see what is needed to create a minimal Windows-based application. This sample application simply displays a window and draws “Hello, world!” in the center of its viewing area. To see the complete code for the sample application, click this icon.
// Standard Windows header file.
#include <windows.h>
// Forward declaration for the message handler.
LRESULT CALLBACK MyWndProc(HWND, UINT, WPARAM, LPARAM);
// WinMain: required for all Windows applications.
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpszCmdLine, int nCmdShow)
{
char szApplicationName[] = “Simple Hello App”;
HWND hwnd;
MSG msg;
// Window class data structure
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW; // style: causes repaint when resizing
wc.lpfnWndProc = (WNDPROC) MyWndProc; // the window procedure
wc.cbClsExtra = 0; // number of extra bytes following the class structure
wc.cbWndExtra = 0; // number of extra bytes following the window instance
wc.hInstance = hInstance; // this instance that the window procedure is within
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); // the class icon
wc.hCursor = LoadCursor(NULL, IDC_ARROW); // the class cursor
wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);// background brush
wc.lpszMenuName = NULL; // the menu resource name
wc.lpszClassName = szApplicationName; // the app name
// Register the window.
RegisterClass(&wc);
// Now create the window.
hwnd = CreateWindow(
szApplicationName, // registered class name
szApplicationName, // window name
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT, // x position of window
CW_USEDEFAULT, // y position of window
CW_USEDEFAULT, // width of window
CW_USEDEFAULT, // height of window
HWND_DESKTOP, // handle to parent window
NULL, // handle to menu identifier
hInstance, // handle to application instance
NULL // pointer to window-creation data
);
// Display the window.
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// Pump messages until a quit message is received.
while(GetMessage(&msg, NULL, 0,0))
{
TranslateMessage(&msg); // translates virtual-key messages into character messages
DispatchMessage(&msg); // dispatches a message to the window procedure
}
return msg.wParam;
}
// This function is called by Windows to handle messages for this application.
LRESULT CALLBACK MyWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
LPCTSTR text= “Hello, world!”;
switch(message)
{
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
// Draw the text approximately in the middle of the window.
// (The upper left corner of the string will be centered.)
RECT rect;
GetClientRect(hwnd, &rect);
TextOut(hdc, (rect.right-rect.left)/2, (rect.bottom-rect.top)/2, text, strlen(text));
EndPaint(hwnd, &ps);
return 0;
// Handle the Exit situation
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
// Call the default handler so that all messages are processed.
return DefWindowProc(hwnd, message, wParam, lParam);
}
Note To run this application, create a new Win32 application, and then add a new .cpp file. Cut and paste this code into it, and then build and run the application.
In this section, we will focus on the segments of code in the sample application that pertain to all Windows-based applications. This section includes the following topics:
® The WinMain Function
Every Windows-based application must contain a WinMain function. WinMain is required to perform three basic tasks:
1. Register the class of the window with the operating system.
2. Create the window in memory and initialize its attributes so it can be displayed.
3. Create a message loop that checks to see if there are any messages for the window in its message queue.
Each of these tasks is described in detail below.
Registering the Window Class
The entry point for every window application is the WinMain function. The WinMain function creates and initializes a WNDCLASS data structure, which is then registered by calling the API function RegisterClass. This data structure defines characteristics of a window, such as the address of the message handler, the window’s background color, the application icon, and the default cursor.
// Window structure
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW; // style: causes repaint when resizing
wc.lpfnWndProc = (WNDPROC) MyWndProc; // the window procedure
wc.cbClsExtra = 0; // number of extra bytes following the class structure
wc.cbWndExtra = 0; // number of extra bytes following the window instance
wc.hInstance = hInstance; // this instance that the window procedure is within
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); // the class icon
wc.hCursor = LoadCursor(NULL, IDC_ARROW); // the class cursor
wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1); // background brush
wc.lpszMenuName = NULL; // the menu resource name
wc.lpszClassName = szApplicationName; // the app name
// Register the window.
RegisterClass(&wc);
Creating and Displaying the Window
Once the window class is registered, WinMain calls the CreateWindow function to create the application’s window. CreateWindow further defines the type of window by passing information about its name, location, and size.
// Now create the window.
hwnd = CreateWindow(
szApplicationName, // registered class name
szApplicationName, // window name
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT, // x position of window
CW_USEDEFAULT, // y position of window
CW_USEDEFAULT, // width of window
CW_USEDEFAULT, // height of window
HWND_DESKTOP, // handle to parent window
NULL, // handle to menu identifier
hInstance, // handle to application instance
NULL // pointer to window-creation data
);
The window is then made visible by calling the following functions:
// Display the window.
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
Processing Messages
Finally, the message loop retrieves and dispatches messages. This is accomplished by using a while loop.
// Pump messages until a quit message is received.
while(GetMessage(&msg, NULL, 0,0))
{
TranslateMessage(&msg); // translates virtual-key messages into character messages
DispatchMessage(&msg); // dispatches a message to the window procedure
}
The GetMessage function retrieves a message from the message queue and places it in the MSG data structure. The TranslateMessage function translates virtual-key messages into character messages. The DispatchMessage function dispatches a message to a window procedure.
On receiving a WM_QUIT message, the GetMessage function returns 0, the WinMain function ends, and the program terminates.
® The Window Procedure
One of the primary purposes of registering a window class is to associate a window to a “window procedure.” The window procedure determines what the window displays in its client area and how the window responds to user input. Window procedures can handle messages with code added by the developer, or by passing messages along to the default window procedure.
The default window procedure, DefWindowProc, is provided by the Windows system and implements many common Win32-based application behaviors, such as minimizing, restoring, or maximizing a window, displaying menu resources, and so on. If DefWindowProc does not handle a message, it is ignored.
In the sample program, the window procedure is called MyWndProc. A window procedure can have any name (as long as it doesn’t conflict with some other name, of course). A Windows-based application can contain more than one window procedure, each with a different name.
Typically, you use a switch and case statement to handle the message as shown in the following code. The MyWndProc function handles two messages, WM_PAINT and WM_DESTROY. The DefWindowProc window procedure must be called to handle all other messages.
// This function is called by Windows to handle messages for this application.
LRESULT CALLBACK MyWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
LPCTSTR text= “Hello, world!”;
switch(message)
{
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
// Draw the text approximately in the middle of the window.
// (The upper left corner of the string will be centered.)
RECT rect;
GetClientRect(hwnd, &rect);
TextOut(hdc, (rect.right-rect.left)/2, (rect.bottom-rect.top)/2, text, strlen(text));
EndPaint(hwnd, &ps);
return 0;
// Handle the Exit situation
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
// Call the default handler so that all messages are processed.
return DefWindowProc(hwnd, message, wParam, lParam);
}
® Windows Functions and MFC
Since MFC applications are based on the Windows development model, they depend on the functionality provided by the WinMain function and window procedure functions.
The WinMain Function and MFC
As with all Windows-based programs, MFC applications have a WinMain function. In an MFC application, however, you don’t write WinMain. It is supplied by the framework and is called when the application starts up. For more information, see Classes in a Minimal MFC Application in Chapter 4.
The Window Procedure and MFC
MFC has an internal messaging system that handles most messages generated by the classes. However, when a message cannot be handled within MFC, the application relies on the default window procedure, DefWindowProc, to handle messages. For more information, see How MFC Processes Messages in Chapter 6.
Self-Check Questions
1. Which of the following statements is true about Windows architecture?
A. A process can have zero or more threads.
B. Each process is allocated 1 MB of virtual memory on startup, and additional 64K blocks as required.
C. Under Win32 multitasking, the highest priority thread will always have unconditional access to the
processor.
D. Keyboard and mouse events are handled asynchronously.
2. Which of the following is not a standard Win32 GUI resource type?
A. Version Information
B. Device Context
C. Accelerator
D. Cursor
3. Which of the following is a required function for all Windows-based applications?
A. WinMain
B. WndMgr
C. MyWndProc
D. GetMenu
4. When WinMain calls the API RegisterClass function, the following data structure is registered:
A. MSG
B. WNDCLASS
C. PAINTSTRUCT
D. RECT


