PMGuide - Window Procedures
Reprint Courtesy of International Business Machines Corporation, © International Business Machines Corporation
Windows have an associated window procedure-a function that processes all messages sent or posted to a window. Every aspect of a window's appearance and behavior depends on the window procedure's response to the messages. This chapter explains how window procedures function, in general, and describes the default window procedure.
About Window Procedures
Every window belongs to a window class that determines which window procedure a particular window uses to process its messages. All windows of the same class use the same window procedure. For example, the operating system defines a window procedure for the frame window class (WC_FRAME), and all frame windows use that window procedure.
An application typically defines at least one new window class and an associated window procedure. Then, the application can create many windows of that class, all of which use the same window procedure. This means that the same piece of code can be called from several sources simultaneously; therefore, you must be careful when modifying shared resources from a window procedure.
Dialog procedures have the same structure and function as window procedures. The primary difference between a dialog procedure and a window procedure is the absence of a client window in the dialog procedure; that is, the controls in a dialog procedure are the immediate child windows of the frame, whereas the controls in a normal window are the grandchildren of the frame. This makes significant differences in the code between the two; for example, WinSendDlgItemMsg does not work from a client window if you pass the client window handle as the first parameter.
Structure of a Window Procedure
A window procedure is a function that takes 4 arguments and returns a 32-bit pointer. The arguments of a window procedure consist of a window handle, a ULONG message identifier, and two arguments, called message parameters, that are declared with the MPARAM data type. The system defines an MPARAM as a 32-bit pointer to a VOID data type (a generic pointer). The message parameters actually might contain any of the standard data types. The message parameters are interpreted differently,depending on the value of the message identifier. The operating system includes several macros that enable the application to cast the information from the MPARAM values into the actual data type. SHORT1FROMMP, for example, extracts a 16-bit value from a 32-bit MPARAM. The window-procedure arguments are described in the following table:
┌───────────────┬─────────────────────────────────────────────┐ │Argument │Description │ ├───────────────┼─────────────────────────────────────────────┤ │hwnd │Handle of the window receiving the message. │ ├───────────────┼─────────────────────────────────────────────┤ │msg │Message identifier. The message will │ │ │correspond to one of the predefined constants│ │ │(for example, WM_CREATE) defined in the │ │ │system include files or be an │ │ │application-defined message identifier. The │ │ │value of an application-defined message │ │ │identifier must be greater than the value of │ │ │WM_USER, and less than or equal to 0xffff. │ ├───────────────┼─────────────────────────────────────────────┤ │mp1,mp2 │Message parameters. Their interpretation │ │ │depends on the particular message. │ └───────────────┴─────────────────────────────────────────────┘
The return value of a window procedure is defined as an MRESULT data type. The interpretation of the return value depends on the particular message. Consult the description of each message to determine the appropriate return value.
Default Window Procedure
All windows in the system share certain fundamental behavior, defined in the default window-procedure function, WinDefWindowProc. The default window procedure provides the minimal functionality for a window. An application-defined window procedure should pass any messages it does not process to WinDefWindowProc for default processing.
Window-Procedure Subclassing
Subclassing enables an application to intercept and process messages sent or posted to a window before that window has a chance to process them. Subclassing most often is used to add functionality to a particular window or to alter a window's default behavior.
An application subclasses a window by using the WinSubclassWindow function to replace the window's original window procedure with an application-defined window procedure. Thereafter, the new window procedure processes any messages that are sent or posted to the window. If the new window procedure does not process a particular message, it must pass the message to the original window procedure, not to WinDefWindowProc, for default processing.
Using Window Procedures
This section explains how to:
- Design a window procedure
- Associate a window procedure with a window class
- Subclass a window
Designing a Window Procedure
The following code fragment shows the structure of a typical window procedure and how to use the message argument in a switch statement, with individual messages handled by separate case statements. Notice that each case returns a specific value for each message. For messages that it does not handle itself, the window procedure calls WinDefWindowProc.
MRESULT ClientWndProc( HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2) { /* Define local variables here, if required. */ switch (msg) { case WM_CREATE: /* Initialize private window data. */ return (MRESULT) FALSE; case WM_PAINT: /* Paint the window. */ return 0; case WM_DESTROY: /* Clean up private window data. */ return 0; default: break; } return WinDefWindowProc (hwnd, msg, mp1, mp2); }
A dialog window procedure does not receive the WM_CREATE message; however, it does receive a WM_INITDLG message when all of its control windows have been created. At the very least, a window procedure should handle the WM_PAINT message to draw itself. Typically, it should handle mouse and keyboard messages as well. Consult the descriptions of individual messages to determine whether your window procedure should handle them.
An application can call WinDefWindowProc as part of the processing of a message. In such a case, the application can modify the message parameters before passing the message to WinDefWindowProc or can continue with the default processing after performing its own operations.
Associating a Window Procedure with a Window Class
To associate a window procedure with a window class, an application must pass a pointer to that window procedure to the WinRegisterClass function. Once an application has registered the window procedure, the procedure automatically is associated with each new window created with that class.
The following code fragment shows how to associate the window procedure in the previous example with a window class:
HAB hab; CHAR szClientClass[] = "My Window Class"; WinRegisterClass(hab, /* Anchor-block handle */ szClientClass, /* Class name */ ClientWndProc, /* Pointer to procedure */ CS_SIZEREDRAW, /* Class style */ 0); /* Window data */
Subclassing a Window
To subclass a window, an application calls the WinSubclassWindow function, specifying the handle of the window to subclass and a pointer to the new window procedure. The WinSubclassWindow function returns a pointer to the original window procedure; the application can use this pointer to pass unprocessed messages to the original procedure. The following code fragment subclasses a push button control window. The new window procedure generates a beep whenever the user clicks the push button.
PFNWP pfnPushBtn; CHAR szCancel[] = "Cancel"; HWND hwndClient; HWND hwndPushBtn; . . . /* Create a push button control. */ hwndPushBtn = WinCreateWindow( hwndClient, /* Parent-window handle */ WC_BUTTON, /* Window class */ szCancel, /* Window text */ WS_VISIBLE | /* Window style */ WS_SYNCPAINT | /* Window style */ BS_PUSHBUTTON, /* Button style */ 50, 50, /* Physical position */ 70, 30, /* Width and height */ hwndClient, /* Owner-window handle */ HWND_TOP, /* Z-order position */ 1, /* Window identifier */ NULL, /* No control data */ NULL); /* No presentation parameters */ /* Subclass the push button control. */ pfnPushBtn = WinSubclassWindow(hwndPushBtn, SubclassPushBtnProc); . . . } /* This procedure subclasses the push button. */ MRESULT EXPENTRY SubclassPushBtnProc(HWND hwnd,ULONG msg,MPARAM mp1, MPARAM mp2) { switch (msg) { /* Beep when the user clicks the push button. */ case WM_BUTTON1DOWN: DosBeep(1000, 250); break; default: break; } /* Pass all messages to the original window procedure. */ return (MRESULT) pfnPushBtn(hwnd, msg, mp1, mp2); }