Content-Length: 12491 Content-Type: text/html
Under PM, since every window of a given window class shares the same window procedure, there must be a mechanism for each instance of a given window class be distinguishable. This is accomplished by every window having a window handle which is passed to the window's procedure for each message received. Associated with each window handle is a small amount of extra data storage that is recreated for each instance of a window class. Since this window data is associated with the window handle, the window procedure can be totally independent of which window is calling it. The window procedure becomes a state machine and the window's extra is the state storage area, in which the window remembers its current state until the next message is received.
Of course this whole argument applies to the less common use of window classes, which is to create a standard window type that can be used by many different subsystems or processes simultaneously. Some PM programmers have only had need to create a window class for their own client windows or dialogs. Since there is usually only one instance of these windows ever in existence, global variables might be used to track state information. That is a valid approach in these cases, but PM makes it trivial to make even these windows inherently reentrant so that they can easily be instantiated multiple times in the future if needed.
Just FYI, you can also use the same window procedure for multiple classes of window. Often times, if I have multiple small dialogs in a single module, I will make one dialog procedure and use it for all of the dialogs. The id of the dialog can be used, within each case of the switch statement used to process the individual messages, to determine the correct action to take. So the dialog procedure is really a nested set of switch statements. The outer one gets you to the correct message handling code. Within the case for each handled message is a switch on the id of the dialog, which will usually call out to a function appropriate for each dialog. Sometimes, if the code is trivial, it can just be done right there in the second switch. This scheme can cut down on redundant code and keeping track with what dialogs use what dialog procedures, but it has its own set of possible gotchas.
The extra window data of a window is accessed via a set of APIs named WinXXXWindowULong(), WinXXXWindowUShort(), and WinXXXWindowPtr(), where XXX is either Query or Set. There is an extra WinSetWindowFlags() that sets bits in a 32 bit field of extra data. There is not a WinQueryFlags() because WindowQueryULong() is fine for reading them. There is a separate flag setting API because it provides a 'relevant bits' mask parameter which lets you indicate which actual bits you want to affect. Otherwise, you would have to query the existing flag field, weave your changes in, then write them back. Instead you can do all that in one operation.
When you call these APIs, you tell it the index of the memory field you want to access or set. These indexes come in two forms, as is discussed next. But the bottom line is that the data is not just a flat memory buffer for you to access at will but a special area that must be accessed via an API.
The Blessed Data, The Common Data
There are two 'classes' of window data. One set is defined by PM and its somewhat magical and the other is window data created by the outside world. In this case the outside world is any window class, including the built in window classes. These magic window data fields are created automatically for every window so they are always available. The data created by each window class for its windows can vary from window class to window class. The amount of memory that a window class needs is indicated when the class is registered by WinRegisterClass().
The indexes for the magic window words are easily recognized in PMWin.H. They are the QWL_xxxx and QWS_xxxx values that have negative numbers. All non-blessed window words have indexes of 0 or greater. There are other window word indexes defined in PMWIN.H, for the frame window class for instance, but they are for window class data and have positive indexes.
If you look at the indexes for the magic words and the ones for the frame window class, you will notice something interesting. The window class specific data seems to be laid out as a single buffer, though I said it was not above. For instance, the QWL_xxx values each start at an index 4 bytes beyond the last one. The fact is that the class specific data probably is a contiguous buffer, but you are not supposed to assume that because it might change. The indexes of the magic words obviously have no relationship to the size of the values they point to. They are just sequentially numbered negative numbers. This probably implies that these values are just stored in various internal structures and the special indexes trigger the access APIs to go get them.
Since the class specific data really is like a contiguous range of bytes that can be referenced at any offset, you can use WinQueryUShort() to query the low or high word of a field that is actually 32 bits just by adjusting the offset. This line would query the top two words of the QWL_USER field. This would not work with the system defined values because they are not indexes in the same sense.
USHORT usVal = WinQueryWindowUShort(hwndMine, QWL_USER+2);
Ad Hoc Window Data
It is important to note that most window data is 'class wide' meaning that is used in the same way by the window procedure for all instances of that window class. But, for very general purpose windows such as the standard PM controls, there is very often a need for some storage that can be used in an ad hoc way by each individual instance of the window. So, all of the standard controls provides a user definable area, QWL_USER, that can be used differently by each instance of the window, because it accessed by client code not by the window procedure. If you write very general purpose window widgets, this is a good practice. QWL_USER is defined as 0, so it is the first index in the class specific window words. But you should probably not assume it will remain so defined. If you create symbolic names for the indexes of your window words, you should technically do them as offsets from QWL_USER, such as QWL_USER+4. This way they will adjust automatically.
The section on Subclassing and Superclassing above covered one way to extend the window storage of a predefined window class. This is sometimes needed to allow extension of the class wide storage while still leaving the user definable storage area alone.
Frame Window Words
The frame window class has the most class defined storage words of any of the predefined controls, about 0x54 bytes or so it seems from perusing the headers. Partly this is because some of them are used when a frame is in 'dialog drag', and partly because the frame control has the most need to play magic tricks in the process of providing standard system functionality. For instance, when a frame is activated, it will put the focus on the last of its descendant windows that had the focus. This is accomplished by its storing the current focus window into QWL_HWNDFOCUSSAVE, when it gets a WM_ACTIVATE message indicating that it is being deactivated. When the frame is minimized or maximize then restored, it goes back to its previous position. It does this by saving away the current size and position in QWS_XRESTORE, QWS_YRESTORE, QWS_CXRESTORE, and QWS_CYRESTORE, when it receives a WM_ADJUSTWINDOWPOS message indicating that it is being minimized or maximized. QWL_FLAGS holds a set of bit flags that give lots of information about the current state of a frame window.
Stupid Window Data Tricks
You can often play nice tricks by knowing the content of the system defined or class defined window words and adjusting them as desired. As discussed above in Changing Window Styles, you can sometimes extract the QWL_STYLES words, modify them, and put them back to change the styles of a window on the fly. As far as I know, you can write a new value to QWS_ID to change the id a window (including to accidently setting it to the same id as another child, which can cause weirdness later.) If you want to control the position to which a frame window will restore, then adjust the restore window words discussed in the previous section. On restoration, the frame window will pull these values out and uses them to restore itself. For a dialog, you can, as far as I know, reenter the dialog after WinDismissDlg() is issued (assuming you used WinLoadDlg()/WinProcessDlg() instead of WinDlgBox()) by clearing the FF_DLGDISMISSED flag and calling WinProcessDlg() again. If you leave FF_DLGDISMISSED set and call it, it will probably just hide itself immediately because it will think it is already dismissed. You can change the window to which focus will be put when the frame is activated by changing QWL_HWNDFOCUSSAVE. Though I have not done it myself, I think you can adjust where the frame will minimize to the next time by adjusting the QWS_MINIMIZE and QWS_MAXIMIZE words. I assume that, when the minimized frame is moved it updates these with the last place it was dropped (or maybe it just waits until its restored and does it then.)
Copyright Notice
| Reply to Author
| Search
| Section Index
| Previous Page
| Next Page
If you see a problem with this web site, contact
edm2_eds@iqpac.com.