Visual C Windows Development Tools

Chapter 21 - Procedure-oriented Windows Applications

Visual C++ 6: The Complete Reference
Chris H. Pappas and William H. Murray, III
  Copyright 1998 The McGraw-Hill Companies

An Application Framework
This section describes the various components that make up a program called SWP.C (simple windows program). The SWP.C program incorporates all of the Windows components minimally necessary to create and display a window (a main window with a border, a title bar, a system menu, and maximize/minimize boxes), draw a diagonal line, print a text message, and allow you to gracefully exit the application. You’ll also learn that the SWP.C program and its related files can serve as templates for future C or C++ Windows applications that you develop. Understanding code that is used over and over will save you time and help foster an understanding of how Windows applications are put together and why they work.
Table 21-1 summarizes frequently encountered data types that were discussed in Chapter 20.
Table 21-1: Frequently Encountered Win32 Types
Type
Description
CALLBACK
Replaces FAR PASCAL in application’s call back routine.
HANDLE
A 32-bit unsigned integer that is used as a handle.
HDC
A handle to a device context.
HWND
A 32-bit unsigned integer that is used as the handle to a window.
LONG
A 32-bit signed integer
LPARAM
Type used for declaration of lParam.
LPCSTR
LPCSTR is the same as LPSTR, but is used for read-only
string pointers.
LPSTR
A 32-bit pointer.
LPVOID
A generic pointer type. It is equivalent to (void *).
LRESULT
Used for the return value of a window procedure.
NULL
An integral zero value. It is frequently used to trigger default parameters or actions for a function.
UINT
An unsigned integer type. The host environment determines the size of UINT. For Windows 95 and NT, it is 32-bits.
WCHAR
A 16-bit UNICODE character. WCHAR is used to represent all of the symbols for all of the world’s languages.
WINAPI
Replaces FAR PASCAL in API declarations.
WPARAM
Used for the declaration of wParam.
There are a number of structures that are frequently encountered by Windows programmers. Table 21-2 will serve as a quick reference for these structures.
Table 21-2: Frequently Encountered Win32 Structures
Structure
Description
MSG
Defines the fields of an input message
PAINTSTRUCT
Defines the paint structure used when drawing inside a window
RECT
Defines a rectangle
WNDCLASS
Defines a window class
We’re now ready to examine the components of a Windows application using these tools.
Windows Application Components
Windows applications contain two common and essential elements, the WinMain( ) function and a window function. The main body of your application is named WinMain( ). WinMain( ) serves as the entry point for the Windows application and acts in a way similar to the way that the main( ) function in standard C or C++ programs work.
The window function, not to be confused with WinMain( ), has a unique role. Recall that a Windows application never directly accesses any window functions. When a Windows application attempts to execute a standard window function, it makes a request to Windows to carry out the specified task. For this reason, all Windows applications must have a call back window function. The call back function is registered with Windows and is called back whenever Windows executes an operation on a window.
The WinMain( ) Function
A WinMain( ) function is required by all Windows applications. This is the point at which program execution begins and usually ends. The WinMain( ) function is responsible for the following:
  Creating and initiating the application’s message processing loop (which accesses the program’s message queue)
  Performing any required initializations
  Registering the application’s window class type
  Terminating the program, usually upon receiving a WM_QUIT message
Four parameters are passed to the WinMain( ) function from Windows. The following code segment illustrates these required parameters as they are used in the SWP.C application:
int WINAPI WinMain(HINSTANCE hInst,HINSTANCE hPreInst,
                  LPSTR lpszCmdLine,int nCmdShow)
The first formal parameter to WinMain( ) is hInst, which contains the instance handle of the application. This number uniquely identifies the program when it is running under Windows.
The second formal parameter, hPreInst, will always contain a NULL indicating that there is no previous instance of this application.
  Note MS-DOS versions of Windows (Windows 3.3 and earlier) used hPreInst to indicate whether there were any previous copies of the program loaded. Under operating systems, such as Windows 95, 98, and NT, each application runs in its own separate address space. For this reason, under Windows 95, 98, and NT, hPreInst will never return a valid previous instance, just NULL.
The third parameter, lpszCmdLine, is a long pointer to a null-terminated string that represents the application’s command-line arguments. Normally, lpszCmdLine contains a NULL if the application was started using the Windows Run command.
The fourth and last formal parameter to WinMain( ) is nCmdShow. The int value stored in nCmdShow represents one of the many Windows predefined constants defining the possible ways a window can be displayed, such as SW_SHOWNORMAL, SW_SHOWMAXIMIZED, or SW_MINIMIZED.
WNDCLASS
The WinMain( ) function registers the application’s main window class. Every window class is based on a combination of user-selected styles, fonts, caption bars, icons, size, placement, and so on. The window class serves as a template that defines these attributes.
  Note Under earlier versions of Windows running over DOS, registered window classes became available to all programs running under Windows. For this reason, the programmer had to use caution when naming and registering classes to make certain that those names used did not conflict with any other application window classes. Windows 95, 98, and NT require that every instance (each copy of an application) must register its own window class.
Basically, the same standard C and C++ structure type is used for all Windows class definitions. The following example is taken directly from WINUSER.H, which is an #include file referenced in WINDOWS.H. The header file contains a typedef statement defining the structure type WNDCLASSW (a UNICODE-compatible definition), from which WNDCLASS is derived:
typedef struct tagWNDCLASSW {
   UINT        style;
   WNDPROC     lpfnWndProc;
   int         cbClsExtra;
   int         cbWndExtra;
   HANDLE      hInstance;
   HICON       hIcon;
   HCURSOR     hCursor;
   HBRUSH      hbrBackground;
   LPCWSTR     lpszMenuName;
   LPCWSTR     lpszClassName;
} WNDCLASSW,
*PWNDCLASSW, NEAR *NPWNDCLASSW, FAR *LPWNDCLASSW;
Windows provides several predefined window classes, but most applications define their own. To define a window class, your application must define a structure variable of the following type:
WNDCLASS wcApp;
The wcApp structure is then filled with information about the window class. The following sections describe the various fields within the WNDCLASS structure. Some of the fields may be assigned a NULL, directing Windows to use predefined values, while others must be given specific values.
Style
The style field names the class style. The styles can be combined with the bitwise OR operator. The style field is made up of a combination of the values shown in Table 21-3.
Table 21-3: Frequently Used Windows Styles
Value
Meaning
CS_BYTEALIGNCLIENT
Aligns a client area on a byte boundary
CS_BYTEALIGNWINDOW
Aligns a window on the byte boundary
CS_CLASSDC
Provides the window class a display context
CS_DBLCLKS
Sends a double-click message to the window
CS_GLOBALCLASS
States that the window class is an application
Global class:
CS_HREDRAW
Redraws the window when horizontal
size changes
CS_NOCLOSE
Inhibits the close option from the system menu
CS_OWNDC
Each window receives an instance for its own
Display context (DC):
CS_PARENTDC
Sends the parent window’s display context (DC)
To the window class:
CS_SAVEBITS
Saves that part of a screen that is covered by another window
CS_VREDRAW
Redraws the window when the vertical
size changes
lpfnWndProc
lpfnWndProc receives a pointer to the window function that will carry out all of the tasks for the window.
cbClsExtra
cbClsExtra gives the number of bytes that must be allocated after the window class structure. It can be set to NULL.
cbWndExtra
cbWndExtra gives the number of bytes that must be allocated after the window instance. It can be set to NULL.
hInstance
hInstance defines the instance of the application registering the window class. This must be an instance handle and cannot be set to NULL.
hIcon
hIcon defines the icon to be used when the window is minimized. This can be set to NULL.
hCursor
hCursor defines the cursor to be used with the application. This handle can be set to NULL. The cursor is valid only within the application’s client area.
hbrBackground
hbrBackground provides the identification for the background brush. This can be a handle to the physical brush or it can be a color value. Color values must be selected from one of the standard colors in the following list. A value of 1 must be added to the selected color.
COLOR_ACTIVEBORDER
COLOR_ACTIVECAPTION
COLOR_APPWORKSPACE
COLOR_BACKGROUND
COLOR_BTNFACE
COLOR_BTNSHADOW
COLOR_BTNTEXT
COLOR_CAPTIONTEXT
COLOR_GRAYTEXT
COLOR_HIGHLIGHT
COLOR_HIGHLIGHTTEXT
COLOR_INACTIVEBORDER
COLOR_INACTIVECAPTION
COLOR_MENU
COLOR_MENUTEXT
COLOR_SCROLLBAR
COLOR_WINDOW
COLOR_WINDOWFRAME
COLOR_WINDOWTEXT
If hbrBackground is set to NULL, the application paints its own background.
lpszMenuName
lpszMenuName is a pointer to a null-terminated character string. The string is the resource name of the menu. This item can be set to NULL.
lpszClassName
lpszClassName is a pointer to a null-terminated character string. The string is the name of the window class.
WNDCLASSEX
Windows offers an expanded definition for WNDCLASS named WNDCLASSEX that allows a small icon to be used for applications. Here is WNDCLASSEX structure:
typedef struct _WNDCLASSEX {
   UINT    style;
   WNDPROC lpfnWndProc;
   int     cbClsExtra;
   int     cbWndExtra;
   HANDLE  hInstance;
   HICON   hIcon;
   HCURSOR hCursor;
   HBRUSH  hbrBackground;
   LPCTSTR lpszMenuName;
   LPCTSTR lpszClassName;
   HICON   hIconSm;
} WNDCLASSEX;
You can see that these two structures are identical, except that WNDCLASSEX includes the hIconSm member, which is the handle of the small icon associated with a window class.
Predefined window classes are available, but most programmers define their own window class.
Defining a Window Class
An application can define its own window class by defining a structure of the appropriate type and then filling the structure’s fields with the information about the window class.
The following listing is from the SWP.C application and demonstrates how the WNDCLASS structure has been defined and initialized.
char szProgName[]="ProgName";
              .
              .
              .
WNDCLASS wcApp;
              .
              .
              .
wcApp.lpszClassName=szProgName;
wcApp.hInstance    =hInst;
wcApp.lpfnWndProc  =WndProc;
wcApp.hCursor      =LoadCursor(NULL,IDC_ARROW);
wcApp.hIcon        =NULL;
wcApp.lpszMenuName =szApplName;
wcApp.hbrBackground=GetStockObject(WHITE_BRUSH);
wcApp.style        =CS_HREDRAW|CS_VREDRAW;
wcApp.cbClsExtra   =0;
wcApp.cbWndExtra   =0;
if (!RegisterClass (&wcApp))
 return 0;
The SWP.C template application is assigned the generic name szProgName and is assigned to the window’s wcApp.lpszClassName.
The second field in WNDCLASS, wcApp.hInstance, is assigned the value returned in hInst after WinMain( ) is invoked. This indicates the current instance of the application. lpfnWndProc is assigned the pointer address to the window function that will carry out all of the window’s tasks. For the SWP.C application, the function is called WndProc( ).
  Note WndProc( ) is a user-defined function name—not a predefined function name. The function must be prototyped before the assignment statement.
The wcApp.hCursor field is assigned a handle to the instance’s cursor, which in this example is IDC_ARROW (representing the default tilted arrow cursor). This assignment is accomplished through a call to the LoadCursor( ) function. Since the SWP.C application has no default icon, wcApp.hIcon is assigned a value of NULL.
When wcApp.lpszMenuName is assigned a value of NULL, Windows understands that the class has no menu. If it did, the menu would have a name, which would appear within quotation marks. The GetStockObject( ) function returns a handle to a brush used to paint the background color of the client area of windows created from this class. For the SWP.C application, the function returns a handle to one of Windows predefined brushes; WHITE_BRUSH.
The wcApp.style window class style has been set to CS_HREDRAW or CS_VREDRAW. All window class styles have identifiers in WINUSER.H that begin with “CS_”. Each identifier represents a bit value. The bitwise OR operation | is used to combine these bit flags. The two parameters used (CS_HREDRAW and CS_VREDRAW) instruct Windows to redraw the entire client area whenever the horizontal or vertical size of the window is changed.
The last two fields, wcApp.cbClsExtra and wcApp.cbWndExtra, are frequently assigned 0. These fields are used to optionally indicate the count of extra bytes that may have been reserved at the end of the window class structure and the window data structure used for each window class.
From previous discussions about instances, you may recall that under earlier 16-bit versions of Windows, an application had to register a window class if it was the first instance or copy loaded. Here is a portion of code that was used for that purpose:
if (!hPreInst)
{
        .
        .
        .
  if (!RegisterClass (&wcApp))
    return FALSE;
}
Windows checks the number of instances by examining the hPreInst parameter, which will always be NULL, and then registers the class.
There are two if statements in the code segment. The first if takes care of filling the WNDCLASS structure when this is the first instance. The second if statement registers the new window class. It does this by sending RegisterClass( ) a long pointer to the window class structure. If Windows cannot register the window class, which can happen if sufficient memory is not available, RegisterClass( ) will return a 0, terminating the program.
Creating a Window
All windows are patterned after some predefined and registered class type. Defining and then registering a window class has nothing to do with actually displaying a window in a Windows application.
A window is created with a call to the CreateWindow( ) function. This process is common for all versions of Windows. While the window class defines the general characteristics of a window, allowing the same window class to be used for many different windows, the parameters for CreateWindow( ) specify more detailed information about the window. If the function call is successful, CreateWindow( ) returns the handle of the newly created window. Otherwise, the function returns a NULL value.
The parameter information for the CreateWindow( ) function falls under the following categories: the class, title, style, screen position, window’s parent handle, menu handle, instance handle, and 32 bits of additional information. For the SWP.C application, this function would take on the following appearance:
hWnd=CreateWindow(szProgName,"Simple Windows Program",
                 WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,
                 CW_USEDEFAULT,CW_USEDEFAULT,
                 CW_USEDEFAULT,(HWND)NULL,(HMENU)NULL,
                 (HANDLE)hInst,(LPSTR)NULL);
The first field, szProgName (assigned earlier), defines the window’s class, followed by the title to be used for the window’s title bar (Simple Windows Program). The style of the window is the third parameter (WS_OVERLAPPEDWINDOW). This standard Windows style represents a normal overlapped window with a caption bar; a system menu icon; minimize, maximize and terminate icons; and a window frame.
The next six parameters (either CS_USEDEFAULT or NULL) represent the initial x and y positions and x and y sizes of the window, along with the parent window handle and window menu handle. Each of these fields has been assigned a default value. The hInst field contains the instance of the program, followed by no additional parameters (NULL).
Showing and Updating a Window
Under Windows, the ShowWindow( ) function is needed to actually display a window. The following portion of code, from the SWP.C application, demonstrates this:
ShowWindow(hWnd,nCmdShow);
The handle of the window created by the call to CreateWindow( ) is held in the hWnd parameter. The second parameter to ShowWindow( ), nCmdShow, determines how the window is initially displayed. This display mode is also referred to as the window’s visibility state.
The nCmdShow parameter can specify that the window be displayed as a normal window (SW_SHOWNNORMAL) or in several other possible forms. For example, substituting nCmdShow with the WINUSER.H constant SW_SHOWMINNOACTIVE, as shown in the following line of code, causes the window to be drawn as an icon:
ShowWindow(hWnd,SW_SHOWMINNOACTIVE);
Other display possibilities include SW_SHOWMAXIMIZED, which causes the window to be active and fill the entire display, along with its counterpart, SW_SHOWMINIMIZED.
The final step in displaying a window requires a call to the Windows UpdateWindow( ) function:
UpdateWindow(hWnd);
A call to ShowWindow( ) with a SW_SHOWNORMAL parameter causes the function to erase the window’s client area with the background brush specified in the window’s class. It is the call to UpdateWindow( ) that generates the familiar WM_PAINT message, causing the client area to be painted.
The Message Loop
With everything in place, the application is ready to perform its main task: processing messages. Recall that Windows does not send input from the mouse or keyboard directly to an application. Windows places all input into the application’s message queue. The message queue can contain messages generated by Windows or messages posted by other applications.
The application needs a message-processing loop once the call to WinMain( ) has created and displayed the window. The most common approach is to use a standard while loop:
while (GetMessage(&lpMsg,NULL,0,0))
{
 TranslateMessage(&lpMsg);
 DispatchMessage(&lpMmsg);
}
The GetMessage( ) Function
The next message to be processed from the application’s message queue can be obtained with a call to the Windows GetMessage( ) function. GetMessage( ) copies the message into the message structure pointed to by the long pointer, lpMsg, and sends the message structure to the main body of the program.
The NULL parameter instructs the function to retrieve any of the messages for any window that belongs to the application. The last two parameters, 0 and 0, tell GetMessage( ) not to apply any message filters. Message filters can restrict retrieved messages to specific categories such as keystrokes or mouse moves. These filters are referred to as wMsgFilterMin and wMsgFilterMax and specify the numeric filter extremes to apply.
Control can be returned to Windows at any time before the message loop is begun. For example, an application will normally make certain that all steps leading up to the message loop have executed properly. This can include making sure that each window class is registered and has been created. However, once the message loop has been entered, only one message can terminate the loop. Whenever the message to be processed is WM_QUIT, the value returned is FALSE. This causes the processing to proceed to the main loop’s closing routine. The WM_QUIT message is the only way for an application to get out of the message loop.
The TranslateMessage( ) Function
Virtual-key messages can be converted into character messages with the TranslateMessage( ) function. The function call is required only by applications that need to process character input from the keyboard. This ability can be very useful because it allows the user to make menu selections without having to use the mouse.
The TranslateMessage( ) function creates an ASCII character message (WM_CHAR) from a WM_KEYDOWN and WM_KEYUP message. As long as this function is included in the message loop, the keyboard interface will also be in effect.
The DispatchMessage( ) Function
Windows sends current messages to
the correct window procedures with the DispatchMessage( ) function. This function makes it easy to add additional windows and dialog boxes to your application. DispatchMessage( ) automatically routes each message to the appropriate window procedure.
The Window Function
Recall that all applications must include a WinMain( ) function and a Windows call back function. Since a Windows application never directly accesses any Windows function, each application must make a request to Windows to carry out any specified operation.
A call back function is registered with Windows and is called back whenever Windows executes an operation on a window. The length of the actual code for the call back function will vary with each application. The window function itself may be very small, processing only one or two messages, or it may be large and complex.
The following code segment (minus application-specific statements) shows the call back window function WndProc( ) as it is used in the SWP.C application:
LRESULT CALLBACK WndProc(HWND hWnd,UINT messg,
                        WPARAM wParam,LPARAM lParam)
{
 HDC hdc;
 PAINTSTRUCT ps;
 
 switch (messg)
 {
   case WM_PAINT:
     hdc=BeginPaint(hWnd,&ps);
             .
             .
             .
     VailidateRect(hWnd,NULL);
     EndPaint(hWnd,&ps);
     break;

   case WM_DESTROY:
     PostQuitMessage(0);
     break;

   default:
     return(DefWindowProc(hWnd,messg,wParam,lParam));
 }
 return(0);
}
Windows expects the name referenced by the wcApp.lpfnWndProc field of the window class structure definition to match the name used for the call back function. WndProc( ) will be the name used for the call back function for all subsequent windows created from this window class.
The following code segment reviews the placement and assignment of the call back function’s name within the window class structure:
   .
   .
   .
wcApp.lpszClassName=szProgName;
wcApp.hInstance    =hInst;
wcApp.lpfnWndProc  =WndProc;
    .
    .
    .
Windows has several hundred different Windows messages that it can send to the window function. These messages are labeled with identifiers that begin with “WM_”. For example, WM_CREATE, WM_SIZE, and WM_PAINT are used quite frequently. These identifiers are also known as symbolic constants.
The first parameter to WndProc( ) is hWnd. hWnd contains the handle to the window to which Windows will send the message. Since it is possible for one window function to process messages for several windows created from the same window class, this handle is used by the window function to determine which window is receiving the message.
The second parameter to the function, messg, specifies the actual message being processed as defined in WINUSER.H. The last two parameters, wParam and lParam, specify any additional information needed to process each specific message. Frequently, the value returned to each of these parameters is NULL. This means that they can be ignored. At other times, the parameters contain a 2-byte value and a pointer, or two word values.
The WndProc( ) function continues by defining two variables: hdc specifies the display context handle, and ps specifies a PAINTSTRUCT structure needed to store client area information.
The call back function is used to examine the type of message it is about to process and then select the appropriate action to be taken. This selection process usually takes place within a standard C switch statement.
Processing WM_PAINT Messages
The first message that WndProc( ) will process in this template is WM_PAINT. This message calls the Windows function BeginPaint( ), which prepares the specified window for painting and fills a PAINTSTRUCT (&ps) with information about the area to be painted. The BeginPaint( ) function also returns a handle to the device context for the given window.
Because Windows is a multitasking operating system, it is possible for one application to display its window or dialog box over another application’s client area. This creates a problem whenever the window or dialog box is closed: a hole appears on the screen where the dialog box was displayed. Windows handles this problem by sending the active application a WM_PAINT message. In this case, Windows requests that the active application update its client area.
Except for the first WM_PAINT message, which is sent by the call to UpdateWindow( ) in WinMain( ), additional WM_PAINT messages are sent under the following conditions:
  When forcing a WM_PAINT message with a call to the InvalidateRect( ) or InvalidateRgn( ) function
  When resizing a window
  When using the ScrollWindow( ) function
  Whenever a portion of a client area has been hidden by a menu or dialog box that has just been closed
Here is how the process works. Any portion of an application’s client area that has been corrupted by the overlay of a dialog box, for example, has that area of the client area marked as invalid. Windows makes the redrawing of a client area efficient by keeping track of the diagonal coordinates of this invalid rectangle. It is the presence of an invalid rectangle that prompts Windows to send the WM_PAINT message.
If several portions of the client area are invalidated, Windows will adjust the invalid rectangle coordinates to encapsulate all invalid regions. In other words, Windows does not send a WM_PAINT message for each invalid rectangle.
The call to InvalidateRect( ) allows Windows to mark the client area as invalid, thereby forcing a WM_PAINT message. An application can obtain the coordinates of the invalid rectangle by calling the GetUpdateRect( ) function. A call to the ValidateRect( ) function validates any rectangular region in the client area and deletes any pending WM_PAINT messages.
The EndPaint( ) function is called when the WndProc( ) function ends its processing of the WM_PAINT messages. This function is called whenever the application is finished outputting information to the client area. It tells Windows that the application has finished processing all paint messages and that it is now OK to remove the display context.
Processing the WM_DESTROY Message
When the Close option is selected by the user from an application’s system menu, Windows posts a WM_DESTROY message to the application’s message queue. The application terminates after it retrieves this message.
The DefWindowProc( ) Function
The DefWindowProc( ) function call, in the default section of WndProc( )’s switch statement, is needed to empty the application’s message queue of any unrecognized and/or unprocessed messages. This function ensures that all of the messages posted to the application are processed.
A Module Definition File
As you learned earlier, LINK provides a command-line equivalent to the module definition files once required by all Windows applications. A module definition file can be used to provide the linker with definitions and descriptive information so that it knows how to organize the application’s executable file for Windows. This information becomes part of the header section of the New Executable file format.
  Note Under Windows 95, 98, and NT, it is very unlikely that you will have to create a module definition file. This information is provided for completeness and backward compatibility.
A module definition file for the SWP.C application might take on the following appearance:
NAME        swp
DESCRIPTION ‘Simple Windows Program’
EXETYPE     WINDOWS
CODE        PRELOAD MOVEABLE DISCARDABLE
DATA        PRELOAD MOVEABLE MULTIPLE
HEAPSIZE    4096
EXPORTS     WndProc      @1
The name statement defines SWP.C as a Windows program (not a dynamic link library) and gives the module a name. This name should be the same name as the program’s executable (.EXE) file.
The description line copies the text into the executable file. Often this is used to embed added information such as a release date, version number, or copyright notice.
The exetype refers to the type of executable file to create.
Both the code and data segments have been marked as preloadable and moveable, allowing Windows to relocate them for any dynamic memory allocation requests. The multiple statement also instructs Windows to create unique data segments for each instance of the application. The use of discardable allows Windows to discard unused program code. This code can be automatically reloaded if necessary.
The heapsize statement specifies an amount of extra, expandable, local memory from within the application’s data segment. The stacksize has been set to 9216. You can experiment with various sizes. Larger values may be necessary for applications with large non-static variables or those applications using recursion.
Finally, the exports statement identifies the application’s dynamic link entry point and specifies the name of the procedure, in this case, WndProc.

Books24x7.com, Inc 2000 –  


Visual C++ 6(c) The Complete Reference
Visual Studio 6: The Complete Reference
ISBN: B00007FYGA
EAN: N/A
Year: 1998
Pages: 207

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net