Alexandre Lision | 8af73cb | 2013-12-10 14:11:20 -0500 | [diff] [blame] | 1 | //------------------------------------------------------------------------------
|
| 2 | // File: ComBase.h
|
| 3 | //
|
| 4 | // Desc: DirectShow base classes - defines a class hierarchy for creating
|
| 5 | // COM objects.
|
| 6 | //
|
| 7 | // Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
| 8 | //------------------------------------------------------------------------------
|
| 9 |
|
| 10 |
|
| 11 | /*
|
| 12 |
|
| 13 | a. Derive your COM object from CUnknown
|
| 14 |
|
| 15 | b. Make a static CreateInstance function that takes an LPUNKNOWN, an HRESULT *
|
| 16 | and a TCHAR *. The LPUNKNOWN defines the object to delegate IUnknown calls
|
| 17 | to. The HRESULT * allows error codes to be passed around constructors and
|
| 18 | the TCHAR * is a descriptive name that can be printed on the debugger.
|
| 19 |
|
| 20 | It is important that constructors only change the HRESULT * if they have
|
| 21 | to set an ERROR code, if it was successful then leave it alone or you may
|
| 22 | overwrite an error code from an object previously created.
|
| 23 |
|
| 24 | When you call a constructor the descriptive name should be in static store
|
| 25 | as we do not copy the string. To stop large amounts of memory being used
|
| 26 | in retail builds by all these static strings use the NAME macro,
|
| 27 |
|
| 28 | CMyFilter = new CImplFilter(NAME("My filter"),pUnknown,phr);
|
| 29 | if (FAILED(hr)) {
|
| 30 | return hr;
|
| 31 | }
|
| 32 |
|
| 33 | In retail builds NAME(_x_) compiles to NULL, the base CBaseObject class
|
| 34 | knows not to do anything with objects that don't have a name.
|
| 35 |
|
| 36 | c. Have a constructor for your object that passes the LPUNKNOWN, HRESULT * and
|
| 37 | TCHAR * to the CUnknown constructor. You can set the HRESULT if you have an
|
| 38 | error, or just simply pass it through to the constructor.
|
| 39 |
|
| 40 | The object creation will fail in the class factory if the HRESULT indicates
|
| 41 | an error (ie FAILED(HRESULT) == TRUE)
|
| 42 |
|
| 43 | d. Create a FactoryTemplate with your object's class id and CreateInstance
|
| 44 | function.
|
| 45 |
|
| 46 | Then (for each interface) either
|
| 47 |
|
| 48 | Multiple inheritance
|
| 49 |
|
| 50 | 1. Also derive it from ISomeInterface
|
| 51 | 2. Include DECLARE_IUNKNOWN in your class definition to declare
|
| 52 | implementations of QueryInterface, AddRef and Release that
|
| 53 | call the outer unknown
|
| 54 | 3. Override NonDelegatingQueryInterface to expose ISomeInterface by
|
| 55 | code something like
|
| 56 |
|
| 57 | if (riid == IID_ISomeInterface) {
|
| 58 | return GetInterface((ISomeInterface *) this, ppv);
|
| 59 | } else {
|
| 60 | return CUnknown::NonDelegatingQueryInterface(riid, ppv);
|
| 61 | }
|
| 62 |
|
| 63 | 4. Declare and implement the member functions of ISomeInterface.
|
| 64 |
|
| 65 | or: Nested interfaces
|
| 66 |
|
| 67 | 1. Declare a class derived from CUnknown
|
| 68 | 2. Include DECLARE_IUNKNOWN in your class definition
|
| 69 | 3. Override NonDelegatingQueryInterface to expose ISomeInterface by
|
| 70 | code something like
|
| 71 |
|
| 72 | if (riid == IID_ISomeInterface) {
|
| 73 | return GetInterface((ISomeInterface *) this, ppv);
|
| 74 | } else {
|
| 75 | return CUnknown::NonDelegatingQueryInterface(riid, ppv);
|
| 76 | }
|
| 77 |
|
| 78 | 4. Implement the member functions of ISomeInterface. Use GetOwner() to
|
| 79 | access the COM object class.
|
| 80 |
|
| 81 | And in your COM object class:
|
| 82 |
|
| 83 | 5. Make the nested class a friend of the COM object class, and declare
|
| 84 | an instance of the nested class as a member of the COM object class.
|
| 85 |
|
| 86 | NOTE that because you must always pass the outer unknown and an hResult
|
| 87 | to the CUnknown constructor you cannot use a default constructor, in
|
| 88 | other words you will have to make the member variable a pointer to the
|
| 89 | class and make a NEW call in your constructor to actually create it.
|
| 90 |
|
| 91 | 6. override the NonDelegatingQueryInterface with code like this:
|
| 92 |
|
| 93 | if (riid == IID_ISomeInterface) {
|
| 94 | return m_pImplFilter->
|
| 95 | NonDelegatingQueryInterface(IID_ISomeInterface, ppv);
|
| 96 | } else {
|
| 97 | return CUnknown::NonDelegatingQueryInterface(riid, ppv);
|
| 98 | }
|
| 99 |
|
| 100 | You can have mixed classes which support some interfaces via multiple
|
| 101 | inheritance and some via nested classes
|
| 102 |
|
| 103 | */
|
| 104 |
|
| 105 | #ifndef __COMBASE__
|
| 106 | #define __COMBASE__
|
| 107 |
|
| 108 | // Filter Setup data structures no defined in axextend.idl
|
| 109 |
|
| 110 | typedef REGPINTYPES
|
| 111 | AMOVIESETUP_MEDIATYPE, * PAMOVIESETUP_MEDIATYPE, * FAR LPAMOVIESETUP_MEDIATYPE;
|
| 112 |
|
| 113 | typedef REGFILTERPINS
|
| 114 | AMOVIESETUP_PIN, * PAMOVIESETUP_PIN, * FAR LPAMOVIESETUP_PIN;
|
| 115 |
|
| 116 | typedef struct _AMOVIESETUP_FILTER
|
| 117 | {
|
| 118 | const CLSID * clsID;
|
| 119 | const WCHAR * strName;
|
| 120 | DWORD dwMerit;
|
| 121 | UINT nPins;
|
| 122 | const AMOVIESETUP_PIN * lpPin;
|
| 123 | }
|
| 124 | AMOVIESETUP_FILTER, * PAMOVIESETUP_FILTER, * FAR LPAMOVIESETUP_FILTER;
|
| 125 |
|
| 126 | /* The DLLENTRY module initialises the module handle on loading */
|
| 127 |
|
| 128 | extern HINSTANCE g_hInst;
|
| 129 |
|
| 130 | /* On DLL load remember which platform we are running on */
|
| 131 |
|
| 132 | extern DWORD g_amPlatform;
|
| 133 | extern OSVERSIONINFO g_osInfo; // Filled in by GetVersionEx
|
| 134 |
|
| 135 | /* Version of IUnknown that is renamed to allow a class to support both
|
| 136 | non delegating and delegating IUnknowns in the same COM object */
|
| 137 |
|
| 138 | #ifndef INONDELEGATINGUNKNOWN_DEFINED
|
| 139 | DECLARE_INTERFACE(INonDelegatingUnknown)
|
| 140 | {
|
| 141 | STDMETHOD(NonDelegatingQueryInterface) (THIS_ REFIID, LPVOID *) PURE;
|
| 142 | STDMETHOD_(ULONG, NonDelegatingAddRef)(THIS) PURE;
|
| 143 | STDMETHOD_(ULONG, NonDelegatingRelease)(THIS) PURE;
|
| 144 | };
|
| 145 | #define INONDELEGATINGUNKNOWN_DEFINED
|
| 146 | #endif
|
| 147 |
|
| 148 | typedef INonDelegatingUnknown *PNDUNKNOWN;
|
| 149 |
|
| 150 |
|
| 151 | /* This is the base object class that supports active object counting. As
|
| 152 | part of the debug facilities we trace every time a C++ object is created
|
| 153 | or destroyed. The name of the object has to be passed up through the class
|
| 154 | derivation list during construction as you cannot call virtual functions
|
| 155 | in the constructor. The downside of all this is that every single object
|
| 156 | constructor has to take an object name parameter that describes it */
|
| 157 |
|
| 158 | class CBaseObject
|
| 159 | {
|
| 160 |
|
| 161 | private:
|
| 162 |
|
| 163 | // Disable the copy constructor and assignment by default so you will get
|
| 164 | // compiler errors instead of unexpected behaviour if you pass objects
|
| 165 | // by value or assign objects.
|
| 166 | CBaseObject(const CBaseObject& objectSrc); // no implementation
|
| 167 | void operator=(const CBaseObject& objectSrc); // no implementation
|
| 168 |
|
| 169 | private:
|
| 170 | static LONG m_cObjects; /* Total number of objects active */
|
| 171 |
|
| 172 | protected:
|
| 173 | #ifdef DEBUG
|
| 174 | DWORD m_dwCookie; /* Cookie identifying this object */
|
| 175 | #endif
|
| 176 |
|
| 177 |
|
| 178 | public:
|
| 179 |
|
| 180 | /* These increment and decrement the number of active objects */
|
| 181 |
|
| 182 | CBaseObject(__in_opt LPCTSTR pName);
|
| 183 | #ifdef UNICODE
|
| 184 | CBaseObject(__in_opt LPCSTR pName);
|
| 185 | #endif
|
| 186 | ~CBaseObject();
|
| 187 |
|
| 188 | /* Call this to find if there are any CUnknown derived objects active */
|
| 189 |
|
| 190 | static LONG ObjectsActive() {
|
| 191 | return m_cObjects;
|
| 192 | };
|
| 193 | };
|
| 194 |
|
| 195 |
|
| 196 | /* An object that supports one or more COM interfaces will be based on
|
| 197 | this class. It supports counting of total objects for DLLCanUnloadNow
|
| 198 | support, and an implementation of the core non delegating IUnknown */
|
| 199 |
|
| 200 | class AM_NOVTABLE CUnknown : public INonDelegatingUnknown,
|
| 201 | public CBaseObject
|
| 202 | {
|
| 203 | private:
|
| 204 | const LPUNKNOWN m_pUnknown; /* Owner of this object */
|
| 205 |
|
| 206 | protected: /* So we can override NonDelegatingRelease() */
|
| 207 | volatile LONG m_cRef; /* Number of reference counts */
|
| 208 |
|
| 209 | public:
|
| 210 |
|
| 211 | CUnknown(__in_opt LPCTSTR pName, __in_opt LPUNKNOWN pUnk);
|
| 212 | virtual ~CUnknown() {};
|
| 213 |
|
| 214 | // This is redundant, just use the other constructor
|
| 215 | // as we never touch the HRESULT in this anyway
|
| 216 | CUnknown(__in_opt LPCTSTR Name, __in_opt LPUNKNOWN pUnk, __inout_opt HRESULT *phr);
|
| 217 | #ifdef UNICODE
|
| 218 | CUnknown(__in_opt LPCSTR pName, __in_opt LPUNKNOWN pUnk);
|
| 219 | CUnknown(__in_opt LPCSTR pName, __in_opt LPUNKNOWN pUnk,__inout_opt HRESULT *phr);
|
| 220 | #endif
|
| 221 |
|
| 222 | /* Return the owner of this object */
|
| 223 |
|
| 224 | LPUNKNOWN GetOwner() const {
|
| 225 | return m_pUnknown;
|
| 226 | };
|
| 227 |
|
| 228 | /* Called from the class factory to create a new instance, it is
|
| 229 | pure virtual so it must be overriden in your derived class */
|
| 230 |
|
| 231 | /* static CUnknown *CreateInstance(LPUNKNOWN, HRESULT *) */
|
| 232 |
|
| 233 | /* Non delegating unknown implementation */
|
| 234 |
|
| 235 | STDMETHODIMP NonDelegatingQueryInterface(REFIID, __deref_out void **);
|
| 236 | STDMETHODIMP_(ULONG) NonDelegatingAddRef();
|
| 237 | STDMETHODIMP_(ULONG) NonDelegatingRelease();
|
| 238 | };
|
| 239 |
|
| 240 | /* Return an interface pointer to a requesting client
|
| 241 | performing a thread safe AddRef as necessary */
|
| 242 |
|
| 243 | STDAPI GetInterface(LPUNKNOWN pUnk, __out void **ppv);
|
| 244 |
|
| 245 | /* A function that can create a new COM object */
|
| 246 |
|
| 247 | typedef CUnknown *(CALLBACK *LPFNNewCOMObject)(__in_opt LPUNKNOWN pUnkOuter, __inout_opt HRESULT *phr);
|
| 248 |
|
| 249 | /* A function (can be NULL) which is called from the DLL entrypoint
|
| 250 | routine for each factory template:
|
| 251 |
|
| 252 | bLoading - TRUE on DLL load, FALSE on DLL unload
|
| 253 | rclsid - the m_ClsID of the entry
|
| 254 | */
|
| 255 | typedef void (CALLBACK *LPFNInitRoutine)(BOOL bLoading, const CLSID *rclsid);
|
| 256 |
|
| 257 | /* Create one of these per object class in an array so that
|
| 258 | the default class factory code can create new instances */
|
| 259 |
|
| 260 | class CFactoryTemplate {
|
| 261 |
|
| 262 | public:
|
| 263 |
|
| 264 | const WCHAR * m_Name;
|
| 265 | const CLSID * m_ClsID;
|
| 266 | LPFNNewCOMObject m_lpfnNew;
|
| 267 | LPFNInitRoutine m_lpfnInit;
|
| 268 | const AMOVIESETUP_FILTER * m_pAMovieSetup_Filter;
|
| 269 |
|
| 270 | BOOL IsClassID(REFCLSID rclsid) const {
|
| 271 | return (IsEqualCLSID(*m_ClsID,rclsid));
|
| 272 | };
|
| 273 |
|
| 274 | CUnknown *CreateInstance(__inout_opt LPUNKNOWN pUnk, __inout_opt HRESULT *phr) const {
|
| 275 | CheckPointer(phr,NULL);
|
| 276 | return m_lpfnNew(pUnk, phr);
|
| 277 | };
|
| 278 | };
|
| 279 |
|
| 280 |
|
| 281 | /* You must override the (pure virtual) NonDelegatingQueryInterface to return
|
| 282 | interface pointers (using GetInterface) to the interfaces your derived
|
| 283 | class supports (the default implementation only supports IUnknown) */
|
| 284 |
|
| 285 | #define DECLARE_IUNKNOWN \
|
| 286 | STDMETHODIMP QueryInterface(REFIID riid, __deref_out void **ppv) { \
|
| 287 | return GetOwner()->QueryInterface(riid,ppv); \
|
| 288 | }; \
|
| 289 | STDMETHODIMP_(ULONG) AddRef() { \
|
| 290 | return GetOwner()->AddRef(); \
|
| 291 | }; \
|
| 292 | STDMETHODIMP_(ULONG) Release() { \
|
| 293 | return GetOwner()->Release(); \
|
| 294 | };
|
| 295 |
|
| 296 |
|
| 297 |
|
| 298 | HINSTANCE LoadOLEAut32();
|
| 299 |
|
| 300 |
|
| 301 | #endif /* __COMBASE__ */
|
| 302 |
|
| 303 |
|
| 304 |
|
| 305 |
|