PMCX: A Combined Solution

by Matt Smith, Prominare, Inc.

In Volume 9 of The Developer Connection News, we introduced the PM Control Extensions (PMCX) found within the IBM Universal Resource Editor (URE). The URE is part of the current Developer's Toolkit for OS/2 Warp (which you can find in the Developer Connection for OS/2 catalog, in the "Developer Toolkits" category).

In Volume 9 we created a single control, called BANNER.DLL, that could be used within windows or dialogs designed within URE. The Banner control was created using the defined PMCX architecture. In this article, we'll take the PMCX architecture a step further - the single .DLL module for each custom control will be adapted to have multiple controls within a single .DLL, while still having singular access to each custom control as required by PMCX. Why is this important? Basically, it has to do with packaging and performance. URE itself utilizes this architecture.

You may have many custom controls that you use within an application. At first glance, you might think that each PMCX control must be contained within its own separate .DLL. To an extent, this is true. The component parts that must be contained within the single .DLL are those referenced by URE directly through the .DLL ordinals. URE uses ordinals to ensure that the functions can always be found and referenced. Some linkers do not treat the exported names the same, and this can create problems when trying to find each of the addresses of the required PMCX functions.

If you understand the design of OS/2, you will see that there is nothing to prevent various functions from residing within different .DLLs. In fact, many applications use this facility to simplify the design and maintenance aspects of the application. In theory, you could place every function within its own .DLL, but this would make your packaging very difficult because you would have to make sure that each .DLL is present and accounted for on your distribution mechanism (that is, your diskettes or CD-ROM). The only advantage to this is that you can treat each function like a replaceable part, thereby making certain types of maintenance easier.

As for performance, this type of design causes your application to take an enormous hit during load time. The OS/2 system loader would have to search for and then load each .DLL and resolve external .DLL references. This takes time. Therefore, for faster loading, you would want to combine these .DLLs into one .DLL. The loading is much faster because only one .DLL is searched for and the external references of the .DLL are only searched for once.

Therefore, the idea is to simplify the use of the PMCX architecture with respect to the final distribution of the application.

PMCX Architecture - A Quick Review
A basic PMCX control contains a minimum of four functions. Three of these functions are exported to allow interaction within the PMCX control. The fourth function is generally the custom control window procedure.

The three functions that are exported are the XxxxRegister, XxxxQuery, and XxxxStyles (where Xxxx is the base name of the control). URE references these functions using the .DLL ordinals defined for each function. XxxxRegister is .DLL ordinal 1, XxxxQuery is .DLL ordinal 2, and XxxxStyles is .DLL ordinal 3. Figure 1 shows the interaction of URE and a typical application with the control.



''Figure 1. PMCX architecture''

''Now, what does the PMCX architecture look like when the actual control window procedure is moved from the PMCX control .DLL to a separate .DLL? Well, Figure 2 shows this quite nicely.''



''Figure 2. Extended PMCX architecture''

Essentially, the registration of the control and the window procedure of the control are placed within a separate .DLL. The XxxxRegister function of the PMCX control calls the actual register function within the control .DLL. This same control register function would be used within the final application. By doing this, the application does not reference any of the required PMCX exported functions. In the current version of URE, a new sample called COMBINED shows this technique. (You can find the samples for this article in the Developer Connection for OS/2 catalog, in category "Source Code from The Developer Connection News", or on disc 1 in the \source\devnews\vol10\pmcx directory.)

The 3D text and line controls (Text3D and Line3D) were adapted along with the addition of a new control, 3D frame, and placed within a single .DLL. The functions required by each control's window procedure were placed within the 3D.C module. Also, control-specific registration functions were added, which contain the WinRegisterClass coding to register the control with OS/2 PM. The only other source code changes necessary were to the XxxxRegister functions of each of the PMCX controls.

The original Text3DRegister was as follows: BOOL EXPENTRY Text3DRegister(HAB hAB) {  return(WinRegisterClass(hAB, pszClassname, Text3DWndProc, CS_SYNCPAINT | CS_SIZEREDRAW, USER_CWINDOWWORDS)); }

The revised Text3DRegister is as follows: BOOL EXPENTRY Text3DRegister(HAB hAB) {  return(RegisterText3D(hAB)); }

In the 3D.C module, RegisterText3D was as follows: BOOL EXPENTRY RegisterText3D(HAB hAB) {  return(WinRegisterClass(hAB, pszClassname, Text3DWndProc, CS_SYNCPAINT | CS_SIZEREDRAW, USER_CWINDOWWORDS)); }

In essence, all that happens is the shifting of contents between one .DLL and a second. When URE calls Text3DRegister, that function then calls Register3DText and the class is registered with PM just like the original Text3D control. The same kind of adaptation occurs with the Line3D control. The 3D frame control was created using this technique.

The final 3D.DLL contains just the code required to register each of the different controls and manage them. The code required for each of the PMCX interfaces to URE are contained within separate .DLLs: 3DTEXT.DLL, 3DLINE.DLL, and 3DFRAME.DLL.

Only 3D.DLL is required to be distributed with an application that uses these controls. No code required by URE is distributed. This makes the final distribution smaller and the loading of the application faster.

One Final Trick
If you are really interested in making your application load even faster by not having the controls within one .DLL or set of .DLLs, you can place the control code within your executable. This technique requires a bit more coordination because one executable cannot use a window or control of another executable.

In this case, you need to design your source code similarly to 3D.DLL. However, instead of a separate .DLL, the source code is compiled for the executable and for the PMCX.DLL. Make sure that the source code is part of the executable and part of the PMCX.DLL. Although you have the control code in two places, when you distribute the final executable it loads extremely fast because it doesn't have to search for, load, and resolve .DLL references.