1240 lines
37 KiB
C
1240 lines
37 KiB
C
/*****************************************************************************
|
|
*
|
|
* Common.c
|
|
*
|
|
* Copyright (c) 1996 Microsoft Corporation. All Rights Reserved.
|
|
*
|
|
* Abstract:
|
|
*
|
|
* Shared stuff that operates on all classes
|
|
*
|
|
* This version of the common services supports multiple
|
|
* inheritance natively. You can pass any interface of an object,
|
|
* and the common services will do the right thing.
|
|
*
|
|
* Contents:
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#include "dinputpr.h"
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* The sqiffle for this file.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#define sqfl sqflCommon
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* USAGE FOR OLE OBJECTS
|
|
*
|
|
* Suppose you want to implement an object called CObj that supports
|
|
* the interfaces Foo, Bar, and Baz. Suppose that you opt for
|
|
* Foo as the primary interface.
|
|
*
|
|
* >> NAMING CONVENTION <<
|
|
*
|
|
* COM objects begin with the letter "C".
|
|
*
|
|
* (1) Declare the primary and secondary vtbls.
|
|
*
|
|
* Primary_Interface(CObj, IFoo);
|
|
* Secondary_Interface(CObj, IBar);
|
|
* Secondary_Interface(CObj, IBaz);
|
|
*
|
|
* (3) Declare the object itself.
|
|
*
|
|
* typedef struct CObj {
|
|
* IFoo foo; // Primary must come first
|
|
* IBar bar;
|
|
* IBaz baz;
|
|
* ... other fields ...
|
|
* } CObj;
|
|
*
|
|
* (4) Implement the methods.
|
|
*
|
|
* You may *not* reimplement the AddRef and Release methods!
|
|
* although you can subclass them.
|
|
*
|
|
* (5) To allocate an object of the appropriate type, write
|
|
*
|
|
* hres = Common_NewRiid(CObj, punkOuter, riid, ppvOut);
|
|
*
|
|
* or, if the object is variable-sized,
|
|
*
|
|
* hres = Common_NewCbRiid(cb, CObj, punkouter, riid, ppvOut);
|
|
*
|
|
* Common_NewRiid and Common_NewCbRiid will initialize both the
|
|
* primary and secondary vtbls.
|
|
*
|
|
* (6) Define the object signature.
|
|
*
|
|
* #pragma BEGIN_CONST_DATA
|
|
*
|
|
* #define CObj_Signature 0x204A424F // "OBJ "
|
|
*
|
|
* (7) Define the object template.
|
|
*
|
|
* Interface_Template_Begin(CObj)
|
|
* Primary_Interface_Template(CObj, IFoo)
|
|
* Secondary_Interface_Template(CObj, IBar)
|
|
* Secondary_Interface_Template(CObj, IBaz)
|
|
* Interface_Template_End(CObj)
|
|
*
|
|
* (8) Define the interface descriptors.
|
|
*
|
|
* // The macros will declare QueryInterface, AddRef and Release
|
|
* // so don't list them again
|
|
*
|
|
* Primary_Interface_Begin(CObj, IFoo)
|
|
* CObj_FooMethod1,
|
|
* CObj_FooMethod2,
|
|
* CObj_FooMethod3,
|
|
* CObj_FooMethod4,
|
|
* Primary_Interface_End(Obj, IFoo)
|
|
*
|
|
* Secondary_Interface_Begin(CObj, IBar, bar)
|
|
* CObj_Bar_BarMethod1,
|
|
* CObj_Bar_BarMethod2,
|
|
* Secondary_Interface_Begin(CObj, IBar, bar)
|
|
*
|
|
* Secondary_Interface_Begin(CObj, IBaz, baz)
|
|
* CObj_Baz_BazMethod1,
|
|
* CObj_Baz_BazMethod2,
|
|
* CObj_Baz_BazMethod3,
|
|
* Secondary_Interface_Begin(CObj, IBaz, baz)
|
|
*
|
|
*****************************************************************************/
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* USAGE FOR NON-OLE OBJECTS
|
|
*
|
|
* All objects are COM objects, even if they are never given out.
|
|
* In the simplest case, it just derives from IUnknown.
|
|
*
|
|
* Suppose you want to implement an object called Obj which is
|
|
* used only internally.
|
|
*
|
|
* (1) Declare the vtbl.
|
|
*
|
|
* Simple_Interface(Obj);
|
|
*
|
|
* (3) Declare the object itself.
|
|
*
|
|
* typedef struct Obj {
|
|
* IUnknown unk;
|
|
* ... other fields ...
|
|
* } Obj;
|
|
*
|
|
* (4) Implement the methods.
|
|
*
|
|
* You may *not* override the QueryInterface, AddRef or
|
|
* Release methods!
|
|
*
|
|
* (5) Allocating an object of the appropriate type is the same
|
|
* as with OLE objects.
|
|
*
|
|
* (6) Define the "vtbl".
|
|
*
|
|
* #pragma BEGIN_CONST_DATA
|
|
*
|
|
* Simple_Interface_Begin(Obj)
|
|
* Simple_Interface_End(Obj)
|
|
*
|
|
* That's right, nothing goes between the Begin and the End.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* CommonInfo
|
|
*
|
|
* Information tracked for all common objects.
|
|
*
|
|
* A common object looks like this:
|
|
*
|
|
* rgvtbl
|
|
* cbvtbl
|
|
* D(dwSig) QIHelper
|
|
* cHoldRef AppFinalizeProc
|
|
* cRef FinalizeProc
|
|
* punkOuter riid
|
|
* unkPrivate 0
|
|
* pFoo -> lpVtbl -> QueryInterface
|
|
* lpVtbl2 Common_AddRef
|
|
* data Common_Release
|
|
* ... ...
|
|
*
|
|
* Essentially, we use the otherwise-unused space above the
|
|
* pointers to record our bookkeeping information.
|
|
*
|
|
* punkOuter = controlling unknown, if object is aggregated
|
|
* lpvtblPunk = special vtbl for controlling unknown to use
|
|
* cHoldRef = Total reference count, including holds
|
|
* cRef = object reference count from application
|
|
* riid = object iid
|
|
* rgvtbl = array of vtbls of supported interfaces
|
|
* cbvtbl = size of array in bytes
|
|
* QIHelper = QueryInterface helper for aggregation
|
|
* AppFinalizeProc = Finalization procedure when app does last Release
|
|
* FinalizeProc = Finalization procedure
|
|
*
|
|
* For secondary interfaces, it looks like this:
|
|
*
|
|
* riid
|
|
* offset to primary interface
|
|
* pFoo -> lpVtbl -> Forward_QueryInterface
|
|
* Forward_AddRef
|
|
* Forward_Release
|
|
* ...
|
|
*
|
|
*
|
|
* What is a hold?
|
|
*
|
|
* There is this annoying situation (particularly with
|
|
* IDirectInputDevice) where an object wants to prevent itself
|
|
* from being destroyed but we don't want to do an AddRef.
|
|
*
|
|
* The classic case (and for now the only one) is an
|
|
* IDirectInputDevice which has been acquired. If we did
|
|
* an honest AddRef() on the Acquire(), and the application does
|
|
* a Release() without Unacquire()ing, then the device would
|
|
* be acquired forever.
|
|
*
|
|
* If you thought that the Unacquire() in the finalization
|
|
* would help, you'd be wrong, because the finalization happens
|
|
* only when the last reference goes away, but the last reference
|
|
* belongs to the device itself and will never go away until
|
|
* the Unacquire() happens, which can't happen because the app
|
|
* already lost its last reference to the device.
|
|
*
|
|
* So instead, we need to maintain *two* refcounts.
|
|
*
|
|
* cRef is the application-visible reference count, accessible
|
|
* via PrivateAddRef() and PrivateRelease(). When this
|
|
* drops to zero, we call the AppFinalize().
|
|
*
|
|
* cHoldRef is the "real" reference count. This is the sum of
|
|
* cRef and the number of outstanding Common_Hold()s. When
|
|
* this drops to zero, then the object is Finalize()d.
|
|
*
|
|
*
|
|
*****************************************************************************/
|
|
|
|
/* WARNING! cin_dwSig must be first: ci_Start relies on it */
|
|
/* WARNING! cin_unkPrivate must be last: punkPrivateThis relies on it */
|
|
|
|
typedef struct CommonInfoN { /* This goes in front of the object */
|
|
RD(ULONG cin_dwSig;) /* Signature (for parameter validation) */
|
|
LONG cin_cHoldRef; /* Total refcount, incl. holds */
|
|
LONG cin_cRef; /* Object reference count */
|
|
PUNK cin_punkOuter; /* Controlling unknown */
|
|
IUnknown cin_unkPrivate; /* Private IUnknown */
|
|
} CommonInfoN, CIN, *PCIN;
|
|
|
|
typedef struct CommonInfoP { /* This is how we pun the object itself */
|
|
PREVTBLP *cip_prevtbl; /* Vtbl of object (will be -1'd) */
|
|
} CommonInfoP, CIP, *PCIP;
|
|
|
|
typedef union CommonInfo {
|
|
CIN cin[1];
|
|
CIP cip[1];
|
|
} CommonInfo, CI, *PCI;
|
|
|
|
#define ci_dwSig cin[-1].cin_dwSig
|
|
#define ci_cHoldRef cin[-1].cin_cHoldRef
|
|
#define ci_cRef cin[-1].cin_cRef
|
|
#define ci_punkOuter cin[-1].cin_punkOuter
|
|
#define ci_unkPrivate cin[-1].cin_unkPrivate
|
|
#define ci_rgfp cip[0].cip_prevtbl
|
|
|
|
#define ci_tszClass cip[0].cip_prevtbl[-1].tszClass
|
|
#define ci_rgvtbl cip[0].cip_prevtbl[-1].rgvtbl
|
|
#define ci_cbvtbl cip[0].cip_prevtbl[-1].cbvtbl
|
|
#define ci_QIHelper cip[0].cip_prevtbl[-1].QIHelper
|
|
#define ci_AppFinalize cip[0].cip_prevtbl[-1].AppFinalizeProc
|
|
#define ci_Finalize cip[0].cip_prevtbl[-1].FinalizeProc
|
|
#define ci_riid cip[0].cip_prevtbl[-1].prevtbl.riid
|
|
#define ci_lib cip[0].cip_prevtbl[-1].prevtbl.lib
|
|
|
|
#ifdef XDEBUG
|
|
#define ci_Start ci_dwSig
|
|
#else
|
|
#define ci_Start ci_cRef
|
|
#endif
|
|
|
|
#define ci_dwSignature 0x38162378 /* typed by my cat */
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Common_Finalize (from Common_Release)
|
|
*
|
|
* By default, no finalization is necessary.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void EXTERNAL
|
|
Common_Finalize(PV pv)
|
|
{
|
|
// 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
|
|
SquirtSqflPtszV(sqfl, TEXT("Common_Finalize(%p)"), pv);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* "Private" IUnknown methods
|
|
*
|
|
* When a COM object is aggregated, it exports *two* IUnknown
|
|
* interfaces.
|
|
*
|
|
* The "private" IUnknown is the one that is returned to the
|
|
* controlling unknown. It is this unknown that the controlling
|
|
* unknown uses to manipulate the refcount on the inner object.
|
|
*
|
|
* The "public" IUnknown is the one that all external callers see.
|
|
* For this, we just hand out the controlling unknown.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
Secondary_Interface(CCommon, IUnknown);
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func PV | thisPunk |
|
|
*
|
|
* Convert a private punk (&cin_unkPrivate) into the beginning of
|
|
* the actual object.
|
|
*
|
|
* @parm PUNK | punkPrivate |
|
|
*
|
|
* The private punk (&cin_unkPrivate) corresponding to some
|
|
* object we are managing.
|
|
*
|
|
* @returns
|
|
*
|
|
* The object pointer on success, or 0 on error.
|
|
*
|
|
* @comm
|
|
*
|
|
* We do not return an <t HRESULT> on error, because the
|
|
* callers of the procedure typically do not return
|
|
* <t HRESULT>s themselves.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#ifndef XDEBUG
|
|
|
|
#define thisPunk_(punk, z) \
|
|
_thisPunk_(punk) \
|
|
|
|
#endif
|
|
|
|
PV INLINE
|
|
thisPunk_(PUNK punkPrivate, LPCSTR s_szProc)
|
|
{
|
|
PV pv;
|
|
if (SUCCEEDED(hresFullValidPitf(punkPrivate, 0))) {
|
|
if (punkPrivate->lpVtbl == Class_Vtbl(CCommon, IUnknown)) {
|
|
pv = pvAddPvCb(punkPrivate,
|
|
cbX(CIN) - FIELD_OFFSET(CIN, cin_unkPrivate));
|
|
} else {
|
|
RPF("%s: Invalid parameter 0", s_szProc);
|
|
pv = 0;
|
|
}
|
|
} else {
|
|
RPF("%s: Invalid parameter 0", s_szProc);
|
|
pv = 0;
|
|
}
|
|
return pv;
|
|
}
|
|
|
|
#define thisPunk(punk) \
|
|
thisPunk_(punk, s_szProc) \
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func HRESULT | Common_QIHelper |
|
|
*
|
|
* Called when we can't find any interface in the standard list.
|
|
* See if there's a dynamic interface we can use.
|
|
*
|
|
* Objects are expected to override this method if
|
|
* they implement dynamic interfaces.
|
|
*
|
|
* @parm PV | pv |
|
|
*
|
|
* The object being queried.
|
|
*
|
|
* @parm RIID | riid |
|
|
*
|
|
* The interface being requested.
|
|
*
|
|
* @parm PPV | ppvObj |
|
|
*
|
|
* Output pointer.
|
|
*
|
|
* @returns
|
|
*
|
|
* Always returns <c E_NOINTERFACE>.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
Common_QIHelper(PV pv, RIID riid, PPV ppvObj)
|
|
{
|
|
HRESULT hres;
|
|
*ppvObj = NULL;
|
|
hres = E_NOINTERFACE;
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func HRESULT | Common_PrivateQueryInterface |
|
|
*
|
|
* Common implementation of <mf IUnknown::QueryInterface> for
|
|
* the "private <i IUnknown>".
|
|
*
|
|
* Note that we AddRef through the public <i IUnknown>
|
|
* (<ie>, through the controlling unknown).
|
|
* That's part of the rules of aggregation,
|
|
* and we have to follow them in order to keep the controlling
|
|
* unknown from getting confused.
|
|
*
|
|
* @parm PUNK | punkPrivate |
|
|
*
|
|
* The object being queried.
|
|
*
|
|
* @parm RIID | riid |
|
|
*
|
|
* The interface being requested.
|
|
*
|
|
* @parm PPV | ppvObj |
|
|
*
|
|
* Output pointer.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* The "Ensure jump is to end" remark boils down to the fact that
|
|
* compilers have failed to recognize this:
|
|
*
|
|
* for (i = 0; i < n; i++) {
|
|
* if (cond) {
|
|
* mumble();
|
|
* break;
|
|
* }
|
|
* }
|
|
* if (i >= n) {
|
|
* gurgle();
|
|
* }
|
|
*
|
|
* and turn it into this:
|
|
*
|
|
* for (i = 0; i < n; i++) {
|
|
* if (cond) {
|
|
* mumble();
|
|
* goto done;
|
|
* }
|
|
* }
|
|
* gurgle();
|
|
* done:;
|
|
*
|
|
* But even with this help, the compiler emits pretty dumb code.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
Common_PrivateQueryInterface(PUNK punkPrivate, REFIID riid, PPV ppvObj)
|
|
{
|
|
PCI pci;
|
|
HRESULT hres;
|
|
EnterProcR(IUnknown::QueryInterface, (_ "pG", punkPrivate, riid));
|
|
|
|
pci = thisPunk(punkPrivate);
|
|
if (pci) {
|
|
if (IsEqualIID(riid, &IID_IUnknown)) {
|
|
*ppvObj = pci;
|
|
OLE_AddRef(pci->ci_punkOuter);
|
|
hres = S_OK;
|
|
} else {
|
|
UINT ivtbl;
|
|
for (ivtbl = 0; ivtbl * sizeof(PV) < pci->ci_cbvtbl; ivtbl++) {
|
|
if (IsEqualIID(riid, ((PCI)(&pci->ci_rgvtbl[ivtbl]))->ci_riid)) {
|
|
*ppvObj = pvAddPvCb(pci, ivtbl * sizeof(PV));
|
|
OLE_AddRef(pci->ci_punkOuter);
|
|
hres = S_OK;
|
|
goto exit; /* Ensure jump is to end */
|
|
}
|
|
}
|
|
hres = pci->ci_QIHelper(pci, riid, ppvObj);
|
|
}
|
|
} else {
|
|
hres = E_INVALIDARG;
|
|
}
|
|
|
|
exit:;
|
|
ExitOleProcPpv(ppvObj);
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func void | Common_FastHold |
|
|
*
|
|
* Increment the object hold count inline.
|
|
*
|
|
* @parm PV | pvObj |
|
|
*
|
|
* The object being held.
|
|
*
|
|
*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func void | Common_Hold |
|
|
*
|
|
* Increment the object hold count.
|
|
*
|
|
* @parm PV | pvObj |
|
|
*
|
|
* The object being held.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void INLINE
|
|
Common_FastHold(PV pvObj)
|
|
{
|
|
PCI pci = pvObj;
|
|
|
|
InterlockedIncrement(&pci->ci_cHoldRef);
|
|
|
|
// 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
|
|
D(SquirtSqflPtszV(sqflObj | sqflVerbose,
|
|
TEXT("%s %p Common_FastHold ci_cRef(%d) ci_cHoldRef(%d)"),
|
|
pci->ci_tszClass,
|
|
pci,
|
|
pci->ci_cRef,
|
|
pci->ci_cHoldRef));
|
|
}
|
|
|
|
STDMETHODIMP_(void)
|
|
Common_Hold(PV pvObj)
|
|
{
|
|
PCI pci = pvObj;
|
|
AssertF(pvObj == _thisPv(pvObj)); /* Make sure it's the primary */
|
|
AssertF(pci->ci_cHoldRef >= pci->ci_cRef);
|
|
Common_FastHold(pvObj);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func void | Common_FastUnhold |
|
|
*
|
|
* Decrement the object hold count. Assumes that the reference
|
|
* count is <y not> dropping to zero.
|
|
*
|
|
* @parm PV | pvObj |
|
|
*
|
|
* The object being unheld.
|
|
*
|
|
*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func void | Common_Unhold |
|
|
*
|
|
* Decrement the object hold count. If the hold count drops
|
|
* to zero, then the object is destroyed.
|
|
*
|
|
* @parm PV | pvObj |
|
|
*
|
|
* The object being unheld.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void INLINE
|
|
Common_FastUnhold(PV pvObj)
|
|
{
|
|
PCI pci = pvObj;
|
|
|
|
AssertF(pci->ci_cHoldRef > 0);
|
|
InterlockedDecrement(&pci->ci_cHoldRef);
|
|
|
|
|
|
// 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
|
|
D(SquirtSqflPtszV(sqflObj | sqflVerbose, TEXT("%s %p Common_FastUnHold ci_cRef(%d) ci_cHoldRef(%d)"),
|
|
pci->ci_tszClass,
|
|
pci,
|
|
pci->ci_cRef,
|
|
pci->ci_cHoldRef));
|
|
|
|
}
|
|
|
|
STDMETHODIMP_(void)
|
|
Common_Unhold(PV pvObj)
|
|
{
|
|
PCI pci = pvObj;
|
|
|
|
AssertF(pci->ci_cHoldRef >= pci->ci_cRef);
|
|
|
|
|
|
// 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
|
|
D(SquirtSqflPtszV(sqflObj | sqflVerbose, TEXT("%s %p Common_Unhold ci_cRef(%d) ci_cHoldRef(%d)"),
|
|
pci->ci_tszClass,
|
|
pci,
|
|
pci->ci_cRef,
|
|
pci->ci_cHoldRef-1));
|
|
|
|
if (InterlockedDecrement(&pci->ci_cHoldRef) == 0) {
|
|
|
|
|
|
// 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
|
|
D(SquirtSqflPtszV(sqflObj | sqflVerbose, TEXT("Destroy %s %p "),
|
|
pci->ci_tszClass,
|
|
pci));
|
|
|
|
/*
|
|
* Last reference. Do an artifical addref so that
|
|
* anybody who does an artificial addref during
|
|
* finalization won't accidentally destroy us twice.
|
|
*/
|
|
pci->ci_cHoldRef = 1;
|
|
pci->ci_Finalize(pci);
|
|
/* Artificial release is pointless: we're being freed */
|
|
|
|
FreePv(pvSubPvCb(pci, sizeof(CIN)));
|
|
DllRelease();
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func ULONG | Common_PrivateAddRef |
|
|
*
|
|
* Increment the object refcount.
|
|
*
|
|
* @parm PUNK | punkPrivate |
|
|
*
|
|
* The object being addref'd.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP_(ULONG)
|
|
Common_PrivateAddRef(PUNK punkPrivate)
|
|
{
|
|
PCI pci;
|
|
ULONG ulRef;
|
|
EnterProcR(IUnknown::AddRef, (_ "p", punkPrivate));
|
|
|
|
pci = thisPunk(punkPrivate);
|
|
if (pci) {
|
|
/*
|
|
* Don't let anyone AddRef from 0 to 1. This happens if
|
|
* somebody does a terminal release, but we have an internal
|
|
* hold on the object, and the app tries to do an AddRef
|
|
* even though the object is "gone".
|
|
*
|
|
* Yes, there is a race condition here, but it's not
|
|
* a big one, and this is only a rough test anyway.
|
|
*/
|
|
if (pci->ci_cRef) {
|
|
/*
|
|
* We must use an interlocked operation in case two threads
|
|
* do AddRef or Release simultaneously. Note that the hold
|
|
* comes first, so that we never have the case where the
|
|
* hold count is less than the reference count.
|
|
*/
|
|
Common_Hold(pci);
|
|
InterlockedIncrement(&pci->ci_cRef);
|
|
ulRef = pci->ci_cRef;
|
|
|
|
// 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
|
|
D(SquirtSqflPtszV(sqflObj , TEXT("%s %p Common_PrivateAddref ci_cRef(%d) ci_cHoldRef(%d)"),
|
|
pci->ci_tszClass,
|
|
pci,
|
|
pci->ci_cRef,
|
|
pci->ci_cHoldRef));
|
|
} else {
|
|
RPF("ERROR: %s: Attempting to addref a deleted object", s_szProc);
|
|
ulRef = 0;
|
|
}
|
|
} else {
|
|
ulRef = 0;
|
|
}
|
|
|
|
ExitProcX(ulRef);
|
|
return ulRef;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func ULONG | Common_PrivateRelease |
|
|
*
|
|
* Decrement the object refcount. Note that decrementing
|
|
* the hold count may cause the object to vanish, so stash
|
|
* the resulting refcount ahead of time.
|
|
*
|
|
* Note that we release the hold last, so that the hold
|
|
* count is always at least as great as the refcount.
|
|
*
|
|
* @parm PUNK | punkPrivate |
|
|
*
|
|
* The object being release'd.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP_(ULONG)
|
|
Common_PrivateRelease(PUNK punkPrivate)
|
|
{
|
|
PCI pci;
|
|
ULONG ulRc;
|
|
EnterProcR(IUnknown::Release, (_ "p", punkPrivate));
|
|
|
|
pci = thisPunk(punkPrivate);
|
|
if (pci) {
|
|
LONG lRc;
|
|
|
|
/*
|
|
* We must use an interlocked operation in case two threads
|
|
* do AddRef or Release simultaneously. And if the count
|
|
* drops negative, then ignore it. (This means that the
|
|
* app is Release()ing something too many times.)
|
|
*/
|
|
|
|
lRc = InterlockedDecrement(&pci->ci_cRef);
|
|
|
|
// 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
|
|
D(SquirtSqflPtszV(sqflObj | sqflVerbose, TEXT("%s %p Common_PrivateRelease ci_cRef(%d) ci_cHoldRef(%d)"),
|
|
pci->ci_tszClass,
|
|
pci->ci_tszClass,
|
|
pci->ci_cRef,
|
|
pci->ci_cHoldRef));
|
|
if (lRc > 0) {
|
|
/*
|
|
* Not the last release; release the hold and return
|
|
* the resulting refcount. Note that we can safely
|
|
* use a fast unhold here, because there will always
|
|
* be a hold lying around to match the refcount we
|
|
* just got rid of.
|
|
*/
|
|
Common_FastUnhold(pci);
|
|
|
|
/*
|
|
* This isn't 100% accurate, but it's close enough.
|
|
* (OLE notes that the value is good only for debugging.)
|
|
*/
|
|
ulRc = pci->ci_cRef;
|
|
|
|
} else if (lRc == 0) {
|
|
/*
|
|
* That was the last application-visible reference.
|
|
* Do app-level finalization.
|
|
*/
|
|
pci->ci_AppFinalize(pci);
|
|
/*
|
|
* Note that we can't do
|
|
* a fast unhold here, because this might be the last
|
|
* hold.
|
|
*/
|
|
Common_Unhold(pci);
|
|
ulRc = 0;
|
|
} else {
|
|
/*
|
|
* The app messed up big time.
|
|
*/
|
|
RPF("ERROR: %s: Attempting to release a deleted object",
|
|
s_szProc);
|
|
ulRc = 0;
|
|
}
|
|
} else {
|
|
ulRc = 0;
|
|
}
|
|
|
|
ExitProcX(ulRc);
|
|
return ulRc;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @global IUnknownVtbl * | c_lpvtblPunk |
|
|
*
|
|
* The special IUnknown object that only the controlling unknown
|
|
* knows about.
|
|
*
|
|
* This is the one that calls the "Real" services. All the normal
|
|
* vtbl's go through the controlling unknown (which, if we are
|
|
* not aggregated, points to ourselves).
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#pragma BEGIN_CONST_DATA
|
|
|
|
_Secondary_Interface_Begin(CCommon, IUnknown,
|
|
(ULONG)(FIELD_OFFSET(CIN, cin_unkPrivate) - cbX(CIN)),
|
|
Common_Private)
|
|
_Secondary_Interface_End(CCommon, IUnknown)
|
|
|
|
#pragma END_CONST_DATA
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* "Public" IUnknown methods
|
|
*
|
|
* These simply forward through the controlling unknown.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func HRESULT | Common_QueryInterface |
|
|
*
|
|
* Forward through the controlling unknown.
|
|
*
|
|
* @parm PUNK | punk |
|
|
*
|
|
* The object being queried.
|
|
*
|
|
* @parm RIID | riid |
|
|
*
|
|
* The interface being requested.
|
|
*
|
|
* @parm PPV | ppvObj |
|
|
*
|
|
* Output pointer.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
Common_QueryInterface(PV pv, REFIID riid, PPV ppvObj)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcR(IUnknown::QueryInterface, (_ "pG", pv, riid));
|
|
|
|
if (SUCCEEDED(hres = hresFullValidPitf(pv, 0)) &&
|
|
SUCCEEDED(hres = hresFullValidRiid(riid, 1)) &&
|
|
SUCCEEDED(hres = hresFullValidPcbOut(ppvObj, cbX(*ppvObj), 2))) {
|
|
PCI pci = _thisPv(pv);
|
|
AssertF(pci->ci_punkOuter);
|
|
hres = OLE_QueryInterface(pci->ci_punkOuter, riid, ppvObj);
|
|
}
|
|
ExitOleProcPpv(ppvObj);
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func ULONG | Common_AddRef |
|
|
*
|
|
* Forward through the controlling unknown.
|
|
*
|
|
* @parm PUNK | punk |
|
|
*
|
|
* The object being addref'd.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP_(ULONG)
|
|
Common_AddRef(PV pv)
|
|
{
|
|
ULONG ulRef;
|
|
HRESULT hres;
|
|
EnterProcR(IUnknown::AddRef, (_ "p", pv));
|
|
|
|
if (SUCCEEDED(hres = hresFullValidPitf(pv, 0))) {
|
|
PCI pci = _thisPv(pv);
|
|
ulRef = OLE_AddRef(pci->ci_punkOuter);
|
|
} else {
|
|
ulRef = 0;
|
|
}
|
|
ExitProcX(ulRef);
|
|
return ulRef;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func ULONG | Common_Release |
|
|
*
|
|
* Forward through the controlling unknown.
|
|
*
|
|
* @parm PUNK | punk |
|
|
*
|
|
* Object being release'd.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP_(ULONG)
|
|
Common_Release(PV pv)
|
|
{
|
|
ULONG ulRc;
|
|
HRESULT hres;
|
|
EnterProcR(IUnknown::Release, (_ "p", pv));
|
|
|
|
if (SUCCEEDED(hres = hresFullValidPitf(pv, 0))) {
|
|
PCI pci = _thisPv(pv);
|
|
ulRc = OLE_Release(pci->ci_punkOuter);
|
|
} else {
|
|
ulRc = 0;
|
|
}
|
|
ExitProcX(ulRc);
|
|
return ulRc;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func HRESULT | __Common_New |
|
|
*
|
|
* Create a new object with refcount 1 and the specific vtbl.
|
|
* All other fields are zero-initialized. All parameters must
|
|
* already be validated.
|
|
*
|
|
* @parm ULONG | cb |
|
|
*
|
|
* Size of object. This does not include the hidden bookkeeping
|
|
* bytes maintained by the object manager.
|
|
*
|
|
* @parm PUNK | punkOuter |
|
|
*
|
|
* Controlling unknown for OLE aggregation. May be 0 to indicate
|
|
* that the object is not aggregated.
|
|
*
|
|
* @parm PV | vtbl |
|
|
*
|
|
* Pointer to primary vtbl for this object. Note that the
|
|
* vtbl declaration macros include other magic goo near the vtbl,
|
|
* which we consult in order to create the object.
|
|
*
|
|
* @parm PPV | ppvObj |
|
|
*
|
|
* Output pointer.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
__Common_New(ULONG cb, PUNK punkOuter, PV vtbl, PPV ppvObj)
|
|
{
|
|
HRESULT hres;
|
|
EnterProc(__Common_New, (_ "uxx", cb, punkOuter, vtbl));
|
|
|
|
hres = AllocCbPpv(cb + sizeof(CIN), ppvObj);
|
|
if (SUCCEEDED(hres)) {
|
|
PCI pciO = (PV)&vtbl;
|
|
PCI pci = pvAddPvCb(*ppvObj, sizeof(CIN));
|
|
RD(pci->ci_dwSig = ci_dwSignature);
|
|
pci->ci_unkPrivate.lpVtbl = Class_Vtbl(CCommon, IUnknown);
|
|
if (punkOuter) {
|
|
pci->ci_punkOuter = punkOuter;
|
|
} else {
|
|
pci->ci_punkOuter = &pci->ci_unkPrivate;
|
|
}
|
|
CopyMemory(pci, pciO->ci_rgvtbl, pciO->ci_cbvtbl);
|
|
*ppvObj = pci;
|
|
|
|
/*
|
|
* On an X86, it is simpler to increment a variable up
|
|
* from zero to one. On a RISC, it is simpler to
|
|
* store the value one directly.
|
|
*/
|
|
#ifdef _X86_
|
|
pci->ci_cHoldRef++;
|
|
pci->ci_cRef++;
|
|
#else
|
|
pci->ci_cHoldRef = 1;
|
|
pci->ci_cRef = 1;
|
|
#endif
|
|
|
|
// 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
|
|
D(SquirtSqflPtszV(sqflObj | sqflVerbose, TEXT("%s %p __Common_New ci_cRef(%d) ci_cHoldRef(%d)"),
|
|
pci->ci_tszClass,
|
|
pci,
|
|
pci->ci_cRef,
|
|
pci->ci_cHoldRef
|
|
));
|
|
|
|
|
|
DllAddRef();
|
|
|
|
// 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
|
|
D(SquirtSqflPtszV(sqflObj, TEXT("Created %s %p "),
|
|
pci->ci_tszClass,
|
|
pci));
|
|
|
|
hres = S_OK;
|
|
}
|
|
|
|
ExitOleProcPpv(ppvObj);
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func HRESULT | _Common_New_ |
|
|
*
|
|
* Create a new object with refcount 1 and the specific vtbl.
|
|
* All other fields are zero-initialized. This entry point
|
|
* validates parameters.
|
|
*
|
|
* @parm ULONG | cb |
|
|
*
|
|
* Size of object. This does not include the hidden bookkeeping
|
|
* bytes maintained by the object manager.
|
|
*
|
|
* @parm PUNK | punkOuter |
|
|
*
|
|
* Controlling unknown for OLE aggregation. May be 0 to indicate
|
|
* that the object is not aggregated.
|
|
*
|
|
* @parm PV | vtbl |
|
|
*
|
|
* Pointer to primary vtbl for this object. Note that the
|
|
* vtbl declaration macros include other magic goo near the vtbl,
|
|
* which we consult in order to create the object.
|
|
*
|
|
* @parm PPV | ppvObj |
|
|
*
|
|
* Output pointer.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
_Common_New_(ULONG cb, PUNK punkOuter, PV vtbl, PPV ppvObj, LPCSTR pszProc)
|
|
{
|
|
HRESULT hres;
|
|
EnterProc(_Common_New, (_ "uxx", cb, punkOuter, vtbl));
|
|
|
|
if (SUCCEEDED(hres = hresFullValidPitf0_(punkOuter, pszProc, 1)) &&
|
|
SUCCEEDED(hres = hresFullValidPcbOut_(ppvObj, cbX(*ppvObj), pszProc, 3))) {
|
|
hres = __Common_New(cb, punkOuter, vtbl, ppvObj);
|
|
}
|
|
ExitOleProcPpv(ppvObj);
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func HRESULT | _Common_NewRiid_ |
|
|
*
|
|
* Create a new object with refcount 1 and the specific vtbl,
|
|
* but only if the object supports the indicated interface.
|
|
* All other fields are zero-initialized.
|
|
*
|
|
* If punkOut is nonzero, then the object is being created for
|
|
* aggregation. The interface must then be &IID_IUnknown.
|
|
*
|
|
* Aggregation is used to allow multiple IDirectInputXxx interfaces
|
|
* to hang off one logical object.
|
|
*
|
|
* It is assumed that the prototype of the calling function is
|
|
*
|
|
* foo(PV this, PUNK punkOuter, RIID riid, PPV ppvObj);
|
|
*
|
|
* @parm ULONG | cb |
|
|
*
|
|
* Size of object. This does not include the hidden bookkeeping
|
|
* bytes maintained by the object manager.
|
|
*
|
|
* @parm PV | vtbl |
|
|
*
|
|
* Pointer to primary vtbl for this object. Note that the
|
|
* vtbl declaration macros include other magic goo near the vtbl,
|
|
* which we consult in order to create the object.
|
|
*
|
|
* @parm PUNK | punkOuter |
|
|
*
|
|
* Controlling unknown for OLE aggregation. May be 0 to indicate
|
|
* that the object is not aggregated.
|
|
*
|
|
* @parm RIID | riid |
|
|
*
|
|
* Interface requested.
|
|
*
|
|
* @parm PPV | ppvObj |
|
|
*
|
|
* Output pointer.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
_Common_NewRiid_(ULONG cb, PV vtbl, PUNK punkOuter, RIID riid, PPV ppvObj,
|
|
LPCSTR pszProc)
|
|
{
|
|
HRESULT hres;
|
|
EnterProc(Common_NewRiid, (_ "upG", cb, punkOuter, riid));
|
|
|
|
/*
|
|
* Note: __Common_New does not validate punkOuter or ppvObj,
|
|
* so we have to. Note also that we validate ppvObj first,
|
|
* so that it will be set to zero as soon as possible.
|
|
*/
|
|
|
|
if (SUCCEEDED(hres = hresFullValidPcbOut_(ppvObj, cbX(*ppvObj), pszProc, 3)) &&
|
|
SUCCEEDED(hres = hresFullValidPitf0_(punkOuter, pszProc, 1)) &&
|
|
SUCCEEDED(hres = hresFullValidRiid_(riid, pszProc, 2))) {
|
|
|
|
if (fLimpFF(punkOuter, IsEqualIID(riid, &IID_IUnknown))) {
|
|
hres = __Common_New(cb, punkOuter, vtbl, ppvObj);
|
|
if (SUCCEEDED(hres)) {
|
|
|
|
/*
|
|
* Move to the requested interface if we aren't aggregated.
|
|
* Don't do this if aggregated! or we will lose the private
|
|
* IUnknown and then the caller will be hosed.
|
|
*/
|
|
|
|
if (punkOuter) {
|
|
PCI pci = *ppvObj;
|
|
*ppvObj = &pci->ci_unkPrivate;
|
|
} else {
|
|
PUNK punk = *ppvObj;
|
|
hres = Common_QueryInterface(punk, riid, ppvObj);
|
|
Common_Release(punk);
|
|
}
|
|
}
|
|
} else {
|
|
RD(RPF("%s: IID must be IID_IUnknown if created for aggregation",
|
|
pszProc));
|
|
*ppvObj = 0;
|
|
hres = CLASS_E_NOAGGREGATION;
|
|
}
|
|
}
|
|
|
|
ExitOleProcPpv(ppvObj);
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Invoke_Release
|
|
*
|
|
* Release the object (if there is one) and wipe out the back-pointer.
|
|
* Note that we wipe out the value before calling the release, in order
|
|
* to ameliorate various weird callback conditions.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void EXTERNAL
|
|
Invoke_Release(PV pv)
|
|
{
|
|
LPUNKNOWN punk = (LPUNKNOWN) pvExchangePpvPv64(pv, 0);
|
|
if (punk) {
|
|
punk->lpVtbl->Release(punk);
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func HRESULT | hresPvVtbl_ |
|
|
*
|
|
* Validate that an interface pointer is what it claims to be.
|
|
* It must be the object associated with the <p vtbl>.
|
|
*
|
|
* @parm IN PV | pv |
|
|
*
|
|
* The thing that claims to be an interface pointer.
|
|
*
|
|
* @parm IN PV | vtbl |
|
|
*
|
|
* What it should be, or something equivalent to this.
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns <c S_OK> if everything is okay, else
|
|
* <c E_INVALIDARG>.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
HRESULT EXTERNAL
|
|
hresPvVtbl_(PV pv, PV vtbl, LPCSTR s_szProc)
|
|
{
|
|
PUNK punk = pv;
|
|
HRESULT hres;
|
|
|
|
AssertF(vtbl);
|
|
if (SUCCEEDED(hres = hresFullValidPitf(punk, 0))) {
|
|
#ifdef XDEBUG
|
|
if (punk->lpVtbl == vtbl) {
|
|
hres = S_OK;
|
|
} else {
|
|
RPF("ERROR %s: arg %d: invalid pointer", s_szProc, 0);
|
|
hres = E_INVALIDARG;
|
|
}
|
|
#else
|
|
/*
|
|
* ISSUE-2001/03/29-timgill Really only want to see the primary interface
|
|
* If we are looking for the primary interface,
|
|
* then allow any interface. All the dual-character set
|
|
* interfaces point all the vtbls at the same function,
|
|
* which uses hresPvT to validate. hresPvT passes the
|
|
* primary interface, hence the need to allow anything
|
|
* if you are asking for the primary interface.
|
|
*
|
|
* The problem is that this is too lenient in the case
|
|
* where we really want to see only the primary interface
|
|
* and not accept any of the secondaries.
|
|
*
|
|
*/
|
|
UINT ivtbl;
|
|
PV vtblUnk = punk->lpVtbl;
|
|
PCI pci = (PV)&vtbl;
|
|
if (pci->ci_lib == 0) {
|
|
for (ivtbl = 0; ivtbl * sizeof(PV) < pci->ci_cbvtbl; ivtbl++) {
|
|
if (pci->ci_rgvtbl[ivtbl] == vtblUnk) {
|
|
hres = S_OK;
|
|
goto found;
|
|
}
|
|
}
|
|
hres = E_INVALIDARG;
|
|
found:;
|
|
} else {
|
|
if (punk->lpVtbl == vtbl) {
|
|
hres = S_OK;
|
|
} else {
|
|
hres = E_INVALIDARG;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|