windows-nt/Source/XPSP1/NT/shell/ext/keyremap/common.c
2020-09-26 16:20:57 +08:00

388 lines
10 KiB
C

/*****************************************************************************
*
* common.c - Shared stuff that operates on all classes
*
* WARNING! The Common services work only if you pass in the
* "primary object". This is vacuous if you don't use multiple
* inheritance, since there's only one object in the first place.
*
* If you use multiple inheritance, make sure you pass the pointer
* to the object that you use as IUnknown.
*
* The exceptions are the Forward_* functions, which work on
* pointers to non-primary interfaces. They forward the call to the
* primary interface.
*
*****************************************************************************/
#include "map.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_New(CObj, ppvOut);
*
* or, if the object is variable-sized,
*
* hres = Common_NewCb(cb, CObj, ppvOut);
*
* If the object supports multiple interfaces, you also need to
* initialize all the secondary interfaces.
*
* CObj *pco = *ppvOut;
* pco->bar = Secondary_Vtbl(CObj, IBar);
* pco->baz = Secondary_Vtbl(CObj, IBaz);
*
* (6) Define the vtbls.
*
* #pragma BEGIN_CONST_DATA
*
* // 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:
*
* riid
* cRef FinalizeProc
* pFoo -> lpVtbl -> QueryInterface
* data Common_AddRef
* data Common_Release
* ... ...
*
* Essentially, we use the otherwise-unused space above the
* pointers to record our bookkeeping information.
*
* cRef = object reference count
* riid = object iid
* FinalizeProc = Finalization procedure
*
* For secondary interfaces, it looks like this:
*
* offset to primary interface
* pFoo -> lpVtbl -> Forward_QueryInterface
* Forward_AddRef
* Forward_Release
* ...
*
*****************************************************************************/
typedef struct CommonInfoN {
D(ULONG cin_dwSig;)
ULONG cin_cRef;
} CommonInfoN, CIN, *PCIN;
typedef struct CommonInfoP {
PREVTBL *cip_prevtbl;
} CommonInfoP, CIP, *PCIP;
typedef struct CommonInfoP2 {
PREVTBL2 *cip2_prevtbl2;
} CommonInfoP2, CIP2, *PCIP2;
typedef union CommonInfo {
CIN cin[1];
CIP cip[1];
CIP2 cip2[1];
} CommonInfo, CI, *PCI;
#define ci_dwSig cin[-1].cin_dwSig
#define ci_cRef cin[-1].cin_cRef
#define ci_rgfp cip[0].cip_prevtbl
#define ci_riid cip[0].cip_prevtbl[-1].riid
#define ci_Finalize cip[0].cip_prevtbl[-1].FinalizeProc
#define ci_lib cip2[0].cip2_prevtbl2[-1].lib
#ifdef DEBUG
#define ci_Start ci_dwSig
#else
#define ci_Start ci_cRef
#endif
#define ci_dwSignature 0x38162378 /* typed by my cat */
/*****************************************************************************
*
* Common_QueryInterface (from IUnknown)
*
* Use this for objects that support only one interface.
*
*****************************************************************************/
STDMETHODIMP
Common_QueryInterface(PV pv, REFIID riid, PPV ppvObj)
{
PCI pci = pv;
HRESULT hres;
EnterProc(Common_QueryInterface, (_ "pG", pv, riid));
AssertF(pci->ci_dwSig == ci_dwSignature);
if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, pci->ci_riid)) {
*ppvObj = pv;
Common_AddRef(pv);
hres = S_OK;
} else {
*ppvObj = NULL;
hres = E_NOINTERFACE;
}
ExitOleProcPpv(ppvObj);
return hres;
}
/*****************************************************************************
*
* Common_AddRef (from IUnknown)
*
* Increment the object refcount and the dll refcount.
*
*****************************************************************************/
STDMETHODIMP_(ULONG)
_Common_AddRef(PV pv)
{
PCI pci = pv;
AssertF(pci->ci_dwSig == ci_dwSignature);
InterlockedIncrement((LPLONG)&g_cRef);
return ++pci->ci_cRef;
}
/*****************************************************************************
*
* Common_Finalize (from Common_Release)
*
* By default, no finalization is necessary.
*
*****************************************************************************/
void EXTERNAL
Common_Finalize(PV pv)
{
SquirtSqflPtszV(sqfl, TEXT("Common_Finalize(%08x)"), pv);
}
/*****************************************************************************
*
* Common_Release (from IUnknown)
*
* Decrement the object refcount and the dll refcount.
*
* If the object refcount drops to zero, finalize the object
* and free it.
*
* The finalization handler lives ahead of the object vtbl.
*
*****************************************************************************/
STDMETHODIMP_(ULONG)
_Common_Release(PV pv)
{
PCI pci = pv;
ULONG ulRc;
AssertF(pci->ci_dwSig == ci_dwSignature);
InterlockedDecrement((LPLONG)&g_cRef);
ulRc = --pci->ci_cRef;
if (ulRc == 0) {
pci->ci_Finalize(pv);
FreePv(&pci->ci_Start);
}
return ulRc;
}
/*****************************************************************************
*
* Forward_QueryInterface (from IUnknown)
*
* Move to the main object and try again.
*
*****************************************************************************/
STDMETHODIMP
Forward_QueryInterface(PV pv, REFIID riid, PPV ppvObj)
{
PCI pci = pv;
LPUNKNOWN punk = pvAddPvCb(pv, 0 - pci->ci_lib);
return Common_QueryInterface(punk, riid, ppvObj);
}
/*****************************************************************************
*
* Forward_AddRef (from IUnknown)
*
* Move to the main object and try again.
*
*****************************************************************************/
STDMETHODIMP_(ULONG)
Forward_AddRef(PV pv)
{
PCI pci = pv;
LPUNKNOWN punk = pvAddPvCb(pv, 0 - pci->ci_lib);
return Common_AddRef(punk);
}
/*****************************************************************************
*
* Forward_Release (from IUnknown)
*
* Move to the main object and try again.
*
*****************************************************************************/
STDMETHODIMP_(ULONG)
Forward_Release(PV pv)
{
PCI pci = pv;
LPUNKNOWN punk = pvAddPvCb(pv, 0 - pci->ci_lib);
return Common_Release(punk);
}
/*****************************************************************************
*
* _Common_New
*
* Create a new object with refcount 1 and the specific vtbl.
* All other fields are zero-initialized.
*
*****************************************************************************/
STDMETHODIMP
_Common_New(ULONG cb, PV vtbl, PPV ppvObj)
{
HRESULT hres;
EnterProc(Common_New, (_ "u", cb));
SquirtSqflPtszV(sqfl, TEXT("Common_New()"));
hres = AllocCbPpv(cb + sizeof(CIN), ppvObj);
if (SUCCEEDED(hres)) {
PCI pci = pvAddPvCb(*ppvObj, sizeof(CIN));
D(pci->ci_dwSig = ci_dwSignature);
pci->ci_rgfp = (PV)vtbl;
*ppvObj = pci;
Common_AddRef(pci);
hres = S_OK;
}
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 = pvExchangePpvPv(pv, 0);
if (punk) {
punk->lpVtbl->Release(punk);
}
}