from
http://progtutorials.tripod.com/COM.htm
- Multiple COM Instances and Singleton Class Factory
- Avoiding Interface Method Name Clash
- Solution 1: substituting inheritance with aggregation
- Solution 2: implement the methods separately before they clash
1 Multiple COM Instances and Singleton Class Factory
For all COM servers created by VC¡¯s AppWizard, both DLL and EXE servers, there is one class factory for each of the coclasses, which is used to create an instance of the coclass.
For DLL server, because the server code resides in the same process of the client, if there are two client processes invoking the same server, there will be two copies of server code in the two processes. In comparison, because EXE server lives in a separate and independent process, and all its class factories once created are registered in the running object table, if there are multiple client processes invoking the same server, only one server process will be started, and this server will serve all client processes.
Normally, when a client calls CoCreateInstances for the same coclass for multiple times, multiple instances of that coclass will be created by the server and live in that server. This is true for both DLL and EXE server.
If you want to make sure that only one instance is created from a coclass, you can put macro
DECLARE_CLASSFACTORY_SINGLETON(ClassName)
in the class definition. Then when CoCreateInstance is called multiple times in the same process (for DLL server) or even in multiple processes (for EXE server), only the first call creates an instance of that coclass, and the rest gets a reference to the same instance.
This macro can only be used safely in a single-thread application. For a multiple-thread application, it will cause problems.
2 Avoiding Interface Method Name Clash
Suppose coclass CAny implements two interfaces IInterf1 and IInterf2, each with a method Clashing with the same signature, then the coclass can only provide one implementation of the clashing method to be shared by the two interfaces. Some times it is not desireable.
2.1 Solution 1: substituting inheritance with aggregation
Create a classes implementing one of the two interfaces with clashing methods:
struct Interf2Impl : public IInterf2
{
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
STDMETHODIMP QueryInterface(REFIID riid, void ** ppv);
STDMETHODIMP Clashing();
CAny * m_pParent;
}; In Interf2Impl, the three IUnknown methods will simply turn and call CAny¡¯s implementations through the CAny * m_pParent. It does not need its own implementation of the three IUnknown methods because this class is used as a member object and does not need its own life-time control. All this class is doing is to provide its own implementation for the clashing method.
Coclass CAny no longer implements the interface which has been implemented by class Interf2Impl. Instead it holds a member object of Interf2Impl:
class CAny : public IInterf1
{
public:
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
STDMETHODIMP QueryInterface(REFIID riid, void ** ppv);
STDMETHODIMP Cashing();
private:
ULONG m_refCount;
Interf2Impl m_impl;
}; The implementation of AddRef and Release in CAny is as normal. QueryInterface will simply cast the member object to its implemented interface and return it to the client:
STDMETHODIMP CAny::QueryInterface(REFIID riid, void ** ppv)
{
...
else if(riid == IID_IInterf2)
{
*ppv = (IInterf1 *)&m_impl;
}
...
} In CAny¡¯s constructor, it will set the member object¡¯s parent pointer to itself:
m_impl.m_pParent = this;
Interf2Impl can be a stand-alone class. To make the design more cohesive, we can also make it an inner class of the coclass CAny.
2.2 Solution 2: implement the methods separately before they clash
Create two wrapper classes to inherit from the two clashing interfaces. Each wrapper class only implements the name-clashing method, leaving all other methods to be implemented by the coclass as usual. Therefore these wrapper classes are still abstract classes:
#include "stdafx.h"
#include "unknwn.h"
#include "winerror.h"
#include <windows.h>
#include <initguid.h> // contains definition of DEFINE_GUID
// {211FE1E1-FA21-11d5-9867-9880AB8D8130}
DEFINE_GUID(IID_IInterf1, 0x211fe1e1, 0xfa21, 0x11d5, 0x98, 0x67, 0x98, 0x80, 0xab, 0x8d, 0x81, 0x30);
// {211FE1E2-FA21-11d5-9867-9880AB8D8130}
DEFINE_GUID(IID_IInterf2, 0x211fe1e2, 0xfa21, 0x11d5, 0x98, 0x67, 0x98, 0x80, 0xab, 0x8d, 0x81, 0x30);
interface IInterf1 : public IUnknown
{
STDMETHOD(Hi)() PURE;
};
interface IInterf2 : public IUnknown
{
STDMETHOD(Hi)() PURE;
};
struct Interf1Impl : public IInterf1
{
STDMETHODIMP Hi()
{
printf("Hi from Interf1Impl!\n");
return S_OK;
}
};
struct Interf2Impl : public IInterf2
{
STDMETHODIMP Hi()
{
printf("Hi from Interf2Impl!\n");
return S_OK;
}
}; Coclass CAny will inherit from the wrapper classes instead of the raw interfaces, and the rest of CAny remains just as normal:
class CAny : public Interf1Impl, public Interf2Impl
{
public:
CAny() : m_refCount(0) {}
STDMETHODIMP_(ULONG) AddRef()
{
return ++m_refCount;
}
STDMETHODIMP_(ULONG) Release()
{
if(--m_refCount == 0)
{
delete this;
return 0;
}
else
return m_refCount;
}
STDMETHODIMP QueryInterface(REFIID riid, void ** ppv)
{
if(riid == IID_IUnknown)
{
// cast to IUnknown * through IInterf1 * to avoid ambiguity
*ppv = (IUnknown *)(IInterf1 *)this;
}
else if(riid == IID_IInterf1)
{
*ppv = (IInterf1 *)this;
}
else if(riid == IID_IInterf2)
{
*ppv = (IInterf2 *)this;
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
return S_OK;
}
private:
ULONG m_refCount;
}; In this approach, the wrapper class does not implement any other method of the inherited interface except for the clashing one, and is therefore much simpler. The coclass itself also experiences very limitted change. So this approach is much better than the previous one. It can also be directly applied in ATL. Using the same Interf1Impl and Interf2Impl, CAny represented in ATL looks like:
class ATL_NO_VTABLE CAny :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CAny, &CLSID_Any>,
public Interf1Impl, // Instead of IInterf1
public Interf2Impl // Instead of IInterf2
{
public:
CAny() {}
DECLARE_REGISTRY_RESOURCEID(IDR_ANY)
DECLARE_NOT_AGGREGATABLE(CAny)
DECLARE_PROTECT_FINAL_CONSTRUCT()
BEGIN_COM_MAP(CAny)
COM_INTERFACE_ENTRY(IInterf1)
COM_INTERFACE_ENTRY(IInterf2)
END_COM_MAP()
};
SeriousMoin v1 (koMoinMoin 1.0a4 Modified)