Using registration-free COM to redirect COM calls

Author: Anton Kaminsky

 

Introduction

 

Recently I came across the following problem: We have a COM class that is used by a few different client applications. During runtime one of the COM methods caused a deadlock when invoked by a specific application. Because in a COM server we don’t have any information about the client application that invoked the method, we can’t know when the method must behave differently in order to avoid the deadlock.

 

This problem can be made more general: Let’s say that you have some third party COM server and your application is one client of many for this COM server. You have found out that one or a few of the methods implemented by the COM server are not suitable for your client. How can you override this behavior without changing your client code (and of course not changing the third party COM server)?

 

The solution I’ve found is using registration-free COM (also known as reg-free COM) to make the client application get a different class when it asks for the third party COM server. This different class will override the methods we wish to override and delegate all others to the real third party COM server.

 

A couple of words about reg-free COM: Basically, Reg Free COM is a way to use COM classes without registering them to the registry. It uses manifests instead of the registry to point the COM clients to the location of the COM class they wish to use.

 

In this post I will show a walkthrough of how to implement such a solution using Visual Studio 2012. The end result is attached so you don’t have to create it by yourself, but reading on will lead to a better understanding of the process.

 

Creating a COM server (represents the 3rd party)

  • Create a new C++ ATL project named ThirdPartyComServer, check Allow merging of proxy/stub code and click Finish.
  • Add a new ATL Simple Object class to the project, name it ThirdPartyComServerImpl and give it a ProgID: ThirdPartyComServer. Click Finish.
  • Open Class View (in the view menu), find the interface IThirdPartyComServerImpl, right-click and choose Add->Add Method.
  • Name the method MethodA and click Finish.
  • Add another method named MethodB.
  • Go to the source file: ThirdPartyComServerImpl.cpp and add an implementation for both methods, for example a MessageBox.

After the actions above, your IDL file should look like this: 

// ThirdPartyComServer.idl : IDL source for ThirdPartyComServer
//

// This file will be processed by the MIDL tool to
// produce the type library (ThirdPartyComServer.tlb) and marshalling code.

import "oaidl.idl";
import "ocidl.idl";

[
	object,
	uuid(A0F11C01-7AF7-40B2-9A30-11B0328E6DBE),
	dual,
	nonextensible,
	pointer_default(unique)
]
interface IThirdPartyComServerImpl : IDispatch{
	[id(1)] HRESULT MethodA(void);
	[id(2)] HRESULT MethodB(void);
};
[
	uuid(076D5428-AB88-44A1-B112-63577305B6B9),
	version(1.0),
]
library ThirdPartyComServerLib
{
	importlib("stdole2.tlb");
	[
		uuid(970C26D5-0220-4FFF-A6AA-EA6FC2338970)		
	]
	coclass ThirdPartyComServerImpl
	{
		[default] interface IThirdPartyComServerImpl;
	};
};

 And your cpp file should look like this:

// ThirdPartyComServerImpl.cpp : Implementation of CThirdPartyComServerImpl

#include "stdafx.h"
#include "ThirdPartyComServerImpl.h"


// CThirdPartyComServerImpl



STDMETHODIMP CThirdPartyComServerImpl::MethodA(void)
{
	::MessageBox(NULL,L"MethodA", L"MethodA", 0);

	return S_OK;
}


STDMETHODIMP CThirdPartyComServerImpl::MethodB(void)
{
	::MessageBox(NULL,L"MethodB", L"MethodB", 0);

	return S_OK;
}

Compile a Release build of the project (note: the build also creates the tlb file, and registers the COM server to the registry).

 

Creating a C# client for the COM server

  • Add a new C# console application project to the solution and call it ClientOfThirdPartyComServer.
  • Go to project references and choose Add Reference. Select COM, then Type Libraries, and look for the ThirdPartyComServerLib there (if it is not there, use browse to find it). Add it to the references. We can now access the COM server methods from our client.
  • Add the following code to the ClientOfThirdPartyComServer program.cs file:
using System;

namespace ClientOfThirdPartyComServer
{
    class Program
    {
        static void Main()
        {
            var serverType = Type.GetTypeFromProgID("ThirdPartyComServer");
            var comServer = (ThirdPartyComServerLib.IThirdPartyComServerImpl)Activator.CreateInstance(serverType);

            comServer.MethodA();

            comServer.MethodB();

        }
    }
}

 

  • In the Project Options->Build, select Platform target: X86
  • Perform a Release Build and run the project (set it as the main project of the solution if needed).

The two message boxes, MethodA and MethodB, should be displayed.

Our goal is to replace the implementation of MethodB, so that when the client (ClientOfThirdPartyComServer) calls it, a different code than the one implemented in the ThirdPartyComServer will be executed. We want to make this change relevant to our client only, since we don’t want to change the behavior for all other clients of the ThirdPartyComServer.

 

Creating a COM class & overriding the behavior of ThirdPartyComServer

1.   Creating the new COM class

    • Add a new ATL project named RegFreeComProxy to the solution.
    • Check Allow merging of proxy/stub code and click Finish.
    • Add a new ATL Simple Object class to the project and name it RegFreeComProxyImpl. Give it a ProgID: ThirdPartyComServer and click Finish.
      We gave our class the same ProgId as the ThirdPartyComServer, we do this because we want to intercept the class created by the client application when it uses this ProgID and provide our class instead.
    • The RegFreeComProxy project should not be registered to the registry since we want it to be accessed from the ClientOfThirdPartyComServer only.
    • Open the RegFreeComProxy project properties, go to Configuration Properties->Linker->General->Register Output and select No.

 

The job of the RegFreeComProxy is to provide an alternative implementation for MethodB of the ThirdPartyComServer, while leaving the original implementation of MethodA.

 

2.   Overriding the existing behavior

  • To implement the IThirdPartyComServerImpl interface in our new class, we need to import the tlb of the ThirdPartyComServer.
  • Remove the default IRegFreeComProxyImpl interface created by Visual Studio, because we want to implement only IThirdPartyComServerImpl without adding any new functionality.
  • Our new class includes a member of type IThirdPartyComServerImpl.  This member will contain the real ThirdPartyComServer and delegate to it the methodsthat we don’t override.

Now your header file should look like this:

#pragma once
#include "resource.h"       // main symbols



#include "RegFreeComProxy_i.h"

#import "..\ThirdPartyComServer\Release\ThirdPartyComServer.tlb" raw_interfaces_only

using namespace ThirdPartyComServerLib;

#if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA)
#error "Single-threaded COM objects are not properly supported on Windows CE platform, such as the Windows Mobile platforms that do not include full DCOM support. Define _CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA to force ATL to support creating single-thread COM object's and allow use of it's single-threaded COM object implementations. The threading model in your rgs file was set to 'Free' as that is the only threading model supported in non DCOM Windows CE platforms."
#endif

using namespace ATL;


// CRegFreeComProxyImpl

class ATL_NO_VTABLE CRegFreeComProxyImpl :
	public CComObjectRootEx<CComSingleThreadModel>,
	public CComCoClass<CRegFreeComProxyImpl, &CLSID_RegFreeComProxyImpl>,	
	public IDispatchImpl<IThirdPartyComServerImpl, &__uuidof(IThirdPartyComServerImpl), &_uuidof(__ThirdPartyComServerLib), /*wMajor =*/ 1, /*wMinor =*/ 0>
{
public:
	CRegFreeComProxyImpl();
	
DECLARE_REGISTRY_RESOURCEID(IDR_REGFREECOMPROXYIMPL)


BEGIN_COM_MAP(CRegFreeComProxyImpl)	
	COM_INTERFACE_ENTRY(IThirdPartyComServerImpl)
	COM_INTERFACE_ENTRY(IDispatch)	
END_COM_MAP()



	DECLARE_PROTECT_FINAL_CONSTRUCT()

	HRESULT FinalConstruct()
	{
		return S_OK;
	}

	void FinalRelease()
	{
	}

public:
	STDMETHOD(MethodA)(void);
	STDMETHOD(MethodB)(void);
private:
	IThirdPartyComServerImpl* ThirdPartyComServerPtr;
};

 

Let’s move on to the source file:

  • In the RegFreeComProxy constructor we will create an instance of the ThirdPartyComServer and store it in our member ThirdPartyComServerPtr.
  • In MethodA we will delegate the implementation.
  • In MethodB we will provide an alternative implementation.

The source file should now look like this:

CRegFreeComProxyImpl::CRegFreeComProxyImpl()
{
	IID innerIID;

	IIDFromString(L"{970C26D5-0220-4FFF-A6AA-EA6FC2338970}", &innerIID);

	HRESULT hr = CoCreateInstance( innerIID,
                    NULL,
                    CLSCTX_INPROC_SERVER,
					__uuidof(IThirdPartyComServerImpl),
                    (void **)&ThirdPartyComServerPtr);
	
}

STDMETHODIMP CRegFreeComProxyImpl::MethodA(void)
{
	return ThirdPartyComServerPtr->MethodA();
}


STDMETHODIMP CRegFreeComProxyImpl::MethodB(void)
{
	::MessageBox(NULL,L"MethodC", L"MethodC", 0);

	return S_OK;
}

 

We must create the ThirdPartyComServer using its CLSID and not ProgId, the reason for this is because we have “stolen” the ProgId in our RegFreeComProxy so we can’t use it to identify the ThirdPartyComServer anymore.

 

3.   Creating a manifest for our ClienOfThirdPartyComServer

The manifest file will tell our client that the ProgId: ThirdPartyComServer  is mapped to our RegFreeComProxy class and not the real ThirdPartyComServer, which is registered in the registry.

  • Right click on the ClientOfThirdPartyComServer project, select Add Item and choose Application Manifest. A new file named app.manifest is added to your project.
  • Open the app.manifest file and add to it the following text:
<file name="RegFreeComProxy.dll" hashalg="SHA1">
    <comClass clsid="{483E077B-F2CF-42F9-92A4-1DD95D76BD29}" tlbid="{889212F0-7A96-40A4-BED3-93507C872EAD}" progid="ThirdPartyComServer" threadingModel="Apartment"></comClass>
  </file>

 (Add it before the closure: </asmv1:assembly> )

 

We assume here that the file: RegFreeComProxy.dll is located in the same location as the executable of our client (ClientOfThirdPartyComServer.exe). You can just modify the Output Path of all the projects in the solution to the same location.

Now everything is ready. Run the client code and you should see message boxes MethodA and MethodC.

 

Summary:

This example shows how we can override methods of a COM server for a specific client, without touching the code of the server or the client, but instead using a manifest file to divert the flow into a new class which contains our logic. In my opinion, it is a rather clean solution to a messy problem.

In this example I used an embedded manifest, which requires re-compiling of the client code. However, you can also use an external manifest file, in that case, you won’t need to recompile the client code.

 


Leave a Comment

We encourage you to share your comments on this post. Comments are moderated and will be reviewed
and posted as promptly as possible during regular business hours

To ensure your comment is published, be sure to follow the Community Guidelines.

Be sure to enter a unique name. You can't reuse a name that's already in use.
Be sure to enter a unique email address. You can't reuse an email address that's already in use.
Type the characters you see in the picture above.Type the words you hear.
Search
Showing results for 
Search instead for 
Do you mean 
About the Author
I've been all over the coding world since earning my degrees have worked several years in c++ and then several in java, finally setteling i...
Featured


Follow Us
The opinions expressed above are the personal opinions of the authors, not of HP. By using this site, you accept the Terms of Use and Rules of Participation.