Integrating WCF Services with COM+

Chris Peiris (Avanade Australia, www.ChrisPeiris.com)
Shawn Cicoria (www.CedarLogic.com)

 

February 2007
Revised August 2007

Applies to:
   Microsoft .NET Framework 3.0
   Windows Vista
   Microsoft Internet Information Services
   Microsoft Visual Studio 2005

Summary: This article will detail step by step instructions to consume COM+ application services from WCF clients. We will also discuss how legacy applications can use applications that expose WCF services built on .NET 3.0. The content for this article is based on Chapter 10 of Pro WCF : Practical Microsoft SOA Implementation by APress. This book is targeted towards beginner to intermediate readers and part of Apress series that discusses WPF, WCF and WF. (30 printed pages)

Download QuickReturns case study description

Contents

  • Integrating WCF Services with COM+
  • Running a COM+ Application as a WCF Service
    • COM+ Application WCF Service Wrapper
      • Using SvcConfigEditor.exe Utility
      • Using ComSvcConfig.exe Utility
      • Client Proxy Generation
      • Visual Basic 6 COM+ Hiding Interfaces
    • .NET Enterprise Services and COM+ Components
      • Client Proxy generation
  • Consuming WCF Services from COM+
    • Typed Contract Service Moniker
    • Metadata Exchange Contract Service Moniker
    • WSDL Contract Service Moniker

Introduced in 1993, Component Object Model (COM) was the basis for other emerging technologies from Microsoft such as Object Linking and Embedding (OLE), ActiveX, and Distributed COM (DCOM). COM was initially introduced to compete with Common Object Request Broker Architecture (CORBA), a language-independent and cross-platform distributed system technology. They did share some core principles, but they were not compatible. Concepts and techniques such as Interface Definition Language (IDL) are present in both technologies. However, binary interoperability didn’t exist.

COM+, introduced in 1998, was more of ancillary technology that was built on COM but did not replace it. Key features of COM+ are transactional components, queued components, role-based security, and object pooling. COM+ 1.5 added features such as application pooling, SOAP services, services without components, and some other features [1]. Not without faults, numerous applications were developed based upon this component technology. As a result, we can’t ignore the existing investment by enterprises in this technology. It would damage Microsoft’s credibility and the marketability if Microsoft introduced a new technology that would force an enterprise to scrap its original investments. Therefore, Microsoft and the WCF team worked hard to provide an evolutionary, as opposed to revolutionary, approach for bridging the technological divide, never forgetting the famous quote “Those who cannot remember the past are condemned to repeat it.” (Attributed to George Santayana, 19th-century philosopher)

Today, COM+ 1.5 is a core part of the Windows platform. Even with the .NET base class library (BCL) COM interoperability still occurs. Runtime callable wrappers (RCW) are used throughout the .NET BCL—one prime example in .NET 3.0 is the web browser Control, which is, as stated in the MSDN documentation, a managed wrapper for the web browser ActiveX control. Additionally, serviced components use COM+ transactions.

Why Integrate with COM+?

Although the future of the Windows platform is destined to be managed code, COM+ will still be around. COM exists in the core Windows platform in addition to the Win32 API (that has gone legacy) but still exists and matures. However, the key concern is the billions of lines of code that independent software developers have written to produce solutions. A great number of solutions have been built on COM+ by enterprises that have also spent billions of dollars on them. Therefore, clearly WCF needs to work with legacy implementations in order to justify an investment in extending existing applications.

Few applications built today are completely stand-alone. In fact, the terms application and solution need a little definition. Generally, an application represents a stand-alone deployable set of components, functionality, and so on. A solution, however, represents a combination of applications coupled together (either tightly or loosely) to address numerous business-processing requirements.

Therefore, today we need to build solutions. These solutions will most likely require integration with existing data and processes that exist in legacy technology, where some of that technology will be COM+. We should also consider that the existing COM+ applications aren’t going to be thrown away and rewritten in .NET and WCF. Since we’re building all these new applications based upon .NET and they’ll of course offer fantastic services that you’ll just need to share, you’ll need a way to provide legacy COM+ applications to call your services, just as any other application would. Fortunately, the WCF team has provided the core tools to facilitate both sides of the interoperability needs.

Note: We refer to QuickReturns Ltd several times in this article. QuickReturns Ltd is a fictitious case study to illustrate WCF business drivers and technical advancements. Please download it from the links at the start and the article and familiarize yourself with the business case.

Running a COM+ Application As a WCF Service

Let’s take a look at a scenario involving QuickReturns Ltd.’s Old Horse position-tracking system (or a custody system). Old Horse was built in the late 1990s using Visual Basic 6. Since other groups within QuickReturns Ltd. leverage Old Horse, the Old Horse development team provided COM interfaces allowing client applications to interoperate with Old Horse using COM+.

When an Asset Manager makes a trade, it’s necessary that the trade information is posted to the custody system. Additionally, as you’ll see later, it’s necessary that the custody system checks with the Asset Manager’s system for pricing and pending trade information.An overview of what the solution architecture is shown in figure 10-1 below.

Bb735856.WCF_COMPlus_Integration_Figure1_thumb(en-us,MSDN.10).gif

Figure 10-1. QuickReturns Ltd.’s Old Horse system

Visual Basic 6 COM+ Component Sample Setup

The example we’ll use is a simple Visual Basic 6 COM (ActiveX DLL) component that exposes two interfaces. The complete solution is contained in accompanying sample code within the Example1 folder. The following code snippets are just the method signatures representing the Visual Basic 6 COM interface. This is an illustration of a Visual Basic 6 component and should not be viewed as best Visual Basic 6 best practice.

When looking at Visual Basic 6 COM components, you’ll soon see that some issues relate to how the WCF interfaces leverage type library information in regard to COM+ application components. Visual Basic 6 COM packs both the component logic and the type library information inside the COM DLL; doing so adds some attributes that prevent some interfaces from being initially wrapped in WCF COM+ integration.

Prior to working through this example, set up a virtual directory in IIS called VB6ComSample that is set for ASP.NET 2.0 and has anonymous access enabled (you can run the script CreateVirtualDirs.bat located in the Example1 samples directory to setup IIS for you).

The PositionManagement interface shown in Listing 10-1 provides a set of simple methods that allow the retrieval of the position for a given ticker, in addition to providing a method for updating the persisted quantity associated with a ticker. One element that is not shown is a constructor. COM+ objects don’t offer a constructor. They can provide information in other ways, such as with an initialization method. Visual Basic 6 and COM offer several ways of providing static configuration information such as COM+ Initialization strings on the configured component; however, that requires implementing IObjectConstructString in Visual Basic 6 and using the ConstructionEnable attribute in .NET. For the example code and to keep it simple, we’re just showing method interfaces. The ability to provide a connection string on object construction would be something that could be provided through COM+ initialization.

Listing 10-1. PositionManagement.cls

'Simple interface that allows a nominal change in the quantity of a position

'ticker: Ticker symbol of security

'quantity: Amount (+/-) to shift existing position

'Throws an error if quantity is not sufficient to support the change 
(overdrawn)

Public Function UpdatePosition(ByVal Ticker As String, _

            ByVal Quantity As Long) As Long

    
Public Function GetQuantity(ByVal Ticker As String) As Long

The second component is the Position component. This class represents mostly a data class with read/write properties. In addition, it has two methods; one provides a retrieval of a specific position for a ticker, and the other returns a concrete Position object for a specific ticker. Listing 10-2 shows the abbreviated class, and the full class is part of the article code in \OldHorsePositionTracking\VB6\PositionManagement.

 

Listing 10-2. Visual Basic 6 Position Class: Position.cls

Public Property Let Quantity(ByVal vData As Long)
...
Public Property Get Quantity() As Long
...
Public Property Let Ticker(ByVal vData As String)
...
Public Property Get Ticker() As String
...
Public Function GetQuantity(ByVal Ticker As String) As Long
...
Public Function GetPosition(ByVal Ticker As String) As Position
...

One additional aspect of the PositonManagement class is that it’s configured in Visual Basic 6 to be an MTS component with a Required transaction setting. This setting is reflected in the generated WCF service inside the configuration file and is handled automatically by the COM+ Integration Wizard, which makes a call to the ComSvcConfig.exe utility. This allows flow from a WCF client to your COM+ component, ultimately being managed by the Microsoft Distributed Transaction Coordinator (MSDTC).

Once the project is built to an ActiveX DLL, it is ready to be installed and configured as a COM+ application. Briefly, for a Visual Basic 6 COM component, you follow these steps to create the OldHorse COM+ application: (We won’t go into too much depth on how to create Visual Basic 6 COM+ applications. Note that these steps for COM+ applications are programmable through the COM Administration type library. However, .NET offers the RegSvcs.exe utility that provides a simple command-line interface for this.)

  1. First, from Administrative Tools, launch Component Services. Alternatively you can also get to this Microsoft Management Console (MMC) via the dcomcnfg.exe command. Then, expand the Component Services node until you are at the computer you want to configure for your COM+ application. In this example, it’s the local machine, or My Computer. Select the COM+ Applications object in the console, right-click, and choose New Application, as shown in Figure 102.

  2. Bb735856.WCF_COMPlus_Integration_Figure2_thumb(en-us,MSDN.10).gif

  3. Figure 10-2. Creating a new COM+ application

  4. At this point you’re presented with the COM+ Application Install Wizard. Click through the first page of the wizard. On the second page, click the Create an Empty Application button, as shown in Figure 10-3.

  5. Bb735856.WCF_COMPlus_Integration_Figure3_thumb(en-us,MSDN.10).gif

  6. Figure 10-3. Creating an empty COM+ application

  7. On the next page of the wizard, enter the name of your application, and ensure you select Library Application as the activation type (see Figure 10-4).

  8. Bb735856.WCF_COMPlus_Integration_Figure4_thumb(en-us,MSDN.10).gif

  9. Figure 10-4. OldHorse library activation

  10. Click through the last page of the wizard. At this point, you should now have a COM+ application defined in Component Services. However, this is an empty package and has no associated components.

  11. The next step is to add your compiled ActiveX DLL into the package. Do that by first selecting the Components tree from within the OldHorse COM+ application. Right-click the Components folder under the OldHorse application, and then choose New | Component, as shown in Figure 10-5.

  12. Bb735856.WCF_COMPlus_Integration_Figure5_thumb(en-us,MSDN.10).gif

  13. Figure 10-5. Adding a new component to the OldHorse application

  14. This opens the COM+ Component Installation Wizard. Click Next in the wizard, and then choose Install New Component(s). Then, navigate to where your Visual Basic 6 COM component’s DLL resides (if you have extracted the samples, it is located in the directory \OldHorsePositionTracking\VB6\PositionManagement). Choose it, and then just click Next until the wizard is dismissed.

  15. At this point you should have a COM+ application with the components shown in Figure 10-6.

  16. Bb735856.WCF_COMPlus_Integration_Figure6_thumb(en-us,MSDN.10).gif

  17. Figure 10-6. Configured OldHorse COM+ application

First, you’ll see two components each with a single interface listed—the name manufactured by the Visual Basic 6 framework. Second, in the right pane, notice the Required transaction attribute. (You can see this view by clicking the detail view.) This attribute forces the activation of this component within a COM+ transaction—either a new transaction or an inherited transactional context from the caller.

COM+ Application WCF Service Wrapper

Once a COM+ application is configured, you’re ready to leverage WCF’s utilities for creating the necessary resources for calling a COM+ component from a WCF client. The primary utility for this is the ComSvcConfig.exe utility. This is a command-line utility that is installed with the .NET 3.0 runtime. Additionally, the SvcConfigEditor.exe utility provides a graphical interface with some additional features that help hide the complexities of the command-line ComSvcConfig.exe utility. One suggestion is to get used to the SvcConfigEditor.exe utility; it facilitates the composition of proper configuration files for WCF with configuration-time validation of many elements.

Using SvcConfigEditor.exe Utility

Before you proceed, it’s important to understand some caveats related to COM+ interoperability with WCF. There are restrictions as to what COM+ interfaces can be exposed as a web service through the COM+ Integration layer. Those restrictions are listed in the SDK, but some of them are as follows:

  • Interfaces that pass object references as parameters: This violates a core tenet of SOA in that passing a reference across a service boundary is expensive.
  • Interfaces that pass types that are not compatible with the .NET Framework COM Interop conversions: This is a general incompatibility issue for types that won’t serialize between the interoperability layers.
  • Interfaces for applications that have application pooling enabled when hosted by COM+: This causes multiple listeners on the same URI moniker issues because there will be more than one application pool attempting to reserve the service endpoint address.
  • Interfaces from managed components that have not been added to the Global Assembly Cache: This is a general limitation of how COM+ hosts configured managed components. There are other means of using COM+ from managed applications (services without components were introduced in COM+ 1.5 [2]), but they are not supported with WCF COM+ integration.

The first item mentioned here is important because given that one of the core tenets of SOA is that boundaries are explicit, it would be expensive to share an interface pointer across the service boundary. Also, given that the default WCF service behavior InstanceContext mode is PerCall, this is something your SOA implementation should consider.

In addition to the previously listed limitations, you’ll soon see some limitations with Visual Basic 6 components and, specifically, how Visual Basic 6 components are implemented.

At this point, you’re ready to create a WCF interoperability layer around your COM+ components. Start by launching the SvcConfigEditor.exe utility, which is located in the Microsoft SDK’s Bin directory. The easiest way is to launch the CMD shell shortcut that gets installed on your Start menu under the Microsoft Windows SDK program group or from within Visual Studio 2005 Tools menu as WCF Service Configuration Editor.

Start with no configuration file, and have the utility generate the necessary parts; this will allow you to call the OldHorse component from a WCF client.

From the menu bar of SvcConfigEditor.exe, select File | Integrate | COM+ Application. At this point you should see a listing of all the COM+ applications, including OldHorse, that are present on the local machine.

 

Bb735856.WCF_COMPlus_Integration_Figure7_thumb(en-us,MSDN.10).gif

Figure 10-7. COM+ Integration Wizard

 

If you expand the OldHorse.PositionManagement node until you are able to see the list of interfaces (which will only be one), then select the _PostionManagement interface, and click Next. At this point, you should see the page shown in Figure 10-8.

 

Bb735856.WCF_COMPlus_Integration_Figure8_thumb(en-us,MSDN.10).gif

Figure 10-8. _PositionManagement interface

 

Keep all selected, and just click Next. This presents the Hosting Mode options. Choose the web hosting in-process mode, which allows per-message activation and hosting within the IIS/WAS worker process. The other hosting options are not available for library-activated (in-process) applications and are enabled when the activation type is Server Activated (out-of-process). Ensure that the Add MEX endpoint option is enabled. This allows clients to leverage WS-Metadata Exchange to query the interface for contract and service information.

The next page of the wizard lists the IIS virtual directories on the local machine. Make sure you choose an IIS virtual directory that is configured for the relevant .NET Framework. For this example, we’ve preconfigured a virtual directory called /localhost/VB6ComSample (see Figure 10-9) that is configured for ASP.NET 2.0.

 

Bb735856.WCF_COMPlus_Integration_Figure9_thumb(en-us,MSDN.10).gif

Figure 10-9. Choosing an IIS virtual directory

 

At that point, click Next, and you’re presented with the summary of options shown in Figure 10-10.

 

Bb735856.WCF_COMPlus_Integration_Figure10_thumb(en-us,MSDN.10).gif

Figure 10-10. COM+ integration summary page

 

Click Next again, and the SvcConfigEditor.exe makes a call to the ComSvcConfig.exe utility with the appropriate command-line options. This generates two files in the virtual directory. If the SvcConfigEditor.exe utility cannot find the ComSvcConfig.exe utility, you’ll be presented with a message box asking you to specify where it can be located (by default the ComSvcConfig.exe utility is located at %SystemRoot% \Microsoft.NET\Framework\v3.0\Windows Communication Foundation directory)

The two resources that are generated provide the WCF wrapper service resource file and a Web.config file. The WCF service file is generated with the COM ProgID as the filename. For this example, the component OldHorse.PositionManagement generates a file OldHorse.PositionManagement.svc. The contents of that file appear in Listing 10-3.

 

Listing 10-3. OldHorse.PositionManagement.svc

<%@ServiceHost
Factory="System.ServiceModel.ComIntegration.WasHostedComPlusFactory"
Service="{f4612210-b755-4e17-87db-f82d9751d582},

{893ae5eb-6e13-4949-881f-5e923ebeb982}"
%>

The SVC file contains a single line that points to the service factory that will provide the COM+ integration—WasHostedComPlusFactory. The second parameter, Service, provides two initialization parameters for the factory class. The first is the GUID for the COM interface as specified by the type library for the COM component. If you leverage a tool such as OleView (which comes with the Windows SDK), view the type library for OldHorse, and dump the IDL, you’ll see that the supplied GUID matches the UUID of the implementation class, which is PositionManagement.

The second parameter represents the COM+ application ID, which is visible by choosing the properties of the COM+ application from the Component Services management console. So, the combination of the application ID and the CLSID (ProgID reference from COM) is a direct pointer that allows the WCF COM+ integration runtime to locate, instantiate, and service the WCF client call.

If you check the properties of the OldHorse.PositionManagement component from within Component Services, you’ll see that the CLSID GUID and application GUID both match the generated GUIDs in the OldHorse.PositionManagement.svc file as shown in the figure below, noting that the 2nd parameter, Application GUID will be different on each machine.

 

Bb735856.WCF_COMPlus_Integration_Figure11_thumb(en-us,MSDN.10).gif

Figure 10-11. OldHorse.PositionManagement properties

Using ComSvcConfig.exe Utility

You can also use the stand-alone ComSvcConfig.exe utility to generate the required resources COM+ application integration. The primary difference is it doesn’t provide the up-front validation that the SvcConfigEditor.exe utility does for validating supported COM interfaces prior to generation. Instead, it provides that information as error messages at runtime.

Using the same COM+ application as an example, the following command generates the required resources for wrapping your COM+ application’s PositionManagement interface in a WCF service and hosting inside IIS/WAS (all on a single line).

ComSvcConfig.exe /install /application:OldHorse
    /contract:OldHorse.PositionManagement,_PositionManagement
    /hosting:was /webdirectory:VB6ComSample /mex

In addition to the /install option listed here, there are two additional primary actions: /list and /uninstall. The /list option enumerates what WCF COM+ integration services currently exist on the local machine. The /uninstall option removes the application .svc file in addition to updating the Web.config (or application configuration) file, removing all references to the identified application and interface.

Client Proxy Generation

At this point, you’re ready to create the client proxy for your WCF COM+ integration, using either the SvcUtil.exe utility or the Visual Studio 2005 Add Service Reference add-in, as described in Chapter 5 of our book. Before proceeding, ensure that the IIS website that you will be using has anonymous access enabled (accessed through the Directory Security tab in IIS Virtual Directory properties). A completed solution appears in the sample code in \VB6ComClient directory.

In this section, you’ll create a simple console application. Start Visual Studio 2005, and create a new Windows console project. Once you have done this, right-click the project (or select the Project menu), and choose Add Service Reference. You can find detailed steps for generating service proxies in Chapter 4 of our book. The URI to specify for the Add Service Reference dialog box looks like Figure 10-12.

 

Bb735856.WCF_COMPlus_Integration_Figure12_thumb(en-us,MSDN.10).gif

Figure 10-12. Adding a service reference to a COM+ WCF wrapper

Once you’ve generated the service reference, you can now provide the client code. Inside the Main method, the example code looks like Listing 10-4, which shows the completed client project’s program.cs class file.

Listing 10-4. WCF COM+ Integration Client

namespace VB6ComClient
{
    class Program
    {
        static void Main ( string[] args )
        {
            OldHorse._PositionManagementClient proxy =
                new VB6ComClient.OldHorse._PositionManagementClient();


            int q = proxy.GetQuantity( "MSFT" );
            Console.WriteLine( "We have " + q + " of MSFT" );
            q = proxy.UpdatePosition( "MSFT", 100 );
            Console.WriteLine( "We now have " + q + " of MSFT" );
            proxy.Close();
            proxy = null;
            Console.WriteLine("Press return to end..." );
            Console.ReadLine();

        }
    }
}

As shown in Listing 10-4, you simply instantiate a proxy type using the default constructor (which reads address, binding, and contract information from the configuration file). Using the _PositionManagementClient object (which was automatically generated from SvcUtil.exe) you then make a call to the methods exposed on the interface.

Consuming the PositionManagement interface from a WCF client is done just like with any other WCF-generated proxy type. In this model, the call is handed from the client over HTTP, which is then received by the IIS/Http.sys listener framework, and finally onto the WCF framework inside the WasHostedComPlusFactory type. The WCF framework does a runtime lookup of the COM+ information, instantiates the Visual Basic 6 COM component, and services the call.

One thing to note is that given the default service InstanceContext behavior is PerCall, the WCF COM+ integration framework will service each call with a new PositionManagement object. Therefore, if you require server-side state, you must modify the service behavior. Please review Chapters 3 and 6 from our book for details about service behavior.

Visual Basic 6 COM+ Hiding Interfaces

When COM was introduced, it provided a capable component architecture that permitted developers to leverage binary compatibility and reuse components across solutions. With this came the complexity of COM (reference tracking especially) and the language of COM itself. A core component of COM definitions are buried inside the type library for each COM component. C/C++ programmers are used to seeing IDL, which describes the COM interfaces of implementation components.

Visual Basic programmers are generally not accustomed to working with IDL. This is because Visual Basic 6 hides the inner workings of COM. However, it is possible to take a contract-first approach in working with Visual Basic 6 and COM.

Generally, you can find good references on the Internet, and the following link provides examples and shows how to provide a contract-first approach to Visual Basic 6 COM development: https://msdn2.microsoft.com/en-us/library/ms810017.aspx.

During the generation of the WCF COM+ integration components for your OldHorse Visual Basic 6 ActiveX DLL, the Position component, while visible in the component selection page as shown in Figure 10-7 earlier in the article, offered no visible interfaces for use with the WCF COM+ integration. This is because Visual Basic 6 generates hidden interfaces for the type library information that is bundled with the COM DLL for nonprimitive types. Generally, when using other COM+ languages, specifically C/C++, generating the type library information, a critical aspect of COM+ programming, is done using IDL and compiled into a type library (TLB) that is then used by the implementation programmer to ensure adherence to the contract. Any interfaces that have any hidden types as parameters or return values are not available in the WCF COM+ integration framework.

For the OldHorse Visual Basic 6 COM implementation, the reason the WCF COM+ Integration Wizard ignores the Position interface is because of the method GetPosition that returns a Position object. Visual Basic 6 has hidden the internally generated _Position (note the underscore) interface from consumers of the type library; therefore, it’s not possible to create a type of _Position by a caller—generally that’s up to the COM component.

Using OleView.exe (which comes with the Windows SDK), if you dump the IDL and inspect the _Position interface, you can see it’s marked with a hidden attribute (see Listing 10-5).

Listing 10-5. OldHorse Visual Basic 6 COM Position IDL

[
  odl,
  uuid(7E22753A-CD1B-4620-A952-E3CDFD456431),
  version(1.0),
  hidden,
  dual,
  nonextensible,
  oleautomation
]
interface _Position : IDispatch {
    [id(0x68030001), propput] HRESULT Quantity([in] long );
    [id(0x68030001), propget] HRESULT Quantity([out, retval] long* );
    [id(0x68030000), propput] HRESULT Ticker([in] BSTR );
    [id(0x68030000), propget] HRESULT Ticker([out, retval] BSTR* );
    [id(0x60030002)] HRESULT GetQuantity(
                     [in] BSTR Ticker,
                     [out, retval] long* );
    [id(0x60030003)] HRESULT GetPosition(
                     [in] BSTR Ticker,
                     [out, retval] _Position** );
};
[
  uuid(E17BC5E8-0378-4775-88DE-BADB73C57F03),
  version(1.0)
]
coclass Position {
    [default] interface _Position;
};

Through the IDL you can see why the WCF COM+ Integration Wizard did not display this interface and how you use interface names when you are using the ComSvcConfig.exe utility. You don’t actually use the class names as declared inside the Visual Basic 6 class files; you use the generated interface names that Visual Basic 6 provides (prefixed with an underscore, _).

So, again, if you require access to the Position object through the WCF service boundary, you have a couple of workarounds (there may be more):

  • Remediate Visual Basic 6 to leverage contract-first COM+ development (see the MSDN article Building COM Components That Take Full Advantage of Visual Basic and Scripting (https://msdn2.microsoft.com/en-us/library/ms810017.aspx) for an approach.
  • Provide a .NET wrapper that interacts directly with Visual Basic 6 COM components and exposes .NET types on the service boundary.
.NET Enterprise Services and COM+ Components

For another example, we’ve included a simple .NET 2.0 class library that represents the OldHorse2 COM+ application but written in .NET 2.0 using Enterprise Services and serviced components.

This solution file is located as part of the sample code in the following directory:

OldHorsePositionTracking\DotNet\OldHorse2Sln

Prior to stepping through this example, set up a virtual directory inside IIS called DotNetComSample that is configured as ASP.NET 2.0 and has anonymous access enabled. The script CreateVirtualDirs.bat will create the IIS virtual directories and set the .NET runtime to 2.0 for the sites.

The solution also contains a couple of batch files (reg.bat and unreg.bat) that handle the global assembly cache (GAC) installation and COM+ application configuration. These batch files use the GacUtil.exe utility and the RegSvcs.exe utility that handles GAC and COM+ registration. As listed in the SDK requirements, a .NET component that is also a serviced component (COM+) must be registered in the GAC, which requires it to have a strong name.

The implementation of OldHorse2 is a mirror image of the Visual Basic 6 COM example, except it uses attributes from the Enterprise Services namespaces. Additionally, the Guid attribute is applied to ensure you leverage a consistent CLSID and APPID instead of relying on the framework to regenerate each time.

For the OldHorse2 project, Listing 10-6 shows the PositionManagement class. The code provides the sample simple interface as the Visual Basic 6 version along with Transaction attributes and AutoComplete attributes for transaction management.

Listing 10-6. OldHorse2 PositionManagement.cs

using System;
using System.EnterpriseServices;
using System.Runtime.InteropServices;

namespace OldHorse2
{
    [Guid( "3B26F4CA-E839-4ab6-86D4-AADB0A8AADA5" )]
    public interface IPositionManagement
    {
        long UpdatePosition( string ticker, long quantity );
        long GetQuantity( string ticker );
    }

    [Guid( "08F01AD6-F3EB-4f41-A73A-270AA942881A" )]
    <strong>   [Transaction(TransactionOption.Required)]</strong>
    <strong>public class PositionManagement : ServicedComponent, IPositionManagement</strong>
    {
        public PositionManagement() {}
        #region IPositionManagement Members

        <strong>[AutoComplete]</strong>
        public long UpdatePosition( string ticker, long quantity )
        {
            IPosition pos = new Position();
            pos = pos.GetPosition( ticker );
            pos.Quantity += quantity;
            return pos.Quantity;
        }
        <strong>[AutoComplete]</strong>
        public long GetQuantity( string ticker )
        {
            IPosition pos = new Position();
            pos = pos.GetPosition( ticker );
            return pos.Quantity;
        }
        #endregion

    }
}

As you can see in the code in Listing 10-6, we’ve specifically provided the interface IPositionManagement that is implemented in the class PositionManagement, which also inherits from ServicedComponent. Additionally, the class has the TransactionOption.Required setting with each method having the AutoComplete attribute from Enterprise Services. This will ensure that each instance and call through the PositionManagement type takes place within a COM+ transaction.

In the same project, we’ve also defined the Position class. Listing 10-7 shows its contents. Notice that we’ve followed the same approach of providing a specific interface and corresponding implementation class.

Listing 10-7. OldHorse2 Position.cs

using System;
using System.Runtime.InteropServices;
using System.EnterpriseServices;

namespace OldHorse2
{
    [Guid( "D428B97A-13C8-4591-8AC3-5E8622A8C8BE" )]
    public interface IPosition
    {
        long Quantity
        { get; set; }

        string Ticker
        { get; set; }

        long GetQuantity( string ticker );
        IPosition GetPosition( string ticker );
    }
    
    [Guid( "02FD3A3B-CFCE-4298-8766-438C596002B4" )]
    public class Position : ServicedComponent, IPosition
    {
        ...
        #region IPosition Members
        public long Quantity
        ...
        public string Ticker
        ...
        public long GetQuantity( string ticker )
        ...
        public IPosition GetPosition( string ticker )
        ...
    }
}

Once the project is compiled to a managed assembly, it’s necessary to register it in the GAC using the GacUtil.exe utility that comes with the .NET 3.0 Framework. The command to register is as follows:

gacutil /i bin\debug\OldHorse2.dll

Once it’s registered in the GAC, you can then install it in COM+. .NET offers a useful command-line utility that does all the work for you. The following command creates the COM+ application along with registering the .NET assembly’s components:

regsvcs bin\debug\OldHorse2.dll

You can attribute the assembly with Enterprise Services types that control the COM+ registration, shown in Listing 10-8; therefore, you don’t have to build the application first and install the components through the wizard. If you want to script this outside of .NET or for non-.NET components, you could leverage the COM+ administrative interfaces for controlling COM+ applications.

Listing 10-8. OldHorse2 Assembly Attributes for COM+

[assembly: ComVisible( true )]
[assembly: Guid( "c41f4ee8-3475-47b6-b381-5e7774e4287d" )]
[assembly: ApplicationName("OldHorse2")]
[assembly: ApplicationActivation(ActivationOption.Library)]
[assembly: ApplicationAccessControl(false)]

These commands are best executed from the Windows SDK command prompt or Visual Studio 2005 command prompt—located under the Tools folder for the Windows SDK and Visual Studio 2005 program groups from your All Programs Start menu Item.Additionally, the commands are contained in the batch files previously mentioned. Once registered, you should now see in Component Services the OldHorse2 application, as shown in Figure 10-13.

Bb735856.WCF_COMPlus_Integration_Figure13_thumb(en-us,MSDN.10).gif

Figure 10-13. OldHorse2 .NET COM+ registration

Now, using OleView.exe (from the Windows SDK), refer to the IDL that is generated by the .NET Framework. The full IDL files are located as part of the sample code in the \OldHorsePositionTracking directory. Listing 10-9 shows the IDL listing.

Listing 10-9. OldHorse2 .NET IDL

[
  odl,
  uuid(D428B97A-13C8-4591-8AC3-5E8622A8C8BE),
  version(1.0),
  dual,
  oleautomation,
  custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, OldHorse2.IPosition)

]
interface IPosition : IDispatch {
...

[
  odl,
  uuid(3B26F4CA-E839-4AB6-86D4-AADB0A8AADA5),
  version(1.0),
  dual,
  oleautomation,
  custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, OldHorse2.IPositionManagement)
]
interface IPositionManagement : IDispatch {
...

The code has been abbreviated here, but you can see that the declared interfaces IPosition and IPositionManagement both do not have the hidden attribute. Therefore, you should have a different experience when you run the WCF COM+ Integration Wizard.

Start the SvcConfigEditor.exe utility, and access the COM+ integration feature. You now see the OldHorse2 application along with both the IPosition and IPositionManagement interfaces available for integration.

Bb735856.WCF_COMPlus_Integration_Figure14_thumb(en-us,MSDN.10).gif

Figure 10-14. OldHorse2 WCF COM+ Integration Wizard

Select the IPositionManagement interface and then click Next. You now see that both methods, as with the Visual Basic 6 COM component, appear (see Figure 10-15).

Bb735856.WCF_COMPlus_Integration_Figure15_thumb(en-us,MSDN.10).gif

Figure 10-15. OldHorse2.PositionManagement interface methods

Click Next two times, and then click Finish. At this point you’ll have two resources generated in the virtual directory root—a Web.config file along with the Service Host file called OldHorse2.PositionManagement.svc.

Client Proxy Generation

Once again, create a Visual Studio 2005 console application, and choose "Add Service Reference" to add to the project using the following URI shown in Figure 10-16.

Bb735856.WCF_COMPlus_Integration_Figure16_thumb(en-us,MSDN.10).gif

Figure 10-16. Adding a service reference to the project

In the completed solution, Listing 10-10 shows the code that performs the same call as the Visual Basic 6 COM client performed. The only difference is the type name no longer is prefixed with an underscore (_). This is because when authoring components in .NET, you have control over the interface names, where in Visual Basic 6 it’s left up to the Visual Basic 6 framework, hidden from normal levels of control. Other than that, there’ no discernable difference from the consumer side, as shown in Listing 10-10.

Listing 10-10. OldHorse2 Position Management Client

namespace DotNetComClient
{
    class Program
    {
        static void Main ( string[] args )
        {
            <strong>OldHorse2.PositionManagementClient();</strong>
            <strong>OldHorse2.PositionManagementClient proxy =</strong>
                <strong>new OldHorse2.PositionManagementClient();</strong>
            long q = proxy.GetQuantity("MSFT");
            Console.WriteLine( "We have " + q + " of MSFT" );
            q = proxy.UpdatePosition( "MSFT", 100 );
            Console.WriteLine( "We now have " + q + " of MSFT" );
            proxy.Close();
            proxy = null;
            Console.WriteLine( "Press return to end..." );
            Console.ReadLine();
        }
    }
}

Consuming WCF Services from COM+

Up to now, we’ve focused on solutions that need to leverage existing legacy application logic that is hosted in COM+. We’ve focused primarily on Visual Basic 6 given its distinct ability to hide some things that you need control over in order to fully leverage and reuse your application logic.

This section approaches the problem scenario from the perspective that these legacy solutions are not stagnant. In fact, it has been estimated that nearly 90 percent [ 3 ] of IT budgets are focused on maintaining and extending existing solutions—many of those built on Visual Basic 6 and other legacy technologies.

So, those applications aren’t going away. In fact, they most likely will need to be extended to support new functionality or just change the way they interface with other applications.

For the examples in this article, you’ll look at how you can make a WCF service look like a COM+ component. This allows your legacy clients that understand COM+ to work with your new .NET 3.0–based applications that expose service endpoints. Note that both the .NET 3.0 and .NET 2.0 runtimes are required when calling from any client. This is a requirement as the dynamic invocation framework is leveraged in process by the client process.

QuickReturns Ltd. Quote Service

The QuickReturns Ltd. system, built on .NET 3.0, provides a quote service using WCF. All parts of the QuickReturns Ltd. application leverage this service. Some of the Old Horse custody systems, however, require the ability to reuse this application logic, and they’ve chosen to use WCF COM integration capabilities. The new QuickReturns Ltd. quote service is hosted in ASP.NET and IIS and exposes its services using WCF.

Alternatively, we’ll also discuss how you can leverage runtime registration of the COM interface through the use of the WSDL and MEX service monikers.

Typed Contract Service Moniker

We’ll provide a quick walk-through for the first scenario, consuming a WCF Service from COM clients. This example will provide both an automation client (VBScript) and an early binding client, Visual Basic 6. The Visual Studio 2005 solution file QuickReturnsQuotes.sln contains the website and proxy projects, located in the Example2 directory.

The first part of the solution is the QuickReturnsQuotes WCF service, which is hosted in IIS and ASP.NET. If you haven’t already run the setup script, to set up this virtual directory in IIS, run the batch file CreateVirtualDirs.bat. The requirements are that IIS is installed along with .NET 2.0 and the .NET 3.0 runtime components.

Open the solution file QuickReturnsQuotes.sln. The solution file contains two projects. The first is the website that was just mapped using the scripts mentioned previously. If the project doesn’t load, there’s a problem with the script on your machine, and you’ll have to map the site manually and reload the project. Ensure that you have IIS and .NET 2.0 installed and ASP.NET registered with IIS (use the aspnet_regiis.exe command in the Framework folder).

The second project represents the proxy that when compiled, with a strong name, will be registered both in the GAC and as a COM interface using the RegSvcs.exe utility that’s part of the .NET 3.0 Framework.

- makeProxy.bat: This is the batch file that calls SvcUtil.exe to generate the proxy stub source files; this file is part of the project prebuild steps.

**reg.bat**: This is the batch file that registers the assembly in the GAC and for COM interoperability; this file is part of the project post-build steps.
  • unreg.bat: This is the batch file that will remove the assembly from the GAC and from COM interoperability.

Note: For the build steps and these batch files to work, Visual Studio 2005 must be installed in the default path. If you chosen a different path or haven’t installed Visual Studio 2005, you need to update the path to the utilities as required.

If you build the solution and all is successful, then you should have a GAC-installed assembly registered for COM interoperability and ready for use by COM clients. To verify, you can open Windows Explorer to the C:\Windows\Assembly path and see the assembly TypedServiceProxy listed, as shown in Figure 10-17.

Bb735856.WCF_COMPlus_Integration_Figure17_thumb(en-us,MSDN.10).gif

Figure 10-17. QuickReturns Ltd. WCF proxy in the GAC

Note: If you haven’t modified any of the project Guid attributes, then the next two steps are not required for this project to work. This would be a normal step in your solutions to validate the correct Interface GUIDs.

The next step is to both verify the registration for COM and retrieve the interface ID that is stored in the registry. The best tool for this is OleView.exe, which comes with the Windows SDK. Start OleView.exe, and open the top-level node labeled Type Libraries. Scroll down until you find TypedServiceProxy, as shown in Figure 10-18.

Bb735856.WCF_COMPlus_Integration_Figure18_thumb(en-us,MSDN.10).gif

Figure 10-18. TypedServiceProxy registered in COM

For the next step, you must retrieve the interface ID (the GUID) for the IQuoteService interface. The OleView.exe utility can view the IDL for any COM registered classes. Double-click the item TypedServiceProxy in the list to open the ITypeLib Viewer, as shown in Figure 10-19.

Bb735856.WCF_COMPlus_Integration_Figure19_thumb(en-us,MSDN.10).gif

Figure 10-19. ITypeLib Viewer for TypedServiceProxy

Find in the right pane of the viewer the IDL definition for the IQuoteService interface (which inherits from IDispatch—implying it supports automation as well as early bind COM clients). Now, just above it (like attributes in .NET) are a list of IDL attributes for this interface. We’re looking for the universally unique identifier (UUID) just above it. For this component, its value is 058E1BEC-C44A-31FB-98C8-9FB223C46FAF.

Inside the project file TypedServiceProxy, you’ll see a VB Script file that illustrates how to call from an automation client. Since this is an early bound client, it requires the interface ID to be part of the service moniker construction string for the GetObject call. The call sequence is into the quote service through COM and then through the WCF framework to the quote service .NET assembly hosted in IIS/ASP.NET.

Listing 10-11 is the source file for QuickReturnsScriptClient.vbs; note the wrap on some lines.

Listing 10-11. QuickReturnsScriptClient.vbs Automation Client

Option Explicit
Dim quoteProxy, moniker, result
moniker = "service:address="
moniker = moniker + "https://localhost/QuickReturnsQuotes/service.svc"
moniker = moniker + ",binding=wsHttpBinding"
moniker = moniker + ",contract={058E1BEC-C44A-31FB-98C8-9FB223C46FAF}"
'... cut comments

Set quoteProxy = GetObject(moniker)
result = quoteProxy.GetQuote("MSFT")
WScript.Echo "MSFT's price is " + CStr(result)

The moniker string value used for the GetObject COM call provides the address endpoint URI in addition to the binding type, which is wsHttpBinding because we’re hosting in IIS. The final parameter of the moniker is the contract type. Using this GUID, the WCF framework looks up the type library information to obtain the COM interface and instantiates the proxy on the client side. The proxy in turn leverages the .NET Framework 3.0 to construct a channel and message for the request through the service boundary. This is all done "automagically" by the WCF components, which must be installed on the client tier as well.

Typed Contract: Early Bound

Visual Basic 6 can use early binding. Early binding allows the lookup and discovery of the interfaces in your COM component at design time. So, at runtime the COM client is expecting that the same UUID of your interface is registered (via type library registration). The type library that needs to be registered and referenced is part of the reg.bat batch file in the TypedServiceProxy project—QuickReturnsProxy.tlb. COM interfaces are to be considered immutable. If they change, then the underlying IDL will change. Therefore, any changes to your interfaces in the base WCF service class will require a regeneration of the proxy and a regeneration of the type library for use by clients.

Note: If you open the Visual Basic 6 project you may need to reset the project references back to the TypedServiceProxy on your machine. That’s accessible from Project | References from within the Visual Basic 6 IDE.

If you look at the Visual Basic 6 project TypedServiceVbClient, you can see that the project should have a reference to the TypedServiceProxy type library. In the button click event handler, you can now make references directly to the types inside the COM interface (see Listing 10-12; please note the line wrap).

Listing 10-12. Early Bound Visual Basic 6 Client

Private Sub Command1_Click()
    Dim obj As TypedServiceProxy.QuoteServiceClient
    Dim moniker, Ticker As String
    Dim price As Double

    moniker = "service:address=https://localhost/QuickReturnsQuotes/
service.svc, binding=wsHttpBinding"
    On Error GoTo ErrHandler

    Ticker = UCase(Trim(txtTicker.Text))
    Set obj = GetObject(moniker)
    price = obj.GetQuote(Ticker)
    MsgBox "Price is " & CStr(price)
    Exit Sub

ErrHandler:
    MsgBox Err.Number & " : " & Err.Description & " : " & Err.Source
End Sub

The obj object is declared to be of the interface proxy type. The moniker is then set to include only the address and the binding type. Since you’re using just the default settings for the wsHttpBinding, you aren’t required to supply a bindingConfiguration value. If you required overriding any of the default settings for the binding, you could supply an application configuration file with the name file.exe.config and place it in the program directory of the client. For this example, the filename would be TypedServiceVbClient.exe.config.

You then use the COM GetObject statement, which makes a call through the COM framework into the Service Control Manager (SCM, or affectionately known as "scum"), activating the COM registered WCF proxy type. Then as each method is called on the activated instance, the WCF framework is responsible for both transforming and marshaling the call from COM into the WCF framework and ultimately across the service boundary to the service class.

Dynamic Discovery

There are scenarios where registering the COM type library is not feasible. An example is Microsoft Excel spreadsheets that require dynamic discovery and invocation, through COM locally to WCF services. For this, the WCF framework and the COM integration provides a dynamic model, or what’s known as late binding.

What the WCF framework provides is the runtime construction of a proxy and COM interface for the COM client at object construction time. By first querying the service metadata, after being provided some initialization parameters, the WCF framework generates both a WCF proxy and a COM callable wrapper that the COM client interfaces with. You currently have two choices for the service monikers: WS-MetatdataExchange (MEX) and WSDL. Given this is a non-typed model, it is callable only by clients that support automation (IDispatch) such as VBScript, Visual Basic 6, Excel, and so on.

Metadata Exchange Contract Service Moniker

WCF supports the WS-MetadataExchange protocol that provides the discovery of services in addition to policy and schema information. Please see Chapter 4 of our book for more information. The WCF COM integration framework uses this to dynamically derive the service endpoint interfaces along with binding and service behavior.

Starting with the scripting sample from the project file in Listing 10.2, there’s an additional VBScript file: QuickReturnsScriptClientMex.vbs. Listing 10-13 shows its contents (note the line wrap).

Listing 10-13. QuickReturns Ltd. Script Using Mex Service Moniker

Option Explicit
Dim quoteProxy, moniker, result


moniker="service:mexAddress=https://localhost/QuickReturnsQuotes/service.svc/mex,"
moniker=moniker + "address=https://localhost/QuickReturnsQuotes/service.svc,"
moniker=moniker + "contract=IQuoteService, "
moniker=moniker + "contractNamespace=https://PracticalWcf/QuoteService, "
moniker=moniker + "binding=WSHttpBinding_IQuoteService, "
moniker=moniker + "bindingNamespace=https://tempuri.org/"

Set quoteProxy = GetObject(moniker)
result = quoteProxy.GetQuote("MSFT")
WScript.Echo "MSFT's price is " + CStr(result) WSDL Contract Service Moniker

From the code in Listing 10-13, you don’t have a local configuration file or a strongly typed object (in COM or .NET). Therefore, you must supply the “discovery” information to the GetObject call. One part is the URI for where the MEX metadata is found. The others are the URI of the service endpoint, binding, and contract information that will be mapped into the MEX response.

The contract and contractNamespace comes directly from the metadata binding information inside the <wsdl:binding> element from the metadata. This must match what the MEX response contains; otherwise, you’ll receive a mismatch on the contract error. For this sample, this represents the <wsdl:binding> element that is visible if you request the WSDL for the service using the following URI:

https://localhost/QuickReturnsQuotes/service.svc?wsdl
WSDL Contract Service Moniker

Similar to how WCF works with the WS-MetadataExchange protocol to dynamically derive the COM and WCF interfaces and types, the service moniker can also work with a WSDL contract. Listing 10-14, contained in the file QuickReturnsScriptClientWsdl.vbs, illustrates how to make a call using the service moniker for WSDL.

  Listing 10-14. QuickReturns Ltd. Script Using WSDL Service Moniker

Option Explicit
Dim quoteProxy, wsdl, moniker, result

wsdl = GetWsdlFromUrl ("https://localhost/QuickReturnsQuotes/service.svc?wsdl" )
moniker="service:wsdl=" & wsdl & ", "
moniker=moniker + "address=https://localhost/QuickReturnsQuotes/service.svc,"
moniker=moniker + "contract=IQuoteService, "
moniker=moniker + "contractNamespace=https://tempuri.org/, "
moniker=moniker + "binding=WSHttpBinding_IQuoteService, "
moniker=moniker + "bindingNamespace=https://tempuri.org/"
Set quoteProxy = GetObject(moniker)

result = quoteProxy.GetQuote("MSFT")
WScript.Echo "MSFT's price is " + CStr(result)

Function GetWsdlFromUrl( strUrl )
    Dim WinHttpReq, resp
    Set WinHttpReq = CreateObject("WinHttp.WinHttpRequest.5)
    resp = WinHttpReq.Open("GET", strUrl, False)
    WinHttpReq.Send()
    GetWsdlFromUrl =
    WinHttpReq.ResponseText
End Function

Note: For the previous dynamic HTTP request for the WSDL to work, the correct version of the WinHttp services component needs to be referenced by the CreateObject call. On some installations, this may be WinHttp.WinHttpRequest.5.1. Please see https://msdn2.microsoft.com/en-us/library/Aa384273.aspx for more information.

The first statement after the variable declarations makes a call to the included function that invokes GetWsdlFromUrl. This VBScript function just makes an HTTP get call to the URI to retrieve the HTTP response, which for that URI is the WSDL document for the service interface.

The moniker initialization string is then composed of the WSDL response along with the remaining service moniker attributes. The WSDL string is an XML response that fully describes the IQuoteService interface exposed at the endpoint address. It’s the same XML you would see if you opened the URL https://localhost/QuickReturnsQuotes/service.svc?wsdl directly from a browser.

Again, using the dynamic service moniker, the COM interface makes a call into the WCF framework to dynamically construct the types necessary to make a round-trip request into the WCF service that is hosted in IIS—all without the COM client knowing the underlying workings of how to work with WCF (other than the moniker construction). What the dynamic generation provides is the generation of a fully configured proxy that matches the service endpoints advertised metadata including policy, security, and contract information.

Briefly, let’s summarize what the high-level steps are required to consume a WCF service as a COM interface leveraging a typed contract service moniker:

  1. Generate a proxy using the WCF SvcUtil.exe utility.
  2. Create a project/solution in Visual Studio 2005 that contains the generated proxy class file. (This is not required, but it makes things easier.)
  3. Add the attribute ComVisible to the solution; you can add this to the AssemblyInfo.cs file.
  4. Provide a strong name for the assembly; this is optional but allows loading to the GAC.
  5. Register the assembly for COM using the RegAsm tool.
  6. Install the assembly in the GAC; this is optional but ensures a single version is loaded.
  7. Create an application configuration file for you client executable; for example, if the client is called OldHorseClient.exe, then the configuration file is OldHorseClient.exe.config. This is the standard .NET configuration file-naming requirements.
  8. Use the GetObject statement in your COM environment (Visual Basic 6, scripting, and so on) with the service moniker to instantiate the WCF service interface.

References

[1] What’s new in COM+ 1.5” on .MSDN at https://msdn2.microsoft.com/en-us/library/ms687608.aspx.

[2] Services without Components in COM+ 1.5. Please refer to https://msdn2.microsoft.com/en-us/library/ms172373.aspx .

[3] Erlikh, L. (2000). "Leveraging legacy system dollars for E-business". (IEEE) IT Pro, May/June 2000, https://doi.ieeecomputersociety.org/10.1109/6294.846201, https://www.cs.jyu.fi/~koskinen/smcosts.htm.

 

Conclusion

This article focused on interoperability with COM, both from a consumer and from a service perspective. WCF and .NET framework 3.0 provides a strong extensible starting point to help in the evolutionary model of moving solutions into the SOA age.

 

Chris Peiris is an avid publisher in the application integration space. He works for Avanade Australia as a Solutions Architect. He is a frequent speaker at professional developer conferences on Microsoft technologies. Chris has written many articles, reviews and columns for various online publications including 15 Seconds, ASPToday, Wrox (Apress) and Developer Exchange (DevX). He has also co-authored many books on WCF, Web Services, UDDI, C#, IIS, Java and Security topics. Chris’s current passions include WCF, .NET 3.0, IBM Message Broker, Biztalk and other EAI implementations. Chris’ complete list of publications and contact details are available at https://www.chrispeiris.com

 

Shawn Cicoria is President of CedarLogic, LLC and lives with his family in Denville, New Jersey. He has MBA's in Finance and Information Systems, and a BA in Economics. Shawn was also a MCT training instructor with SetFocus, located in Parsippany, NJ. He has been working in systems for nearly 20 years, mostly in financial services, has worked on many platforms including VMS, UNIX variants, and, for most of the past decade, Microsoft Windows.  Shawn's been concentrating on distributed technologies such as COM[+], J2EE, and, for the past 5 years, .NET, SOAP, BizTalk, and Database technologies — and now .NET Fx3. Shawn's home on the Net can be found at www.Cicoria.com.

 

Avanade. /systems.solutions.success/ from Accenture & Microsoft

Avanade is a global IT consultancy dedicated to using the Microsoft platform to help enterprises achieve profitable growth. Through proven solutions that extend Microsoft technologies, Avanade helps enterprises increase revenue, reduce costs, and reinvest in innovation to gain competitive advantage. Our consultants deliver value according to each customer's requirements, time line, and budget by combining insight, innovation, and the talent of our global workforce. Additional information can be found at www.avanade.com.