Activation Tricks

A collection of notes and code samples detailing automatic delegator attachment to objects.

Introduction

Keith Brown's very cool Universal Delegator allows Hooks to be attached to objects that get QI notifications, and a chance to pre & post process COM calls. Currently there are no automatic way to attach a delegator and one (or more) hooks to an object at creation time, there are a number of approaches that may be able to provide this functionality.

API Hooks

These could potentially provide the most transparent implementation, however current investigation hasn't revealed any way to achieve this. The SysInternals site shows how to do API hooks for the base O.S. functionality (such as in the excellent RegMon tool). Looking through the DDK docs though, i saw no references to any COM functionality.

NEW !
I've been playing around with Detours, which allows you to inject API hooks into a running process, i knocked up a simple API hook that hooks CoCreateInstance, and wraps each object in the TraceHook. Here's a sample output (from some code using ADO) that uses the Detours InjDLL tool.
2420: 1572: CoCreateInstance MSVSA.InprocessEventCreator.1
2420: 1572: CoCreateInstance {00000323-0000-0000-C000-000000000046}
2420: 1572: MSVSA.InprocessEventCreator.1, ISystemDebugEventFire::[method3]()
2420: 1572: MSVSA.InprocessEventCreator.1, IMarshal::GetUnmarshalClass ()
2420: 1572: MSVSA.InprocessEventCreator.1, IMarshal::GetMarshalSizeMax ()
2420: 1572: MSVSA.InprocessEventCreator.1, IMarshal::MarshalInterface ()
2420: 1572: MSVSA.InprocessEventCreator.1, ISystemDebugEventFire::[method5]()
2420: 1572: CoCreateInstance MSDASC.MSDAINITIALIZE.1
2420: 1572: MSDASC.MSDAINITIALIZE.1, IDataInitialize::GetDataSource ()
2420: 1572: CoCreateInstance MSDADC.1
2420: 1572: MSDADC.1, OLE (Part 3 of 5)::[method4]()
2420: 1572: MSDASC.MSDAINITIALIZE.1, OLE (Part 3 of 5)::[method4]()
2420: 1572: MSDASC.MSDAINITIALIZE.1, OLE (Part 3 of 5)::[method3]()
2420: 1572: CoCreateInstance COMSVCS.DispenserManager
2420: 1572: COMSVCS.DispenserManager, OLE (Part 3 of 5)::[method3]()
2420: 1572: COMSVCS.DispenserManager, OLE (Part 3 of 5)::[method3]()
2420: 1572: COMSVCS.DispenserManager, OLE (Part 3 of 5)::[method3]()
2420: 1572: CoCreateInstance MSDASQLErrorLookup.1
2420: 1572: MSDASQLErrorLookup.1, IErrorLookup::GetHelpInfo ()
2420: 1572: MSDASQLErrorLookup.1, IErrorLookup::GetErrorDescription ()
2420: 1572: CoCreateInstance MSDASQLErrorLookup.1
2420: 1572: MSDASQLErrorLookup.1, IErrorLookup::GetHelpInfo ()
2420: 1572: MSDASQLErrorLookup.1, IErrorLookup::GetErrorDescription ()
2420: 1572: CoCreateInstance MSDASQLErrorLookup.1
2420: 1572: MSDASQLErrorLookup.1, IErrorLookup::ReleaseErrors ()
2420: 1572: CoCreateInstance MSDADC.1
2420: 1572: MSDADC.1, OLE (Part 3 of 5)::[method4]()
2420: 1572: MSDADC.1, OLE (Part 3 of 5)::[method4]()
2420: 1572: MSDADC.1, OLE (Part 3 of 5)::[method4]()
Download the TraceHook Injector source & binaries. You will need to install Detours and TraceHook first.

Note : I haven't had much luck yet injecting into IIS or COM+, due to security related problems, I'm still tinkering with this.

Shim Class Factory

Following the same approach (and therefore also the same limitations) as MTS, a shim class factory could be built, which by altering the registry would get loaded / called as requests for a particular object are made. This shim class factory could then load the real component, call the real class factory, and finally use the delegator to attach a hook.

Download CFShim, an example of this for In Process objects.
Notes


Custom Class Factory

or more correctly a modified standard ATL class factory, whilst on the one hand, this involves actual code changes to the objects, it does provide a cleaner solution.

have a look at THClassFactory.h, which provides an example of overriding the standard ATL class factory.


DECLARE_CLASSFACTORY_INTERCEPTOR

An awesome idea from Chris Sells, to extend the Custom Class Factory approach, by having the hooks declared in the type library (using custom attributes), and a custom class factory that can stack up a number of hooks during the call to CreateInstance. The custom class factory is assigned to the class using DECLARE_CLASSFACTORY_INTERCEPTOR(CClass), e.g.
calcsvr.idl

#include "icfattr.h"		// for Interceptor ClassFactory attributes

	[
		uuid(43B6EC69-A742-11D2-90EB-00104B2168FE),
		helpstring("Calc Class"),
		HOOK1("UDTraceHookSvr.TraceHook:name=mycalc%ID%"),
		HOOK2("SomeOtherHook")
	]
	coclass Calc
	{
		[default] interface ICalc;
	};

calc.h

class ATL_NO_VTABLE CCalc : 
	public CComObjectRootEx<CComSingleThreadModel>,
	public CComCoClass<CCalc, &CLSID_Calc>,
	public IDispatchImpl<ICalc, &IID_ICalc, &LIBID_CALCSVRLib>
{
public:

DECLARE_CLASSFACTORY_INTERCEPTOR(CCalc)

// rest of class definition
Upto 5 hooks can be stacked on the coclass, using the HOOK1 to HOOK5 macro's. The string following the progid is parsed in to IPropertyBag, token replacement is performed on %ID% by replacing it with the instance number, multiple items can be added to the property bag by separating then with a semi colon, e.g. HOOK1("SomeHook:start=foo;end=bar;name=server%ID%"). The property bag would be initialized with 3 entries start with a value of foo, end with a value of bar and name with a value of server1. An instance of the hook is created, and if the hook supports IPersistPropertyBag, the load method is called passing a pointer to the property bag. This allows for stateful hooks to be initialized. The hooks are added in reverse order, such that HOOK1 will get called first, followed by HOOK2 etc.
Download DECLARE_CLASSFACTORY_INTERCEPTOR

Back to Simons COM stuff
(c) 1999,2000 Simon Fell, this page last updated 16 April 2000