[ Next ] [ Previous ] | Chapter 9 | [ Contents ] [ Chapter 8: Interfacing with OS/2 Devices ] [ Chapter 10: Window Management ]

### Introduction to Windows.

#### Introduction

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.

#### What Is a Window?

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.)

Figure 9.1 A Window. | 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: The frame window The title bar The system menu The close/maximize/minimize buttons The client window

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:

- CS_MOVENOTIFY
- CS_SIZEREDRAW
- CS_HITTEST
- CS_PUBLIC
- CS_FRAME
- CS_CLIPCHILDREN
- CS_CLIPSIBLINGS
- CS_PARENTCLIP
- CS_SAVEBITS
- CS_SYNCPAINT

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 INCLUDE Files

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.

#### The Window Procedure Definition

`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.

Y Coordinate | X Coordinate
SHORT2 (16 bits) | SHORT1 (16 bits)
32-bit MPARAM mp1
Figure 9.3 Breakdown of a message-parameter variable

#### Helper Macros

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.

Table 9.1 Macros to Convert into MPARAM
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.

Table 9.2 Macros to Convert from MPARAM
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.

Table 9.3 Macros to Convert from MRESULT
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.

Table 9.4 Macros to Convert to MRESULT
Macro | Converts to MRESULT
MRFROMP | PVOID
MRFROMSHORT | SHORT
MRFROM2SHORT | 2 SHORTs
MRFROMLONG | ULONG

#### Presentation Manager Program Initialization

> `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.)

#### Creating a New Class

> `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.

#### Creating a Window

| 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
Figure 9.4 Frame creation flags

Table 9.5 Frame Creation Flags Description
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.

#### Message, Message, Who's Got the 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.

#### Terminating a Program

| 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;`

#### The Window Procedure Revisited

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.

The default action for these messages is listed in the online reference for the Toolkit. A few messages are very important to a window procedure. These will be covered later in this chapter.
In this example the window procedure, ClientWndProc, is very small. It's not quite the smallest window procedure available, but it's pretty close.

`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 | [ Contents ] [ Chapter 8: Interfacing with OS/2 Devices ] [ Chapter 10: Window Management ]