The WndProc Procedure

The WndProc procedure—by whatever name you prefer—is the point where each application actually begins to function. Remember, the WndProc procedure receives messages indirectly from the operating system but determines the application’s response to the messages received.

Previously, when the application window class was registered, the address of the WndProc subroutine was passed to Windows as:

wc.lpfnWndProc = WndProc;

And, given this address, Windows calls WndProc directly, passing event messages in the form of four parameters, as:

long FAR PASCAL WndProc( HWND hwnd,   UINT msg,
                         UINT wParam, LONG lParam )

The four calling parameters received correspond to the first four fields of the MSG structure described in the previous section, beginning with the hwnd parameter identifying the window to which the message is directed. Because most applications have only one client window that will receive the message, this parameter may seem superfluous. This parameter will, however, frequently be needed as an argument for use by other processes.

At the present, it’s the second calling parameter, msg, that is immediately crucial and identifies the window event message. The third and fourth parameters, wParam and lParam, provide amplifying information to accompany the window event message.

Typically, the WndProc procedure does relatively little or nothing outside the switch...case responding to the msg parameter. In the WinHello demo, local response is provided for only two event messages: the WM_PAINT and WM_DESTROY messages. All other event messages are handled by default (by the operating system).

The first of these two, WM_PAINT, is a message that is generally not issued directly. It will be issued indirectly anytime an application window is created, moved, resized, restored from an icon, or uncovered by a change in some other application window; it will also be issued indirectly when something occurs—in this or in some other application—to invalidate the client area of the present application.

The DOS equivalent of the WinHello program would consist principally of a print statement, possibly with an optional clear screen statement. For the Windows version, however, there are two main reasons for the differences:

Before anything can be written to the client window, the first requirement is for the application to retrieve a handle (hdc) to the device context (the output device or, in this example, the screen). After the screen update is finished, this handle will be released by calling the EndPaint function.

   switch( msg )
   {
      case WM_PAINT:
         hdc = BeginPaint( hwnd, &ps );
         GetClientRect( hwnd, &rect );

After retrieving the device-context handle, the GetClientRect procedure is called to retrieve the rect structure with coordinates describing the client window. The rect structure consists of four fields, which report coordinates for the client window. However, the coordinates reported are relative to the client window itself. Therefore, the left and top fields are returned as zeros, and the right and bottom fields return the current width and height of the client window (reported in pixels).

Once the window coordinates have been retrieved, the rect structure can be used as an argument in the next step to specify the region where the actual message will be drawn.

         DrawText( hdc, “Hello, World!”, -1, &rect,
                   DT_SINGLELINE | DT_CENTER | DT_VCENTER );

Since print statements, per se, cannot be used in Windows (because they are unsuited for a graphics display environment), the DrawText function is used instead. DrawText begins with the hdc argument providing access to the active display, followed by the string (text) to be drawn.

The third parameter, -1, indicates that the string argument is a null-terminated string. Alternatively, this parameter could be a value specifying the string length, with the second parameter an indirect reference to a character array.

The fourth argument is the address of the rect structure, identifying an area where the string will be drawn. The fifth argument is a combination of flags that set alignment and restrict the text drawn to a single display line. Other elements affecting the display, such as font, size, and color, use the system default settings (although these factors are subject to change, as you will see in future demos).

NOTE

The sprintf statement can still be used to format text to a buffer array, but direct screen print statements are not allowed.

Last, the EndPaint function is called, again with the client window handle and the paint structure (ps) as arguments. This function releases the device context and validates the now-restored client area, and incidentally, completes the response to the WM_PAINT message.

         EndPaint( hwnd, &ps );
         return( 0 );

The second application message requiring a local response is the WM_DESTROY message, which is issued when the application is ready to close. This message can be generated via several channels, as will be shown later, but for our example, it is issued only if or when the system menu Close option is selected.

      case WM_DESTROY:
         PostQuitMessage(0);
         break;

The WM_DESTROY message is issued to give the application an opportunity to do any necessary cleanup before shutting down. Therefore, as circumstances demand, the application response at this point could include provisions for calling a dialog box to request confirmation, for closing or saving files, or for any other final tasks required for a smooth exit.

Finally (unless, of course, termination is to be aborted), the WM_DESTROY response is completed by calling the PostQuitMessage function that, in turn, places a WM_QUIT message in the application’s message queue to terminate the message loop in WinMain.

Explicit handling has been provided for only two of the messages that might be sent to this application. As a default case, provisions are also required to return all messages that have not been explicitly handled here to Windows for processing.

      default:                   // if message unprocessed,
         return(                 // return to Windows
            DefWindowProc( hwnd, msg, wParam, lParam ) );
   }
   return( NULL );
}

This default provision returns the message—precisely as it was originally received—to Windows, and then it also returns the result from DefWindowProc to the Windows calling process. This final provision should be considered standard for all WndProc message-handler procedures.

© 1998 SYBEX Inc. All rights reserved.