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.
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.
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
- This will only work for InProc DLL's that implement the IClassFactory interface. It also works with DLL registered with MTS
as long as the DLL is registered with the Shim FIRST.
- As this uses registry changes to get the Shim activated, running regsvr32 will zap the entries.
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.
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