How do I? - Part 6

by Eric Slaats

Hi Again. I had a not so swell month. I got a new machine and I seem to have lost the wrestling match I'm having with it. This means I didn't have that much time for fun stuff.

This new machine sounds good when you hear its features: a Pentium Pro 200 with 64 Mb and 3Gb hard disk. Sounds good, eh? Now the problem: the video card. It's a Virge3d with 4 Mb. Previously, I used a Diamond Stealth pro on my 486DX2 with great results but the Virge drivers are refusing to work on the resolution I'm used to.

The bugs are really 'not of this earth'. Drag and drop works with one item; if I try to drag two items, it works within a folder but when I go over the Desktop, Lockup; when the folder is maximized, lockup. VIEW.EXE works fine, except when the cursor keys are used, which produces a lockup. When an OS/2 VIO window is opened, I can't drag anything because, yes, a lockup. The MLE control works perfectly, unless I try to select anything, because...

The strange thing is that at 1024x768 resolution most of these bugs don't occur. This means I'm now typing this article on a 640x480x16 (Grunt) screen. This will probably be the last time I ever try a Virge card. To make matters worse, the hard disk in this new machine isn't exactly cooperating either. Many black screens because stuff can't be found, etc. Well, enough whining, let's get down to business. I'll let you know this fight turned out next month.

Unexploited Features
OS/2 is an amazing operating system. It has a lot of features that are very attractive like Drag-n-Drop, presentation parameters, Profiles etc. What always amazes me though, is that there aren't many programs really making use of all these goodies. Take a simple thing like saving the main window position and the presentation parameters used by that window. A lot of applications I have tried don't use these OS/2 goodies at all. OS/2 Works in the BonusPak for example: no presentation parameter support, no window position saving, no scalable status bar, etc. This is all the more amazing because these features are very easy to implement.

Don't get me wrong here, implementing full drag-n-drop facilities (as in Smalled or as with DragText) isn't simple. It's very hard! But, implementing basic stuff like saving a window position with presentation parameters is very easy and doesn't take that much work. That's exactly what we're starting to investigate this month.

A number of terms are mentioned above that may need explaining. So before we get into the programming stuff we'll take a look at what these terms mean.

Presentation parameters. I love them! PPs are a fantastic way to change (and remember) information on colours and fonts for virtually every part of your application. Everyone who uses OS/2 knows about them but probably won't recognize them under the name 'presentation parameters'. PPs are very easy to apply, simply use the font or colour palettes in the System Setup folder and drop a font or colour on the area you wish to change. We'll take a look at how to save the PP information in a moment.

Profiles. This is another feature of OS/2 everyone uses whether they want to or not. Everything you do in your system, like changing colours, changing the position of windows or icons, etc., gets recorded. This way OS/2 can restore all your settings when you restart the system. OS/2 uses two profiles, OS2.INI for user settings (we're going to use this one too) and the OS2SYS.INI for system settings.

The OS/2 profile can be compared to the SYSTEM.INI file in Windows. However, the OS/2 version of the profile is a binary database and is far superior to the Windows version.

Besides these two INI files, it is also possible to use a private profile. (We'll delve into that another time.)

Now, after that rather long introduction, what are we going to do this month? We'll add a feature to our simple program (ZIP, 14.5k) that will save the window position and all the colour and font information that is added by using PPs. Because a lot of people don't like the fact that programs mess with their OS2.INI file, we'll also add a menu option to remove this info from the OS2.INI file.

What happens when a program is closed?
Saving window size and position information as well as PP information is best done when closing an application. This means we don't have to act on every change to a window's settings, we simply save everything on the event of closing the application. So let's find out what happens when a program is closed.

There are a number of messages that play a role in shutting down an application. Of course there is the WM_QUIT message (this is reviewed in more depth in an earlier article). This message isn't very convenient to use to handle last minute stuff because it is this message that shuts the message queue for the application down.

There is also the WM_CLOSE message. This message is sent by the PM when a user selects the 'Close' menu item from the System Menu or double clicks the System Menu icon. The default window procedure (WinDefWindowProc) reacts to this message by posting a WM_QUIT, so this also closes the application. However, we can't rely on WM_CLOSE to always happen on the event of shutting down an application (a WM_QUIT can be posted by another action).

So what are we looking for? There is another message that is sent by the PM to an application just before it shuts down. This is the WM_SAVEAPPLICATION message (a convenient name). It is this message that is made to use for saving the state of the application and indeed, it's this message we will use in our next example.

The developers of OS/2 2.0 added two functions that really make it a breeze to save and restore the current status of the frame window. These are the functions WinStoreWindowPos and WinRestoreWindowPos. These functions save the size and place information as well as the PP information of all the frame controls. (Note: when controls like a status bar are added, it pays to make them frame controls because this way you won't have to worry about saving the font, colour, etc. separately. We will delve into that some other time.)

Let's take a look at WinStoreWindowPos first. The drawback of these functions is that they save their information in the OS2.INI file. As I mentioned above, not everyone likes this! The WinStoreWindowPos function takes three parameters: PSZ    pAppName;  // String which contains application name. PSZ    pKeyName;  // String which contains the key name. HWND   hwnd;      // Window handle of the window to save. rc = WinRestoreWindowPos(pAppName, pKeyName, hwnd); To understand these parameters, we need to know a little about profiles. Profiles are binary databases that handle information in two levels. The highest level is the Application level. Under the Application level, keys can be created and data can be assigned to every key. This can be anything from simple strings or a simple integer to very complex structures.

The WinStoreWindowPos function only needs to know the Application name and the key name for the data in the OS2.INI file. Normally, we assign names for these with 'defines' in the header file. This way it's easier to keep track of which names are used. Extracting of values from the frame window, is handled completely by WinStoreWindowPos. So the action we take at the WM_SAVEWINDOWPOS is really quite simple. // Piece in the Sample6.h file // // Save the window size and position on exit // case WM_SAVEAPPLICATION: {        WinStoreWindowPos(APPNAME, WINPOS,                                    WinQueryWindow(hwnd, QW_PARENT)); } break; Looks simple, right? Well, that's all there is to it! There's one thing that needs a little explanation though, the line WinQueryWindow(hwnd, QW_PARENT)). This is the window handle the function needs to store the data of the frame window. With this call we ask the Client window what the handle of the parent (the frame) is.
 * 1) define APPNAME "Sample6"
 * 2) define WINPOS "Sample6WinPos"

Now for restoring the window data. We want to do this when we are sure the frame window is created. This code is also very simple: WinRestoreWindowPos(APPNAME,      // Restore size/place and PP from OS2.INI                     WINPOS,        //                     hwndFrame))    // There are a few pitfalls here though. The first time the program is started, there is no entry in the OS2.INI file. This means there is no information present to put the window in a certain location.

No problem, we've got the FCF_SHELLPOSITION flag defined as a window creation flag. And indeed, the first time the program is run, this takes care of the sizing and positioning of the window.

However, if there is an entry in the OS2.INI, the FCF_SHELLPOSITION flag causes the window first to be shown in the size and place determined by the PM and then flashes it to the size stored from the previous session. This isn't exactly elegant. So what do we do? We need to set the window size and position ourselves if the WinRestoreWindowPos call fails. Luckily, this function returns a value to determine if it successfully found and used size/position/pp values. If this is not the case, we've got to call the WinSetWindowPos function with a size and place explicitly listed. The code to handle this looks like this: //- // Restore size/place and PP fromOS2.INI //- if (!WinRestoreWindowPos(APPNAME, WINPOS, hwndFrame)) {     WinSetWindowPos(hwndFrame,                      NULLHANDLE,                      10,10,550,300,                      SWP_ACTIVATE| SWP_MOVE| SWP_SIZE |SWP_SHOW); } To see how this works, open the window, resize and replace it and drop colours and fonts on the titlebar and menu (remember that activated and deactivated windows can have a different titlebar colour). You may even want to change the border colour this way. Close the window and open it again and observe the results. (Note that the client window area is not accepting PP drops.)

Now for the last item in this article. It would be nice to remove the entry sample6.exe puts in the OS2.INI file. This can be done with one of the profile API calls built into OS/2. The PrfWriteProfileData function can be used to put data in a profile, but also to remove data from a profile. Without going into detail about this right now (I will some other time), I'll just show you the function that will take care of business. To make this accessible, there is just one menu entry. It calls the following function: PrfWriteProfileData(HINI_USERPROFILE, APPNAME, NULL, NULL, 0); This will effectively remove the entry from the OS2.INI file. However, we're not out of the woods yet! Because the program is still active, closing it will activate the WM_SAVEAPPLICATION event and thus the information will get saved again. To fix this for the sample, I used a rather inelegant global boolean to switch the save status. Once the menu gets clicked, the application will close and because of the status of the boolean (bSave), no info will be saved. Check out the sample code to see how this is done.

Concluding Notes
This month's column described one of the things I like best. Applications that remember what users are doing. I hope I've showed you how easy the basics are. Also, remember that people like to keep their OS2.INI clean so you might think twice about using it. In my opinion though, one of the reasons for using the OS2.INI file is that it's very fast. The OS2.INI info is likely to already be sitting in memory. That's one of the reasons programs like Smalled use the OS2.INI, to ensure a fast startup. There are some programs available that let you examine the OS2.INI file. For example the Initor (2.0) by Jobst Schmalenbach does a very nice job. Check it out.

'Til next month, Bye.