|
|
//+---------------------------------------------------------------------------
//
// File: combase.h
//
// Contents: IUnknown and COM server declarations.
//
// Using this file:
//
// 1. For each C++ class you want to expose as a COM object, derive from
//
// CComObjectRootImmx - standard COM object
// CComObjectRootImmx_NoDllAddRef - internal COM object
// CComObjectRootImmx_InternalReference - internal/external object
// CComObjectRoot_CreateInstance - standard COM object, exposed directly
// from class factory.
// CComObjectRoot_CreateInstanceSingleton - standard COM object, exposed
// directly from class factory, one instance per thread.
// CComObjectRoot_CreateInstanceSingleton_Verify - standard COM object,
// exposed directly from class factory, one instance per thread.
// Includes callback to fail create instance for custom reasons,
// and a callback after the singleton is successfully created.
//
// 2. For each C++ class, declare the interfaces exposed by QueryInterface
// using the BEGIN_COM_MAP_IMMX macro. IUknown will be automatically mapped
// to the first interface listed. The IUnknown methods are implemented by
// the BEGIN_COM_MAP_IMMX macro.
//
// 3. Use the BEGIN_COCLASSFACTORY_TABLE macro to declare the COM objects you
// want to expose directly from the class factory.
//
// 4. Implement DllInit(), DllUninit(), DllRegisterServerCallback(),
// DllUnregisterServerCallback(), and GetServerHINSTANCE.
// Behavior commented next to the prototypes below.
//
// Example:
//
// // this class is exposed by the class factory
// class MyCoCreateableObject : public ITfLangBarItemMgr,
// public CComObjectRoot_CreateInstance<MyCoCreateableObject>
// {
// MyCoCreateableObject();
//
// BEGIN_COM_MAP_IMMX(MyCoCreateableObject)
// COM_INTERFACE_ENTRY(ITfLangBarItemMgr)
// END_COM_MAP_IMMX()
// };
//
// // this class is only exposed indirectly, through another interface
// class MyObject : public ITfLangBarItemMgr,
// public CComObjectRootImmx
// {
// MyCoCreateableObject();
//
// BEGIN_COM_MAP_IMMX(MyObject)
// COM_INTERFACE_ENTRY(ITfLangBarItemMgr)
// END_COM_MAP_IMMX()
// };
//
// // in .cpp file, declare the objects exposed through the class factory
// // along with thier clsid and description. Currently the apartment
// // threading model is assumed.
// BEGIN_COCLASSFACTORY_TABLE
// DECLARE_COCLASSFACTORY_ENTRY(CLSID_TF_LangBarItemMgr, MyCoCreateableObject::CreateInstance, TEXT("TF_LangBarItemMgr"))
// END_COCLASSFACTORY_TABLE
//
// // finally implement DllInit(), DllUninit(), DllRegisterServerCallback(),
// // DllUnregisterServerCallback(), and GetServerHINSTANCE.
//----------------------------------------------------------------------------
#ifndef UNKNOWN_H
#define UNKNOWN_H
#include "private.h"
#ifdef __cplusplus
//+---------------------------------------------------------------------------
//
// extern prototypes that the server dll must implement
//
//----------------------------------------------------------------------------
// Called on every new external object reference.
BOOL DllInit(void); // Called when an external reference goes away.
void DllUninit(void);
// Should return the server dll's HINSTANCE.
HINSTANCE GetServerHINSTANCE(void); // Should return a mutex callable whenever the server ref count changes (i.e. not during DllMain).
CRITICAL_SECTION *GetServerCritSec(void);
//+---------------------------------------------------------------------------
//
// COM server export implementations.
//
//----------------------------------------------------------------------------
HRESULT COMBase_DllGetClassObject(REFCLSID rclsid, REFIID riid, void **ppvObj); HRESULT COMBase_DllCanUnloadNow(void); HRESULT COMBase_DllRegisterServer(void); HRESULT COMBase_DllUnregisterServer(void);
//+---------------------------------------------------------------------------
//
// CComObjectRootImmx_NoDllAddRef
//
// Use this base class if you don't want your COM object to AddRef the server.
// Typically you DON'T want to do this.
//----------------------------------------------------------------------------
class CComObjectRootImmx_NoDllAddRef { public: CComObjectRootImmx_NoDllAddRef() { // Automatic AddRef().
m_dwRef = 1L;
#ifdef DEBUG
m_fTraceAddRef = FALSE; #endif
}
protected:
virtual BOOL InternalReferenced() { return FALSE; }
void DebugRefBreak() { #ifdef DEBUG
if (m_fTraceAddRef) { DebugBreak(); } #endif
}
long m_dwRef; #ifdef DEBUG
BOOL m_fTraceAddRef; #endif
};
//+---------------------------------------------------------------------------
//
// CComObjectRootImmx
//
// Use this base class for standard COM objects.
//----------------------------------------------------------------------------
class CComObjectRootImmx : public CComObjectRootImmx_NoDllAddRef { public: CComObjectRootImmx() { void DllAddRef(void); DllAddRef(); }
~CComObjectRootImmx() { void DllRelease(void); DllRelease(); } };
//+---------------------------------------------------------------------------
//
// CComObjectRootImmx_InternalReference
//
// Use this base class for COM objects that have an "internal reference".
//
// With this base class, the server will only be AddRef'd if the object
// ref count reaches 2. The server will be Release'd when the object
// ref count returns to 1. This allows the object to be held internally
// (and even released in DllMain) while still allowing outside references.
//----------------------------------------------------------------------------
class CComObjectRootImmx_InternalReference : public CComObjectRootImmx_NoDllAddRef { public: CComObjectRootImmx_InternalReference() {}
BOOL InternalReferenced() { return TRUE; } };
typedef BOOL (*VERIFYFUNC)(IUnknown *pUnkOuter, REFIID riid, void **ppvObj); typedef void (*POSTCREATE)(REFIID riid, void *pvObj);
// helper function, don't call this directly
template<class DerivedClass> static HRESULT Unk_CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObj, VERIFYFUNC pfnVerify, POSTCREATE pfnPostCreate) { DerivedClass *pObject; HRESULT hr;
if (ppvObj == NULL) return E_INVALIDARG;
*ppvObj = NULL;
if (pUnkOuter != NULL) return CLASS_E_NOAGGREGATION;
if (pfnVerify != NULL && !pfnVerify(pUnkOuter, riid, ppvObj)) return E_FAIL;
pObject = new DerivedClass;
if (pObject == NULL) return E_OUTOFMEMORY;
hr = pObject->QueryInterface(riid, ppvObj);
pObject->Release();
if (hr == S_OK && pfnPostCreate != NULL) { pfnPostCreate(riid, ppvObj); }
return hr; }
//+---------------------------------------------------------------------------
//
// CComObjectRoot_CreateInstance
//
// Use this base class for standard COM objects that are exposed directly
// from the class factory.
//----------------------------------------------------------------------------
template<class DerivedClass> class CComObjectRoot_CreateInstance : public CComObjectRootImmx { public: CComObjectRoot_CreateInstance() {}
static HRESULT CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObj) { return Unk_CreateInstance<DerivedClass>(pUnkOuter, riid, ppvObj, NULL, NULL); } };
//+---------------------------------------------------------------------------
//
// CComObjectRoot_CreateInstance_Verify
//
// Use this base class for standard COM objects that are exposed directly
// from the class factory.
//----------------------------------------------------------------------------
template<class DerivedClass> class CComObjectRoot_CreateInstance_Verify : public CComObjectRootImmx { public: CComObjectRoot_CreateInstance_Verify() {}
static HRESULT CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObj) { return Unk_CreateInstance<DerivedClass>(pUnkOuter, riid, ppvObj, DerivedClass::VerifyCreateInstance, DerivedClass::PostCreateInstance); } };
// helper function, don't call this directly
template<class DerivedClass> static HRESULT Unk_CreateInstanceSingleton(IUnknown *pUnkOuter, REFIID riid, void **ppvObj, VERIFYFUNC pfnVerify, POSTCREATE pfnPostCreate) { DerivedClass *pObject; HRESULT hr;
if (ppvObj == NULL) return E_INVALIDARG;
*ppvObj = NULL;
if (pUnkOuter != NULL) return CLASS_E_NOAGGREGATION;
pObject = DerivedClass::_GetThis();
if (pObject == NULL) { if (pfnVerify != NULL && !pfnVerify(pUnkOuter, riid, ppvObj)) { hr = E_FAIL; goto Exit; }
pObject = new DerivedClass;
if (pObject == NULL) { hr = E_OUTOFMEMORY; goto Exit; }
hr = pObject->QueryInterface(riid, ppvObj);
pObject->Release();
if (hr == S_OK) { Assert(DerivedClass::_GetThis() != NULL); // _GetThis() should be set in object ctor on success
if (pfnPostCreate != NULL) { pfnPostCreate(riid, *ppvObj); } } } else { hr = pObject->QueryInterface(riid, ppvObj); }
Exit: return hr; }
//+---------------------------------------------------------------------------
//
// CComObjectRoot_CreateSingletonInstance
//
// Use this base class for standard COM objects that are exposed directly
// from the class factory, and are singletons (one instance per thread).
//
// Classes deriving from this base must implement a _GetThis() method which
// returns the singleton object, if it already exists, or null.
//----------------------------------------------------------------------------
template<class DerivedClass> class CComObjectRoot_CreateSingletonInstance : public CComObjectRootImmx { public: CComObjectRoot_CreateSingletonInstance() {}
static HRESULT CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObj) { return Unk_CreateInstanceSingleton<DerivedClass>(pUnkOuter, riid, ppvObj, NULL, NULL); } };
//+---------------------------------------------------------------------------
//
// CComObjectRoot_CreateSingletonInstance_Verify
//
// Use this base class for standard COM objects that are exposed directly
// from the class factory, and are singletons (one instance per thread).
//
// Classes deriving from this base must implement a VerifyCreateInstance
// method that is called before allocating a new singleton instance, if
// necessary. The Verify method can return FALSE to fail the class factory
// CreateInstance call for any reason.
//
// The derived class must also implement a PostCreateInstance method that will
// be called after allocating a new singleton, if the createinstance is about
// to return S_OK (QueryInterface succecceded). This method is intended for
// lazy load work.
//
// typedef BOOL (*VERIFYFUNC)(IUnknown *pUnkOuter, REFIID riid, void **ppvObj);
// typedef void (*POSTCREATE)(REFIID riid, void *pvObj);
//
// Classes deriving from this base must implement a _GetThis() method which
// returns the singleton object, if it already exists, or null.
//----------------------------------------------------------------------------
template<class DerivedClass> class CComObjectRoot_CreateSingletonInstance_Verify : public CComObjectRootImmx { public: CComObjectRoot_CreateSingletonInstance_Verify() {}
static HRESULT CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObj) { return Unk_CreateInstanceSingleton<DerivedClass>(pUnkOuter, riid, ppvObj, DerivedClass::VerifyCreateInstance, DerivedClass::PostCreateInstance); } };
//+---------------------------------------------------------------------------
//
// BEGIN_COM_MAP_IMMX
//
//----------------------------------------------------------------------------
#define BEGIN_COM_MAP_IMMX(class_type) \
STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject) \ { \ BOOL fUseFirstIID = FALSE; \ \ if (ppvObject == NULL) \ return E_INVALIDARG; \ \ *ppvObject = NULL; \ \ if (IsEqualIID(IID_IUnknown, riid)) \ { \ /* use first IID for IUnknown */ \ fUseFirstIID = TRUE; \ }
//+---------------------------------------------------------------------------
//
// COM_INTERFACE_ENTRY
//
//----------------------------------------------------------------------------
#define COM_INTERFACE_ENTRY(interface_type) \
if (fUseFirstIID || IsEqualIID(IID_##interface_type, riid)) \ { \ *ppvObject = (interface_type *)this; \ } \ else
//+---------------------------------------------------------------------------
//
// COM_INTERFACE_ENTRY_IID
//
//----------------------------------------------------------------------------
#define COM_INTERFACE_ENTRY_IID(interface_iid, interface_type) \
if (fUseFirstIID || IsEqualIID(interface_iid, riid)) \ { \ *ppvObject = (interface_type *)this; \ } \ else
//+---------------------------------------------------------------------------
//
// COM_INTERFACE_ENTRY_FUNC
//
//----------------------------------------------------------------------------
#define COM_INTERFACE_ENTRY_FUNC(interface_iid, param, pfn) \
/* compiler will complain about unused vars, so reuse fUseFirstIID if we need it here */ \ if (IsEqualIID(interface_iid, riid) && \ (fUseFirstIID = pfn(this, interface_iid, ppvObject, param)) != S_FALSE) \ { \ return (HRESULT)fUseFirstIID; /* pfn set ppvObject */ \ } \ else
//+---------------------------------------------------------------------------
//
// END_COM_MAP_IMMX
//
//----------------------------------------------------------------------------
#define END_COM_MAP_IMMX() \
{} \ if (*ppvObject) \ { \ AddRef(); \ return S_OK; \ } \ \ return E_NOINTERFACE; \ } \ \ STDMETHODIMP_(ULONG) AddRef() \ { \ void DllAddRef(void); \ DebugRefBreak(); \ \ if (m_dwRef == 1 && InternalReferenced()) \ { \ /* first external reference to this object */ \ DllAddRef(); \ } \ \ return ++m_dwRef; \ } \ \ STDMETHODIMP_(ULONG) Release() \ { \ void DllRelease(void); \ long cr; \ \ DebugRefBreak(); \ \ cr = --m_dwRef; \ Assert(cr >= 0); \ \ if (cr == 1 && InternalReferenced()) \ { \ /* last external reference just went away */ \ DllRelease(); \ } \ else if (cr == 0) \ { \ delete this; \ } \ \ return cr; \ }
// here for backwards compat with old atl based code, unused
#define IMMX_OBJECT_IUNKNOWN_FOR_ATL()
// class factory entry
typedef HRESULT (STDAPICALLTYPE * PFNCREATEINSTANCE)(IUnknown *pUnkOuter, REFIID riid, void **ppvObj);
typedef struct { const CLSID *pclsid; PFNCREATEINSTANCE pfnCreateInstance; const TCHAR *pszDesc; } OBJECT_ENTRY;
// instantiated by the BEGIN_COCLASSFACTORY_TABLE macro
extern const OBJECT_ENTRY c_rgCoClassFactoryTable[];
//+---------------------------------------------------------------------------
//
// DllRefCount
//
// Returns the number of outstanding object references held by clients of the
// server. This is useful for asserting all objects have been released at
// process detach, and for delay loading/unitializing resources.
//----------------------------------------------------------------------------
inline LONG DllRefCount() { extern LONG g_cRefDll;
// our ref starts at -1
return g_cRefDll+1; }
class CClassFactory;
//+---------------------------------------------------------------------------
//
// BEGIN_COCLASSFACTORY_TABLE
//
//----------------------------------------------------------------------------
#define BEGIN_COCLASSFACTORY_TABLE \
const OBJECT_ENTRY c_rgCoClassFactoryTable[] = {
//+---------------------------------------------------------------------------
//
// DECLARE_COCLASSFACTORY_ENTRY
//
// clsid - clsid of the object
// cclass - C++ class of the object. This macro expects to find
// ClassName::CreateInstance, which the CComObject bases provide by default.
// desc - description, default REG_SZ value under the CLSID registry entry.
//----------------------------------------------------------------------------
#define DECLARE_COCLASSFACTORY_ENTRY(clsid, cclass, desc) \
{ &clsid, cclass##::CreateInstance, desc },
//+---------------------------------------------------------------------------
//
// END_COCLASSFACTORY_TABLE
//
//----------------------------------------------------------------------------
#define END_COCLASSFACTORY_TABLE \
{ NULL, NULL, NULL } }; \ CClassFactory *g_ObjectInfo[ARRAYSIZE(c_rgCoClassFactoryTable)] = { NULL };
#endif // __cplusplus
#endif // UNKNOWN_H
|