═══ 1. Contents ═══ The SMART Programming Guide for Portability Version 2.1B September 1995 (C) 1991, 1994, 1995 One Up Corporation All Rights Reserved Topics: Introduction Portability Basics Factors Affecting Portability Designing code for Portability Windows Issues Windows Functionality Not Supported OS/2 Changes Summary CONTINUE ═══ 2. Introduction ═══ PROGRAMMING FOR PORTABILITY Programming applications for portability between platforms requires careful planing and decision making before the application is started. The new GUI based platforms make it convenient to program by providing rich interface API (Application Programming Interface) functions available to the developer for performing many of the GUI calls, along with memory management, I/O and other service functions. Unfortunately each platform has a different set of API functions to perform similar functionality. With the platform specific API calls completely imbedded and dispersed in the application code, porting to a different platform is time consuming and very costly. Worse yet, numerous uses of functionality that may exist in one platform, yet unsupported in another platform will require large portions of the application to be rewritten. Exploiting the powerful features of one platform may lead to a non-portable application. The ultimate case of non-portability is in the extensive use of third party libraries in object or executable code which does not support multiple platforms. The following sections will outline the steps to more portable programming for applications. Specific suggestions will be made regarding the functionality and issues that will cause difficulty in porting Windows applications to OS/2. By using the programming suggestions offered in this document you will be able to create programs that are more portable to new operating platforms. CONTINUE - Portability Basics BACK - Contents CONTENTS ═══ 3. Portability Basics ═══ BASIC RULES FOR PORTABILITY The rules for new application development which provide portability are straight forward.  Isolate as much of the platform dependent functions as possible. Although you may introduce an additional layer of code, you will find a great deal more portability in creating your own functional layer to isolate the platform.  Understand that exploitive features in one platform may not be portable to another. Plan around this reality if you need the portability.  Be careful in the use of third party libraries if you do not have access to the source code. You simply cannot port someone else's object files.  Choose a programming language that is supported with tools and compilers on the platforms that you wish to serve.  Research programming tools and code generators that support multiple platforms. In many cases these products can effectively generate the code for the platforms that you are targeting.  Develop a set of development and coding standards that can be used by the programming members of your project. This will help prevent any one of the developers writing platform dependent code.  Plan and make use of macros, ifdefined preprocessor code blocks and other coding techniques that may enable you to integrate multiple platform dependencies within the same source code files.  Stick to basics, where possible. Tricky solutions may depend upon platform dependent functionality and subtle side effects that may require thorough knowledege to port.  Make liberal use of coding comments so that the code can be changed to meet the requirements of another platform. Quite often the original programmer is not the one to modify the code for porting to a new platform. CONTINUE - Factors Affecting Portability BACK - Introduction CONTENTS ═══ 4. Factors Affecting Portability ═══ FACTORS AFFECTING PORTABILITY There are many factors that affect the difficulty of porting code from any platform to OS/2. The factors include:  complexity of the code  "cleanliness" of the code  exploited functionality in the source platform  use of third-party libraries  abstractness of the design  tools being used  experience level of the programmers porting the code. Most applications have evolved over several years and have been worked on by programmers of various levels of experience. The result is usually an application with many blocks of code which 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 is dependent upon the owner of the library providing a version for each platform. If there was one item which is recommended 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. CONTINUE - Designing code for Portability BACK - Basics CONTENTS ═══ 5. Designing Code for Portability ═══ DESIGNING CODE FOR PORTABILITY Most applications are designed with a single operating system platform in mind. Designing applications which can be ported to run on multiple platforms requires a little more forethought and understanding of the target platforms. The key to designing an application for portability lies in abstracting the various platform functionality that is required by the application. For example, memory management functions differ from one platform to another. However, the basic functionality of allocating and freeing memory is the same. By abstracting this type of functionality into individual modules or components the process of porting simplifies to replacing these abstract modules. The core functionality of your application remains the same. There will certainly be some functionality of a platform that you will want to exploit in your application. Multiple threads or long filenames may be something that you want to take advantage of in OS/2. Since this kind of functionality may not exist on another platform it is recommended that you place any exploitive functionality into a separate module. You will want to provide a function which checks the platform and indicates which of the exploitive features are supported on the current platform. That way the application can provide an alternate path of execution if the support is not available. The operating system services provide the necessary functionality to make your application work on a specific platform. By separating the core logic from the operating system services the changes are reduced to the modules which implement the abstracted functionality. Remember that the proprietary logic in your application is what differentiates your application from other applications. The following list identifies several items which should be abstracted in your application design to make platform portability easier. Keep in mind that all of these recommendations are for the purpose of minimizing the number of changes required to port your application to another platform. 1. Eliminate datatype dependencies. Wherever possible define macros for declaring datatypes and for accessing portions of the datatype. If a variable is to contain more than one piece of data (e.g., packing two words into a double-word variable) define macros which pack and unpack the data. Use the "sizeof" directive to determine size and offset of datatypes. 2. Use "message crackers" for accessing message parameters. Define macros which return the message parameter value that is being referenced. The format of messages differs between platforms. Using macros to return the requested message parameter value allows you to modify the macro for the ported platform and affect the change throughout the code. 3. Use "message forwarding" macros for sending and posting messages. Define macros which format and send or post messages to a window. The format and identity messages differs between platforms. Using macros to format parameter values allows you to modify the macro for the ported platform and affect the change throughout the code. 4. Abstract the memory-management functions into a separate module. Define a set of functions for allocating, reallocating, locking, unlocking, freeing, and querying memory. 5. Abstract the file input/output functions into a separate module. Define a set of functions for opening, reading, writing, closing, and querying files and directories. Include functions for searching, moving, renaming, deleting, and copying files. Also include file name parsing functions in the module. 6. Abstract the setting of graphics attributes into a separate module. Define a set of functions for setting and querying line, fill, bitmap, and font attributes. This includes color, style, and pattern attributes. Also abstract the creation, retrieving, and deletion of drawing areas (i.e., device-context or presentation space). 7. Abstract coordinate calculations functions into macros or functions. Coordinate systems can differ across platforms. Define a set of macros or functions for calculating vertical coordinates for graphics primitives. For example, define a function called "MoveDown" for calculating a y-coordinate which is positioned a specified distance below another coordinate. 8. Abstract graphic primitive functions into a separate module. Define a set of functions for performing the graphic drawing capabilities required for your application. For example, define functions called "DrawFilledRect" and "DrawPie" to draw a rectangle which is filled with a brush or pattern and to draw a closed arc. CONTINUE - Windows Issues BACK - Factors Affecting Portability CONTENTS ═══ 6. Windows Issues ═══ WINDOWS TO OS/2 IMPLEMENTATION ISSUES There are approximately 4700 points of difference between Windows (3.1) and OS/2 and this 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 even more amazing to realize that there are over 14,000 points of difference between Win32 and OS/2 2.1. It is not appropriate to list all of these items here. However, there are a number of functional areas found in common with most applications which cause the most amount of difficulty and effort in the porting process. While many of the areas have what appear to be simple solutions the practical aspect of it is that they can cause either a lot of rework or redesign to integrate properly into the ported code.  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 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 and 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 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 is 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 re-sized 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 and 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 tabstops 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. The setting of 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 which 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. CONTINUE - Windows Functionality Not Supported BACK - Designing code for Portability CONTENTS ═══ 7. Windows Functionality Not Supported ═══ WINDOWS 3.1 FUNCTIONALITY NOT SUPPORTED IN OS/2 2.1 The following categories of APIs or functionality are not directly supported in OS/2. These items require that you either rework the application to implement an alternative strategy or remove the functionality from the application to successfully port to OS/2.  OLE - Not available  DDEML - Not available  MDI - Use MDI library (see SMARTAUX\SMARTMDI\MDI.DOC)  DIB driver interface (DIB.DRV) - Not available  Direct printer driver interface - Not available  Selector-based memory manipulation - Conversion required  TrueType Support - Not available  Tabs in a listbox - Convert to ownerdraw listboxes  Common color dialog - Not available  Common find/replace dialog - Not available  Window Property APIs - Not available  Metafile enumeration APIs - Not available  VersionInfo API - Not available  LZ Compression API - Not available CONTINUE - OS/2 Changes BACK - Windows Issues CONTENTS ═══ 8. OS/2 Changes ═══ OS/2 1.3 FUNCTIONALITY SIGNIFICANTLY CHANGED IN OS/2 2.1 The following categories of APIs or functionality have changed significantly from 16 bit OS/2 to 32 bit OS/2. Most of the APIs listed are not available in the 32-bit version of OS/2. The semaphore functions have changed in name and definition, however the same capabilities are available. They do require changes to any existing semaphores being used.  Semaphore API conversions - Conversion required  DosFindNotifyFirst - Not available  DosFindNotifyNext - Not available  DosFindNotifyClose - Not available  WinMsgSemWait - Not available  WinMsgMuxSemWait - Not available  WinAvailMem - Not available  WinLockHeap - Not available  WinCatch - Not available  WinThrow - Not available  GpiRealizeColorTable - Not available  GpiUnrealizeColorTable - Not available  WinInstStartApp - Not available  DosPrintJobGetId - Not available  DosGetPPID - Not available  DosGetPrty - Not available  DosReadAsync - Not available  DosWriteAsync - Not available  WinRegisterWindowDestroy - Not available CONTINUE - Summary BACK - Windows Functionality Not Supported CONTENTS ═══ 9. Summary ═══ IN SUMMARY Developing applications that are portable between platforms requires planning and development adherence to techniques of functional encapsulation and isolation. Knowledge of the platform dependence of the source and targeted platforms helps to identify the functions that are best isolated into convenient, modifiable modules. RESTART BACK - OS/2 Changes CONTENTS