960 lines
27 KiB
C
960 lines
27 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 <windows.h>
|
|
#include <windowsx.h>
|
|
#include <objbase.h>
|
|
#include <regstr.h>
|
|
#include <setupapi.h>
|
|
#include <cfgmgr32.h>
|
|
#include <devguid.h>
|
|
#include <stdio.h>
|
|
|
|
#include <stilog.h>
|
|
#include <stiregi.h>
|
|
|
|
#include <sti.h>
|
|
#include <stierr.h>
|
|
#include <stiusd.h>
|
|
#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 <t HRESULT> on error, because the
|
|
* callers of the procedure typically do not return
|
|
* <t HRESULT>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 <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 "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
|
|
* <f AddRef>/<f Release>), we
|
|
* do our own artificial <f AddRef>/<f Release> 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 <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 = 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;
|
|
}
|
|
|