/***************************************************************************** * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include "wia.h" #include "stipriv.h" #include "stiapi.h" #include "stirc.h" #include "debug.h" */ #include "sticomm.h" #define DbgFl DbgFlCommon /***************************************************************************** * * 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 * 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 * cRef = object reference count * riid = object iid * rgvtbl = array of vtbls of supported interfaces * cbvtbl = size of array in bytes * QIHelper = QueryInterface helper for aggregation * FinalizeProc = Finalization procedure * * For secondary interfaces, it looks like this: * * riid * offset to primary interface * pFoo -> lpVtbl -> Forward_QueryInterface * Forward_AddRef * Forward_Release * ... * *****************************************************************************/ /* WARNING! cin_dwSig must be first: ci_Start relies on it */ typedef struct CommonInfoN { /* This goes in front of the object */ RD(ULONG cin_dwSig;) /* Signature (for parameter validation) */ ULONG 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_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_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_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 MAXDEBUG #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) { DebugOutPtszV(DbgFlCommon, TEXT("Common_Finalize(%08x)"), 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 on error, because the * callers of the procedure typically do not return * s themselves. * *****************************************************************************/ #ifndef MAXDEBUG #define thisPunk_(punk, z) \ _thisPunk_(punk) \ #endif PV INLINE thisPunk_(PUNK punkPrivate, LPCSTR s_szProc) { PV pv = NULL; if (SUCCEEDED(hresFullValidReadPdw(punkPrivate, 0))) { if (punkPrivate->lpVtbl == Class_Vtbl(CCommon, IUnknown)) { pv = pvAddPvCb(punkPrivate, cbX(CIN) - FIELD_OFFSET(CIN, cin_unkPrivate)); } else { // WarnPszV("%s: Invalid parameter 0", szProc); pv = NULL; } } 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 . * *****************************************************************************/ 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 for * the "private ". * * Note that we AddRef through the public * (, 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 "compiler issue" remark boils down to the fact that the * compiler fails 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; /* see "compiler issue" comment above */ } } hres = pci->ci_QIHelper(pci, riid, ppvObj); } } else { hres = E_INVALIDARG; } exit:; ExitOleProcPpv(ppvObj); return hres; } /***************************************************************************** * * @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) { ulRef = ++pci->ci_cRef; } else { ulRef = 0; } ExitProcX(ulRef); return ulRef; } /***************************************************************************** * * @doc INTERNAL * * @func ULONG | Common_PrivateRelease | * * Decrement the object refcount. * * If the object refcount drops to zero, finalize the object * and free it, then decrement the dll refcount. * * To protect against potential re-entrancy during finalization * (in case finalization does an artificial * /), we * do our own artificial / up front. * * @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) { ulRc = --pci->ci_cRef; if (ulRc == 0) { ++pci->ci_cRef; pci->ci_Finalize(pci); /* Artificial release is pointless: we're being freed */ FreePv(pvSubPvCb(pci, sizeof(CIN))); DllRelease(); } } 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))) { 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; pci->ci_cRef++; DllAddRef(); 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 = hresFullValidPdwOut_(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 IStillImageXXX 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 = hresFullValidPdwOut_(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(PPV pv) { LPUNKNOWN punk = (PV)pvExchangePpvPv((PPV)pv, (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

. * * @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 if everything is okay, else * . * *****************************************************************************/ HRESULT EXTERNAL hresPvVtbl_(PV pv, PV vtbl, LPCSTR s_szProc) { PUNK punk = pv; HRESULT hres; AssertF(vtbl); if (SUCCEEDED(hres = hresFullValidReadPdw(punk, 0))) { #ifdef MAXDEBUG if (punk->lpVtbl == vtbl) { hres = S_OK; } else { RPF("ERROR %s: arg %d: invalid pointer", s_szProc, 0); hres = E_INVALIDARG; } #else 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; }