Figure 4 IMPLEMENT_IUnknownCT
////////////////////////////////////////////////////////////////
// Macro expansion for IMPLEMENT_IUnknownCT(CMyComClass)
STDMETHODIMP_(ULONG) CMyComClass::AddRef()
{
CMDTARGENTRYTR(_T("CMyComClass(%p)::AddRef "),this);
DWORD dwRef = ExternalAddRef();
CTTRACE(_T("> returns count=%d\n"),dwRef);
return dwRef;
}
STDMETHODIMP_(ULONG) CMyComClass::Release()
{
CMDTARGENTRYTR(_T("CMyComClass(%p)::Release "),this);
DWORD dwRef = ExternalRelease();
CTTRACE(_T("> returns count=%d\n"),dwRef);
return dwRef;
}
STDMETHODIMP CMyComClass::QueryInterface(REFIID iid, LPVOID* ppvRet)
{
CTCHECKARG(ppvRet);
CMDTARGENTRYTR(_T("CMyComClass(%p)::QueryInterface(%s) "), this, _TR(iid));
HRESULT hr = ExternalQueryInterface(&iid, ppvRet);
CTTRACE(_T("> returns %s, *ppv=%p, count=%d\n"),
_TR(hr), *ppvRet, CCmdTarget::m_dwRef);
return hr;
}
// Expansion of CMDTARGENTRYTR.
// NOTE: when you create a CCmdTarget object, MFC initializes
// m_pModuleState = AfxGetModuleState();
//
AFX_MANAGE_STATE(m_pModuleState);
TRACE(...);
Figure 5 CBandObj
//////////////////
// CBandObj -- A typical COM class that implements several interfaces.
// in .h file:
class CBandObj : public CWnd,
// interfaces
// public IOleWindow, // inherited from IDeskBand
// public IDockingWindow, // inherited from IDeskBand
public IDeskBand,
public IObjectWithSite,
public IInputObject,
// public IPersist, // inherited from IPersistStream
public IPersistStream,
public IContextMenu,
// implementations using ComToys
public CTOleWindow,
public CTDockingWindow,
public CTPersist,
public CTPersistStream,
public CTInputObject,
public CTInputObjectSite,
public CTContextMenu
{
public:
CBandObj(REFCLSID clsid);
virtual ~CBandObj();
// override to implement QueryInterface
virtual LPUNKNOWN GetInterfaceHook(const void* iid);
// interfaces implemented using ComToys
DECLARE_IUnknown();
DECLARE_IOleWindow();
DECLARE_IDockingWindow();
DECLARE_IInputObject();
DECLARE_IPersist();
DECLARE_IPersistStream();
DECLARE_IContextMenu();
DECLARE_IObjectWithSite();
// IDeskBand
STDMETHOD (GetBandInfo) (DWORD, DWORD, DESKBANDINFO*);
};
// in .cpp file:
CBandObj::CBandObj(REFCLSID clsid) :
// initialize all the implementation classes
CTMfcComObj(this),
CTOleWindow(this),
CTDockingWindow(this),
CTPersist(clsid),
CTPersistStream(),
CTInputObject(this),
CTContextMenu(this, CTMfcComObjFactory::GetFactory(clsid)->GetResourceID())
{
.
.
.
}
/////////////////
// These macros implement all the interfaces using the ComToys
//
IMPLEMENT_IUnknown (CBandObj, CTMfcComObj);
IMPLEMENT_IOleWindow (CBandObj, CTOleWindow);
IMPLEMENT_IDockingWindow (CBandObj, CTDockingWindow);
IMPLEMENT_IPersist (CBandObj, CTPersist);
IMPLEMENT_IContextMenu (CBandObj, CTContextMenu);
IMPLEMENT_IPersistStream (CBandObj, CTPersistStream);
IMPLEMENT_IInputObject (CBandObj, CTInputObject);
//////////////////
// This CCmdTarget override is used to bypass MFC's interface
// maps in CCmdTarget::InternalQueryInterface.
//
LPUNKNOWN CBandObj::GetInterfaceHook(const void* piid)
{
REFIID iid = *((IID*)piid);
if (iid==IID_IUnknown)
return (IDeskBand*)this;
#define IF_INTERFACE(iid, iface) \
if (iid==IID_##iface) \
return (iface*)this; \
IF_INTERFACE(iid, IOleWindow);
IF_INTERFACE(iid, IDockingWindow);
IF_INTERFACE(iid, IObjectWithSite);
IF_INTERFACE(iid, IInputObject);
IF_INTERFACE(iid, IPersist);
IF_INTERFACE(iid, IPersistStream);
IF_INTERFACE(iid, IContextMenu);
IF_INTERFACE(iid, IDeskBand);
return NULL;
}
Figure 8 Registrar Variables
Registrar variables automatically defined by ComToys
(CTComObjFactory::OnInitRegistryVariables)
%CLSID% = class ID (GUID) (COleObjectFactory::m_clsid)
%MODULE% = full pathname of DLL
%Title% = title (resource substring 0)
%ClassName% = human-readable COM class name (resource substring 1)
%ProgID% = ProgID (resource substring 2)
%ThreadingModel% = "", "Apartment", "Free", or "Both" (m_nThreadingModel)
Figure 10 The Simplicity of Smart Pointers
//////////////////
// Typical COM function implemented WITHOUT smart pointers. This is even
// worse when there are many exit paths.
//
STDMETHODIMP CExplorerBar::SetSite(IUnknown* punkSite)
{
// If a site is being held, release it.
if(m_pSite) {
m_pSite->Release();
m_pSite = NULL;
}
// If punkSite is not NULL, a new site is being set.
if (punkSite) {
•
•
•
// Get and keep the IInputObjectSite pointer.
if (SUCCEEDED(punkSite->QueryInterface(IID_IInputObjectSite,
(LPVOID*)&m_pSite))) {
return S_OK;
}
return E_FAIL;
}
return S_OK;
}
//////////////////
// Same COM function implemented WITH smart pointers. 12 lines reduced to 2.
// Because m_pSite is declared as CComQIPtr<IInputObjectSite>, there's no
// need to call QueryInterface(IID_IInputObjectSite), nor any need to AddRef
// the new pointer or Release the old one. The smart pointer takes care of
// everything.
//
STDMETHODIMP CBandObj::SetSite(IUnknown* punkSite)
{
if (m_pSite = punkSite) {
.
.
.
}
return punkSite && !m_pSite ? E_FAIL : S_OK;
}