[ Next ] [ Previous ] | Chapter 9 |
The basic building block for all Presentation Manager (PM) programming is a window. Most items displayed on the screen are windows, of some shape or fashion. A window is designed to react to messages sent to it either from the system or from another window. These messages are placed into a message queue that is unique to each PM application. A message is used to signal events that happen to a window. For example, a WM_CREATE message is sent when a window is halfway through its creation process; a WM_SlZE message is sent after the user has sized the window; a WM_DESTROY message just before the destruction of the window is complete. Each window has a specific window procedure that is used to respond back to the system when a message is sent. The programmer is responsible for creating this window procedure. The window procedure is a switch statement that will filter out certain messages that are of interest to the application. The messages that are not interesting can be passed on to a default window procedure or a default dialog procedure. For instance, the programmer may want to initialize some data in the WM_CREATE message processing or free up memory when the WM_DESTROY is received.
The first thing to understand when beginning Presentation Manager programming is the concept of a window. A window is a graphical image of a rectangle that sits on the screen and is used to provide a uniform interface with which a user can interact. (See Figure 9.1.)
![]() |
A window can be sized larger or smaller, it can be opened or closed, it can be made visible or invisible. Suffice it to say that there are a lot of things to do with a window. |
![]() Figure 9.2 Drawing of a window's components |
Figure 9.2 looks like one window but, in reality,
it is seven windows:
|
Each of the five windows has a window procedure associated with it. In most cases, the programmer will be able to use the system-defined window procedures for all but the client window. The window procedure is a function that tells the window how to behave. Windows that share the same window procedure belong to the same window class. This is a familiar concept for those readers acquainted with object-oriented programming.
Imagine a fast food restaurant. Each item on the menu could be considered one class - a hot dog class, a hamburger class, and a pizza class. Suppose mustard, mayo, relish, or cheese could be put on a hot dog, in any combination. Each of these condiments would he a hot dog style.
The same is true for window classes. There are many predefined window
classes, including some classes specific to pen computing and the multimedia
extensions. The classes specific to Presentation Manager are:
Symbolic constant | Meaning |
WC_FRAME | The Frame control class |
WC_COMBOBOX | Combo box control class |
WC_BUTTON | Button control class |
WC_MENU | Menu control class |
WC_STATIC | Static text control class |
WC_ENTRYFIELD | Entryfield control class |
WC_LISTBOX | Listbox control class |
WC_SCROLLBAR | Scroll bar control class |
WC_TITLEBAR | Titlebar control class |
WC_MLE | Multi-line edit control class |
WC_SPINBUTTON | Spinbutton control class |
WC_CONTAINER | Container control class |
WC_SLIDER | Slider control class |
WC_VALUESET | Valueset control class |
WC_NOTEBOOK | Notebook control class |
Each window class is very different from the others. Some of these predefined classes will be covered in later chapters. The client window, which is the area inside the window frame, belongs to a user-defined class. Each window class also contains a set of window styles specific to that class. There is a set of class styles available to all classes. The styles are:
These styles will be covered in more detail in the section entitled "Window
Stylin".
Once we know a little bit about the window classes the operating system
offers, we can decide which are best suited for our application, or, as
most of us do-it-yourselfers will do, you can create your own. So, let's
do just that.
WIN1.C
WIN1.MAK
WIN1.DEF
The OS/2 Toolkit provides oodles and oodles of header files. These files contain structure definitions, function prototypes, and many system-defined constants to make OS/2 programs much easier to read. The large size of these files and the tremendous amount of overhead they create make it advantageous to selectively pick and choose those parts that are applicable to a program. This is done by placing a series of #defines before the inclusion of OS2.H. In this program, we will use #define INCL_WIN.
#define INCL_WIN
#include <os2.h>
This is an all-encompassing define that will include the necessary headers for all the Win... functions. This is overkill in most cases, but for our first example we'll keep things simple.
MRESULT EXPENTRY ClientWndProc(
HWND hwndWnd,
ULONG ulMsg,
MPARAM mpParm1,
MPARAM mpParm2 );
Window procedures are declared in a very special way, using the prefix MRESULT EXPENTRY. In OS2DEF.H, these expand to VOID * _System. The return type, MRESULT, gives the window procedure the freedom to return whatever it needs to by using the VOID * type. The _System tells the C-compiler that the operating system will be calling the function. It is a good idea to use the Presentation Manager-defined data types when dealing with window procedures and messages. There is a good probability that some definitions will change when moving to other machine architectures, and by using the defined data types, we save some headaches if we need to port the application to some other version of OS/2. A more detailed explanation of window procedure is in the section "The Window Procedure Revisited"
The function's parameters are HWND hwndWnd, ULONG ulMsg, MPARAM mpParm1, and MPARAM mpParm2. This may look very familiar to Microsoft Windows programmers. The variable hwndWnd is a window handle. Each window has its own unique window handle, and most Win... functions will include this as a parameter. In this case, hwndWnd is the window to which the message is being sent. The parameter ulMsg is the specific message being sent so the window. We will cover messages in more detail in Chapter 11.
The last two parameters are mpParm1 and mpParm2 which have the type MPARAM. These are "shape-shifter" parameters. MPARAM is really a PVOID in disguise. This gives the operating system two 32-bit spaces to insert whatever data corresponds to the message being sent. These values could be pointers or short or long integers. For example. the message WM_MOUSEMOVE is sent whenever the mouse is moved. The first message parameter, mpParm1, would contain two SHORTs. The second message parameter, mpParm2, also contains two SHORTs. Figure 9.3 provides a breakdown of a message-parameter variable.
|
||
![]() |
||
|
Many data-type conversions are necessary in a Presentation Manager application
because of the multiple data types that can be used as an MPARAM or MRESULT.
MRESULT is the value returned by the window procedure and is also a "shape-shifter".
The Toolkit includes a group of helper macros to make these conversions
easier.
Table 9.1 presents the macros used to convert some standard data type
into a MPARAM data type that can be used when sending or posting a window
message.
Macro | Converts into MPARAM |
MPFROMVOID | 0 |
MPFROMP | PVOID |
MPFROMHWND | HWND |
MPFROMCHAR | CHAR |
MPFROMSHORT | SHORT |
MPFROM2SHORT | 2 SHORTs |
MPFROMSH2CH | 2 CHARs |
MPFROMLONG | ULONG |
Table 9.2 presents the macros used to convert a MPARAM data type into
a standard data type that can be used when receiving a window message.
Macro | Converts from MPARAM |
PVOIDFROMMP | PVOID |
HWNDFROMMP | HWND |
CHAR1FROMMP | CHAR |
CHAR2FROMMP | second CHAR |
CHAR3FROMMP | third CHAR |
CHAR4FROMMP | fourth CHAR |
SHORT1FROMMP | low SHORT |
SHORT2FROMMP | high SHORT |
LONGFROMMP | ULONG |
Table 9.3 presents the macros used to convert a MRESULT data type into
standard data type that can be used to examine a return value for the window
procedure.
Macro | Converts from MRESULT |
PVOIDFROMMR | PVOID |
SHORT1FROMMR | low SHORT |
SHORT2FROMMR | high SHORT |
LONGFROMMR | ULONG |
Table 9.4 presents the macros used to convert a standard data
typo into a MRESULT data type that can be used to construct a return value
from the window procedure.
Macro | Converts to MRESULT |
MRFROMP | PVOID |
MRFROMSHORT | SHORT |
MRFROM2SHORT | 2 SHORTs |
MRFROMLONG | ULONG |
habAncbor = WinInitialize ( 0 ) ;
hmqQueue = WinCreateMsgQueue( habAncor,0) ;
The beginning of a PM program will always start with a few things. First, WinInitialize is called to obtain an anchor block handle, or HAB. An anchor block is specific to each thread that contains a window procedure.
HAB WinInitialize( ULONG. flOptions)
The only parameter for WinInitialize is a ULONG that is used for initialization options. In a PM environment, this should be 0. An anchor block currently contains error information for each thread and also may be used for "future portability issues". Each Presentation Manager thread should obtain its own anchor block for two reasons: portability and also to obtain error information specific to that thread.
HMQ WinCreateMsgQueue( HAB habAnchor, Long lQueuesize )
WinCreateMsgQueue will create a message queue for the thread that called the function. The message queue is how Presentation Manager communicates back and forth with the windows. The first parameter is the anchor block handle, habAnchor. The second parameter is the queue size. A parameter of 0 indicates the default queue size in OS/2, which holds 10 messages. A full queue will cause the user interface to respond rather slowly and sometimes to stop responding completely. The default queue size should be fine for most applications. If a queue is getting too full, the program should be checked to see where messages are getting backlogged. (One of the requirements for a PM interface is a crisp user response. Any response that consumes more than 100 milliseconds probably should be put in a separate thread. See Chapter 30 for more information on multithreading in a PM program.)
WinRegisterClass
( habAnchor,
CLS_CLIENT,
ClientWndProc,
0,
0 ) ;
The function WinRegisterClass is used to create a new class of windows, in this case CLS_CLIENT.
BOOL WinRegisterClass
( HAB hab,
PSZ pszClassName,
PFNWP pfnWndProc,
ULONG flStyle,
ULONG cbWindowData)
The first parameter is the anchor, habAnchor. The next
parameter is the class name. This parameter is a null-terminated string.
The next parameter is the window procedure the class is assigned to, ClientWndProc.
The fourth parameter is the class styles used for the new class. We're
not going to use any class styles for now, so we put 0 here. The last parameter
is the number of bytes of storage space that will be tacked on to each
window belonging to this class. This piece of space is commonly referred
to as "window words." This is covered in more detail later.
![]() |
By now readers are probably thinking "But I just wanted to create one lousy window". Well, this is it, the function call you've been waiting for: WinCreateStdWindow. This function actually creates five windows as stated earlier; but only two that are of any interest to us - the frame window and the client window. |
ulFlags = FCF_TITLEBAR |FCF_SYSMENU | FCF_SIZEBORDER | FCF_MINMAX | FCF_SHELLPOSITION | FCF_TASKLIST ;
hwndFrame = WinCreateStdWindow(
HWND_DESKTOP,
WS_VISIBLE,
&ulFlags,
CLS_CLIENT,
"Titlebar",
0L,
NULLHANDLE,
0,
&hwndClient );
The function returns the frame window handle.
/* This function creates a standard window. */
#define INCL_WINFRAMEMGR /* Or use INCL_WIN,
INCL_PM, Also in COMMON section */
#include <os2.h>
HWND hwndParent;
/* Parent-window handle. */
ULONG flStyle;
/* Frame-window style. */
PULONG pflCreateFlags; /* Frame-creation
flags. */
PSZ pszClassClient;
/* Client-window class name. */
PSZ pszTitle;
/* Title-bar text. */
ULONG flStyleClient;
/* Client-window style. */
HMODULE Resource;
/* Resource identifier. */
ULONG ulId;
/* Frame-window identifier. */
PHWND phwndClient;
/* Client-window handle. */
HWND hwndFrame;
/* Frame-window handle. */
hwndFrame = WinCreateStdWindow(hwndParent,
flStyle, pflCreateFlags, pszClassClient,
pszTitle, flStyleClient, Resource,
ulId, phwndClient);
The. first parameter specified is the parent of the frame window. We'll
discuss parents and owners in a minute. The second parameter is the frame
style. A frame can draw from two sets of styles: frame styles, because
this is a frame window; and window styles, because the frame class is a
subset of the window class "window". The most common window style available
is WS_VISIBLE. Yep, you guessed it, this means the window is not only created
but will show up as well.
The third parameter is the frame flags. Frame flags describe how the
frame will look. The possible descriptors are OR'ed together. Figure 9.4
is a diagram of all the possible descriptors and the bits that correspond
to them.
Bit | Constant |
0 | FCF_TITLEBAR |
1 | FCF_SYSMENU |
2 | FCF_MENU |
3 | FCF_SIZEBORDER |
4 | FCF_MINBUTTON |
5 | FCF_MAXBUTTON |
6 | FCF_VERTSCROLL |
7 | FCF_HORZSCROLL |
8 | FCF_DLGBORDER |
9 | FCF_BORDER |
10 | FCF_SHELLPOSITION |
11 | FCF_TASKLIST |
12 | FCF_NOBYTEALIGN |
13 | FCF_NOMOVEWITHOWNER |
14 | FCF_ICON |
15 | FCF_ACCELTABLE |
16 | FCF_SYSMODAL |
17 | FCF_SCREENALIGN |
18 | FCF_MOUSEALIGN |
[...] |
|
24 | FCF_HIDEBUTTON |
25 |
|
26 | FCF_CLOSEBUTTON |
[...] | |
30 | FCF_AUTOICON |
Flag | Description |
FCF_TITLEBAR | Creates a title bar on the frame. |
FCF_SYSMENU | Creates a system menu on the frame. |
FCF_MENU | Creates an application menu on the frame. This is loaded from the resource file or .DLL. (See Chapter 12 for more information.) |
FCF_SIZEBORDER | Creates a sizing border on the frame. |
FCF_MINBUTTON | Creates a minimize button on the frame. |
FCF_MAXBUTTON | Creates a maximize button on the frame. |
FCF_MINMAX | Creates both a minimize and maximize button on the frame. |
FCF_VERTSCROLL | Creates a vertical scroll bar on the frame. |
FCF_HORZSCROLL | Creates a horizontal scroll bar on the frame. |
FCF_DLGBORDER | Creates the thick dialog box border on the frame. |
FCF_BORDER | Creates a thin border on the frame. |
FCF_SHELLPOSITION | The system determines the initial size and placement of the frame window. |
FCF_TASKLIST | Adds the program title to the task list and window title to the window list. |
FCF_NOBYTEALIGN | Do not optimize window movements in 8 pel multiples. |
FCF_NOMOVEWITHOWNER | The frame window will not move when the owner is moved. |
FCF_ICON | An icon is added to the frame. This is loaded from the resource file or .DLL. (See Chapter 12 for more information.) |
FCF_ACCELTABLE | An accelerator table is added to the frame. This is loaded from the resource file or DLL. (See Chapter 12 for more information) |
FCF_SYSMODAL | The frame window is system modal. |
FCF_SCREENALIGN | The frame window is positioned relative to the desktop rather than relative to the owner window. |
FCF_MOUSEALIGN | The frame window is positioned relative to the position of the mouse rather than relative to the owner window. |
FCF_HIDEBUTTON | Creates "hide" button on the frame |
FCF_HIDEMAX | Creates "hide" and maximize buttons on the frame. |
FCF_CLOSEBUTTON | use when no other min/max button is present |
FCF_AUTOICON | A WM_PAINT message will tot be sent to the application when the frame window is iconized |
FCF_STANDARD | FCF_TITLEBAR | FCF_SYSMENU | FCF_MINBUTTON | FCF_MAXBUTTON | FCF_SIZEBORDER |FCF_ICON |FCF_MENU | FCF_ACCELTABLE | FCF_SHELLPOSITION |FCF_TASKLIST |
In this example, we'll use the following flags:
FCF_TITLEBAR, FCF_SYSMENU, FCF_SIZEBORDER, FCF_MINMAX,
FCF_SHELLPOSITION, FCF_TASKLIST
![]() |
Gotcha!Be sure to pass a pointer to a ULONG as this parameter |
The fourth parameter is the name of the window class that the client
window will belong to; in this case we use the string defined by CLS_CLIENT.
The next parameter is the window text for the title bar. The sixth parameter
is the client window style. Since we defined the parent of the client window
hwndFrame to have the style WS_VISIBLE, the client, as a child of hwndFrame,
will inherit the WS_VISIBLE style. This means we don't have to specify
any window styles here; we'll just leave that a 0.
The next parameter is the resource ID location. The next parameter
contains the resource ID for the frame window. This one resource ID will
point to all the resources that are defined for the frame. This includes
the menu, icon, accelerator table, and any other items defined using the
frame creation fags. For more information on resources, see Chapter 12.
The last parameter is the address of a window handle. Presentation
Manager will place the client window handle into this variable upon the
function's return.
If WinCreateStdWindow fails, NULLHANDLE is returned.
Before we attempt to do anything else, it is a good idea to check the return
handle to make sure it is valid; if not, the application should quit, preferably
with some sort of error message.
bLoop = WinGetMsg ( habAnchor,
&qmMsg
NULLHANDLE,
0,
0);
while ( bLoop)
{
WinDispatchMsg ( habAnchor, &qmMsg ) ;
bLoop = WinGetMsg( habAnchor,
&qmMsg,
NULLHANDLE,
0,
0 ) ;
} /* endwhile */
The two functions WinGetMsg and WinDispatchMsg, are the keys to getting the message queue up and running. Without some form of message retrieval and dispatch the system will respond with a "Program not responding..." error message. The secret to a well thought out Presentation Manager application is a message queue that is quick and responsive. WinGetMsg will retrieve the message from the message queue and place it into the variable qmMsg. The QMSG structure looks very similar to the variables that are passed to the window procedure. Eventually the QMSG structure will be passed on to ClientWndProc or the window procedure for the window receiving the message. WinGetMsg and WinDispatchMsg form a post office for messages. They pick up the messages and then make sure that the messages are delivered to the correct window.
BOOL WinGetMsg(
HAB hab,
PQMSG pqmsgmsg,
HWND hwnfFilter,
ULONG ulFirst,
ULONG ulLast )
The first parameter of WinGetMsg is the anchor block handle. The next one is the address of the QMSG structure that will handle the retrieved message information. The next three parameters are not used in this example. They provide a way for WinGetMsg to choose selectively which messages to pick out of the queue. By specifying zeroes here, WinGetMsg will retrieve all messages from the message queue in the order they were placed there. After the message is retrieved from the queue, it is then passed on to WinDispatchMsg.
MRESULT WinDispatchMsg ( HAB hab, PQMSG pgmsgMsg ) ;
It is WinDispatchMsg's job to take the message from the qmMsg variable and send it on to the window procedure associated with the window it is addressed to. For instance, if qmMsg.hwnd were equal to hwndWnd, WinDispatchMsg would take qmMsg and send it on to ClientWndProc.
/* QMSG structure */
typedef struct _QMSG /* qmsg */
{
HWND hwnd; /*
window handle that msg is being sent to */
ULONG msg; /*
the message itself */
MPARAM mp1; /* Message Parameter
1 */
MPARAM mp2; /* Message Parameter
2 */
ULONG time; /*
Time msg was sent */
POINTL ptl; /* mouse position
when msg was sent */
ULONG reserved;
} QMSG;
typedef QMSG *PQMSG;
The QMSG structure contains a lot of very interesting information about the message. The first field in the structure, hwnd, is the window handle the message is for. The field msg is the constant identifying the message. Some common messages are WM_CREATE, WM_PAINT, WM_QUIT and WM_SIZE. The next two parameters, mp1 and mp2, are the message parameters. Each message has a set use for these parameters. Usually they are used to convey more information about the message. The time field contains the time the message was sent, and the ptl field is a structure that contains the mouse position when the message was sent.
![]() |
You may have noticed that WinGetMsg and WinDispatchMsg were running in a while loop. While WinGetMsg returns a TRUE value, this loop continues to process messages. When WinGetMsg receives a WM_QUIT, WinGetMsg returns FALSE and will fall out of the loop. At this point, the user has elected to close the application, and it's time for the final cleanup. We have created three things that need to be destroyed - the frame window hwndFrame, hmqQueue, and habAnchor. Each of these items has its own destroy function. |
BOOL WinDestroyMsgQueue( HMQ hmq ) ;
BOOL WinDestroyWindow(HWND hwnd );
BOOL WinTerminate (HAB hab);
By destroying hwndFrame, we also are destroying the client window, the title bar, and all the other windows that are children of the frame.
WinDestroyWindow (hwndFrame );
} /* endif */
WinDestroyMsgQueue( hmqQueue ) ;
WinTerminate (habAnchor);
return 0;
You might have looked over main and thought. "Is this
it?" Well, no. We've presented just the tip of the iceberg. The window
procedure is the meat of a Presentation Manager program. A window
procedure's sole purpose in life is to respond to the messages for the
window that belongs to it. It is also important to realize that multiple
windows can and will access the same window procedure. Programmers must
be very careful with static and global variables or flags. They can come
back to haunt developers if two windows are accessing the same procedure.
Is is a good idea to avoid these if at all possible.
Most window procedures are nothing more than a giant switch
statement, with a case for each message. A window procedure does not have
to respond to every message; it can filter the majority of the messages
through to a function, WinDefWindowProc or WinDefDlgProc.
This function lets the system handle messages in a system default manner.
As the creator of the window procedure, it is the programmer's job to pick
out which messages will trigger a response in your program. For instance,
when a WM_SIZE message is received, the programmer may wish to reflow any
text on the window so that it is all visible and centered. Passing messages
on to WinDefWindowProc or WinDefDlgProc
is very safe.
![]() |
Gotcha!
Be very careful about accidentally reversing WinDefWindowProc and WinDefDlgProc. Strange things can occur when calling WinDefWindowProc for a dialog box or using WinDefDlgProc for a non-dialog box window. |
MRESULT EXPENTRY ClientWndProc ( HWND hwndWnd,
ULONG ulMsg,
MPARAM mpParm1,
MPARAM mpParm2 )
{
switch ( ulMsg ) {
case WM_ERASEBACKGROUND:
return MRFROMSHORT ( TRUE ) ;
default:
return WinDefWindowProc ( hwndWnd,
ulMsg,
mpParm1,
mpParm2 ) ;
} /* endswitch */
return MRFROMSHORT ( FALSE ) ;
}
The only message that is utilized in ClientWndProc is WM_ERASEBACKGROUND. This message is used to fill the client window with the system-window background color. If we let this message pass on to WinDefWindowProc, the background of the window would be transparent and the desktop would show through. By returning TRUE, we tell the system to paint the client window with the background color. In some cases, this message doesn't need to be processed if the painting is handled in the WM_PAINT message. In a window procedure, most messages have a default handling of returning FALSE. Programmers can save a few extra function calls by returning FALSE themselves from the handled instead of calling WinDefWindowProc.
[ Next ] [ Previous ] | Chapter 9 |