Porting Windows Applications to OS/2: Problems and Solutions

By Jeff English

Porting Windows application source code to native OS/2 code is doable! The process of analyzing, porting, compiling, and testing the code, as well as exploiting the OS/2 environment, may seem like an overwhelming task. That's why I wrote this article - to dispel the fears, to show that it can be done, and to discuss how One UP Corporation's porting tools can help.

Before discussing what is involved, it must be understood that there is no magic header file that will port your code automatically. While there are many similarities between Windows and OS/2, there are many more conceptual and architectural differences that make such a header file improbable.

The development of applications that can run on multiple platforms can be done, with a limited number of possibilities. Cross-platform API libraries and application frameworks (such as XVT and zApp) that enable you to develop for multiple platforms are really only useful for new development, and you become dependent upon the vendor for updates and support of other platforms.

Given that you already have a Windows application that must be ported, another option is the Mirrors tool kit. The Mirrors tool kit provides a layer that implements the Windows functionality either internally or via multiple calls to OS/2. While the Mirrors tool kit was designed as a tactical solution for quickly migrating an application to OS/2, it is not intended as a long-term strategic solution.

The only other solution is to port your existing Windows application to native OS/2 code. Doing so not only allows you to convert to a true 32-bit application, but allows you to exploit the 32-bit memory management, pre-emptive multitasking, and multithreading capabilities of OS/2. The result is an application that is faster and better integrated than any of the other possible approaches. Getting there, however, is not necessarily an easy task.

The Porting Process
Porting existing code is a much more difficult process than writing a new application from scratch. Unless you wrote the code being ported - which usually isn't the case - you can't tell whether the code that you are modifying is correct until the entire application has been ported and compiled. There is not the immediate feedback of windows being created, manipulated, and drawn upon that is available when a new application is developed.

The process of porting Windows applications to OS/2 can best be described as a non-linear process. The methodology and techniques applied to the porting process have a greater impact when several passes are made on the code. Each pass, or phase, attacks more complex problems than the previous pass.

Figure 1 illustrates the recommended flow of porting activity, where each block represents a separate pass on the source code. During the analysis phase, and all the way through the platform-specific phase, specific items and characteristics of the application are identified to better tailor the porting activity. ''Figure 1. Recommended Flow of Porting Activity''

Analysis
During the analysis phase, the entire application's source code is scanned for Windows keywords. This analysis provides a quantified look at the code, and gives the necessary information to size, staff appropriately, and schedule the porting activity.

A detailed analysis involves classifying all of the functions, messages, typedefs, and symbols into various categories of difficulty, from easiest to hardest. The easiest ones are those that have a one-to-one correlation in OS/2; the hardest ones are those that have no equivalent definition in OS/2 (e.g., DDEML).

There are approximately 4,700 points of difference between Windows and OS/2. A point of difference is defined to be a function, message, typedef, or symbol that is defined in one of the Windows header files, and must be converted when used in OS/2. The difficulty of each item is classified in one of six categories, as shown in Figure 2. ''Figure 2. Six Conversion Categories''

During this phase, the code is also scanned for items that may cause difficulty in porting from 16-bit to 32-bit code. For example, structures that contain "int" data types and send output to a file should be changed to a "short" data type if file compatibility is a concern.

Code Replacement
The code-replacement phase uses automated tools to quickly convert all category 010 and most category 020 items. The automated replacement of these items eliminates the very time-consuming process of manually changing each and every one of these items. As shown later, about 74 percent of all keywords that are identified for porting are in category 020 or less.

Another very time-consuming process is the conversion of resource files (e.g., dialogs and menus). The amount of time and difficulty will depend upon the number of dialogs and other resource types that must be converted. During this phase, the resource files should be automatically converted to OS/2 resource files.

API Macros / Functions
Many Windows functions are classified as categories 020 or 030, and they occur several hundred times in the source code. If any of these functions performs an immediate action (e.g., GlobalAlloc) and doesn't rely on the state of other parameters or objects (e.g., SelectObject), it may be desirable to simply implement many such functions internally. This provides the immediate benefit of not having to modify every occurrence and to duplicate multiple OS/2 function calls. It also provides the longer-term benefit of making it easier to debug and modify the changes.

Code-Assist Replacement
Many changes that are identified as category 030 or 040 are similar in nature throughout a specific application. In many cases, the change that is made for one occurrence of a keyword can be applied to several other occurrences of the same keyword. A tool that provides the capability to note the way in which a change is made to one item, and can replay the same sequence to another occurrence of the same item, is useful at this point. Other types of changes can be made easier by having a tool that can provide a list of selections, information about required parameters, or templates for the ported code.

Unsupported Features
There will most likely be some features of the Windows environment that are not directly supported in OS/2. These category 050 items must be resolved by the programmer, by either removing the functionality altogether, or implementing an alternate strategy to perform a similar function.

Platform-Specific Features
The addition of features specific to OS/2 usually occurs after the application is ported and running. It is not recommended to attempt to add multithreading or Workplace Shell capabilities while the code is partially ported and in an unstable state. However, when an area for adding these capabilities has been identified, you may want to tag certain portions of the code as it is being ported. During this later stage of the port, you can add OS/2-specific features to make it a more marketable or a more tightly integrated product. You should try to add platform-specific code in a separate module or DLL.

Factors Affecting the Porting Process
Many factors affect the difficulty of porting code from Windows to OS/2. They include: the complexity of the code; "cleanliness" of the code; how much and to what extent the Windows environment was exploited; the use of third-party libraries; the abstractness of the design; the tools being used; and the experience level of the programmers doing the port.

Most Windows applications have evolved over several years, and have been worked on by programmers with various levels of experience. The result is usually an application with many blocks of code that have similar functionality but use a completely different set of API calls. While programming standards may be in place now, the standards most likely have changed over the years, and the strictness to which programmers stick to those standards, is questionable at times.

Many Windows applications also get away with using memory and object handles after they have been released. This will cause a problem when the code is ported to the protected-memory environment of OS/2. A good guide for helping to identify such a problem is to run your application using the debug version of Windows.

The use of third-party libraries in an application makes it extremely difficult to port your application to OS/2 or any other platform. The future portability of the application depends upon the owner of the library providing a version for each platform.

If there is one item that I would recommend when designing an application, it would be to try to use abstract design techniques. Whenever a system level, third-party library, or operating system-dependent function is required, it should be given a functional name and placed into either a separate source file or library. For example, instead of calling GlobalAlloc every time that memory is allocated, create a function called AllocateMemory. This will centralize the porting activity to the single function, and will enable the main logic of the application to remain mostly intact during the porting process.

Ultimately, the success and amount of time it takes to complete a Windows-to-OS/2 port comes down to the experience level of the programmers and the tools used in porting the code. Ideally, you would want programmers who have a wide range of experience in both Windows and OS/2. Given that there are a limited number of programmers with this kind of experience, it is better to have a programmer with more experience in OS/2 than in Windows.

Porting Tools
Tools that assist the programmer in modifying the code and suggesting possible changes can speed up the porting process. The proper tools can, to some extent, reduce the need for experts on both the Windows and OS/2 platforms.

The Source Migration And Reporting Tool (SMART), developed by One UP Corporation, has been used to analyze several large Windows applications. In addition to reporting the number of points of difference (hits), the tool also reports the relative difficulty of porting an application. Obviously, category 020 items require more effort than category 010; category 030, more effort than category 020; and so on. The average of all the data collected leads to a general observation about the distribution of the category of items, and where the effort must be spent in porting an application.

Figure 3 shows a graph of the frequency of occurrence of each category of items found, and the distribution of the effort involved in porting each category. The accompanying data for the graph is shown in Figure 4. For example, 26.5 percent of all hits are category 020 items, but this amounts to only 17.7 percent of the effort. ''Figure 3. Occurrence of Each Category and the Porting Effort Involved'' ''Figure 4. Occurrence of Each Category and the Porting Effort Involved''

All category 010 items and most category 020 items can be ported with an intelligent global search-and-replace utility. While this resolves 73.3 percent of all items to be ported, it would only complete 17.7 percent of the required work effort. For the remaining 82.3 percent of the work effort, a set of tools is needed to identify the items to port, recommend the appropriate OS/2 code to replace the Windows code, and allow you to hyperlink among all occurrences of an item.

SMART can be used to migrate source code by inserting templates, code samples, prototypes, and comments whenever a Windows point of difference is encountered. Using a hyperlink editor, such as SourceLink, a programmer makes multiple passes on the source code, first attacking the category 020 items, then category 030 items, and so on. In the very near future, SMART will have added intelligence to perform the code-assist replacement functionality of the porting process.

In addition to porting source code, SMART also includes a utility for converting Windows resource files to OS/2 resource files in native format, and for converting Windows icons and cursors to OS/2 icons and cursors. OS/2 currently recognizes many of the Windows bitmap formats. However, if you have a bitmap that is not recognized by OS/2, there are several bitmap conversion utilities available, including the OS/2 Multimedia Image Converter.

Windows-to-OS/2 Implementation Issues
As mentioned earlier, there are approximately 4,700 points of difference between Windows and OS/2, and this number doesn't even cover the changes required for conversion from 16-bit to 32-bit code, or the use of third-party libraries. It is not appropriate to list all of these items here. However, there are a number of functional areas, found in most applications, that cause the most amount of difficulty and effort in the porting process. While many of these functional areas have what appear to be simple solutions, they can actually require either a lot of rework or redesign to integrate properly into the ported code. Here are some of these functional areas:
 * Resource files must be converted from Windows to OS/2 format.
 * Dialog units in Windows are calculated based on the font used in the dialog, while dialog units in OS/2 are calculated based on the default system proportional font.
 * Font names and selections must be converted to those available in OS/2.
 * Menus for dialogs must be defined at run-time, not in the resource file.
 * Icons, cursors, and bitmaps must be converted to an OS/2-recognized format.
 * Multiple Document Interface (MDI) must be implemented in the application.
 * The frame window, frame-control windows, and client windows are all separate windows in OS/2. Subclassing must be performed in OS/2 to monitor and process non-client window activity.
 * Windows brushes and pens must be converted to area- and line-bundle attributes. Different OS/2 functions are used for drawing filled and non-filled objects, as well for as drawing a nominal-width line versus a wide line.
 * APIs for drawing an arc, chord, pie, etc. require several function calls in OS/2.
 * The OS/2 coordinate system has lower-left origin, versus Windows' upper-left origin. All coordinate calculations for positioning windows and drawing must be converted to be relative to the lower-left corner. If window positions are to remain relative to the upper-left corner when a parent window is resized, code must be added to reposition child windows.
 * The CS_CLASSDC class style is not supported in OS/2.
 * Regions are bottom-right-exclusive in Windows, but top-right-exclusive in OS/2.
 * The bits for monochrome bitmaps in OS/2 are reverse from those of Windows.
 * Windows metafiles must be converted to OS/2 metafiles. Metafile enumeration is not directly supported in OS/2.
 * Applications have less direct control over printing properties in OS/2. Applications should use the job properties dialog to allow users to modify the printing characteristics.
 * Support of tab stops in a listbox is not available in OS/2. Columns are supported in OS/2 by using the container class.
 * Cursors are not registered with a window class in OS/2. Setting the pointer in OS/2 is performed during the WM_CONTROLPTR or WM_MOUSEMOVE message.
 * Background brushes are not registered with a window class in OS/2. A background-fill color can be set by setting a presentation parameter for the window.
 * Control activity messages are received in a WM_CONTROL message instead of the WM_COMMAND message.
 * Processes must explicitly gain access to shared memory, and all processes having access to shared memory must free it before the memory is freed. This differs from Windows, where the shared memory is freed when the creator of the memory frees it.
 * Memory and window classes allocated and registered by a DLL are not available to other processes. In Windows, the ownership of window classes and memory objects is based on the code-segment that registered the class or allocated the memory. In OS/2, all resources, classes, and memory objects are owned by the process that allocated or registered them.
 * OLE is not supported in OS/2.
 * There is not a color or print common dialog in OS/2. The options available for the font and file common dialog differ from Windows.

Simplify and Expedite Porting with SMART
Porting of source code from Windows to OS/2 is not an easy process. There are always architectural components of the Windows environment that are used by an application and require a programmer to decide the direction of the implementation in OS/2.

Designing an application with portability in mind means abstracting the design to the point that system-dependent and third-party library functions are kept separate from the integral logic of the application. Simplifying and expediting the porting process is possible, and in fact being done today, with tools like SMART.