Back to the software page

The home of COMWatch

Contact me 
Tip jar:
Thank you!
Powered by PayPal

Download COMWatch 0.70


July 24, 2009: version 0.70 released. Fixed a bug, VS2008 compatibility added.

March 21, 2009: version 0.60 released. Fixed a serious bug.

What is it about

Debugging COM Automation clients in native Microsoft Visual C++ sucks. Specifically, it sucks because the VC++ debugger, unlike the Visual Basic debugger, has no built-in capability of displaying contents of Automation objects the way they were meant. This is how automation objects look in the Visual Basic debugger:


Meanwhile in Visual C++, all you can see is the hex value of the interface pointer. Things aren't made easier by the fact that Visual C++ comes with three different sets of helper classes - MFC, ATL and Native COM.

VC++ supplies some infrastructure for third-party visualization in the debugger. There's the powerful autoexp.dat file, there's the Expression Evaluation Add-in (EEAddin) API, and finally in the managed (.NET) world there's the DebuggerVisualizer attribute and its respective API. Yet none of these technologies are quite right for this particular job. When it comes to Automation, extracting object's contents necessarily involves making debug-time function calls, and both EEAddin and autoexp.dat assume that all there is to see is available somewhere in the memory.

So I've devised a solution - the COMWatch. It's a static (.LIB) library that you should link to your project, and it provides a set of functions you can call in the Watch window to list the contents of Automation objects. They are compatible with all three smart pointer classes of VC++ - _com_ptr_t<>, CComPtr<> and COleDispatchDriver.

With COMWatch, a COM object in the Watch window might look like this:


The trick is in the _sp() function. It's implemented in the COMWatch library, takes an address of a smart pointer (thus the "SP" designation), and returns a data structure with the readable object contents. Finally, via the magic of autoexp.dat, the said structure is displayed in the most natural way. Still no match for the VB debugger, but better than nothing.

There's more. With COMWatch, like in the VB debugger, you can evaluate expressions against the object, and even call its methods from the Watch window and observe the result.


Download the COMWatch archive by clicking the link below:
COMWatch 0.60
Unzip it into some local folder.
Browse to that folder and run register.js by double-clicking it.
Close and reopen Visual Studio.


Usage of COMWatch boils down to calling its functions in the debugger's Watch window, passing the Automation objects from your program as parameters. You start by #including COMWatch.h in your project; the best place to do so would be stdafx.h. If your project doesn't have one, you may include it in the C++ file(s) where you intend to debug Automation. The point of that is that object examination functions (_sp() and such) are visible to the debugger within the execution context of the breakpoint where you want to examine objects. The path to COMWatch.h is provided to the Visual Studio settings during the registration; if the registration went through correctly, there's no need to specify the path manually.

Compile your project and run it under the debugger all the way to a breakpoint where a valid Automation object is available as a variable - either as a smart pointer object or as a raw interface pointer.

Let's assume, for example's sake, that your project consumes the services of the Microsoft ActiveX Data Objects (ADO) using Native COM. Somewhere in your project, there would be an #import statement somewhat like this:

#import "libid:{EF53050B-882E-4776-B643-EDA472E8E3F2}" rename("EOF", "ADOEOF")

Then let's assume we're debugging a function where an ADO Connection object is declared and initialized:

ADODB::_ConnectionPtr MyConn;

Activate the Watch window, and type a watch expression:


Expand the object tree and enjoy the view. The same trick is applicable to ATL and MFC smart pointers as well. Probably other smart pointer classes, too - as long as they have the raw interface pointer as the first data element and contain no virtual functions.

Smart pointers vs. interface pointers

If your application uses, for one or another reason, raw interface pointers instead of smart pointer classes, you may examine those in the Watch window using the _ip() function instead of _sp(). "IP" stands for Interface Pointer. You pass the raw interface pointer to it as a parameter, like this:

In your code: ADODB::_Connection *pMyConn;

In the Watch window: _ip(pMyConn)

The whole set of COMWatch functions is implemented in two flavors - _sp and _ip.

Depth of scanning

Some object hierarchies can be very  deep - they can even have infinite depth (e. g. self-references or reference cycles). When examining objects, COMWatch examines, by default, up to 3 levels down the nesting. You can modify that behavior by using _spd() instead of _sp(), and providing the depth of object examination as a second parameter. For the interface pointer users, there's _ipd() in addition to _ip(). Note that object examination might take a while for rich object hierarchies. MSHTML objects are a good example of this - even on decent hardware it may take a few seconds to scan an HTML element object.

Evaluating expressions

Sometimes you're looking at an object just to see what's in it, sometimes you know exactly what you want. COMWatch supports evaluating expressions against the objects being scanned. Instead of _sp(), you can use _spe() ("E" stands for "expression"), and provide an expression in JavaScript to be evaluated with the object in question as the context. For example, with the ADO Connection object from the sample above, you can use the following statement in the Watch window:

_spe(&MyConn, "ConnectionString")

to see the value of the connection string.

The syntax of expressions is pure JavaScript. You use "." for property/method access within nested subobjects, [] to iterate in collections. You may use escaped double quotes to denote strings within the expression, or you may use single quotes. The evaluation context of the expressions is the object being examined - its properties and  methods are treated as globals during evaluation. The length and complexity of expressions is not limited. If the result of the expression is an object, it will be expanded and spelled out in the usual COMWatch way; if it's a variable, it will be displayed as a single line. For expressions that evaluate to long strings (for example, innerHTML of some HTML document), COMWatch supports evaluating to the clipboard. Instead of _spe(), you use the function _spec() ("C" for "copy") - if the result of the expression is a long string (over 25 characters), the function will copy it on the clipboard instead of trying to put it into memory for the debugger to render. For interface pointer users, there are functions _ipe(), _ipec() with the identical functionality.

The issue with smart pointer references

The expression evaluator of VC++ has problems passing the address of a reference variable to a function. For example, if you have something like this:

ADODB::_ConnectionPtr Conn;
ADODB::_ConnectionPtr &ConnRef(Conn);

and try to watch the expression _sp(&ConnRef), the Watch window will report an error. It's a known issue that I have yet to address. As a workaround, you may use _ip() with the raw interface pointer as a parameter, like this:



Some aspects of COMWatch can be controlled by #defining certain macros prior to including COMWatch.h. The following macros are supported:

In the unlikely event of a naming conflict between COMWatch's object dump functions and the names in your project, define this macro to disable function declarations in the global namespace. Instead of the global namespace, _sp() and its brethren will be declared in the COMWatch namespace. During debugging, you'll have to provide the namespace explicitly in the Watch window.

By default, there's a #pragma statement to include COMWatch.lib in the build during Debug builds. Define this macro to disable that behavior. You will then have to specify COMWatch.lib in the project properties, or linker errors will occur.

By default, COMWatch is neither compiled nor linked on Release builds. Define this macro to override that behavior.


The naming convention of COMWatch is rather straightforward. All functions start with either _sp or _ip; that determines the datatype of the first argument - a pointer to a smart pointer object or an interface pointer. Then there are some additional letters that specify additional functionality: "E" means "Expression", "D" means "Depth", "C" means "Copy".

This function zoo had to be created because the Watch window cannot resolve functions with the same name and different argument lists; neither does it support default argument values.


Function Action
_sp(pObj) Spells out the contents of the pObj.
_spe(pObj, szExpr) Evaluates szExpr against pObj, spells out the results.
_spec(pObj, szExpr) Evaluates szExpr against pObj, if the result is a long string, copies it to the clipboard, else spells out the results.
_spd(pObj, nDepth) Spells out the contents of the pObj down to the nDepth'th level of nesting.
_sped(pObj, szExpr, nDepth) Evaluates szExpr against pObj, spells out the results down to the nDepth'th level of nesting.
_ip(pUnk) Spells out the contents of the pUnk.
_ipe(pUnk, szExpr) Evaluates szExpr against pUnk, spells out the results.
_ipec(pUnk, szExpr) Evaluates szExpr against pUnk, if the result is a long string, copies it to the clipboard, else spells out the results.
_ipd(pUnk, nDepth) Spells out the contents of the pUnk down to the nDepth'th level of nesting.
_iped(pUnk, szExpr, nDepth) Evaluates szExpr against pUnk, spells out the results down to the nDepth'th level of nesting.


Argument Type Meaning
pObj void * Address of a COM smart pointer object (_com_ptr_t, or CComPtr, or COleDispatchDriver) to inspect.
pUnk IUnknown * Interface pointer to a COM object to inspect.
nDepth unsigned int Depth of scanning. 1 means immediate children but no further.
szExpr const char * JavaScript expression to evaluate against the object.

Requirements and limitations

COMWatch is officially supported only in Visual Studio .NET 2005, for Native C++ projects.

In VS.NET 2003 and earlier versions, the autoexp.dat functionality is limited; there's no way to specify how a certain class is to be expanded. If there's public demand out there, I'll think of something. Once Visual Studio 9 (codename "Orcas") is out, I will work on making sure COMWatch plays nice with it. Unless, of course, Microsoft decides to implement a similar functionality.

COMWatch only supports Automation objects - that is, objects that implement IDispatch. It doesn't work with custom interfaces.

When browsing an object, certain datatypes (subtypes of VARIANT) are not expanded properly. Specifically, in this version SAFEARRAY expansion is not supported. Again, by popular demand I might add that. You might encounter a message like "unsupported VARTYPE". Don't hesitate to contact me with those.

Dynamic object properties (i. e. the stuff of IDispatchEx) are not supported in this release.

Win64 is not supported. Adding that would require quite a bit of popular demand - for one thing, I don't have a 64-bit Windows machine yet. The library was only tested on Windows NT-line operating systems; there might be some issues on Windows 95/98/Me.

Manual registration

If registration of COMWatch fails, you might have to do it manually. COMWatch must be registered in two places - autoexp.dat and Visual Studio's include/lib directory settings.

The autoexp.dat file is located, by default, under Program Files\Microsoft Visual Studio 8\Common7\Packages\Debugger and it's a plain text file. Use any text editor (like Notepad) to open that file. Then in the COMWatch folder, there's a file called autoexp.txt. Paste the contents of COMWatch's autoexp.txt into Visual Studio's autoexp.dat under the [Visualizer] section. At the bottom of the file, right above the [hresult] section header line and its attendant comments would be a good place.

Then you have to open Tools/Options in the main menu, navigate to Projects and Solutions/VC++ Directories, and provide the path to the COMWatch folder under both Include files and Library files. You may omit this last step, but then you'll have to specify those paths at the project/file level for every project.

Terms of use

COMWatch is freeware. Download it and use it all you want. E-mail me at if you have troubles. The contact box is above.

Release history

Release 0.50 - November 20, 2007. The first one, probably very buggy. Known bug with smart pointer references. SAFEARRAYs are not supported.

Release 0.60 - March 21, 2009. Bug fixes. are not supported.

Release 0.70 - July 24, 2009. Bug fix, added VS2008 support.

site stats