In this installment of the Ripsaw article series we’ll start putting some actual code into an implementation of the COM interfaces that we specified in the last article. For those of you who just joined the series, Ripsaw is a log viewer for Windows, similar to the Unix
tail utility. This series of articles details my rewrite of the application from the ground up.
dispinterface declaring the events that a Ripsaw library object can fire. In this article we’ll start implementing these interfaces, with the goal of providing just enough functionality to allow us to execute the test script.
I’m implementing this project with Visual Studio 2010, which is still in beta as of this writing. You may download the solution, projects, and source code if you’d like to follow along, but if you haven’t downloaded and installed the 2010 beta yet, you’ll need to adapt the code to your IDE and C++ compiler of choice if you wish to build it (I recommend Visual Studio 2008).
Implementing the COM Interfaces
To briefly recap the design, there will be a Ripsaw COM library that exports objects representing sources of updates, and a Ripsaw client (such as a log viewer or a script) will create objects that fire events whenever the source changes its status. Now that we’ve specified the interfaces, it’s time to implement them just enough to run the test script that we created in the last installment. I like to use ATL to implement the low-level, boilerplate code that’s necessary for
IDispatch, though once the wizard is done I usually take over and move all the generated code around. I’ve never liked round-trip development with code generators.
There are three interfaces (for now) that need to be implemented:
_IRipsawEvents. The first interface is the base interface for not only
IRipsawFile but other potential interfaces that might be added later on as the library is developed. That means I want to create the implementation in such a way that I can easily extend it to other sources of data that Ripsaw might use. Unfortunately, if I took the code that the ATL wizard spat out, I’d have a really difficult time reusing it. I need to split the ATL code into a generic interface implementation and a concrete object implementation.
In ATL, when implementing an object derived from
IDispatch, you usually get a class definition that looks like this:
class ATL_NO_VTABLE RipsawFile :
public CComCoClass<RipsawFile, &CLSID_RipsawFile>,
public IDispatchImpl<IRipsawFile, &IID_IRipsawFile, &LIBID_RipLib, /*wMajor =*/ 1, /*wMinor =*/ 0>
That works fine, until it doesn’t. If you ever want to derive a class from
RipsawFile, you’re going to run into problems. What I usually do is replace
IDispatchImpl with my own template class that derives from
IDispatchImpl and implements the interface that my object is going to expose. In this case, I want my object to expose both
IRipsawObject, so I’ll start by creating a generic template implementation of the base
IRipsawObject interface. The template will be defined in a header file named IRipsawObjectImpl.h.
template <class Root, class T, const IID* piid = &__uuidof(T),
const GUID* plibid = &ATL::CAtlModule::m_libid,
WORD wMajor = 1, WORD wMinor = 0,
class tihclass = ATL::CComTypeInfoHolder>
class IRipsawObjectImpl :
public ATL::IDispatchImpl<T, piid, plibid, wMajor, wMinor, tihclass>
/* Internal public implementation */
IRipsawObjectImpl() : isOpen(FALSE)
/* IRipsawObject interface */
HRESULT STDMETHODCALLTYPE get_Name(
__out BSTR* pName)
*pName = NULL;
HRESULT STDMETHODCALLTYPE get_IsOpen(
__out VARIANT_BOOL* pRetVal)
LONG retVal = 0;
*pRetVal = retVal ? VARIANT_TRUE : VARIANT_FALSE;
HRESULT STDMETHODCALLTYPE Close()
VARIANT_BOOL tmpIsOpen = VARIANT_FALSE;
BSTR name = 0;
HRESULT STDMETHODCALLTYPE WaitForUpdate(
__in ULONG timeout,
__out BSTR* pNewData)
*pNewData = NULL;
/* Internal protected implementation */
HRESULT STDMETHODCALLTYPE put_IsOpen(
__in VARIANT_BOOL value)
virtual HRESULT Notify_Open(BSTR fileName)
virtual HRESULT Notify_Close(BSTR fileName)
virtual HRESULT Notify_Update(BSTR updateData)
There is quite a bit going on in this template, so let’s go over its various parts.
If you look back at the example class definition for
RipsawFile, you’ll see that it derived from
CComObjectRootEx<CComMultiThreadModel>. This is because, when the wizard generated the code, the user selected the option to generate an object that was free-threaded. This is an implementation detail of the object, not of the interface, so that’s the first thing we need to abstract away from our generic implementation of the
IRipsawObject interface. The problem, however, is that
CComObjectRootEx provides a lot of useful types that we should take advantage of, such as methods for guarding blocks of code that need to be serialized when accessed from multiple threads. What I decided to do was make
Root a template parameter and derive the implementation from that type. The concrete implementation may provide any type for this parameter as long as it implements the definitions expected from that type, such as
This is a class that is provided by ATL to implement the ugly details of
IDispatch. While the average COM developer can (usually) implement a workable
IUnknown rather easily, implementing
IDispatch is a good deal more complicated. We know, based on our IDL, that
IRipsawObject is derived from
IDispatch, so this is an implementation detail that’s appropriate for our generic implementation. Classes that derive from
IRipsawObject will also pick up the
IDispatch implementation. All of the template parameters to
Root are passed along to
All objects that implement the
IRipsawObject interface are expected to fire events to notify clients about open, close, and update events. How these events are generated is an implementation detail best left to the concrete object implementation, but I may need to fire events from the generic implementation. I decided to create stub methods that will be overridden in the concrete implementation.
The Rest of the Code
The remainder of the header is devoted to minimal implementations of the methods and properties defined in the
IRipsawObject interface. Most of these will be overridden in
IRipsawFile and other derived interfaces. Note that the implementations use
ObjectLock to serialize access to internal data. This is because we want to allow the generic implementation to work for free-threaded object implementations, which may be called by more than one thread at a time. The
ObjectLock type is expected to be provided by the
Root template parameter.
I Could Use Some More Cowbell
This still isn’t enough code to create a usable
IRipsawFile implementation, but since that’s the default interface that we need to expose from our COM object we’ll have to derive another generic implementation from
IRipsawObject that will let us finally execute our test script. We’ll tackle that template class in the next installment in the series. If you want to cheat and peek ahead, look at the file IRipsawFileImpl.h in the source code.