Using the WCF Service Moniker to Communicate Between Legacy COM Apps and New .NET Framework Apps

Introduction

Software engineering is maybe the only engineering discipline were building an entirely new wing to a building is considered maintenance. Applications are extended and modified for years. Applications that once used the most modern technologies of the organization have become legacy themselves, yet in a lot of the cases we want them to be able to communicate with their new siblings.

If your organization used Microsoft Visual Basic in the past, you will probably be migrated to .Net by now. Visual Basic is build around Microsoft's COM technology, a technology that a lot of developers refer to as "the DLL hell". With .Net Microsoft has made a clean start. At the same time they made interoperability between .Net and COM possible, but that means that your COM interop assemblies will suffer of the same problems as your old components did in the past.

Enterprise applications build today are using n-tier architectures, where the communication between the backend and the fronted in Microsoft environments is mostly done using (WCF) web service technology. Therefore I will present you another choice for interoperability between COM based applications and the new .Net applications: the WCF Service Moniker.

The WCF Service Moniker

The Microsoft COM Service Moniker is automatically installed when you install the .Net framework. It can be used in 3 ways:
  1. Using a typed contract
  2. Using a MEX endpoint
  3. Using the WSDL of the web service

In this article I will show you how to consume a simple WCF service using the three possible ways. The COM client used for this demo will be developed in Excel VBA, but you could develop it in every programming language that supports COM components. I will also explain how you can easily debug such client applications.

The demo WCF service

We will create simple WCF service that accepts a string as input and returns a strings as output. First we create a new "WCF Service Application":

new wcf service

Inside the project: we create the following service contract:

[ServiceContract]
public interface IHelloName
{
    [OperationContract]
    string Hello(string name);
}

Although it is not mandatory anymore with WCF 4.0 to configure the end points, we need to do this for the tutorial because we will need a mex endpoint for the second example. This is not exposed by default in WCF. The configuration for our service is the following:

<system.serviceModel>
	<services>
		<service behaviorConfiguration="serviceBehavior"
		         name="HelloNameService.HelloName">
			<endpoint binding="basicHttpBinding"
				      bindingName="BasicHttpBinding_IHelloName"
			          contract="HelloNameService.IHelloName" />
			<endpoint address="mex"
					  binding="mexHttpBinding"
					  contract="IMetadataExchange" />
		</service>
	</services>
	<behaviors>
		<serviceBehaviors>
			<behavior name="serviceBehavior">
			    <serviceMetadata httpGetEnabled="true" />
				<serviceDebug includeExceptionDetailInFaults="false" />
			</behavior>
	    </serviceBehaviors>
	</behaviors>
	<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>

This needs to be added into the web.config of the WCF service project.
We are now done with our WCF service, so you can press F5 to start the WCF service.

WCF Service

Using the WCF Service Moniker to Communicate Between Legacy COM Apps and New .NET Framework Apps

Consuming a WCF service using a typed contract

Creating the typed contract

First we are going to use svcutil.exe to create the source for a WCF client application for our service. To do this, start the Visual Studio Command Line and use the following command:

Svcutil.exe http://localhost:49170/HelloName.svc /config:EXCEL.EXE.config

You should notice that the tool creates 2 files: "HelleName.cs" and "EXCEL.EXE.config". The ".cs" file will in a minute be added to a Visual Studio class library project while the ".config" file must be placed in the same location as EXCEL.EXE.

Remark: Please make that the port number is the correct one for the WCF service running on your system.

Remark 2: If your COM application has another name then EXCEL.EXE, you should name the config file ".exe.config".

Next we create a cryptographic key pair with: "sn.exe -k helloKey.snk". You only need to perform this step once; if you recompile our recreate your typed contract you should reuse the same key pair.

Now we are going to create the class library that will contain our typed contract: create a new Class Library project and the source file generated in step 1 to I and modify the AssemblyInfo.cs to set ComVisible to true.

[assembly: ComVisible(true)]

To compile the class library we must also add a reference to "System.ServiceModel".

[add System.ServiceModel reference.png]

And the cryptographic key created must be added in the signing tab:

[strong name key.png]

Now we can just compile our assembly with Visual Studio and afterwards we register the types in the compiled assembly with "Regasm.exe". This allows the types to be used in the COM environment:

Regasm.exe \bin\Release\HelloNameContract.dll /tlb

After the tlb is created we are going to open it to view its UUID; therefore enter "oleview" in the Visual Studio command prompt to launch the Microsoft OLE/COM Object Viewer and choose File > View TypeLib and choose the tlb file generated in the previous step. Write down the UUID for IHelloName, because we will need it in a minute.

[oleview.png]

Finally, we register our assembly inside the GAC:

Gacutil.exe /i \bin\Release\HelloNameContract.dll

Creating the COM client

Now it is time to fire up Microsoft Excel and to open the VBA editor. First add a reference to the typelib we just created for the typed contract.

[add com reference.png]

Finally add the following code to create the service moniker:

Sub ServicemonkercallTyped()
    Dim service As IHelloName
    Dim monString As String

    monString = "service4:address=http://localhost:49170/HelloName.svc," & _
                "binding=basicHttpBinding, bindingConfiguration=BasicHttpBinding_IHelloName," & _
                "contract={3EA9FDEB-1AC5-324D-A7CA-F74422CF4415}"

    Set service = GetObject(monString)

    MsgBox (service.Hello("Excel Typed"))
End Sub

We are now ready to launch the VBA code. You should see the following message:

[hello excel typed.png]

Remark: In my example I have used Visual Studio 2010 to compile and sign the assembly, but of course you could automate all the steps completely by using MSBuild.

Using the WCF Service Moniker to Communicate Between Legacy COM Apps and New .NET Framework Apps

Consuming a WCF service using a MEX endpoint

If you don't mind static typing or if you don't want to have a dependency on the type library; using a MEX endpoint can be a good alternative. But be aware that the service moniker does not support data contracts in this mode.

If you have not done this already, first configure the WCF service to expose the MEX endpoint. Finally we must add the following code inside our VBA client:

Sub ServicemonkercallMex()
    Dim service As Object
    Dim monString As String

    monString = "service4:mexaddress=http://localhost:49170/HelloName.svc/mex" & _
                ", address=http://localhost:49170/HelloName.svc" & _
                ", contract=IHelloName, contractNamespace=http://tempuri.org/" & _
                ", binding=BasicHttpBinding_IHelloName, bindingNamespace=http://tempuri.org/"

    Set service = GetObject(monString)

    MsgBox (service.Hello("Excel Mex Endpoint"))
End Sub

Remark: don't forget to change the address of the service to the correct one for your system.

If you should have customized the contract namespace or the binding namespace, then you should modify them in the connection string. Otherwise "http://tempuri.org/" is always the default value in WCF for both namespaces.

If you launch the VBA code, you should see the following message:

[hello excel mex endpoint.png]

Using the WCF Service Moniker to Communicate Between Legacy COM Apps and New .NET Framework Apps

Consuming a WCF service using the WSDL

This is the last option to use the WCF service moniker. Using the locally cached WSDL has advantages; you don't need to create a class library for your WCF service and it does not need to expose meta data. Not exposing meta data is in enterprise environments often required to increase the security of the service. But be aware that, just like with the mex endpoint, the service moniker does not support data contracts in this mode.

Retrieving the WSDL

Before using the WCF service moniker, lets first look at the WSDL exposed by the WCF service. In order to do this, open your browser and enter the url "http://localhost:49170/HelloName.svc?wsdl".
Please note that this URL will be probably be different on your system.

[service page in internet explorer.png]

The first thing to notice is that the WSDL document is importing other documents. Because we want to call our web service by using a single file saved on disk, this behavior is not feasible. Luckily we were not the first .Net developers that wanted to have a "flat" WSDL. Other programming languages with less advanced web service stacks were having problems with this aspect of the WSDL generated by WCF, so Tomas Restrepo and Cristian Weyers came up with an alternative ServiceHostFactory that modifies the WCF stack to expose a flat WSDL. You can find this extension called "Flat WSDL" at http://www.thinktecture.com/media/9297/flatwsdl.zip

Download it and add the class library "Thinktecture.ServiceModel.Extensions.Description.dll" as a reference to our WCF service project. We can now modify the svc file of our service to change the ServiceHostFactory that IIS or WAS will be using. To do this provide the value "Thinktecture.ServiceModel.Extensions.Description.FlatWsdlServiceHostFactory" for the "Factory" property.

[change svc file.png]

If you now relaunch the WCF service and open the WSDL again, you will see that is has been "flattened"; it does not import any documents anymore. Save the WSDL to a file on your hard disk (I chose the path "c:\temp\IHelloName.wsdl").

[wsdl flattened.png]

Creating the COM client

Enter the following code in the VBA editor of Excel:
Sub ServiceMonikerCallWSDL()
    Dim service As Object
    Dim monString As String

    Const ForReading = 1
    Set objFSO = CreateObject("Scripting.FileSystemObject")
    Set objFile = objFSO.OpenTextFile("c:\temp\IHelloName.wsdl", ForReading, False, TristateTrue)
    wsdlContract = objFile.ReadAll
    objFile.Close

    On Error GoTo Exception:

    monString = "service4:address='http://localhost:49170/HelloName.svc'" & _
                ", wsdl='" & wsdlContract & "'" & _
                ", binding=BasicHttpBinding_IHelloName, bindingNamespace=http://tempuri.org/" & _
                ", contract=IHelloName, contractNamespace=http://tempuri.org/"

    Set service = GetObject(monString)

    MsgBox (service.Hello("Excel WSDL"))

    Exit Sub

Exception:
    MsgBox Err.Description
End Sub

Remark: don't forget to change the address of the service and the path of the WSDL file to the correct ones for your system.

Before executing this code, we will first disable the publishing of metadata to prove that we can call the WCF service without using its exposed meta data. We can also remove the custom ServiceHostFactory in the svc file.

[disable-meta-data-detailed.png]

If we now restart the WCF service and execute the VBA code in Excel we get the following response:

[hello excel wsdl.png]

Using the WCF Service Moniker to Communicate Between Legacy COM Apps and New .NET Framework Apps

Debugging the COM Service Moniker

If you have made errors while implementing the samples yourself; you will have noticed that exceptions returned by the GetObject call are rather cryptic. Is hard to discover the cause error with a rather general message like "Automation error, Invalid syntax".

[automation error.png]

Luckily I have found 2 ways to debug the COM Service Moniker. The moniker is implemented in C# which allows you to intercept its exceptions using the Visual Studio debugger. To do this: we must first configure our Visual Studio debugger to also break on external code, goto: "Tools > Options > Debugging > Enable Just My Code (Managed only)" and uncheck it.

[debugging.png]

Next we must attach our debugger to the Excel process: "Debug > Attach to process".

[attach debugger.png]

Now, we are ready to relaunch our VBA code, when an exception occurs inside the Service Moniker the Visual Studio will break and show you the managed message.

[argumentexception.png]

Although the Visual Studio debugger is a good choice for use in a developer environment; it is impractical in a test or server environment. By using Reflector; I discovered that the Service Monikes uses the System.Diagnostics framework. We can thus configure the moniker to trace its activity. We will configure this with a tool that is part of the Windows SDK: "Microsoft Service Configuration Editor". The default location for this tool is "C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SvcConfigEditor.exe".

Open the EXCEL.EXE.config created earlier with the Service Configuration Editor and go to the diagnostics information. On that screen you need to enable the tracing and the auto flush.

[svcconfig.png]

Besides the Service Configuration Editor, the Windows SDK also contains a tool to view the WCF trace files. You can find it at "C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SvcTraceViewer.exe". When we now restart Excel and relaunch our VBA code, we can see the more readable description of the exception in the trace viewer.

[svc trace viewer.png]

Remark: In a production environment you should not enable auto flush because it will hurt performance.

Conclusion

In this article I have shown a lesser know method for communication between COM based applications and their .Net cousins. Hopefully the examples where clear for all to understand and I hope that I made my point clear that the Service Moniker is a very powerful feature. Altough I have used Visual Studio 2010, you could also use Visual Studio 2008 and .Net 3.5 for the implementation of the WCF service. If you have further questions, don't hesitate to contact me.



About the Author

Pieter De Rycke

My name is Pieter De Rycke and I currently work in Brussels at the IT department of one of the biggest insurance companies in Belgium; AG Insurance. I was graduated as civil engineer in computer science at the university of Ghent in Belgium.

Informatics is both a job and my hobby. I like reading in my free time about software design patterns, programming languages, database development, I am also constantly busy writing small and larger software applications and experimenting with new technologies.

My blog:
http://pieterderycke.wordpress.com/

Related Articles

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • On-demand Event Event Date: September 10, 2014 Modern mobile applications connect systems-of-engagement (mobile apps) with systems-of-record (traditional IT) to deliver new and innovative business value. But the lifecycle for development of mobile apps is also new and different. Emerging trends in mobile development call for faster delivery of incremental features, coupled with feedback from the users of the app "in the wild." This loop of continuous delivery and continuous feedback is how the best mobile …

  • Webinar on September 23, 2014, 2 p.m. ET / 11 a.m. PT Mobile commerce presents an array of opportunities for any business -- from connecting with your customers through mobile apps to enriching operations with mobile enterprise solutions. Join guest speaker, Michael Facemire, Forrester Research, Inc. Principal Analyst, as he discusses the new demands of mobile engagement and how application program interfaces (APIs) play a crucial role. Check out this upcoming webinar to learn about the new set of …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds