How do I? - Part 10
by Eric Slaats
Hello, hello. I'm writing this column before the deadline for once. When OS/2 e-Zine! hits the street (or, the web, rather), I'll be lying somewhere on a Dutch beach enjoying the sun (hopefully) and having a vacation without a computer near. My wife and young kids won't allow me a number of things on vacation, amongst which are a portable computer, a guitar, books on computer stuff and books on mechanics. This will mean cold turkey for a couple of weeks, but don't worry, I'm sure I can sneak in some literature about Java and some other stuff disguised as a copy of a new Clive Custler novel <g>. No guitar is a bigger problem. But then, since the other two of my band members are away with their families also, I guess it won't hurt that much.
Last month I gave a quick intro on dialog editors and, more importantly, how to use the WS_GROUP flag. I mentioned last month that this isn't a well known feature. To stress my point, take a look at PMMail, for example. Try to enter a new address in the address book and then try to advance to the next line with the cursor down key. You'll see what I mean. The examples of this behaviour are legion. (I hope the article last month will do some good.)
What we did last month isn't going to make dialogs work though. It's just like Delphi, Visual Cafe and other visual programming tools; to draw windows is easy. (But don't underestimate this, a good looking interface really is a big deal.) There isn't any programming involved. To make a dialog do something, programming is definitely needed.
This month we'll delve into the way dialog programs are constructed. This means we've got to take a look at the general architecture, just like we did with frame windows. And we'll take a look at what happens when a dialog is called with the WinLoadDlg or WinDlgBox API's. Finally, we'll see that it's even possible to create an application that consists entirely of dialogs. (The attractive thing about this is that we don't have to do all the frame stuff.) But more about that later. First things first.Last month we learned some essential stuff about dialogs. We learned two ways to start them, but also that a dialog has a window procedure of its own. We also saw that the default handling for this window procedure wasn't the same as for normal window procedures. The default call for dialogs is WinDefDlgProc. Here, I repeat my warning of last month: don't use WinDefDlgProc in dialogs; it may function sometimes, but can cause strange and erratic behaviour. And it's a bug that's very hard to find!
In what ways is a dialog window procedure different from a normal window procedure? Well, for us simple programmers there's only one bit that is really interesting. A dialog doesn't get the WM_CREATE message like a normal frame window does when it gets created. A dialog will receive a WM_INITDLG message on creation. Let's take a look at an important part of that message first, the mp2 parameter.
The second message parameter of WM_INITDLG contains a pointer (pcreate) to a user defined data area. What is this pointer and how do we use it? In fact, it isn't exactly a pointer, it's a long integer. Since a "long" and a pointer in OS/2 are both 32-bit numbers, they can be interchanged (be careful though). The value received in mp2 is set when calling WinLoadDlg or WinDlgBox.
WinDlgBox(HWND_DESKTOP, // Parent hwnd, // Owner WinDefDlgProc, // Dialog window procedure NULLHANDLE, // Where is the dialog resource? DIALOG1, // Dialog Resource ID --------> 0); // Creation parameter (used in WM_INITDLG) WinLoadDlg(HWND_DESKTOP, // Parent hwnd, // Owner WinDefDlgProc, // Dialog window procedure NULLHANDLE, // Where is the dialog resource? DIALOG1, // Dialog Resource ID --------> 0); // Creation parameter (used in WM_INITDLG)
In the above samples, the creation parameter is filled with a zero, but this is the place where the pointer is filled in. You can pass any long in here and it will come out as mp2 in the WM_INITDLG message. In most cases the information passed is used to initialize the dialog. Anyway, that's what WM_INITDLG is all about. (We'll delve into more initialization stuff next month).
However, if we want to use the information the pointer in mp2 is pointing to in handling the messages sent to the dialog, we run into a problem. The mp2 pointer is only valid during the WM_INITDLG call. In other messages, mp2 has another meaning! Well, no problem, let's simply save this info in a temporary variable.
This can be done. Simply create a long (or more appropriate a PVOID) variable and assign mp2 in WM_INITDLG.
This leads to another problem, though. The Dialog Window Procedure is left after a message call is handled. So the variable will be destroyed and has a random value the next time the dialog is entered for, say, a WM_COMMAND message.
This phenomenon can be viewed in this month's sample (ZIP, 15k). In the sample a string is created in the frame window procedure. A pointer to this string is passed to the dialog. In the WM_INITDLG, the contents of this string are written into ENTRYFIELD1 and a copy is made in the PVOID variable, mp2Pointer2. If OK is pressed, a WM_COMMAND message is sent to the dialog and the dialog will try to write the contents of the data mp2Pointer2 is pointing to in ENTRYFIELD2. You'll notice that this displays rubbish.
To handle this problem we can use a static variable. A static variable can be viewed as a global variable in the meaning that it keeps its value and isn't destroyed when the procedure terminates. It only has a local scope though. So the next time a procedure is entered, the static variable still has its value. We can observe this in ENTRYFIELD3 where the contents of the static variable are written to. If you try this, you'll find that this keeps the value intact.
Well, that's it for this month. I've got to get busy packing for vacation. Have a good holiday.