Feedback Search Top Backward Forward

Programming the WPS Using the UICL

Written by Juergen Riedl


[Note: Files can be found here. Ed.]


About one year ago I posted a question onto the net asking whether or not it is possible to develop WPS classes with IBM's User Interface Class Libraries (UICL), but even IBM itself couldn't give me an answer, so I forgot about it. In April this year, however, I was offered by the OS/2 User Group of the Technical University of Munich (Bavaria / Germany) to give a review of the book about the UICL entitled OS/2 C++ Class Library, Power GUI Programming with C Set++ by Leong, Law, Love, Tsuji and Olson. I remembered my wish a year ago, started my first simple steps to program the WPS using the UICL, stumbled into some mysterious probems. But with some luck I got it to work ("1% inspiration but 99% transpiration", Thomas Edison).

This submission is the attempt to share my experiences with you, in the hope to enable you to easily write applications which are completely integrated into the WPS using the UICL. If this is to your taste then follow me on the journey to see that it is not too difficult.

The Paradigms

Frequently, IBM has created sophisticated APIs but never invested sufficient effort to publish the required documents to enable programmers to use them. This situation has slightly changed for the WPS [1, 2] and has been considerably improved for the UICL [3].

The WPS represents the paradigm which enables you to use your computer in an document-centered fashion. But due to the lack of good guidance, developing WPS classes is a rather difficult task. The PM API is abstracted from you; you don't see any function responsible for message processing; you don't even see an easy way to extent their functionality. In my opinion this is the reason why 99% of all OS/2 applications follow the old PM style paradigm. Any WPS programmer will be seen as a guru, which in fact they are. <grin>

I made my first steps of WPS programming guided by the book Client/Server Programming with OS/2 2.1 by Orfaly and Harkey. But I didn't like WPS programming very much - even small bugs in my classes required rebooting the machine. So it was burdensome. This changed dramatically with the advent of OS/2 3.0 Warp which designed the WPS as a DSOM server. Instead of rebooting the machine as before, the WPS was usually the one restarted. Along the way, I learned to like the UICL, which presented the obvious question: why should I program in the old PM style after having tasted the sweet flavour of the UICL?

At a first glance, the UICL appears to be a wrapper of PM. If you take the time, though, you will realize that it is more: it provides the means of creating sophisticated classes with few lines of code instead of hundreds as the pure PM programmer is forced to do. Even though the UICL is that powerful it seems to me to contradict the aim of the WPS, which is to support the document-centered paradigm. All of the "programs" should be seamlessly integrated into the shell, like extensions; no one should start any application first and generate the document afterwards as in the past. But with the UICL the obvious way is to generate an EXE which will be attached to the usual program notebook of the WPS. Wouldn't it be nice to be able to open a customized notebook of this application as any other WPS object?

Mastering the First Step

You have read the file README.1ST and set up your environment accordingly? All right, lets start.

What do we want to do? We want to develop a WPS application comprising a notebook and a frame window. Will this application be a "killer application?" No, it is only a sample to show how to implement the WPS and UICL combination. The development of the "killer application" is up to you. But it will become far easier in my opinion.

We will begin by subclassing WPAbstract. WPAbstract provides two notebook pages, the Window page and the General page. For the first step, we will extend this class to open an ordinary "Hello World" frame window by double clicking on its icon. After that we will examine how to add customized notebook pages to the already defined ones. And, of course, all of this will be done using the UICL!

Since we are dealing with the WPS we must use the SOM IDL. (The new VisualAge++ compiler may change this with its Direct To SOM capability) But it is not difficult to follow the code; examine the file OS2UG.IDL (for information on SOM and WPS please read EDM/2 issues 2-8 and 2-9):

interface XWPS : WPAbstract
#ifdef __SOMIDL__
  implementation {

    //# Class Modifiers
    majorversion = 1;
    minorversion = 2;
    dllname = "os2ug.dll";
    metaclass = M_XWPS;
    callstyle = oidl;
    passthru C_xih_before = "#include <os2.h>";

    //# Method Modifiers
    wpModifyPopupMenu : override;
    wpMenuItemSelected: override;
    wpOpen		    : override;
    wpAddSettingsPages: override;

#endif // __SOMIDL__
As you might assume looking at the class declaration interface, XWPS : WPAbstract sounds like class XWPS : public WPAbstract in C++. Outside of the implementation block, neither additional instance variables nor functions are declared. For that reason, no release order section is required. To be compliant with the WPS call style, set callstyle = oidl. If you will write CORBA-compliant DSOM classes, you must set callstyle = corba. But this doesn't apply to WPS GUI classes yet. During this article, we will override some WPS instance methods, which begin with the letters wp. For the first step, we will focus on three methods; the method wpAddSettingsPages() is the subject of the second step. Each of them will be discussed separately.

Now take a look onto the metaclass declaration interface M_XWPS:

interface M_XWPS
// same as interface M_XWPS: M_WPAbstract
#ifdef __SOMIDL__
  implementation {

    //# Class Modifiers
    majorversion = 1;
    minorversion = 2;
    callstyle = oidl;

    //# Method Modifiers
    wpclsQueryStyle: override;
    wpclsQueryTitle: override;
    wpclsQueryDefaultView: override;

#endif // __SOMIDL__
Here you see that SOM is slightly different from C++. It provides different types of classes and subsequently different types of methods: class methods and instance methods. Class methods are tied to metaclasses. Metaclasses are responsible for the management of class resources, and they provide the "bed" for the instance of a class like you do with the constructor of a C++ class. For the first step we need to override three class methods, which begin with the letters wpcls. Each of them will be discussed separately.

After you have declared the interfaces in your IDL file, you will run the SOM compiler SC against it. Enter SC OS2UG.IDL to compile the file. The SOM compiler generates an XH, an XIH, a CPP and a DEF file. Because it is common programming style to separate each function into a separate file, please do so accordingly. Unfortunately, the SOM compiler not only generates declarations, but also definitions within its header files; later you have to write a source file which #include's all of the separate files.

Overriding wpclsQueryStyle

While developing WPS classes, you need to register and deregister them many times. You can avoid this by disabling the generation of a template of your WPS class. If you do not, you can only get rid of the template by rebooting into a command shell without the WPS running and delete the corresponding DLL's. With an additional reboot you can continue development. I disable the generation of a template to make life easier; this is achieved as follows (see the file m_qstyle.cpp):

SOM_Scope ULONG  SOMLINK wpclsQueryStyle(M_XWPS *somSelf)
    /* M_XWPSData *somThis = M_XWPSGetData(somSelf); */

    return (M_XWPS_parent_M_WPAbstract_wpclsQueryStyle(somSelf)
		| CLSSTYLE_NEVERTEMPLATE); // no templates
} // END wpclsQueryStyle()
It returns the parents style or'd with the style CLSSTYLE_NEVERTEMPLATE. That's all you need. The result is that now the template checkbox on the General page is not available. Thus you can deregister your class after deleting the instance and delete the DLL without the need to reboot your machine.

Overriding wpclsQueryTitle

If you like to have a default title of your new class you will set it here (see the file m_qtitle.cpp).

SOM_Scope PSZ  SOMLINK wpclsQueryTitle(M_XWPS *somSelf)
    /* M_XWPSData *somThis = M_XWPSGetData(somSelf); */

    return (ClassTitle);
} // END wpclsQueryTitle()

Overriding wpclsQueryDefaultView

When you double click on the icon, which view should be opened? The settings notebook or the frame window? Our sample class opens a frame window (see the file m_qdefvw.cpp). This is done by declaring the value OPEN_XWPS1 (see the file os2ug.h) which is one of three possible frame windows:

SOM_Scope ULONG  SOMLINK wpclsQueryDefaultView(M_XWPS *somSelf)
    /* M_XWPSData *somThis = M_XWPSGetData(somSelf); */

    return (OPEN_XWPS1);
}// END wpclsQueryDefaultView()

Overriding wpModifyPopupMenu

When you click with mouse button 2 (default right) on your application's icon, you would like to find a menu reflecting your default view. This will be added to the overridden function above (see the file modpupmu.cpp):

SOM_Scope BOOL  SOMLINK wpModifyPopupMenu(XWPS *somSelf,
							HWND hwndMenu,
							HWND hwndCnr,
							ULONG iPosition)
   zString	  ModulePathName;             // DLL Module path and name
   HMODULE	  hmod;				// DLL MODULE handle

   /* XWPSData *somThis = XWPSGetData(somSelf); */

   // Get Module Name and Handle
   const IDynamicLinkLibrary dynid(cDllName); // dll name: OS2UG.DLL

   hmod = dynid.handle();

   // Insert "Hello OS2UG" into Open Menu
   if (!somSelf->wpInsertPopupMenuItems( hwndMenu, 0,
					   hmod, ID_OPENMENU, WPMENUID_OPEN))    {
	DebugMsg("Error in wpModifyPopupMenu()",ModulePathName );
   return (TRUE);
} // END wpModifyPopupMenu()
To retrieve the module handle of the DLL which contains the binaries for your popup menu I chose to use an instance of IDynamicLinkLibrary, a class of the UICL. The usual approach would be to call the SOM function somLocateClassFile(), but this doesn't work on my machine (your mileage may vary):

ModulePathName = (*SOMClassMgrObject)
DosQueryModuleHandle( ModulePathName, &hmod);
It would be a nice exercise for you to try both methods. <grin>

Overriding wpMenuItemSelected

Now you have decided to open the "Hello OS2UG" view and the WPS processes the overridden method wpMenuItemSelected() (see the file muimsel.cpp). Here you will call wpViewObject() to open a view. This function calls your application's frame window routine:

SOM_Scope BOOL  SOMLINK wpMenuItemSelected(XWPS *somSelf,
								HWND hwndFrame,
								ULONG ulMenuId)
    /* XWPSData *somThis = XWPSGetData(somSelf); */

    switch( ulMenuId )
	 case OPEN_XWPS1:
	    somSelf->wpViewObject( hwndFrame, OPEN_XWPS1, 0);
	 case OPEN_XWPS2:
	    somSelf->wpViewObject( hwndFrame, OPEN_XWPS2, 0);
	 case OPEN_XWPS3:
	    somSelf->wpViewObject( hwndFrame, OPEN_XWPS3, 0);
	    return (XWPS_parent_WPAbstract_wpMenuItemSelected
				(somSelf, hwndFrame, ulMenuId));
    return TRUE;

} // END wpMenuItemSelected()
Have you discovered that you can open three different views OPEN_XWPS1, OPEN_XWPS2 and OPEN_XWPS3? These start a frame window in either a separate thread, within the WPS thread of your notebook, or in a separate process, respectively.

Overriding wpOpen

Here you see the transition from the WPS world to your application's world, the "Hello WPS" frame window (see the file open.cpp). Here you call one of the CreateTUM_OS2_UGView*() methods, which are responsible for creating your frame window:

SOM_Scope HWND  SOMLINK wpOpen(XWPS *somSelf, HWND hwndCnr,
						ULONG ulView, ULONG param)
   /* XWPSData *somThis = XWPSGetData(somSelf); */

   switch (ulView)
     case OPEN_XWPS1:
	     ptrIcon = somSelf->wpQueryIcon();
	     return CreateTUM_OS2_UGViewA(somSelf);
     case OPEN_XWPS2:
	     ptrIcon = somSelf->wpQueryIcon();
	     return CreateTUM_OS2_UGViewB(somSelf);
     case OPEN_XWPS3:
	     ptrIcon = somSelf->wpQueryIcon();
	     return CreateTUM_OS2_UGViewC(somSelf);

   }	 // end switch (ulView)

   return (XWPS_parent_WPAbstract_wpOpen
		    (somSelf, hwndCnr, ulView, param));
 } // END wpOpen()

Creating Your Frame Window

If you're wondering why there hasn't been a lot of UICL usage, do not worry - to escape the WPS programming style a bit of work has to be done. But this was only a few lines of code; now you can start to fly. If you are ready to take off into the UICL heaven, let's begin.

Within the function CreateTUM_OS2_UGViewB() you have the choice to create your frame window similar to the PM style (here in pseudo code, the actual code is in view_b.cpp):

HWND CreateTUM_OS2_UGViewB(XWPS * somSelf){// view_b.cpp

	  get the module handle;
	  create the customized frame window;
	  extract the frame window handle;

somSelf->wpRegisterView(frame window handle, frame window title);
return (frame window handle);
}// end CreateTUM_OS2_UGView()
Or you may create a separate thread for your application with the advantage of programming your application as usual in UICL. First, you create an EXE file. After finishing, you rename the main() function to something else, which is called by the thread; finally, you change some lines of code as illustrated below (in pseudo code, the actual code is in view_a.cpp and threadm.cpp). Please compare threadm.cpp with main.cpp:

HWND CreateTUM_OS2_UGViewA(XWPS * somSelf){// view_a.cpp
	  set up a semaphore;
	  IThread myThread(threadMain, semaphore adress);

	  wait until semaphore is posted but no longer than x seconds;
	  obtain frame window handle;

somSelf->wpRegisterView(frame window handle, frame window title );
return (frame window handle);
}// end CreateTUM_OS2_UGView()

void threadMain(void * arg) // threadm.cpp
	  obtain the handle to the binary of the resource
	  create customized frame window;
	  extract the frame window handle;
	  post frame window handle;

}// end threadMain()
The third choice is to start your frame window as separate process. Here you have to use named semaphores. The pseudocode is identical with the threaded example with the exception that instead of a thread (see the file view_a.cpp) the process will be started by DosExecPgm() (see the files view_c.cpp and main.cpp). Here is where the second part of the project comes into play.

Look back at the discussion of the wpOpen() method: you may create a frame window in another thread, the same thread, as well as in another process within the same sample application. The best way to illustrate the differences is to start the clock with the second-hand enabled. I have programmed the frame window with the choice of "bigjob" being either multithreaded or running within the same thread. Start "bigjob" and examine what happens with both the second-hand and the WPS when you try to select a different application!

Now we will add some customized notebook pages to the WPS notebook. The page contains a "Welcome" bitmap and an MLE instance. [Note: the bitmap is a customized OS2LOGO.BMP, some of my friends got shocked seeing the "OS/2 Warp 2 Alpha" during start-up of my machine. It is only a joke; please do not send any email asking if I know any secrets about a new release of OS/2.]

Overriding wpAddSettingsPages

Here you see an excerpt of adsetpgs.cpp. It is that simple to add additional pages to your WPS notebook. Is it really that simple? Yes, but you need a workaround for some strange behaviour which I noticed on my machine at least. If I would omit the IString instance the notebook would be reduced in size; thus none of the notebook pages are shown in entirety (see the note in the file adsetpgs.cpp).

SOM_Scope BOOL  SOMLINK wpAddSettingsPages(XWPS *somSelf,
								HWND hwndNotebook)
   /* XWPSData *somThis = XWPSGetData(somSelf); */

   Boolean rc;

	s1("workaround for strange behaviour. Keep this string her!");

   // pages are added in reverse order, that is the last page first

   rc = somSelf->wpAddObjectGeneralPage(hwndNotebook);
   if (!rc) {
	DebugMsg("Error", "wpAddObjectGeneralPage()");
   } // endif

   addMLENotebookPage( hwndNotebook );

   rc = somSelf->wpAddObjectWindowPage(hwndNotebook);
   if (!rc) {
	DebugMsg("Error", "wpAddObjectWindowPage()");
   } // endif

   addFirstNotebookPage( hwndNotebook);

   return (rc);
} // END wpAddSettingsPages()
By principle any function to add to a UICL notebook page looks the same. Let's have a look on the pseudo code:

void addAnyNotebookPage( HWND * notebook)
	  prepare the notebook and the notebooks frame window handle;

	  if a UICL frame window proxy doesn't exist create one;
	  if a UICL notebook window proxy doesn't exist create one;

	  prepare a notebook settings instance;
	  add settings instance as first notebook page;
	  attach a customized page settings handler to the notebook;
} // END  addAnyNotebookPage()
The customized page handler takes care that the page is initialized only once, when the page is first shown. Only the page which will be shown will be instantiated. Thus the opening of a notebook is much quicker than you creating all page instances at once. I recommend this technique, because the user shouldn't be forced to wait too much.


So my friend, you have followed me to the end of the journey. I hope you will be able to create the killer application, fully integrated into the WPS with the help of the UICL. The UICL improves the programming of OS/2 allowing you to develop applications quicker and with less effort.


[1] Client/Server Programming with OS/2 2.1 by Robert Orfali and Dan Harkey, VNR / ISBN 0-442-01833-9

[2] OS/2 Warp Workplace Shell API by Mindy Pollak, Wiley / ISBN 0-471-03872-5

[3] OS/2 C++ Class Library Power GUI Programming with C-Set++ by Kevin Leong, William Law, Robert Love, Hiroshi Tsuji, and Bruce Olson, VNR ISBN 0-442-01795-2