COM, OLE and ActiveX Hooking


   - General information
   - Auto Monitoring
      - Auto Monitoring COM Objects Creation File Syntax
      - Auto Monitoring Files Syntax
   - Static Monitoring
      - Static Monitoring Files Syntax
      - Static Monitoring Files: How to generate ?
   - Interface Methods Creating Objects (since 5.1 version only)
   - Overriding, or Adding Pre/Post call hooks
   - Hooking Objects Creation And Destruction
   - COM Options
   - COM Tools

   - Advanced information


General information

To enable COM hooking, click the toolbar button on the main window before starting the application to hook.
Next it's recommended to attach application at startup to avoid COM object creation miss
With default options you don't need to do anything else.

Use Monitoring File Builder to automatically generate monitoring files for COM interfaces (Use the "COM Type Library" or "COM All Registered Type Library" options)



COM (and so OLE and ActiveX) hooking is an extension to API hooking.
That means all processes, modules, monitoring and overriding filters work as the classical API way.
Monitoring files too support the same functions and parameter flags.

In all the following we will use "COM", but all applies to COM OLE and ActiveX (OCX)

The first thing you should decide is if you want to use automatic COM hooking or static COM hooking.
  - Automatic hooks (recommanded) spy all objects created with a set of functions (and matching some specified classes according to CLSID filters).
They allow a lazy way for classes supporting IDispatch Interface (you don't need to write a monitoring file).
They allow to use the COM Interaction Tool, which allow you to do call methods of hooked interfaces (as the Remote Call dialog for API). It spyes object life cycle too.
  - Use static hooks only if you exactly know which components you want to hooks, and if you don't want to use the COM Interaction Tool.

In conclusion, auto monitoring gives you more control on COM objects.

To see all COM components available on your computer; convert CLSID or IID to or from their name; or get objects methods addresses (VA, RVA or RAW) use the Com Tools

Auto Monitoring

COM Auto Monitoring can be enabled/disabled thanks to button
It can be done before starting hooking (recommended to not loose first objects creation); or when hooking is already started.

COM auto hooking works spying a set of API.
So the first thing to do is to specify which API creating COM objects you want to hook.
The configuration file (COM options / "File defining hooked function for COM objects creation") comes with some well-known API.
So you just need to adjust it according to your needs.

To avoid to auto hook all created COM objects with specified API, you can use an exclusion or inclusion CLSID filter file (COM options / "Use CLSID filter")

COM options allow you to ask for various reports too : reporting created COM objects, reporting Interfaces with no monitoring files associated...

Monitoring files for COM auto hooking are located in "monitoring files\COM\" directory. (syntax is described here)

If no monitoring file is available for a specified Interface, and Interface support the IDispatch Interface (and according to COM options), IDispatch interface will be parsed automatically. That means even you haven't created a monitoring file for such interface, methods exposed throw IDispatch will be hooked.
Notice : Methods exposed throw IDispatch are methods you can view with an Ole/ActiveX methods browser (also known as tlb browser).

For Interfaces not supporting IDispatch, you sadly still have to create monitoring files (see COM Auto Monitoring files syntax).

When you disable COM Auto Monitoring while hooking is started, you will be asked if you want to unhook already hooked objects.
If you answer "Yes", you won't see COM logs anymore : every non static hooks to COM methods will be destroyed.
By ansewering "No", you will continue to see logs generated by already hooked COM methods, but no new method will be hooked.




Auto Monitoring: COM Objects Creation File Syntax

3 things are necessary to hook a created COM object :
   - the class ID (CLSID) used for creation
   - the interface ID (IID) used for creation
   - the pointer to created object returned by the function

So API definitions must specify where to find these information. The syntax is the following:
DllName|FunctionName|Option1|Option2...|OptionN or
WinApiOverrideDllNameLike|FunctionName|Option1|Option2...|OptionN
where options are

1) Stack size (the stack size needed for all parameters)
   StackSize= / x64StackSize= for all definitions, specify the stack size in bytes of all parameters.

2) CLSID (for all definitions provide information on CLSID)
   CLSIDStackIndex= / x64CLSIDStackIndex= bytes count between stack position (ESP/RSP) and CLSID parameter address
   REFCLSIDStackIndex= / x64REFCLSIDStackIndex= bytes count between stack position (ESP/RSP) and CLSID* parameter address
   CLSIDValue= CLSID string value (used for function not requiring a CLSID parameter)

3) IID
(not needed if function has a MULTI_QI parameter)
   IIDStackIndex= / x64IIDStackIndex= bytes count between stack position (ESP/RSP) and IID parameter address
   REFIIDStackIndex= / x64REFIIDStackIndex= bytes count between stack position (ESP/RSP) and IID* parameter address
   IIDValue= IID string value (used for function not requiring an IID parameter like DirectDrawCreate)

4) Object(s) pointer(s)

a) For single object creation (like CoCreateInstance)
   ObjectStackIndex= / x64ObjectStackIndex= bytes count between stack position (ESP/RSP) and object pointer parameter address

b) For multiple objects creation (like CoCreateInstanceEx)
   ObjectArrayStackIndex= / x64ObjectArrayStackIndex= bytes count between stack position (ESP/RSP) and MULTI_QI array parameter address
   ObjectArraySizeStackIndex= / x64ObjectArraySizeStackIndex= bytes count between stack position (ESP/RSP) and MULTI_QI array size parameter address

c) If object pointer is the returned value (like Direct3DCreate8) since 5.1 version only
   ObjectIsReturnedValue

x86 and x64 share same configuration files (to avoid to provide same definition twice),
so x86 and x64 information must be present

XXX_StackIndex values are the following :
   x86 : (0 based parameter index) *4
   x64 : RCX,RDX,R8,R9, and ( (0 based param index) - 4 ) *8 for parameters index greater or equal to 4

Examples
1) For CoCreateInstance(REFCLSID rclsid,LPUNKNOWN pUnkOuter,DWORD dwClsContext,REFIID riid,LPVOID * ppv);
   ole32.dll|CoCreateInstance|StackSize=20|REFCLSIDStackIndex=0|REFIIDStackIndex=12|ObjectStackIndex=16|x64StackSize=8|x64REFCLSIDStackIndex=RCX|x64REFIIDStackIndex=R9|x64ObjectStackIndex=0

2) For CoCreateInstanceEx(REFCLSID rclsid,IUnknown* punkOuter,DWORD dwClsCtx,COSERVERINFO* pServerInfo, ULONG cmq, MULTI_QI* pResults);
   ole32.dll|CoCreateInstanceEx|StackSize=24|REFCLSIDStackIndex=0|ObjectArraySizeStackIndex=16|ObjectArrayStackIndex=20|x64StackSize=16|x64REFCLSIDStackIndex=RCX|x64ObjectArraySizeStackIndex=0|x64ObjectArrayStackIndex=8

2) IDirect3D8* Direct3DCreate8(UINT SDKVersion);
   D3d8.dll|Direct3DCreate8|StackSize=4|CLSIDValue={1DD9E8DA-1C77-4d40-B0CF-98FEFDFF9512}|IIDValue={1DD9E8DA-1C77-4d40-B0CF-98FEFDFF9512}|ObjectIsReturnedValue|x64StackSize=0


Notice : When you found no information on CLSID, do not hesitate to put object IID as CLSID, because internally in WinApiOverride, CLSID are only used as a unique identifier to identify objects of the same type (that's we do in the example of Direct3DCreate8)

Notice: A particular treatment is done for CoGetClassObject to hook CreateInstance method of IID_IClassFactory (this function definition is provided in the default COM Object Creation Hooked Functions file that comes with WinAPIOverride binaries)

As methods of interface can create objects too, you may want to hook these created objects. See Interface Methods Creating Objects in this case



Auto Monitoring Files Syntax

Since version 5.1.10, Monitoring File Builder can build COM auto monitoring files automatically from type library, ocx, dll and exe
From "Monitoring File Builder", select the "COM All Registered Type Library" options, and then click the "Generate" button.
You will earn a lot of time !!!

1) Monitoring files for COM auto hooking must be located in "monitoring files\COM\" directory.

2) Monitoring file name must be "Interface_ID.txt" where Interface_ID is the IID of the interface
By the way for IDispatch auto monitoring file name is "{00020400-0000-0000-C000-000000000046}.txt"

Definitions are API like.
The only difference is that you have to specify vtbl index instead of dll name.
VTBLIndex=Index| [ReturnType] FuncName( ParamType [paramName] ) [;]
where Index is 0 based index



Examples

  VTBLIndex=0|IUnknown::QueryInterface(IUnknown* pObject,IID* pIid,PVOID* ppInterface)|Out|FailureIfNegativeRet
  VTBLIndex=1|IUnknown::AddRef(IUnknown* pObject);
  VTBLIndex=2|IUnknown::Release(IUnknown* pObject);
  VTBLIndex=3|HRESULT IDispatch::GetTypeInfoCount(IUnknown* pObject,UINT* pctinfo);|Out|FailureIfNegativeRet

Notice: the interface name "IUnknown::" or "IDispatch::" is optionnal.
I find it better because it's avoid confusion with API having the same name; and it gives information on interfaces in logs.


YOU HAVE TO ADD "IUnknown* pObject" as first parameter for all functions which are not static
This comes from C++ methods calling way

Some methods can create COM objects.
To spy these new objects, you need to add a specific option : see COM : Interface Methods creating objects

As you can see in previous examples, you can use all optionnal flags for parameters,
functions direction, breaking and failure values defined for API monitoring files (see API advanced syntax)


As COM use a lot of derived interfaces, a keyword has been introduced to avoid to copy and past base interfaces definitions.
If your interface derived from another one, you can use
BaseIID=IID
where IID is like {00000000-0000-0000-C000-000000000046}

Examples
By the way if you want to include IUnknown
;include IUnknown
BaseIID={00000000-0000-0000-C000-000000000046}


Using this notation, the IDispatch monitoring file can be like following
;include IUnknown
BaseIID={00000000-0000-0000-C000-000000000046}
VTBLIndex=3|HRESULT IDispatch::GetTypeInfoCount(IUnknown* pObject,UINT* pctinfo);|Out|FailureIfNegativeRet
VTBLIndex=4|HRESULT IDispatch::GetTypeInfo(IUnknown* pObject, UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo);|Out|FailureIfNegativeRet
VTBLIndex=5|HRESULT IDispatch::GetIDsOfNames(IUnknown* pObject,REFIID riid,OLECHAR** rgszNames,UINT cNames, LCID lcid, DISPID* rgDispId);|Out|FailureIfNegativeRet
VTBLIndex=6|HRESULT IDispatch::Invoke(IUnknown* pObject,DISPID dispIdMember,REFIID riid,LCID lcid,WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult,EXCEPINFO* pExcepInfo, UINT* puArgErr);|Out|FailureIfNegativeRet




These includes are transmitted through monitoring files interfaces definitions.
That means, if your interface is derived from IDispatch (which is derived from IUnknown),
as IDispatch monitoring file contains the definition "BaseIID={00000000-0000-0000-C000-000000000046}" for IUnknown,
you only have to add IDispatch monitoring file not the IUnknown one.
That means you only have to write "BaseIID={00020400-0000-0000-C000-000000000046}" to include both IDispatch and IUnknown definitions.


Using "BaseIID=" translate base monitoring file options (activated or not, parameters and function options)
If you don't want such options translation, you have to copy and paste methods definitions of base interfaces into each derived interface.

Example : create a COM monitoring file from Interface definition
Static Monitoring

Notice: COM static monitoring doesn't need COM auto hooking to be enabled

Static monitoring files can be loaded and unloaded using the same way as API monitoring files.

Static monitoring is usefull if object creation is not catched by COM auto hooking; it can occur for object created with none standard methods like CoCreateInstance.
In fact if mehtod used to create COM object is not referenced in the file "COM_ObjectCreationHookedFunctions.txt", the object and it's child objects methods won't be hooked.
It can be used if you want to target a only specific set of COM method.

The synatx is the following


Static Monitoring Files Syntax

COM@CLSID@IIDVTBLIndex={IID}:Index| [ReturnType] FuncName( ParamType [paramName] ) [;]
where CLSID is the class ID of the object, IID is the interface ID, and Index the 0 based VTBL index;

or

COM@CLSID@BaseIID={IID}

where CLSID is the class ID of the object and IID is the interface ID.
In this second case, static hook will hook all functions defined inside the interface monitoring file used for COM auto hooking.

Examples

For single function definition
COM@{C74190B6-8589-11D1-B16A-00C0F0283628}@IIDVTBLIndex={00020400-0000-0000-C000-000000000046}:56|HRESULT AboutBox(IDispatch* pIDispatch);|FailureIfNegativeRet |BreakBeforeAndAfterCall

For hooking a full interface already defined in a COM auto monitoring file
COM@{d45fd2fc-5c6e-11d1-9ec1-00c04fd7081f}@BaseIID={A7B93C91-7B81-11D0-AC5F-00C04FD97575}


Static Monitoring Files: How to generate ?
There are 2 cases depending if COM object can be created by CoCreateInstance

Case 1: The object can be created by CoCreateInstance

Imagine you like to hook IMsRdpClient2 of the Microsoft activeX Microsoft RDP Client Control (mstscax.dll)
First find the CLSID. From the "COM Interaction and Tools" menu, use the "Show Computer ActiveX / Known CLSID"
Next enter "mstscax" in the search field
Copy the CLSID or Prog Id of the found item; and then use the "Show Methods Addresses" menu
Inside the "Methods Address" dialog, enter the found Prog Id and the interface Id of Interface name (Interface ID aka IID can be found from the "IID <-> Interface Name Menu")
Once done, click the "Show" button. You will get all the methods address with there VTBL indexes and address, and methods address.
If some COM Interfaces definitions are missing, you can generate them with "Monitoring File Builder",
by selecting the "COM Type Library" or "COM All Registered Type Library" options, and then click the "Generate" button
This will provide you information for the existing Interfaces and will display methods information

With this you will get method address inside the dll
So you have to create a monitoring file (use the on created for interface {E7E17DC4-3B71-4BA7-A8E6-281FFADCA28F} aka {E7E17DC4-3B71-4BA7-A8E6-281FFADCA28F}.txt)
and replace vtblindex by Dll_Internal@ syntax aka
DLL_INTERNAL@0xHexAddress@DllName| [ReturnType] FuncName(ParamType [paramName]) [;]
(see monitoringfiles:basic)

//VTBLIndex=7|VOID* __stdcall IMsRdpClient2::Server(IUnknown* pObject, BSTR)
DLL_INTERNAL@0x2ec760@mstscax.dll|VOID* __stdcall IMsRdpClient2::Server(IUnknown* pObject, BSTR)
//VTBLIndex=8|BSTR __stdcall IMsRdpClient2::Server(IUnknown* pObject, PVOID* RetValue)|Out
DLL_INTERNAL@0x2ecb10@mstscax.dll|BSTR __stdcall IMsRdpClient2::Server(IUnknown* pObject, PVOID* RetValue)|Out
//VTBLIndex=9|VOID* __stdcall IMsRdpClient2::Domain(IUnknown* pObject, BSTR)
//VTBLIndex=10|BSTR __stdcall IMsRdpClient2::Domain(IUnknown* pObject, PVOID* RetValue)|Out
//VTBLIndex=11|VOID* __stdcall IMsRdpClient2::UserName(IUnknown* pObject, BSTR)
DLL_INTERNAL@0x2ecb80@mstscax.dll|VOID* __stdcall IMsRdpClient2::UserName(IUnknown* pObject, BSTR)
//VTBLIndex=12|BSTR __stdcall IMsRdpClient2::UserName(IUnknown* pObject, PVOID* RetValue)|Out
DLL_INTERNAL@0x2ecbb0@mstscax.dll|BSTR __stdcall IMsRdpClient2::UserName(IUnknown* pObject, PVOID* RetValue)|Out

Notices :
1) use the "Relative Virtual Address" value (the Raw address can be used for disassembly if you want to see COM function asm code)
2) Depending on the control version you may won't get same RVA !!!


Once done save the file under mstscax_static_hook.txt inside the "monitoring files" folder (not the COM sub folder)
Disable COM Auto hooking (you don't need it)
Load the mstscax_static_hook.txt monitoring file at startup
This is dirty raw method but you will be sure you won't miss a function call even if you miss the COM object creation


Case 2: The object can't be created by CoCreateInstance

In case object creation can't be done with CoCreatInstance, the "Methods Address" dialog of case 1 will display no information at all on your COM object and methods (you get an "Error creating object" error).
In that case you have to find method address manually.
To do so, you need to create a small test program calling the method for which you need information.
Build it and put a breakpoint before the function call.
When breakpoint is reached, go to the disasm window; hit step into and bypass the first jump which is vtbl indirection by hitting step into once more.
At this step you should be at function startup; but warning this is the virtual address and dll can be loaded elsewhere in other process, so you need to compute relative address.
To get the RVA, you have to remove module start address from the virtual address you just found.
RVA=VA-dll_base_address
Dll start address should be shown inside the modules view of your debugger.
If your debugger doesn't display modules start addresses, use Dumper provided with WinApiOverride, and click the process of your test program. You will get all modules start address.
Once done, write the monitoring file manually like described in case 1 (don't forget to add the first argument "IUnknown* pObject")

Interface Methods creating objects

Not only API can create COM objects. Some methods of interface can do it too like IDirect3D8::CreateDevice.
So you have to have a way to hook these created objects.
The solution is to use a syntax similar to COM Object Creation File Syntax options added after the Auto Monitoring File Syntax

Syntax
VTBLIndex=Index| [ReturnType] FuncName( ParamType [paramName] ) [;] |ObjectCreation(Option1|Option2...|OptionN)
It can be resumed by
AutoMonitoringFileSyntax | ObjectCreation( ComObjectCreationFileSyntax_Options )


You have one more key word than ComObjectCreationFileSyntax which is CurrentObjectCLSID and is used like |CLSIDValue=CurrentObjectCLSID
This allow to add interface to the current CLSID used (can be usefull in some case like for IDispatch::GetTypeInfo)

Examples
Notice : definition must be on a single line, they are on multiple lines here only for presentation

a) For IDirect3D8::CreateDevice, as we don't know CLSID of IDirect3DDevice8, we use IID_IDirect3DDevice8 as CLSID (see COM object creation file syntax notice), and IID_IDirect3DDevice8 value is {7385E5DF-8FE8-41D5-86B6-D7B48547B6CF}
so we have

VTBLIndex=15|HRESULT IDirect3D8::CreateDevice(IUnknown* pObject, UINT Adapter,D3DDEVTYPE DeviceType,HWND hFocusWindow,DWORD BehaviorFlags,D3DPRESENT_PARAMETERS* pPresentationParameters,IDirect3DDevice8** ppReturnedDeviceInterface);|FailureIfNegativeRet
|ObjectCreation(CLSIDValue={7385E5DF-8FE8-41D5-86B6-D7B48547B6CF}|IIDValue={7385E5DF-8FE8-41D5-86B6-D7B48547B6CF}|ObjectStackIndex=24|x64ObjectStackIndex=16)



b) For IDispatch::GetTypeInfo, we have to use same CLSID as the currently used, and the IID_ITypeInfo value is {00020401-0000-0000-C000-000000000046}
so we have

VTBLIndex=4|HRESULT IDispatch::GetTypeInfo(IUnknown* pObject,UINT iTInfo,LCID lcid,ITypeInfo** ppTInfo);|Out|FailureIfNegativeRet
|ObjectCreation(CLSIDValue=CurrentObjectCLSID|IIDValue={00020401-0000-0000-C000-000000000046}|ObjectStackIndex=12|x64ObjectStackIndex=R9)




Overriding, or Adding Pre/Post call hooks

Notice: COM overriding doesn't need COM auto hooking to be enabled.

Example can be found in directory "Overriding Dll SDK\COM\QueryInterfaceRestriction"


Overriding a COM method can be done using COM@CLSID@IIDVTBLIndex={IID}:Index as dll name, in an overriding, pre or post dll array (see overriding dll)
where CLSID is the class ID of the object, IID is the interface ID, and Index the 0 based VTBL index

By the way to override the AboutBox method of Microsoft TreeView Control 6 SP4 we can do the following

  1. STRUCT_FAKE_API pArrayFakeAPI[]=
  2. {
  3. // reminder: - stdcall AboutBox(IDispatch* pObject) vtbl index=56 <-- definition and vtbl index provided by "Com Tools"/"Show Methods addresses"
  4. // - Com definition : COM@CLSID@IIDVTBLIndex=IID:Index
  5. // Microsoft TreeView Control 6 SP4 (CLSID {C74190B6-8589-11D1-B16A-00C0F0283628})
  6. // IDispatch Interface ({00020400-0000-0000-C000-000000000046})
  7. // AboutBox VTBL Index : 56
  8. {_T("COM@{C74190B6-8589-11D1-B16A-00C0F0283628}@IIDVTBLIndex={00020400-0000-0000-C000-000000000046}:56"),
  9. _T("AboutBox"),
  10. (FARPROC)mAboutBox,
  11. StackSizeOf(IDispatch*),
  12. 0 // FirstBytesCanExecuteAnywhereSize is no use for com
  13. },
  14.  
  15. {_T(""),_T(""),NULL,0,0}// last element for ending loops
  16. };
replacing method with the following stupid function

  1. HRESULT __stdcall mAboutBox(IDispatch* pIDispatch)
  2. {
  3. UNREFERENCED_PARAMETER(pIDispatch);
  4. MessageBox(NULL,_T("I don't remember :D"),_T("AboutBox [Overrided]"),MB_OK|MB_TOPMOST|MB_ICONINFORMATION);
  5. return S_OK;
  6. }

All overriding arrays (faking, pre and post method call) can be used with COM.


A full COM overriding dll source code is available in "Overriding Dll SDK\COM\QueryInterfaceRestriction\" folder



Hooking Objects Creation And Destruction

Notice: COM objects creation hooking doesn't need COM auto hooking to be enabled.

Example can be found in directory "Overriding Dll SDK\COM\COMObjectCreationSpy"

To hook COM object creation, you only have to create an overriding dll exporting the following function
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Export COMObjectCreationCallBack function only if you want to monitor COM object creation
  3. // parameters :
  4. // in : CLSID* pClsid : pointer to CLSID used for object creation
  5. // IID* pIid : pointer to IID used for object creation
  6. // PVOID pObject : pointer to newly created com object
  7. // PRE_POST_API_CALL_HOOK_INFOS* pHookInfos : struct defined in ExportedStructs.h
  8. // return : tells to continue or stop callback chain for this object creation
  9. // TRUE to continue to report this object creation in other COMObjectCreationCallBack functions of other overriding dll
  10. // FALSE to stop reporting this object creation in other COMObjectCreationCallBack functions of other overriding dll
  11. ///////////////////////////////////////////////////////////////////////////////
  12. extern "C" __declspec(dllexport)
  13. BOOL __stdcall COMObjectCreationCallBack(CLSID* pClsid,IID* pIid,PVOID pObject,PRE_POST_API_CALL_HOOK_INFOS* pHookInfos)
  14. {
  15. // your filters for pClsid
  16. // your filters for pIid
  17.  
  18. // you can change object properties or call object methods here
  19.  
  20. MessageBox(0,_T("Com Object Created"),_T("Information"),MB_OK|MB_TOPMOST|MB_ICONINFORMATION);
  21.  
  22. return TRUE;
  23. }

COM object creation hooking use the same configuration file as COM auto hooking (see Auto Monitoring COM Objects Creation File Syntax);
that means only COM objects created with specified APIs are reported.


The parameter 4, PRE_POST_API_CALL_HOOK_INFOS* pHookInfos, gives you some information like:
   - the return address of API having created object,
   - the handle of module having done call
   - if current call match current overriding modules filters


To hook COM object creation, you only have to create an overriding dll exporting the following function

  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Export COMObjectDeletionCallBack function only if you want to monitor COM object destruction
  3. // parameters :
  4. // in : CLSID* pClsid : pointer to CLSID used for object creation
  5. // PVOID pIUnknownInterfaceAssociatedToObject : pointer to the IUnknown interface of the com object
  6. // PVOID pInterfaceReturnedAtObjectCreation : pointer to the interface returned at com object creation (the PVOID pObject value provided in COMObjectCreationCallBack)
  7. // PRE_POST_API_CALL_HOOK_INFOS* pHookInfos : struct defined in ExportedStructs.h see definition for help
  8. // return : tells to continue or stop callback chain for this object creation
  9. // TRUE to continue to report this object deletion in other COMObjectDeletionCallBack functions of other overriding dll
  10. // FALSE to stop reporting this object deletion in other COMObjectDeletionCallBack functions of other overriding dll
  11. ///////////////////////////////////////////////////////////////////////////////
  12. extern "C" __declspec(dllexport)
  13. BOOL __stdcall COMObjectDeletionCallBack(CLSID* pClsid,PVOID pIUnknownInterfaceAssociatedToObject,PVOID pInterfaceReturnedAtObjectCreation,PRE_POST_API_CALL_HOOK_INFOS* pHookInfos)
  14. {
  15.  
  16. MessageBox(0,_T("Com Object Destroyed"),_T("Information"),MB_OK|MB_TOPMOST|MB_ICONINFORMATION);
  17.  
  18. return TRUE;
  19. }