You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
572 lines
21 KiB
572 lines
21 KiB
//+---------------------------------------------------------------------------
|
|
//
|
|
// 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
|