//==========================================================================; // // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR // PURPOSE. // // Copyright (c) 1992 - 1998 Microsoft Corporation. All Rights Reserved. // //--------------------------------------------------------------------------; // Base class hierachy for creating COM objects, December 1994 /* a. Derive your COM object from CComBase b. Make a static CreateInstance function that takes an IUnknown* and an HRESULT*. The IUnknown* defines the object to delegate IUnknown calls to. The HRESULT * allows error codes to be passed around constructors. It is important that constructors only change the HRESULT * if they have to set an ERROR code, if it was successful then leave it alone or you may overwrite an error code from an object previously created. c. Have a constructor for your object that passes the IUnknown* and HRESULT* to the CComBase constructor. You can set the HRESULT if you have an error, or just simply pass it through to the constructor. The object creation will fail in the class factory if the HRESULT indicates an error (ie FAILED(HRESULT) == TRUE) d. Create a CComClassTemplate with your object's class id and CreateInstance function. Then (for each interface) either Multiple inheritance 1. Also derive it from ISomeInterface 2. Include DECLARE_IUNKNOWN in your class definition to declare implementations of QueryInterface, AddRef and Release that call the outer unknown 3. Override NDQueryInterface to expose ISomeInterface by code something like if (riid == IID_ISomeInterface) { return GetInterface((ISomeInterface *) this, ppv); } else { return CComBase::NDQueryInterface(riid, ppv); } 4. Declare and implement the member functions of ISomeInterface. or: Nested interfaces 1. Declare a class derived from CComBase 2. Include DECLARE_IUNKNOWN in your class definition 3. Override NDQueryInterface to expose ISomeInterface by code something like if (riid == IID_ISomeInterface) { return GetInterface((ISomeInterface *) this, ppv); } else { return CComBase::NDQueryInterface(riid, ppv); } 4. Implement the member functions of ISomeInterface. Use \() to access the COM object class. And in your COM object class: 5. Make the nested class a friend of the COM object class, and declare an instance of the nested class as a member of the COM object class. NOTE that because you must always pass the outer unknown and an hResult to the CComBase constructor you cannot use a default constructor, in other words you will have to make the member variable a pointer to the class and make a NEW call in your constructor to actually create it. 6. override the NDQueryInterface with code like this: if (riid == IID_ISomeInterface) { return m_pImplFilter-> NDQueryInterface(IID_ISomeInterface, ppv); } else { return CComBase::NDQueryInterface(riid, ppv); } You can have mixed classes which support some interfaces via multiple inheritance and some via nested classes */ #ifndef __COMBASE__ #define __COMBASE__ #include #include #include extern int g_cActiveObjects; STDAPI CreateCLSIDRegKey(REFCLSID clsid, const char *szName); STDAPI RemoveCLSIDRegKey(REFCLSID clsid); #ifdef DEBUG // We chose a common name for our ASSERT macro, MFC also uses this name // So long as the implementation evaluates the condition and handles it // then we will be ok. Rather than override the behaviour expected we // will leave whatever first defines ASSERT as the handler (i.e. MFC) #ifndef ASSERT #define ASSERT(_x_) if (!(_x_)) \ DebugAssert(TEXT(#_x_),TEXT(__FILE__),__LINE__) #endif #define EXECUTE_ASSERT(_x_) ASSERT(_x_) #define ValidateReadPtr(p,cb) \ {if(IsBadReadPtr((PVOID)p,cb) == TRUE) \ DebugBreak("Invalid read pointer");} #define ValidateWritePtr(p,cb) \ {if(IsBadWritePtr((PVOID)p,cb) == TRUE) \ DebugBreak("Invalid write pointer");} #define ValidateReadWritePtr(p,cb) \ {ValidateReadPtr(p,cb) ValidateWritePtr(p,cb)} #else #ifndef ASSERT #define ASSERT(_x_) ((void)0) #endif #define EXECUTE_ASSERT(_x_) ((void)(_x_)) #define ValidateReadPtr(p,cb) 0 #define ValidateWritePtr(p,cb) 0 #define ValidateReadWritePtr(p,cb) 0 #endif /* The DLLENTRY module initialises the module handle on loading */ extern HINSTANCE g_hInst; /* On DLL load remember which platform we are running on */ /* Version of IUnknown that is renamed to allow a class to support both non delegating and delegating IUnknowns in the same COM object */ #ifndef INDUNKNOWN_DEFINED DECLARE_INTERFACE(INDUnknown) { STDMETHOD(NDQueryInterface) (THIS_ REFIID, LPVOID *) PURE; STDMETHOD_(ULONG, NDAddRef)(THIS) PURE; STDMETHOD_(ULONG, NDRelease)(THIS) PURE; }; #define INDUNKNOWN_DEFINED #endif class CBaseObject { public: CBaseObject() {g_cActiveObjects++;} ~CBaseObject() {g_cActiveObjects--;} }; /* An object that supports one or more COM interfaces will be based on this class. It supports counting of total objects for DLLCanUnloadNow support, and an implementation of the core non delegating IUnknown */ class CComBase : public INDUnknown, CBaseObject { private: IUnknown* m_pUnknown; /* Owner of this object */ protected: /* So we can override NDRelease() */ volatile LONG m_cRef; /* Number of reference counts */ public: CComBase(IUnknown* pUnk); virtual ~CComBase() {}; // This is redundant, just use the other constructor // as we never touch the HRESULT in this anyway CComBase(IUnknown* pUnk,HRESULT *phr); /* Return the owner of this object */ IUnknown* GetOwner() const { return m_pUnknown; }; /* Called from the class factory to create a new instance, it is pure virtual so it must be overriden in your derived class */ /* static CComBase *CreateInstance(IUnknown*, HRESULT *) */ /* Non delegating unknown implementation */ STDMETHODIMP NDQueryInterface(REFIID, void **); STDMETHODIMP_(ULONG) NDAddRef(); STDMETHODIMP_(ULONG) NDRelease(); }; /* Return an interface pointer to a requesting client performing a thread safe AddRef as necessary */ STDAPI GetInterface(IUnknown* pUnk, void **ppv); /* A function that can create a new COM object */ typedef CComBase *(CALLBACK *LPFNNewCOMObject)(IUnknown* pUnkOuter, HRESULT *phr); /* A function (can be NULL) which is called from the DLL entrypoint routine for each factory template: bLoading - TRUE on DLL load, FALSE on DLL unload rclsid - the m_ClsID of the entry */ typedef void (CALLBACK *LPFNInitRoutine)(BOOL bLoading, const CLSID *rclsid); #define CheckPointer(p,ret) {if((p)==NULL) return (ret);} /* Create one of these per object class in an array so that the default class factory code can create new instances */ struct CComClassTemplate { const CLSID * m_ClsID; LPFNNewCOMObject m_lpfnNew; }; /* You must override the (pure virtual) NDQueryInterface to return interface pointers (using GetInterface) to the interfaces your derived class supports (the default implementation only supports IUnknown) */ #define DECLARE_IUNKNOWN \ STDMETHODIMP QueryInterface(REFIID riid, void **ppv) { \ return GetOwner()->QueryInterface(riid,ppv); \ }; \ STDMETHODIMP_(ULONG) AddRef() { \ return GetOwner()->AddRef(); \ }; \ STDMETHODIMP_(ULONG) Release() { \ return GetOwner()->Release(); \ }; HINSTANCE LoadOLEAut32(); #endif /* __COMBASE__ */