/***************************************************************************** * * 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 "fnd.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 = NOERROR; } else { *ppvObj = NULL; hres = ResultFromScode(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 = NOERROR; } 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); } }