Managing lifetine of native C++ objects using ATL (repost)
I removed original post since code contained a bug. Thanks to über brain of Über Brain (pun intended :-)) it was found.
It is well known that C++ developer is responsible for keeping track of when objects are created and destroyed since C++ does not provide native object lifetime management facilities. ATL developers are familiar with CComPtr<T> class that helps to track lifetime of COM objects. It is extremely useful since one does not have to care about proper pairing of AddRef anf Release calls anymore. Would it be also nice for ATL to have something similar to track instances to other (plain) objects, i.e. to have something like RefPtr. Of course it is possible to directly use RefPtr or any other third party solution, but ATL is already here and is probably already included in the project. So let's see what is we can do with ATL.
It is, of course, possible to turn any object into a full blown COM object. However, you probably don't want to define an interface for every class, create an IDL file, compile it, include type library, edit registry scritps and deal with future install/uninstall issues. Especially when it is completely unnecessary. With a few simple modification it is possible to turn almost any class into a COM-like object that ATL will be completely happy with. And you will still be able to invoke non-virtual methods, pass around non-IDL compliant data types such as references and even directly access data members of the class. Here is the code:
CComModule _Module;
class __declspec( uuid("e8f40118-3a42-4fd7-89ed-6df86581ac88") )
CObject:
public CComObjectRootEx<CComSingleThreadModel>,
public IUnknown
{
public:
DECLARE_NOT_AGGREGATABLE(CObject)
BEGIN_COM_MAP(CObject)
COM_INTERFACE_ENTRY(CObject)
COM_INTERFACE_ENTRY(IUnknown)
END_COM_MAP()
CObject()
{ m_iMember = 1; }
int GetMember() const
{ return m_iMember; }
int m_iMember;
};
template<class T>
HRESULT AtlCreateInstance(REFIID refIID, void** ppInterface)
{
CComObject<T>* pNewInstance;
HRESULT hr;
if(!ppInterface)
return E_INVALIDARG;
*ppInterface = NULL;
hr = CComObject<T>::CreateInstance(&pNewInstance);
if(SUCCEEDED(hr))
{
pNewInstance->AddRef();
hr = pNewInstance->QueryInterface(refIID, ppInterface);
pNewInstance->Release();
}
return hr;
}
int _tmain(int argc, _TCHAR* argv[])
{
CComPtr<CObject> srpObject;
int x = 0;
HRESULT hr;
hr = ::AtlCreateInstance<CObject>(__uuidof(CObject), (void**)&srpObject);
if(SUCCEEDED(hr))
{
x = srpObject->GetMember();
x = srpObject->m_iMember;
x = sizeof(CObject);
}
return x;
}
As you can see, a few changes were made to a plain C++ class:
1. __declspec( uuid()) was added which assigned a GUID to the class. This allowed class to participate in the QueryInterface. I.e. one can call QueryInterface(__uuidof(CObject), ...).
2. COM_MAP was added with IUnknown and the object itself.
3. DECLARE_NOT_AGGREGATABLE was added which reduced COM/ATL overhead to 8 bytes (refcount and vtable pointer). sizeof(CObject) is 12.
That's it, now you can use the class with CComPtr which will track instance lifetime for you. If you can't or don't want to define CComModule, you can change code in AtlCreateInstance from
hr = CComObject<T>::CreateInstance(&pNewInstance);
if(SUCCEEDED(hr))
to
pNewInstance = new CComObjectNoLock<T>;
if(pNewInstance)
CComObjectNoLock does not provide CreateInstance so you'll have to use new directly. Note that if your object has FinalConstruct, it will not get called when you are creating object with the new.
Comments
- Anonymous
February 28, 2006
You mention using CComObjectNoLock when when you can't or don't want to have an ATLModule. However, I think there's a better way to use CComObjectNoLock and still ensure that FinalConstruct etc... still get called properly - CComCreator
CComPtr<IDesiredInterface> pDesiredInterface;
CComCreator< CComObjectNoLock< T > >::CreateInstance( NULL, IID_IDesiredInterface, (LPVOID*)&pDesiredInterface );
Thoughts?