IntroductionMany software companies are now developing Component Object Model (COM) applications on the Windows NT platform. COM is a language-independent component API developed by Microsoft. COM is not a part of UNIX®, thus it simply is not possible to compile and link COM applications on the Solaris Operating Environment (OE). However, some third-party software companies have licensed COM from Microsoft and have created UNIX implementations that support the building and execution of COM applications on the Solaris OE. This report deals specifically with a pilot project that was conducted at a major software development company (referred to subsequently as "the client company") in the United States. The purpose of this project was to investigate the feasibility of creating a port to the Solaris platform of a COM application that had been developed solely on the Windows NT Visual C++ development environment. Since COM does not exist on the Solaris OE, it was necessary to use a third-party COM implementation to enable code written for Windows to be built and run on the Solaris platform. Mainsoft and Software AG are two companies that sell and support COM implementations for the Solaris OE. Both companies were considered for this project, but for reasons that will be discussed later, the project used Mainsoft. Two other COM implementations became available after the inception of this project: COMsource, from The Open Group, and XPCOM, from Netscape. Another company, Bristol Technology, also offers a Solaris COM implementation, but at the time of this project, Bristol was engaged in a lawsuit with Microsoft. Certainly, any future COM-porting projects should review the product offerings from all of these companies. Executive SummaryThis project deals with the porting of the lowest level of the client company's code base. This portion of the application contains the code used for the generation and manipulation of geometry and spatial references. The functionality contained at this level is used throughout the entire spectrum of the client company's applications. These applications are COM intensive and use many coding techniques and Win32 function calls specific to the Microsoft environment. Several obstacles had to be dealt with in order to successfully complete this project. The most important was the difference between the two compilers used, Microsoft's Visual C++ and Sun's SC 4.2 compiler for the Solaris OE. Many coding constructs and compiler options used in the Microsoft world are not ANSI standard and thus not supported on the Solaris platform. Also, the company made extensive use of Microsoft-specific extensions to the C++ compiler that do not exist on Sun's C++ compiler for the Solaris platform. Neither Mainsoft nor any of the other Win32 on UNIX implementations supports other APIs such as DAO (Data Access Objects). If an entire product line is to be fully supported on the Solaris OE, something will need to be done to replace the unsupported Windows-specific APIs with ones that will perform the same functions on UNIX. The project revealed that making changes to the application's code helped avoid many of the compiler problems. Also, removing Microsoft-specific extensions and coding constructs can simplify the porting effort. In addition, changes could be made to Sun's 4.2 compiler for Solaris systems in order to support some of the ANSI standard functionality that it currently does not support. Some problems disappear when using the Mainsoft Version 3.3 Build 67 release of MainWin, which supports the 5.0 compiler for the Solaris platform. Moreover, some changes also need to be made to the MainWin code to better support Native COM on the Solaris OE. (Please note: the 5.0 compiler shipped as part of Sun WorkShop compilers, while 6.0 was a Forte product, and the 7.0 compilers are available as Sun ONE Studio 7.) The length of time it takes to port an application is directly related to the amount of Microsoft-specific extensions that are used in the application. First, you must determine how the extension is used in the application, and then you must implement an appropriate workaround to perform the same function in the Solaris OE. If the application uses tools that automatically generate code during the build process, this too will increase the porting time, as you must find another means to produce that code on UNIX. We would urge any ISVs to eliminate the use of Microsoft-specific extensions and code generation so that they can produce applications on any platform from a single source tree. Once we overcame all of the obstacles to compiling and linking, the shared objects were built and registered on the Sun system just as they are on Windows. MainWin provided modified versions of the Microsoft tools used to perform these functions. In order to test the execution of the COM objects, we used a sample drawing program written by a developer. The program worked correctly and had the same look, feel, and functionality as it had on Windows. Performance numbers were not available since the sample program was quite small. This effort proved to be highly successful in showing that a mature COM application built entirely in a Windows NT environment could be ported to the Solaris OE and function correctly. MainsoftMAchine INdependent SOFTware (Mainsoft) is an Israel-based company that specializes in providing means of running COM and MFC (Microsoft Foundation Class) applications on UNIX platforms. Mainsoft's chief product is MainWin, which is used to run Windows on UNIX.
Mainsoft provides hotline support with access via e-mail. The MainWin product also includes a tool called "mwspr" that allows the developer to log problems. The goal of this project was to see if a software company could establish concurrent builds of its product on both NT and the Solaris OE. Mainsoft promoted the use of its engineering services department to do a one-time port of the NT code to the Solaris platform. Engineering services are separate from developer and runtime support, and thus require funding from the ISV, a hardware partner, or an end user. Having Mainsoft port the code would only be helpful if the modified source was returned to the ISV so that the changes could be integrated into the code base. This would allow for compilation on both NT and the Solaris OE from a single source tree. Other COM Implementations
About COMCOM allows for communication between components via binary interfaces. These components can be created from a number of different programming languages, such as C++, C, Java, Pascal, and so on. Components exist as Dynamic Link Libraries (DLLs) or as executables, and are connected through interfaces. A COM interface is a specific memory structure that contains an array of function pointers. Each array element contains the address of a function implemented by the component. In C++, COM interfaces are implemented using abstract base classes that contain virtual functions. Components are configured in the registry and are loaded and deleted by the application as needed, and reference counting is used to enable components to delete themselves when they are no longer required. The most common method of controlling reference counting is to use Smart Pointers, which can be constructed in several ways. One of the more popular methods of constructing Smart Pointers is to use the Interface Definition Language (IDL) to describe interface data, which is then compiled with the MIDL compiler to create a type library. The type library is a binary header file that the Visual C++ compiler imports when the components are compiled. The interfaces are created along with the references to the Smart Pointers. A Globally Unique Identifier (GUID) is a 128-bit structure used to uniquely identify components and interfaces. Utilities exist that automatically generate GUIDs. These GUIDs contain information about the computer on which they were generated and the time they were generated, guaranteeing a unique number in the universe. When an interface is modified, a new GUID must be generated and used with the new version of that interface. Two good references on COM are Inside COM by Dale Rogerson (ISBN 1-57231-349-8) and Essential COM by Don Box (ISBN 0-201-63446-5). Experience at the Software Development CompanyPorting EnvironmentInitially both the Mainsoft and Software AG COM implementations were considered for this particular project. Installing Software AG was quite involved and took over half an hour to complete. Although Software AG gave us a free evaluation copy of its product, it did not provide any documentation for an installation on the Solaris platform. To complete the installation on the Solaris OE, we had to follow Software AG's standard installation procedure and then rely on some notes that had been faxed to us by its staff. By comparison, the installation of Mainsoft's MainWin product was very clean and simple. Mainsoft provides a single script that installs the entire product from the CD. We decided to begin the porting effort using MainWin because it appeared to be the cleanest implementation of the two products, and because it had the MFC support.
Mainsoft provided a C-shell to set up its environment variables. In order for this to work, a MainWin home environment variable had to be set first. To do this, we set up a script to set MainWin's home environment variable,
One of the first things we did was compile and run the examples from the book Inside COM. This helped us become familiar with the MainWin scripts and the options that were used to create a makefile. Note: To avoid name mangling that can occur with C++ compilers, it was absolutely necessary to make sure that there was an Initially, the project was using MainWin Version 3.1 Build 65. Supporting Smart Pointers required three modified header files and an upgrade to MainWin Version 3.2 Build 66. The modified header files were downloaded from the Mainsoft web page. Version 3.2 of MainWin is compatible with Sun's 4.2 compiler for the Solaris platform, but does not work with Sun's 5.0 compiler for Solaris. The three files that we downloaded to enable support of Smart Pointers were:
comip.h
comdef.h
comutil.h
They were contained in the tar file:
nativecom.tar.Z
These files replaced their old versions in the directory:
/usr/local/mainwin/userx/public/sdk/inc/
Smart Pointer Implementation
The first problem to resolve was that of the Smart Pointer implementation. Smart Pointers are used to handle reference counting in a COM application, and are used extensively in the application's code. Typically Smart Pointers are defined in
There is no
Fortunately, the
As part of the client company's implementation of Smart Pointers, we made changes to the file
We encountered a forward referencing error when compiling the
Another problem was that the named GUID constants' initialization statements were declared Compiler DifferencesThe next group of issues has to do with functionality that is not available on Sun's 4.2 compiler for Solaris.
The Microsoft Visual C++ compiler recognizes several
#ifndef __filename_h__
#define __filename_h__
body of code...
#endif
Other The Solaris compiler also does not recognize the
xMin = NumConst;
was changed to:
((WKS*)this)->xMin = NumConst;
in order to "cast away" The 5.0 compiler for Solaris supports
namespace SUN {
class Micro {}
}
The class would be referenced either by specifying:
SUN::Micro {}
or by having the line:
using namespace SUN
appear before any references to the Micro class in other files.
One of the client company's directories used Building the ApplicationOther items necessary for building the COM application have to do with modifications to the makefile and files that need to be created in order to hold information important to the use of Smart Pointers. Some parameters that needed to be set in the makefile to add support for specific functionality were:
When declaring interface pointers as Smart Pointers, there was a problem that caused the program to call incorrect functions. For this reason, lines that read like this:
IWin32ShapePtr ipPixelGeometry = pPoly:
had to be rewritten like:
IWin32ShapePtr ipPixelGeometry(pPoly);
and the line:
ATL_DECLARE_UUIDOF(IWin32Shape)
was added to the The first line in the file was:
#include <atldef.h>
with lines such as:
ATL_DECLARE_UUIDOF(IPersistVariant)
used to declare the interface pointers. The line:
ATL_MODULE = 1
had to be added to the makefile in order to specify that the ActiveX Template Library be used. Issues Specific to the 4.2 Compiler for the Solaris PlatformIssues arose with the Solaris 4.2 compiler regarding the forward referencing of a template class. In order to compile correctly, the line:
friend class ClassFactory;
was changed to:
friend class ClassFactory<class T>;
Declaring templates in the private area of a class is not allowed on Solaris. These declarations had to be moved to an area of the code where they could be seen globally.
The Solaris compiler does not allow functions that are defined in private areas as The Solaris compiler does not seem to fully expand some nested macros. There are instances where the nesting is three or more levels deep and the compiler does not understand the macros. As a result, it was necessary to manually expand the code so that the compiler could understand it. The Solaris compiler does not support the use of explicit functions. To address this issue, this keyword was simply removed everywhere it was found. The phrase "Overloading Ambiguity" was written out by the compiler in several different areas. Performing the proper casting often remedied this problem, as shown in the following example: The line:
_name = (_bstr_t) _variant_t(*name);
was modified with a temporary variable:
VARIANT tmp_var;
{
tmp_var = _variant_t(*name);
_name = (_bstr_t)tmp_var;
}
Another instance of Overloading Ambiguity had to do with the inline bool operator != (ISpatRefPtr & ipSR1, ISpatRefPtr & ipSR2) The not equal operator had to be renamed in order to avoid the conflict: inline bool operatorNotEqual (ISpatRefPtr & ipSR1, ISpatRefPtr & ipSR2) There were several places where inline functions were not allowed because the body of the function had not yet been defined at the time the function was referenced. In many instances the The Solaris compiler does not recognize an anonymous structure within an anonymous union declared in the private area of a class. To handle this issue, both the union and the structure had to be named; then those names were added to the references in the code. Some of the statements using the
bFromOnNode ? pEdge->u.s.m_FromNode = 1
: pEdge->u.s.mToNode = 1;
This needed to be changed to:
bFromOnNode ? (pEdge->u.s.m_FromNode = 1) :
(pEdge->u.s.mToNode = 1);
There are cases where the compiler "complained" about the left side of the equation not being an
(unsigned char)(*(m_args[paramCount-1].pbVal)) = retVal.bVal;
The following modified line compiled correctly:
*(unsigned char*)(m_args[paramCount-1].pbVal) = retVal.bVal;
Another example of a problem with an lvalue showed up as an error related to the & operator. In this case, the compiler complained about the following line, stating that the & operator could only be applied to a variable or other lvalue. This confusion may have been caused by the fact that there were different
pTangent->PutCoords(&Point(tangentFrom),
&Point(tangentTo));
The code had to be broken up so that the & operator was applied only to properly declared lvalue variables and the correct Point function was used:
ISV__Point from(tangentFrom);
ISV__Point to(tangentTo);
pTangent->PutCoords(&from, &to);
The client company uses a Microsoft shortcut when reinitializing the members of an anonymous structure within an anonymous union. In the following code, m_Bits(0) zeros out the remaining members of the structure:
AbcImpl::AbcImpl()
: m_Bits(0)
{
m_IsEmpty = 1;
}
This code had to be rewritten so that each member of the structure would be addressed individually. The following code also accounts for the namespace issue and shows how the union and structure were named:
ISV__AbcImpl::ISV__AbcImpl()
{
u.s.m_IsEmpty = 1;
u.s.m_AnglesValid = 0;
u.s.m_RadiusValid = 0;
u.s.m_IsCCW = 0;
u.s.m_IsMinor = 0;
u.s.m_IsLine = 0;
u.s.m_IsPoint = 0;
}
There were places in the code where a
polygonGraph.Load(IGeometryPtr(this), false);
which needed to be expanded to resolve inheritance along with adding the
polygonGraph.Load(static_cast<IGeometry*>
(static_cast<IPolygon*>(this)), false);
Removing a
if ((from.x * from.y > c_Fudge) == static_cast<bool>(u.s.m_IsCCW))
had the
if ((from.x * from.y > c_Fudge) == (bool)(u.s.m_IsCCW))
Several of these problems can be fixed by modifying the client company's code so that it will compile on both the Microsoft Visual C++ and Solaris compilers. This is preferable to trying to modify the Solaris compiler to compile the Microsoft-specific code. Issues Specific to Microsoft FeaturesSome functions that are used by the client company are specific to the Microsoft world. Most of them have names that are slightly different on UNIX. Also, some definitions do not exist on UNIX.
MainWin has added a definition for the function ...mainwin/userx/public/sdk/inc/crt/io.h and had to be included with ifdef statements:
#if defined(unix)
#include <io.h>
#endif
The function name
#include <math.h>
had to be added to any file that uses the The Visual C++ compiler allows for the use of optional variables in a function argument list. Adding the following line to the top of the
_variant_t vtMissing (DISP_E_PARAMNOTROUND, VT_ERROR);
The definitions:
UINT32
__int16
__int32
do not exist in UNIX or in the MainWin definitions, and were defined respectively in the client company's code as:
UINT
_int16
_int32
Issues with Mainsoft
In order to deal with compiler errors that result from the use of large integers, instances of
foo->LowPart
foo->HighPart
need to be changed to:
foo->u.LowPart
foo->u.HighPart
The following line did not compile and complained about an "unexpected type name" when initializing a static template variable:
template
const unsigned short
BaseArray::c_ElementSize = sizeof ELEMENT;
Simply placing () around the word
template
const unsigned short
BaseArray::c_ElementSize = sizeof (ELEMENT);
We investigated this problem with different Solaris compilers on different systems, and it was only found to be a problem when compiling in the MainWin development environment. Also, there is no support of DAO by Mainsoft. ConclusionWith the use of third-party software such as MainWin, it is possible to compile and execute true COM applications on the Solaris platform. While Java technology or any open API is the correct solution to developing platform-independent applications, porting COM applications to Solaris provides companies with an interim method of offering products on Sun systems. This project has shown that a company that develops COM applications on Windows can easily expand its customer base by offering fully functional versions on the Solaris platform. ResourcesSolaris Developer Connection has an NT Migration section with information on porting issues, post-porting tasks, migration tools, and more. About the AuthorCraig Winter is a member of the technical staff in Sun's Market Development Engineering group. For over six years he has been specializing in ISV application support for various MCAD, MCAE, and GIS vendors. | ||||||||
|
| ||||||||||||