This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we've left these URLs in the text, but disabled the links.


December 1998

Microsoft Systems Journal Homepage

Build Reliable and Scalable N-tier Applications that Run on Both Windows NT and Unix

Download the code (5KB)

At the time this article was written, Mai-lan Tomsen was a program manager on the cross-platform COM team at Microsoft. She is currently working on an e-commerce service provided by QPass Inc. and can be reached at mai-lan@qpass.com.

It's gotten a lot easier to be a Windows NT® developer working on both Windows® and Unix platforms. Microsoft and major Unix vendors are cooperating to improve interoperability between COM and Unix. Working in a heterogeneous environment (with different operating systems, databases, and platforms) is a fact of life for most developers. Now you can use Microsoft COM to extend Windows NT-based n-tier applications to the Unix platform.
      This article describes how you can build reliable and scalable n-tier applications that work across Windows NT and Unix platforms. To help you understand the movement toward interoperability, I'll start off by explaining the relationships that Microsoft and other operating system vendors have established and what that means to you. I'll describe the extent of Windows NT functionality on Unix by explaining what COM support Unix vendors can implement and the conformance test suite that Unix implementations must pass. A thorough knowledge of the requirements for COM implementations on Unix will help you understand how your COM-based Windows app works on the Unix platform. I'll walk you through the steps of porting a Windows NT-based application to Unix, and describe options for developers using Visual C++® and Visual Basic®. Finally, I'll discuss other interoperability efforts that are underway and how you can use them in your cross-platform application.
      Several Unix vendors have committed to or are implementing COM support. See http://www.microsoft.com/com for the latest list of system vendors who have agreed to implement COM on their platforms. In this article, when I refer to Unix, assume that I am referring to the different Unix flavors that support or will support COM. VMS, MVS, and other non-Windows operating systems also fall under this rubric.

COM on Unix Basics
      Essentially, COM on Unix allows you to wrap existing logic on Unix in COM interface classes. You can also replace front-end legacy logic on Unix with COM or Microsoft Transaction Server (MTS) components running on Windows NT.
      If you're building COM-based apps in a heterogeneous environment, this kind of interoperability between Windows and Unix gives you many new options for development and deployment. Running COM applications on Unix extends the flexibility of your Windows NT-based enterprise app. Building COM wrappers for existing logic lets you call that logic from MTS or COM-based components running on Windows or Unix. Using another logical tier consisting of MTS servers as a front-end for legacy code provides even more advantages, such as:

  • Building more scalable applications
  • Keeping legacy code that works
  • Using the Windows NT programming model and improvements as the application evolves
  • Using familiar tools in Microsoft Visual Studio® (such as Visual C++, Visual Basic, and Visual J++) to develop server applications that use legacy code on Unix

      Let's examine each of these points in detail.
      Figure 1 depicts the traditional two-tier application architecture. In a traditional two-tier application, scaling is an expensive operation for both server resources and your budget. The client tier consists of "fat" clients, which contain the business logic for the application and have a much larger footprint than, say, an Internet client. Since the server hosts the data and the client contains the application, each client requires a database connection. Multiple users mean multiple connections, which degrades the performance and scalability of the application. Two-tier applications are also very expensive to scale in terms of hardware; generally, you have to acquire a more powerful database and host server to support an increased client load.
Figure 1  Two-tier Architecture
       Figure Two-tier Architecture


      Three-tier applications helped the scalability problem by moving the business logic out of the client and onto a separate tier (see Figure 2). As a result, the client tier consists of only "thin" clients that contain client logic to call the business logic appropriately. Thinner clients, whether they are browsers or standard applications, also simplify supporting and upgrading your application on the whole. For example, if you need to change some piece of business logic in your application, you modify components in the middle tier. You do not have to change and redeploy your clients unless you change the user interface.

Figure 2  Three-tier Architecture
      Figure 2 Three-tier Architecture


      On the business tier, MTS provides an easy programming model and ideal runtime for your business logic components. The MTS infrastructure allows you to share state within a process and pool resources like database connections. Even deploying clients is easier, since MTS provides an automated client-executable generation facility in the MTS Explorer, its graphical user interface tool.
      A three-tier MTS application provides much greater flexibility to grow your application for an increased user base through its programming model and MTS Explorer. For example, you can "scale out" your MTS application by installing MTS servers on multiple nodes. Multiple clients can call different installations of your MTS server, which allows you to balance your client load across servers. You can further enhance the performance of your application by sharing a single MTS transaction monitor, Microsoft Distributed Transaction Coordinator (MS DTC), between multiple MTS nodes. In a scaled-out scenario, you can divide 1000 clients into 10 groups of 100 users and have each group call an MTS server on a separate node. The 10 MTS nodes can then access a single database on an MTS node or a separate server. Moving the database connection relationship from one-to-one (client to database) to many-to-one (using MTS) allows you to scale your large production applications without rearchitecting them to fit an evolving user base.

N-tier Interoperability
      The next evolution of distributed applications is the n-tier architecture, which separates the architecture of your application into as many tiers as you need (see Figure 3). For applications that cross platforms, n-tier means building an additional logical tier as the front-end for your legacy applications.

Figure 3  N-tier Interoperability Architecture
       Figure 3 N-tier Interoperability Architecture


      Consider the following scenario. You're brought in to upgrade a parts inventory system for a car manufacturer. Your experience is in Windows NT-based development. You inherit a legacy CORBA inventory application on a Sun Solaris-based Unix machine. The application works, but has not been able to scale effectively with the user base. However, you want to avoid scaling up the app by upgrading to more powerful (and more expensive) hardware. The inventory data lives in an IBM IMS/CICS database.
      To further complicate matters, you'd like to use an MTS inventory system built with Visual Basic that you've already deployed successfully at another branch of the company. The solution is to meld the existing two-tier legacy application and the three-tier MTS application into an n-tier application. Essentially, you would be extending your three-tier MTS application from Windows NT to Unix.
      In this case, there are four logical tiers: the client running on either Windows NT or Windows 9x; MTS business objects running on Windows NT; legacy logic wrapped in COM classes running on Solaris; and data on an IBM IMS/CICS mainframe.
      For the MTS tier, you deploy and scale-out the inventory application by installing MTS on different nodes. You use the MTS Explorer to automatically generate client executables that point to different MTS servers and push the client EXEs to users through email, an intranet Web page, or Systems Management Server.
      To bridge from the Windows NT app to the legacy CORBA code on Unix, have MTS components talk over DCOM to COM-wrapped CORBA objects on Unix, or have MTS components talk to an object bridge on Windows NT, which talks to an object bridge on Unix to access CORBA objects. If you did not have to use CORBA objects, you could use COM Transaction Integrator (COMTI) to access the data stored on the mainframe directly, or wrap existing logic in COM interface classes.
      To create a bridge between the MTS business logic tier and the legacy application on the Solaris box, you can use object bridges supplied by IONA Technologies or VisualEdge, which will support DCOM as a wire protocol. COMTI for IMS and CICS in Microsoft SNA Server 4.0 lets you access data and participate in transactions on an IBM IMS/CICS database. Transaction interoperability based on Transaction Internet Protocol (TIP) is also in the works. I'll talk more about TIP when I discuss the interop effort on the data tier at the end of this article.
      To have your MTS components talk to the CORBA objects, you must write COM wrapper classes using Sun's C++ compiler or an equivalent compiler. Your MTS components will then be able to call the COM-wrapped objects on Unix. The result is an n-tier application that allows you to extend your Windows NT application to Unix. This lets you accommodate an increase in users by scaling out MTS servers and using legacy code at the same time. In addition, you can easily add transactional message queuing using Microsoft Message Queue (MSMQ) Server, or roll in Internet clients using the tight integration between Internet Information Server (IIS) 4.0, the Windows NT Server built-in Web server and MTS 2.0. It's also important to note that your new n-tier application is well-positioned to take advantage of future improvements in cross-platform interoperability.

Vendor Partnerships
      Product integration in a heterogeneous environment is not possible without committed relationships between an operating system and third-party vendors. Let's go over a few of the key partnerships in the COM/Unix interoperability movement. New partnerships with other vendors are continually in progress; if vendors that you work with are not discussed in this article, contact them directly to see if there's an agreement in the works.
      Microsoft provides direct support to vendors porting COM and is working with vendors such as COMPAQ (with their recent acquisitions of Digital and Tandem), Siemens Nixdorf Information Systems, and Silicon Graphics to port COM to their platforms. For Sun Solaris and AIX, Microsoft is going one step further by providing the COM port in its entirety. Independent software vendors like IONA Technologies and VisualEdge are also licensing the COM sources to build better bridges between Unix and Windows NT.
      To ensure compatibility across the different Unix flavors, Unix implementations of COM must pass a conformance test and fulfill a base set of performance requirements. Standardizing the level of COM support for different Unix flavors is key to making sure that Windows NT-based applications run across platforms reliably.
      If you're wondering exactly what code is being licensed for COM, it's simple—just about everything that you need to run a COM-based application. That means source code based on the COM specification (which describes the COM binary standard), Distributed COM (DCOM), OLE 2.0, as well as other services. Figure 4 describes COM services being ported to Unix in more detail.

The COM Conformance Test Suite
      Vendors' implementations of COM support must port all of COM and then pass COM functionality, system, and interoperability tests before product release. Since you're going to be running COM applications on Unix, I'll explain the standard of conformance for COM implementations on the various Unix flavors.
      The Microsoft conformance test suite is available to Microsoft's porting partners (the operating system vendors that Microsoft supports directly), or can be obtained with the COM sources from Microsoft. The conformance suite tests COM functionality by calling COM functions and a small subset of Win32® functions that COM requires. The test suite systematically runs COM functions and interfaces through simple functionality tests. Figure 5 describes the COM features and associated APIs that are tested.
      Worried about the completeness of COM conformance testing? Although the test suite checks as much COM functionality as possible, there are some special-case scenarios that require the Unix vendor to do more than a simple check of a return value. These limitations in coverage stem from ambiguities in the COM specification, in which some requirements are left undefined. For example, the suite does not specifically test pointers that are allocated with either CoTaskMemAlloc or IMalloc and are freed with IMalloc::Free or CoTaskMemFree (respectively) because that requirement for allocating and freeing is not stated in the COM specification.
      For cases like these, operating systems vendors are provided several test options to make sure that COM functionality is supported. Take the case of the CoGetCurrentProcess function, which does not return an error. The test suite checks for a return value from the Windows NT daemon. If the daemon is not running, the connection attempt fails, which in turn fails the call; the CoGetCurrentProcess call will return 0. It is impossible for the caller to distinguish between this 0 return value and a 0 value representing a correct return value. The program may think that the call works when in fact the daemon is not running. To solve this problem, the test suite documentation lays out several options. Implementors can do any of the following:

  • Assume that if CoGetCurrentProcess returns without crashing, then the test passes.
  • Call CoGetCurrentProcess twice and verify that the function returns the same value in each instance.
  • Spawn multiple threads. Each thread calls CoGetCurrentProcess repeatedly. The implementor can then verify that the function returned the same value for each call within the same thread. If each thread received a unique value and they are all 0, then the test passes.

      As you can see from the types of required tests with COM source licensing, Microsoft is interested in maintaining conformity across COM implementations. This helps ensure that the developers can obtain the same functionality for the COM application whether it's running on Windows NT or Unix.

Developing COM for Unix
      Previously, programmers of Windows NT-based apps had to learn a new platform if they wanted to work with an application on Unix. It was even more difficult for developers using Visual Basic because they had to learn a new development platform and a new programming language, since Visual Basic-based apps do not run on Unix. With the COM support that Unix vendors are providing, programmers can build cross-platform applications using tools that are familiar from building Windows NT-based apps. COM wrappers can be built using Visual C++ to wrap existing Unix objects.
      One of the more compelling reasons to take advantage of COM on Unix is the ability to use dual interfaces. If you implement IDispatch in your COM wrapper, your component will be supported in a Visual Basic-based or any scripting client. This allows you to expand your client-tier components to be hosted by C-based, Visual Basic-based, or Internet clients. On your business tier, you can use Visual C++, Visual Basic, or Visual J++ to build an MTS application that calls the COM-wrapped objects.
      Some COM and Visual Studio utilities that you may use on Windows NT are not available on the Unix platform. For example, COM's DCOMCNFG utility is not ported to Unix; you have to use REGEDIT instead. You also will not be able to use OLEView or any of the other OLE tools available in Visual Studio with your COM app on Unix.

Developing with Visual C++
      Developers who are building COM-based apps with Visual C++ can use the new COM/Unix interoperability in two ways. They can wrap existing Unix objects in COM interface classes or build COM applications and port them to Unix.
      Building a COM wrapper for Unix objects is simple. COM lends itself naturally to interoperability with legacy applications because encapsulation allows you to separate what the object looks like from its actual implementation details. At the most basic level, COM applications must follow implementation requirements for object lifetime and object inheritance at the interface level. You can provide access to objects running on Unix by writing an interface wrapper class that supports IUnknown. Keep in mind that the interface class only describes what you want the client to think that the underlying source looks like. As long as your client thinks the Unix object is a COM object, your client can instantiate, use, and release instances of the object on Unix just like any other COM object.
      The one significant coding change between COM applications for Windows and for Unix results from the lack of a resource compiler on Unix. The Visual C++ resource compiler creates a binary resource file based on the resource definition file. Since Unix doesn't have a resource compiler, you must add the binary data to the application code and create a resource table in the executable file's header by hand. (Note that dialog resources are not supported.) This data in the resource table should include the location, name, type, and language of each resource in the executable file. Figure 6 provides an example of hand-coding your resource table.
      Apart from the manual coding of resource table strings, you don't have to do much customization to make your COM application run on Unix. There are, however, a series of steps you must take to port your application.

Porting COM to Unix with Visual Studio
      If you're porting a COM application that you've built using Visual Studio to Unix, follow these steps:
Step 1 Create a Unix makefile like the one in Figure 7.
Step 2 Change file names to reconcile case differences between the platforms. While Unix is case-sensitive, Visual Studio is not, which can result in some unexpected errors. For example, Visual Studio generates a file called StdAfx.h upon compilation, but also includes stdafx.h. Unix will not recognize that the two names refer to the same file.
Step 3 Copy source files from an MS-DOS window to the Unix box. Note that MS-DOS and Unix represent the end of a line in a text file differently. If you do a simple copy over to Unix, it will interpret your text as one long line of text with garbage thrown in where you expected your lines to end.
      You can circumvent the Carriage Return Line Feed (CRLF) mangling by any one of the following methods:

  • Transfer the source code files using ASCII-mode FTP.
  • Copy files in binary mode and using Unix conversion tools (like DOS2UNIX or UNIX2DOS) on the Unix end.
  • Use InfoZip to transfer files. The –a option on unzip automatically converts the text files for Unix.
Step 4 Change WinMain to main since you're moving from a Win32 application.
Step 5 Change GetCommandLine since MS-DOS is not available on Unix. You can use the CreateCommandLine function in Figure 8 to create a command line.
Step 6 If you have used the ATL for development, you have to add values for all default arguments in the ATL-generated files. Note that some C++ compilers cannot compile ATL-generated files. Contact your C++ compiler vendor for more information about their ATL support.
Step 7 Remove any UpdateRegistryFromResource calls and add DECLARE_REGISTRY in the class definition so that your class can register using ATL. For example:

 DECLARE_REGISTRY(CAtlTest2,_T("AtlTest.AtlTest2.1"),
     _T("AtlTest.AtlTest2"), IDS_ATLTEST2_DESC, THREADFLAGS_BOTH)
Step 8 Compile the server. This generates two binaries: the server executable or DLL itself and the proxy stub DLL. You can differentiate them by the "ps" in the name of the proxy stub DLL. For example, if your server DLL is called libMyServer.so, the proxy stub DLL is named libMyServerps.so.
Step 9 Finally, register both the server and the proxy stub binaries by running regsvr, which is conveniently part of the COM implementation on Unix.

 regsvr libMyServer.so
 regsvr libMyServerps.so
Developing with Visual Basic
      Developers using Visual Basic have a more restricted scope with legacy code since Visual Basic-based applications don't run on Unix. These developers must already have legacy components wrapped in COM interface classes (using C++) in order to extend the functionality of the legacy application. Programmers using Visual Basic will not be able to write additional COM components and deploy them on a Unix platform. Instead, they must evolve the legacy application into an n-tier application using MTS.
      If the Unix objects are properly wrapped in a COM interface class, you don't have to do any magic to make your MTS components talk to the Unix objects. Since COM operates on the concept of polymorphism (clients treat all objects the same despite differences in object logic), all the MTS components care about is if they can get a pointer to the Unix object, instantiate it, call it, and release it. How the MTS component uses the data in the Unix object is your decision.

Interoperability on the Data Tier
      Although interoperability advances for cross-platform development are more recent, database integration across platforms has been underway for some time. Here's a list of what you can do right now on your data tier:

  • Make transactional calls from MTS components to any XA-compliant database that has provided a driver. Currently, SQL Server, Oracle, and IBM DB2 provide drivers that allow MS DTC to act as the transaction manager for MTS component transaction operations.
  • Access databases on IBM CICS and IMS mainframes from MTS using COMTI.
  • Talk to IBM MQSeries using MSMQ and a Level 8 bridge for transactional message queuing.

      Microsoft's ODBC provides a solid foundation for interoperability on the data tier by giving a single data interface for multiple SQL data stores. This driver layer lets you talk to Oracle, SQL Server, Sybase, or IBM DB2. Microsoft provides other powerful data access methods, as well. For example, the latest version of OLE DB (released with Microsoft Visual Studio 6.0) includes session pooling, auto transaction enlistment, OLAP, SQL Server, and Oracle native providers.
      OLE DB and ADO are exciting, new data access technologies that make accessing data stores much easier. You can use the ADO programming model as a layer over ODBC to access data sources. Not only is ADO much easier to program than direct ODBC, but it also offers you the flexibility of using disconnected recordsets. OLE DB also offers a more flexible programming model than ODBC, as well as letting you get at OLAP, spatial, mail, directory, and index services and text. These two key Microsoft data access technologies are an important part of n-tier architecture development. Using them with an n-tier application lets you take advantage of their current functionality and improvements in future releases.
      There are other related interoperability technologies that you can use in a heterogeneous environment. For example, Microsoft SNA Server 4.0 offers COMTI for IMS and CICS, which lets MTS or COM components access and participate in transactions on IBM mainframes using LU 6.2. (See the "COMTI Interoperability Details" sidebar for a quick example of the gory details behind interoperability technology.)
      The database interop story is improving as rapidly as operating systems are changing. Sybase and Informix are currently working on their XA-compliant drivers to be used with MTS. In addition, IBM is working on implementing direct support for MSMQ, which will greatly improve the performance of transactional message queues with IBM mainframes.
      Two interesting interoperability developments in the next generation of COM are the support for TIP and manual transactions (sometimes called "bring your own transactions"). TIP is a proposed Internet Engineering Task Force (IETF) standard. Unlike previous IETF standard protocols, TIP is a very simple two-phase commit protocol; TIP only specifies how different nodes agree on the outcome of a transaction. The actual subject matter on which the nodes agree moves through other protocols like HTTP. A transaction manager (like MS DTC) can use TIP on one Internet node to communicate with a transaction manager on another node.
      Manual transactions let an MTS component that is not already associated with a transaction to acquire one from another transaction manager. In MTS 2.0, component transactions are automatic. Transactional attributes are set at deployment, but the MTS runtime controls transaction lifetimes (as part of resource pooling). In the next generation of COM, you will be able to manually associate a preexisting transaction with a component. This means that COM components can be associated with transactions whose lifetimes are controlled by a TP monitor, OTS, or DBMS.
      Other vendors have already announced their planned support for TIP or manual transactions. Compaq plans to use TIP to integrate MTS and Digital ACMS. Digital ACMS users on Digital Open VMS, Digital Unix, and Windows NT will be able to participate in MTS transactions through TIP. IONA Technologies is using TIP to integrate its transaction monitor (OrbixOTM) with MTS so that transactions can start in MTS components or OrbixOTM/CORBA objects and flow between platforms.

Bringing Interoperability to Users
      COM interoperability extends your Windows NT programming knowledge and applications to the Unix platform. As a developer, you benefit in several significant ways from the new interoperability. First, you can scale out legacy Unix applications by adding one or more MTS business tiers and avoid expensive hardware upgrades. Second, you can wrap existing Unix objects in COM classes. Finally, you can rearchitect your application into n-tiers without rewriting all your components.
      These are just the immediate benefits. Interoperability relationships continue to develop with agreements to be announced between Microsoft and platform vendors, database vendors, and other third-party vendors. The results of these relationships will extend the interoperability of Windows NT-based applications to a wide variety of platforms and products.
      Ideally, you want your users to be as polymorphic as COM clients; they shouldn't have to care if their application runs on Windows NT or Unix—as long as the application works the same. By extending your COM/MTS applications onto Unix, you can scale and improve legacy applications without disrupting your users.
See the "Windows NT Security on Unix" sidebar

From the December 1998 issue of Microsoft Systems Journal.