Home > C#, WCF > Using the WCF Service Moniker to communicate between legacy COM applications and new .Net applications

Using the WCF Service Moniker to communicate between legacy COM applications and new .Net applications

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 COM Service Moniker.

The COM 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 a typed contract and using the MEX endpoint. 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.

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”:

Inside the project: we create the following service contract:

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

We implement the service as follows:

public class HelloName : IHelloName
{
	public string Hello(string name)
	{
		return "Hello " + 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.

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 “<appname>.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”.

 

 

<!–[if gte mso 9]> <![endif]–><!–[if gte mso 9]> Normal 0 21 false false false NL-BE X-NONE X-NONE <![endif]–><!–[if gte mso 9]> <![endif]–> <!–[endif]–>First we are going to use svcutil.exe to create the source for a WCF client application for

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

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 <projectpath>\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.

Finally, we register our assembly inside the GAC:

Gacutil.exe /i <projectpath>\bin\Release\HelloNameContract.dll

Creating the COM client

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

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:

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.

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. 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_IHelloName, bindingNamespace=http://tempuri.org/"

    Set service = GetObject(monString)

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

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:

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”.

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.

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

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.

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 Relfector; 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.

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.

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.

Advertisements
Categories: C#, WCF Tags: , , ,
  1. Lavanya
    November 19, 2010 at 05:35

    Thank you for such a detailed article. But I am having problems in getting this to work. I hit the notorious Run-time -214722120 Automation error exception and am unable to get VS to break on this exception. Also tried the EXCEL.EXE.config way, but the log file is not even created. So no idea of what the actual underlying exception could be. Funny thing is that I am able to run a vbs script using the same code. But the same thing does not work from within Excel. Would be great if you can shed some light on this.

    Thanks
    Lavanya

    • pieterderycke
      November 19, 2010 at 22:00

      Hello Lavanya,

      Which version of .Net are you using? .Net 4.0 or .Net 3.5? Which Office version are you using? Can you also give the service moniker connection string?

      Regards,
      Pieter

  2. Jim Meehan
    January 26, 2011 at 22:26

    When trying to register the service client, I receive the following warning:

    Types registered successfully
    Type library exporter warning processing ‘ErrorServiceClient, ErrorClient’. Warn
    ing: Type library exporter encountered a type that derives from a generic class
    and is not marked as [ClassInterface(ClassInterfaceType.None)]. Class interfaces
    cannot be exposed for such types. Consider marking the type with [ClassInterfac
    e(ClassInterfaceType.None)] and exposing an explicit interface as the default in
    terface to COM using the ComDefaultInterface attribute.
    Assembly exported to ‘C:\VisualStudioProjects\Samples\ErrorClient\ErrorClient\bi
    n\Release\ErrorClient.tlb’, and the type library was registered successfully

    When I get the service moniker object, it fails with the error:

    Type load for contract interface ID failed with error: Interface not registered.

    I have added the attribute [ClassInterface(ClassInterfaceType.None)] to the definition of ErrorServiceClient, but it still fails. Should I be doing something with the ComDefaultInterface attribute?

  3. Jim Meehan
    January 27, 2011 at 00:29

    Svcutil generates code that includes the following class:

    public partial class ErrorServiceClient : System.ServiceModel.ClientBase, IErrorService

    When i try register the dll, it issues a warning about ErrorServiceClient inheriting a generic interface. At runtime, it says that the interface is not defined. What is the workaround for this issue?

    • pieterderycke
      January 28, 2011 at 08:55

      You don’t have to worry, that warning is normal. If you receive “Assembly exported to ‘C:\VisualStudioProjects\Samples\ErrorClient\ErrorClient\bin\Release\ErrorClient.tlb’, and the type library was registered successfully”, everything was successful. If you have more questions, feel free to ask them 🙂

  4. Tahir
    March 10, 2011 at 08:17

    Very good article. I however have a cpl of questions. Is this moniker solution dependent on certain frameworks?

    Lets say my COM client machine only has framework 2.0. Can I still implement this moniker solution for my COM client machine? Basically, can this moniker solution communicate with a WCF written in framework 3.5 whereas my client only has framework 2.0?

    Thanks

    • pieterderycke
      March 10, 2011 at 09:49

      WCF is only available from .Net 3.0 onwards, so your client needs to have at least that version installed. But a .Net 3.0 WCF service moniker should normally be able to communicate with a 4.0 WCF service (or a 3.5).

      But keep in that the connection string of .Net 3.0 (and .Net 3.5) service moniker starts with “service:” instead of “service4:”.

  5. Tahir
    March 10, 2011 at 16:44

    Thank you Pieterdery. Thats what I wanted to know.

  6. dhaz
    March 9, 2012 at 13:03

    I am getting a error “The maximum message size quota for incoming messages (65536) has been exceeded. To increase the quota, use the MaxReceivedMessageSize property on the appropriate binding element.
    ” when the bulk amount os data is passed.How to over come this issue

    • pieterderycke
      March 9, 2012 at 13:55

      This is not possible. One of annoying the limitations of the service moniker is that you cannot change binding properties. If this is blocking, you should create a REST service in WCF and call this REST service without the service moniker. In this case, you have to develop your own VBA proxy.

  7. som o'neelse
    March 30, 2012 at 12:14

    Can you get VBA to use a service method that returns an array of strings?

  8. Andy
    April 19, 2012 at 10:57

    I’m getting an Run-time error ‘-2146233054 (80131522)’: Automation error.
    I’m using Access 2010 and VS2010 .NET3.5
    i’m using this connectionstring:
    address = “service:mexaddress=http://localhost:7339/WcfService.svc/mex” & _
    “, address=http://localhost:7339/WcfService.svc” & _
    “, contract=IWcfService, contractNamespace=http://tempuri.org/” & _
    “, binding=BasicHttpBinding_IWcfService_IWcfService, bindingNamespace=http://tempuri.org/”

    In web.config:

    What am i missing?

  9. August 19, 2012 at 06:25

    Can you show an example using a SharePoint 2010 WCF Service? http:///_vti_bin/ListData.svc

  10. Nozz
    February 22, 2013 at 19:28

    som o’neelse :
    Can you get VBA to use a service method that returns an array of strings?

    I’m very interesting on this. Any idea?? Thanks in advance.

    • pieterderycke
      February 22, 2013 at 19:36

      No, this is not supported by the service moniker. If possible, I recommend developing a REST service and calling this REST service from Excel.

  11. Cal Braun
    March 15, 2013 at 16:42

    Trying this out and getting a WSDL error. I posted this question to stackoverflow, any ideas?

    http://stackoverflow.com/questions/15436383/getting-wsdl-error-when-calling-service-using-wcf-service-moniker

    Thanks!

  12. Palin
    February 4, 2014 at 17:18

    I had some trouble with the service moniker. It´s looks as he is not a part of the 4.0 Frameworke Client Proviel. After installing the Extended Version. It works.

    Hope that could Help other.

  13. February 11, 2014 at 19:43

    Thank you Pieter, mainly thanks to your blog i was able to create a service in C# that can be called from VBA in MS Access2013, which in turn accesses an DevExpress MiddleTier DataServer Service. I based both services on TopShelf.

    It was an extremely frustrating experience trying to understand all those WCF configuration options in WCF and the moniker string in Access, but in the end it all worked out!

    I’ve created a migration path to a new generation of software (based on XAF) where the users can still use their familiar program in MS Access.

    Thanks!

    • pieterderycke
      February 11, 2014 at 19:50

      great to hear 🙂

  14. July 26, 2014 at 14:44

    Thank you for your blog.
    I wonder if complex data type for the method parameters is supported by moniker?
    If not, what are my options?

    • pieterderycke
      July 27, 2014 at 10:39

      Complex data types ares not supported by the WCF Service Moniker. But this is already an old article. If I today would want to call services from an Excel application, I would implement the services as REST services (with the ASP.NET Web API framework) and call these REST services from Excel. Excel has support for performing HTTP calls and can parse XML.

      • Divya Jain
        April 28, 2016 at 14:04

        Do you have any blog where you consume the webapi inside excel

  15. DuminduL
    November 27, 2015 at 01:10

    Hi, Thanks for the post! Great Job! After couple of attempts, managed to get mex communication working on both MS Access and MS Excel 365. WCF hosted on local IIS. To all friends who’s reading this post, just pay close attentions to every instructions author has posted.

  16. Divya Jain
    April 28, 2016 at 13:54

    Need help … Implemented the same solution but now getting the below error

    net.tcp://ip/DataAccess/Service1.svc/ did not receive a reply within the configured timeout (00:01:00). The time allotted to this operation may have been a portion of a longer timeout. This may be because the service is still processing the operation or because the service was unable to send a reply message. Please consider increasing the operation timeout (by casting the channel/proxy to IContextChannel and setting the OperationTimeout property) and ensure that the service is able to connect to the client.

  1. November 2, 2010 at 21:09
  2. January 8, 2011 at 11:01
  3. December 8, 2014 at 10:29

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: