Introduction to PM Programming
Written by Larry Salomon, Jr.
The purpose of this column is to provide the readers out there who are not familiar with PM application development the information necessary to satisfy their curiousity, educate themselves, and give them an advantage over the documentation supplied by IBM. Of course, much of this stuff could probably be found in one of the many books out there, but the problem with books in general is that they don't answer the questions you have after you read the book the first time through.
I will gladly entertain feedback from the readers about what was "glossed over" or what was detailed well, what tangential topics need to be covered and what superfluous crap should have been removed. This feedback is essential in guaranteeing that you get what you pay for. :)
It should be said that you must not depend solely on this column to teach you how to develop PM applications; instead, this should be viewed as a supplement to your other information storehouses (books, the network conferences, etc.). Because this column must take a general approach, there will be some topics that you would like to see discussed that really do not belong here. Specific questions can be directed to the Scratch Patch, where an attempt to answer them will be made.
Last month, we finished dissecting the (initial) HELLO program that was introduced in volume 2, issue 1. This month we will starts to look at a significantly changed version of HELLO in order to set the direction of this column for the next few issues. To recap, though, HELLO's purpose was simply to demonstrate the minimum number of calls required to do anything useful. Other than that, it didn't do anything useful. :)
Up to this point, we have introduced and continued discussion on two areas of PM application development - messages and functions. Now we are going to add two more - dialog box implementation and resource files.
In most programs, it is necessary to gather (even frequently) data from the user. This may be as simple as getting a filename, or it may be as complex as (gulp) filling out an online tax form. In any case, what is the method by which input is obtained? CUA defines the mechanism to be a dialog box. What is a dialog box, you ask? To be precise, it is a window whose sole purpose is to receive data from the user in order for the application to continue. Because it is a window, it has the window characteristics discussed when this column began.
Every one has seen a dialog box or two; whether it is when your word processor prompts you for a file name to load, or when your spreadsheet program asks you to select a printer, you cannot escape these facts of (GUI) life. However, using them is one thing - implementing them is quite another.
There are two components to dialog box implementation - screen layout and code. We will begin to look at each in this issue, and will continue in more detail in future issues as we also look at the various controls.
Okay, so you are a Pascal hacker from DOS trying to learn C under OS/2. To make it worse, you have to also learn PM, according to your boss. But if that weren't enough, now you can tack on another language to learn, another skill to master. It is called the resource language, and it is the language in which dialog boxes (among other things) are designed.
Simply put, a dialog box is a collection of child windows. Each window is defined separately, with a specific order. At the next level, there are groups of windows, beginning with the first one having a special style - WS_GROUP - and ending just prior to the beginning of the next group or with the end of the dialog box definition. Finally, a dialog definition begins with a DIALOG keyword, followed by a BEGIN keyword, and ends with an END keyword.
A sample dialog definition follows:
DLGTEMPLATE DLG_GRAPHTYPE LOADONCALL MOVEABLE DISCARDABLE BEGIN DIALOG "Create graph", DLG_GRAPHTYPE, 64, 23, 195, 170, NOT FS_DLGBORDER | WS_VISIBLE, FCF_SYSMENU | FCF_TITLEBAR | FCF_SIZEBORDER BEGIN GROUPBOX "Graph type", -1, 10, 105, 170, 60 LTEXT "Field(s) to graph", -1, 10, 90, 100, 8 CONTROL "", DGT_UB_BARGRAPH, 15, 110, 50, 45, WC_BUTTON, BS_USERBUTTON | WS_GROUP | WS_TABSTOP | WS_VISIBLE CONTROL "", DGT_UB_LINEGRAPH, 70, 110, 50, 45, WC_BUTTON, BS_USERBUTTON | WS_GROUP | WS_TABSTOP | WS_VISIBLE CONTROL "", DGT_UB_SCATTERGRAPH, 125, 111, 50, 45, WC_BUTTON, BS_USERBUTTON | WS_GROUP | WS_TABSTOP | WS_VISIBLE LISTBOX DGT_LB_HEADERLIST, 10, 30, 175, 60, LS_HORZSCROLL | LS_EXTENDEDSEL | WS_GROUP LISTBOX DGT_LB_FULLIST, 10, 30, 175, 60, LS_HORZSCROLL | LS_EXTENDEDSEL | WS_GROUP | NOT WS_VISIBLE DEFPUSHBUTTON "~Graph", DGT_PB_GRAPH, 10, 10, 40, 13, WS_GROUP PUSHBUTTON "~Settings...", DGT_PB_SETTINGS, 55, 10, 40, 13 PUSHBUTTON "Cancel", DLG_PB_CANCEL, 100, 10, 40, 13 PUSHBUTTON "Help", DLG_PB_HELP, 145, 10, 40, 13 END END
As you can see, there are many more details regarding these definitions; fortunately, you don't have to know them. There is a utility called DLGEDIT which allows you to design, on the screen, your dialog boxes and save the result. We won't describe how to use this utility; see the accompanying documentation for this. What is the result? The human-readable source is saved with the extension .DLG and the binary form is saved with the extension .RES. In order to explain more, we need to broaden the scope of our discussion to...
A resource file - with the extension .RC - contains the definition of the various resources used by the application and each of these resources can be loaded or accessed via a Win*() function specific to that resource type. The resource language also includes C-style preprocessor commands to allow the inclusion of C header files containing #define statements for various constants that are needed by both the resource file and the application.
For dialogs, Microsoft decided that the Dialog Box Editor shouldn't have to deal with parsing the entire resource file, so it added dialog definition files (.DLG) to the fold. Integrating the two can get unwieldy - at the end of the .RC file, you indicate the dialog file to include:
...but at the beginning of the dialog file, you must somehow also include the #define statements for each dialog, so there is a different statement:
DLGINCLUDE 1 MYDLGS.H
Whoa! Why the number on the DLGINCLUDE statement but not the RCINCLUDE statement? The answer is that Microsoft thought that different dialogs would have different sets of constants, so the number is the identifier of the dialog (meaning you can have different #include files refer to different dialog box definitions).
If the reasoning seems flawed to you, that is because it is. My personal taste is to append the .RC file with the contents of the .DLG file. Preference is the key word here. Since the dialog editor reads the binary file (.RES), it doesn't matter how we manipulate the human-readable source.
In addition to a dialog screen definition, there is code to be written. Since a dialog is much like a "main window", you can imagine that the code is some form of a window procedure and this is correct. It is called a dialog procedure and differs from a window procedure in the following ways:
This message occurs when a dialog box is being created.
Parameters param1 hwndFocus (HWND) The handle of the window to receive the input focus when initialization is completed. param2 pvData (PVOID) Points to the application data specified as the last parameter on the call to WinDlgBox() or WinLoadDlg(). Returns reply bFocusChanged (BOOL) Focus change indicator: FALSE Give the input focus to the window in param1 TRUE The input focus was assigned by the application to another window, so do not give the input focus to any window.We will examine in detail how dialog procedures are written and used in later issues.
Dialog boxes come in two flavors, and the type determines the function used to display the dialog box.
Modal dialog boxes are those that require interaction by the user before the application or system can continue. An "Open file" dialog is an example of this.
Modeless dialog boxes are those that do not require input for the application to continue. A toolpad is an example of this.
For the former, WinDlgBox() is used to display and process the dialog. The latter uses WinLoadDlg(). Both functions take the same parameters.
ULONG WinDlgBox(HWND hwndParent, HWND hwndOwner, PFNWP pfnDlgProc, HMODULE hmModule, ULONG ulDlgId, PVOID pvData);
Dismissal versus Destruction
When a dialog is dismissed, it is by default not destroyed until the application exits. This is for performance reasons; since a large percentage of dialogs are reused by an application many times, this reduces the time between the call to WinDlgBox() and the time the dialog is actually displayed.
The ramifications are that if you allocate resources in the dialog procedure, you cannot wait until the WM_DESTROY message to free them, or else you are asking for trouble. When do you do it? I have yet to find a good answer to that question...
This month, we began to look at dialog boxes and resource files. These are collectively large topics, so we will continue next month with these. In the meantime, a sample application has been provided as hello.zip; this is an enhancement of the previous Hello world application that was discussed in previous issues.
Further down the road, we will begin looking at each of the individual controls, what their purpose is, and how they are used (primarily from the context of a dialog box). Stay tuned!