mirror of https://github.com/PCSX2/pcsx2.git
283 lines
9.3 KiB
C++
283 lines
9.3 KiB
C++
//==========================================================================;
|
|
//
|
|
// 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 - 1996 Microsoft Corporation. All Rights Reserved.
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
|
|
// Base class hierachy for creating COM objects, December 1994
|
|
|
|
/*
|
|
|
|
a. Derive your COM object from CUnknown
|
|
|
|
b. Make a static CreateInstance function that takes an LPUNKNOWN, an HRESULT *
|
|
and a TCHAR *. The LPUNKNOWN defines the object to delegate IUnknown calls
|
|
to. The HRESULT * allows error codes to be passed around constructors and
|
|
the TCHAR * is a descriptive name that can be printed on the debugger.
|
|
|
|
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.
|
|
|
|
When you call a constructor the descriptive name should be in static store
|
|
as we do not copy the string. To stop large amounts of memory being used
|
|
in retail builds by all these static strings use the NAME macro,
|
|
|
|
CMyFilter = new CImplFilter(NAME("My filter"),pUnknown,phr);
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
|
|
In retail builds NAME(_x_) compiles to NULL, the base CBaseObject class
|
|
knows not to do anything with objects that don't have a name.
|
|
|
|
c. Have a constructor for your object that passes the LPUNKNOWN, HRESULT * and
|
|
TCHAR * to the CUnknown 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 FactoryTemplate 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 NonDelegatingQueryInterface to expose ISomeInterface by
|
|
code something like
|
|
|
|
if (riid == IID_ISomeInterface) {
|
|
return GetInterface((ISomeInterface *) this, ppv);
|
|
} else {
|
|
return CUnknown::NonDelegatingQueryInterface(riid, ppv);
|
|
}
|
|
|
|
4. Declare and implement the member functions of ISomeInterface.
|
|
|
|
or: Nested interfaces
|
|
|
|
1. Declare a class derived from CUnknown
|
|
2. Include DECLARE_IUNKNOWN in your class definition
|
|
3. Override NonDelegatingQueryInterface to expose ISomeInterface by
|
|
code something like
|
|
|
|
if (riid == IID_ISomeInterface) {
|
|
return GetInterface((ISomeInterface *) this, ppv);
|
|
} else {
|
|
return CUnknown::NonDelegatingQueryInterface(riid, ppv);
|
|
}
|
|
|
|
4. Implement the member functions of ISomeInterface. Use GetOwner() 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 CUnknown 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 NonDelegatingQueryInterface with code like this:
|
|
|
|
if (riid == IID_ISomeInterface) {
|
|
return m_pImplFilter->
|
|
NonDelegatingQueryInterface(IID_ISomeInterface, ppv);
|
|
} else {
|
|
return CUnknown::NonDelegatingQueryInterface(riid, ppv);
|
|
}
|
|
|
|
You can have mixed classes which support some interfaces via multiple
|
|
inheritance and some via nested classes
|
|
|
|
*/
|
|
|
|
#ifndef __COMBASE__
|
|
#define __COMBASE__
|
|
|
|
/* The DLLENTRY module initialises the module handle on loading */
|
|
|
|
extern HINSTANCE g_hInst;
|
|
|
|
/* On DLL load remember which platform we are running on */
|
|
|
|
extern DWORD g_amPlatform;
|
|
extern OSVERSIONINFO g_osInfo; // Filled in by GetVersionEx
|
|
|
|
/* Version of IUnknown that is renamed to allow a class to support both
|
|
non delegating and delegating IUnknowns in the same COM object */
|
|
|
|
DECLARE_INTERFACE(INonDelegatingUnknown)
|
|
{
|
|
STDMETHOD(NonDelegatingQueryInterface) (THIS_ REFIID, LPVOID *) PURE;
|
|
STDMETHOD_(ULONG, NonDelegatingAddRef)(THIS) PURE;
|
|
STDMETHOD_(ULONG, NonDelegatingRelease)(THIS) PURE;
|
|
};
|
|
|
|
typedef INonDelegatingUnknown *PNDUNKNOWN;
|
|
|
|
|
|
/* This is the base object class that supports active object counting. As
|
|
part of the debug facilities we trace every time a C++ object is created
|
|
or destroyed. The name of the object has to be passed up through the class
|
|
derivation list during construction as you cannot call virtual functions
|
|
in the constructor. The downside of all this is that every single object
|
|
constructor has to take an object name parameter that describes it */
|
|
|
|
class CBaseObject
|
|
{
|
|
|
|
private:
|
|
|
|
// Disable the copy constructor and assignment by default so you will get
|
|
// compiler errors instead of unexpected behaviour if you pass objects
|
|
// by value or assign objects.
|
|
CBaseObject(const CBaseObject& objectSrc); // no implementation
|
|
void operator=(const CBaseObject& objectSrc); // no implementation
|
|
|
|
private:
|
|
static LONG m_cObjects; /* Total number of objects active */
|
|
|
|
protected:
|
|
#ifdef DEBUG
|
|
DWORD m_dwCookie; /* Cookie identifying this object */
|
|
#endif
|
|
|
|
|
|
public:
|
|
|
|
/* These increment and decrement the number of active objects */
|
|
|
|
CBaseObject(TCHAR *pName);
|
|
~CBaseObject();
|
|
|
|
/* Call this to find if there are any CUnknown derived objects active */
|
|
|
|
static LONG ObjectsActive() {
|
|
return m_cObjects;
|
|
};
|
|
};
|
|
|
|
|
|
/* 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 CUnknown : public INonDelegatingUnknown,
|
|
public CBaseObject
|
|
{
|
|
|
|
private:
|
|
const LPUNKNOWN m_pUnknown; /* Owner of this object */
|
|
|
|
protected: /* So we can override NonDelegatingRelease() */
|
|
volatile LONG m_cRef; /* Number of reference counts */
|
|
|
|
public:
|
|
|
|
CUnknown(TCHAR *pName, LPUNKNOWN pUnk, HRESULT *phr);
|
|
virtual ~CUnknown() {};
|
|
|
|
/* Return the owner of this object */
|
|
|
|
LPUNKNOWN 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 CUnknown *CreateInstance(LPUNKNOWN, HRESULT *) */
|
|
|
|
/* Non delegating unknown implementation */
|
|
|
|
STDMETHODIMP NonDelegatingQueryInterface(REFIID, void **);
|
|
STDMETHODIMP_(ULONG) NonDelegatingAddRef();
|
|
STDMETHODIMP_(ULONG) NonDelegatingRelease();
|
|
|
|
/* Return an interface pointer to a requesting client
|
|
performing a thread safe AddRef as necessary */
|
|
|
|
HRESULT GetInterface(LPUNKNOWN pUnk, void **ppv);
|
|
|
|
|
|
};
|
|
|
|
#if defined(_MSC_VER) && (_MSC_VER<1300)
|
|
/* The standard InterlockedXXX functions won't take volatiles */
|
|
static inline LONG InterlockedIncrement( volatile LONG * plong )
|
|
{ return InterlockedIncrement( const_cast<LONG*>( plong ) ); }
|
|
|
|
static inline LONG InterlockedDecrement( volatile LONG * plong )
|
|
{ return InterlockedDecrement( const_cast<LONG*>( plong ) ); }
|
|
|
|
static inline LONG InterlockedExchange( volatile LONG * plong, LONG new_value )
|
|
{ return InterlockedExchange( const_cast<LONG*>( plong ), new_value ); }
|
|
#endif
|
|
|
|
/* A function that can create a new COM object */
|
|
|
|
typedef CUnknown *(*LPFNNewCOMObject)(LPUNKNOWN 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 (*LPFNInitRoutine)(BOOL bLoading, const CLSID *rclsid);
|
|
|
|
/* Create one of these per object class in an array so that
|
|
the default class factory code can create new instances */
|
|
|
|
class CFactoryTemplate {
|
|
|
|
public:
|
|
|
|
const WCHAR *m_Name;
|
|
const CLSID *m_ClsID;
|
|
LPFNNewCOMObject m_lpfnNew;
|
|
LPFNInitRoutine m_lpfnInit;
|
|
|
|
BOOL IsClassID(REFCLSID rclsid) const {
|
|
return (IsEqualCLSID(*m_ClsID,rclsid));
|
|
};
|
|
|
|
CUnknown *CreateInstance(LPUNKNOWN pUnk, HRESULT *phr) const {
|
|
return m_lpfnNew(pUnk, phr);
|
|
};
|
|
};
|
|
|
|
|
|
/* You must override the (pure virtual) NonDelegatingQueryInterface 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(); \
|
|
};
|
|
|
|
#endif /* __COMBASE__ */
|
|
|
|
|