855 lines
22 KiB
C++
855 lines
22 KiB
C++
|
//*** qistub.cpp -- QI helpers (retail and debug)
|
||
|
// DESCRIPTION
|
||
|
// this file has the shared-source 'master' implementation. it is
|
||
|
// #included in each DLL that uses it.
|
||
|
// clients do something like:
|
||
|
// #include "priv.h" // for types, ASSERT, DM_*, DF_*, etc.
|
||
|
// #include "../lib/qistub.cpp"
|
||
|
|
||
|
#include "qistub.h"
|
||
|
|
||
|
#define DM_MISC2 0 // misc stuff (verbose)
|
||
|
|
||
|
// hack-o-rama: shlwapi/qistub.cpp does #undef DEBUG but its PCH was
|
||
|
// built DEBUG, so lots of bad stuff happens. work-around it here.
|
||
|
#undef DBEXEC
|
||
|
#ifdef DEBUG
|
||
|
#define DBEXEC(flg, expr) ((flg) ? (expr) : 0)
|
||
|
#else
|
||
|
#define DBEXEC(flg, expr) /*NOTHING*/
|
||
|
#endif
|
||
|
|
||
|
#ifdef DEBUG // {
|
||
|
//*** CUniqueTab {
|
||
|
// DESCRIPTION
|
||
|
// key/data table insert and lookup, w/ interlock.
|
||
|
class CUniqueTab
|
||
|
{
|
||
|
public:
|
||
|
void * Add(int val);
|
||
|
void * Find(int val, int delta);
|
||
|
void Reset(void);
|
||
|
|
||
|
// n.b. *not* protected
|
||
|
CUniqueTab(int cbElt);
|
||
|
virtual ~CUniqueTab();
|
||
|
|
||
|
protected:
|
||
|
|
||
|
private:
|
||
|
void _Lock(void) { EnterCriticalSection(&_hLock); }
|
||
|
void _Unlock(void) { LeaveCriticalSection(&_hLock); }
|
||
|
|
||
|
CRITICAL_SECTION _hLock;
|
||
|
// key + (arbitrary) limit of 4 int's of client data
|
||
|
#define CUT_CBELTMAX (SIZEOF(int) + 4 * SIZEOF(int))
|
||
|
int _cbElt; // size of an entry (key + data)
|
||
|
// (arbitrary) limit to catch clients running amuck
|
||
|
#define CUT_CVALMAX 256 // actually, a LIM not a MAX
|
||
|
HDSA _hValTab;
|
||
|
};
|
||
|
|
||
|
CUniqueTab::CUniqueTab(int cbElt)
|
||
|
{
|
||
|
InitializeCriticalSection(&_hLock);
|
||
|
ASSERT(cbElt >= SIZEOF(DWORD)); // need at least a key; data optional
|
||
|
_cbElt = cbElt;
|
||
|
_hValTab = DSA_Create(_cbElt, 4);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
CUniqueTab::~CUniqueTab()
|
||
|
{
|
||
|
DSA_Destroy(_hValTab);
|
||
|
DeleteCriticalSection(&_hLock);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
struct cutent {
|
||
|
int iKey;
|
||
|
char bData[CUT_CBELTMAX - SIZEOF(int)];
|
||
|
};
|
||
|
struct cfinddata {
|
||
|
int iKey;
|
||
|
int dRange;
|
||
|
void *pEntry;
|
||
|
};
|
||
|
|
||
|
int _UTFindCallback(void *pEnt, void *pData)
|
||
|
{
|
||
|
#define INFUNC(base, p, range) ((base) <= (p) && (p) <= (base) + (range))
|
||
|
struct cfinddata *pcd = (struct cfinddata *)pData;
|
||
|
if (INFUNC(*(int *)pEnt, pcd->iKey, pcd->dRange)) {
|
||
|
pcd->pEntry = pEnt;
|
||
|
return 0;
|
||
|
}
|
||
|
return 1;
|
||
|
#undef INFUNC
|
||
|
}
|
||
|
|
||
|
//*** CUniqueTab::Add -- add entry if not already there
|
||
|
//
|
||
|
void * CUniqueTab::Add(int val)
|
||
|
{
|
||
|
struct cfinddata cd = { val, 0, NULL };
|
||
|
|
||
|
_Lock();
|
||
|
|
||
|
DSA_EnumCallback(_hValTab, _UTFindCallback, &cd);
|
||
|
if (!cd.pEntry) {
|
||
|
int i;
|
||
|
// lazy,lazy,lazy: alloc max size and let DSA_AppendItem sort it out
|
||
|
struct cutent elt = { val, 0 /*,0,...,0*/ };
|
||
|
|
||
|
TraceMsg(DM_MISC2, "cut.add: add %x", val);
|
||
|
if (DSA_GetItemCount(_hValTab) <= CUT_CVALMAX) {
|
||
|
i = DSA_AppendItem(_hValTab, &elt);
|
||
|
cd.pEntry = DSA_GetItemPtr(_hValTab, i);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_Unlock();
|
||
|
|
||
|
return cd.pEntry;
|
||
|
}
|
||
|
|
||
|
//*** CUniqueTab::Find -- find entry
|
||
|
//
|
||
|
void * CUniqueTab::Find(int val, int delta)
|
||
|
{
|
||
|
struct cfinddata cd = { val, delta, NULL };
|
||
|
|
||
|
DSA_EnumCallback(_hValTab, _UTFindCallback, &cd);
|
||
|
if (cd.pEntry) {
|
||
|
// TODO: add p->data[0] dump
|
||
|
TraceMsg(DM_MISC2, "cut.find: found %x+%d", val, delta);
|
||
|
}
|
||
|
return cd.pEntry;
|
||
|
}
|
||
|
|
||
|
//*** _UTResetCallback -- helper for CUniqueTab::Reset
|
||
|
int _UTResetCallback(void *pEnt, void *pData)
|
||
|
{
|
||
|
struct cutent *pce = (struct cutent *)pEnt;
|
||
|
int cbEnt = *(int *)pData;
|
||
|
// perf: could move the SIZEOF(int) into caller, but seems safer here
|
||
|
memset(pce->bData, 0, cbEnt - SIZEOF(int));
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
//*** Reset -- clear 'data' part of all entries
|
||
|
//
|
||
|
void CUniqueTab::Reset(void)
|
||
|
{
|
||
|
if (EVAL(_cbElt > SIZEOF(int))) {
|
||
|
_Lock();
|
||
|
DSA_EnumCallback(_hValTab, _UTResetCallback, &_cbElt);
|
||
|
_Unlock();
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
// }
|
||
|
#endif // }
|
||
|
|
||
|
//*** QueryInterface helpers {
|
||
|
|
||
|
//*** FAST_IsEqualIID -- fast compare
|
||
|
// (cast to 'LONG_PTR' so don't get overloaded ==)
|
||
|
#define FAST_IsEqualIID(piid1, piid2) ((LONG_PTR)(piid1) == (LONG_PTR)(piid2))
|
||
|
|
||
|
#ifdef DEBUG // {
|
||
|
//*** DBNoOp -- do nothing (but suppress compiler optimizations)
|
||
|
// NOTES
|
||
|
// this won't fool compiler when it gets smarter, oh well...
|
||
|
void DBNoOp()
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
void DBBrkpt()
|
||
|
{
|
||
|
DBNoOp();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//*** DBBreakGUID -- debug hook (gets readable name, allows brkpt on IID)
|
||
|
// DESCRIPTION
|
||
|
// search for 'BRKPT' for various hooks.
|
||
|
// patch 'DBQIiid' to brkpt on a specific iface
|
||
|
// patch 'DBQIiSeq' to brkpt on Nth QI of specific iface
|
||
|
// brkpt on interesting events noted below
|
||
|
// NOTES
|
||
|
// warning: returns ptr to *static* buffer!
|
||
|
|
||
|
typedef enum {
|
||
|
DBBRK_NIL = 0,
|
||
|
DBBRK_ENTER = 0x01,
|
||
|
DBBRK_TRACE = 0x02,
|
||
|
DBBRK_S_XXX = 0x04,
|
||
|
DBBRK_E_XXX = 0x08,
|
||
|
DBBRK_BRKPT = 0x10,
|
||
|
} DBBRK;
|
||
|
|
||
|
DBBRK DBQIuTrace = DBBRK_NIL; // BRKPT patch to enable brkpt'ing
|
||
|
GUID *DBQIiid = NULL; // BRKPT patch to brkpt on iface
|
||
|
int DBQIiSeq = -1; // BRKPT patch to brkpt on Nth QI of DBQIiid
|
||
|
long DBQIfReset = FALSE; // BRKPT patch to reset counters
|
||
|
|
||
|
TCHAR *DBBreakGUID(const GUID *piid, DBBRK brkCmd)
|
||
|
{
|
||
|
static TCHAR szClass[GUIDSTR_MAX];
|
||
|
|
||
|
SHStringFromGUID(*piid, szClass, ARRAYSIZE(szClass));
|
||
|
|
||
|
// FEATURE: fold these 2 if's together
|
||
|
if ((DBQIuTrace & brkCmd) &&
|
||
|
(DBQIiid == NULL || IsEqualIID(*piid, *DBQIiid))) {
|
||
|
TraceMsg(DM_TRACE, "util: DBBreakGUID brkCmd=%x clsid=%s (%s)", brkCmd, szClass, Dbg_GetREFIIDName(*piid));
|
||
|
// BRKPT put brkpt here to brkpt on 'brkCmd' event
|
||
|
DBBrkpt();
|
||
|
}
|
||
|
|
||
|
if (DBQIiid != NULL && IsEqualIID(*piid, *DBQIiid)) {
|
||
|
//TraceMsg(DM_TRACE, "util: DBBreakGUID clsid=%s (%s)", szClass, Dbg_GetREFIIDName(*piid));
|
||
|
if (brkCmd != DBBRK_TRACE) {
|
||
|
// BRKPT put brkpt here to brkpt on 'DBQIiid' iface
|
||
|
DBNoOp();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// BRKPT put your brkpt(s) here for various events
|
||
|
switch (brkCmd) {
|
||
|
case DBBRK_ENTER:
|
||
|
// QI called w/ this iface
|
||
|
DBNoOp();
|
||
|
break;
|
||
|
case DBBRK_TRACE:
|
||
|
// looped over this iface
|
||
|
DBNoOp();
|
||
|
break;
|
||
|
case DBBRK_S_XXX:
|
||
|
// successful QI for this iface
|
||
|
DBNoOp();
|
||
|
break;
|
||
|
case DBBRK_E_XXX:
|
||
|
// failed QI for this iface
|
||
|
DBNoOp();
|
||
|
break;
|
||
|
case DBBRK_BRKPT:
|
||
|
// various brkpt events, see backtrace to figure out which one
|
||
|
DBNoOp();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return szClass;
|
||
|
}
|
||
|
#endif // }
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
CUniqueTab *DBpQIFuncTab;
|
||
|
|
||
|
STDAPI_(BOOL) DBIsQIFunc(int ret, int delta)
|
||
|
{
|
||
|
BOOL fRet = FALSE;
|
||
|
|
||
|
if (DBpQIFuncTab)
|
||
|
fRet = BOOLFROMPTR(DBpQIFuncTab->Find(ret, delta));
|
||
|
|
||
|
return fRet;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// perf: shell split means FAST_IsEqIID often fails, so QI_EASY is off.
|
||
|
#define QI_EASY 0 // w/ shell split, seems to be too rare
|
||
|
|
||
|
#ifdef DEBUG // {
|
||
|
int DBcQITot, DBcQIUnk, DBcQIErr, DBcQIEasy, DBcQIHard;
|
||
|
|
||
|
LPCQITAB DBpqitStats; // BRKPT: patch to enable QITABENT profiling
|
||
|
#define DBSTAT_CNT 20
|
||
|
int DBcStats[DBSTAT_CNT + 3]; // 0..n, overflow, IUnknown, E_FAIL
|
||
|
|
||
|
#define DBSI_FAIL (-1)
|
||
|
#define DBSI_IUNKNOWN (-2)
|
||
|
#define DBSI_OVERFLOW (-3)
|
||
|
|
||
|
#define DBSI_SPEC(i) (DBSTAT_CNT - 1 + (-(i)))
|
||
|
|
||
|
//***
|
||
|
// DESCRIPTION
|
||
|
// search for 'BRKPT' for various hooks.
|
||
|
// patch 'DBpqitStats' to gather stats on that QITAB
|
||
|
// then break into debugger (ctrl+C) and dumpa 'DBcStats l 24'
|
||
|
// then sort high count guys to the front, and 0 count guys to the end
|
||
|
//
|
||
|
void DBQIStats(LPCQITAB pqitab, INT_PTR i)
|
||
|
{
|
||
|
if (pqitab != DBpqitStats)
|
||
|
return;
|
||
|
|
||
|
if (i >= DBSTAT_CNT)
|
||
|
i = DBSI_OVERFLOW;
|
||
|
if (i < 0)
|
||
|
i = DBSTAT_CNT - 1 + (-i);
|
||
|
DBcStats[i]++;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
void DBDumpQIStats()
|
||
|
{
|
||
|
int i;
|
||
|
TCHAR *p;
|
||
|
TCHAR buf[256];
|
||
|
|
||
|
TraceMsg(TF_QISTUB, "qi stats: tot=%d unk=%d err=%d easy(%d)=%d hard=%d",
|
||
|
DBcQITot, DBcQIUnk, DBcQIErr, QI_EASY, DBcQIEasy, DBcQIHard);
|
||
|
|
||
|
if (DBpqitStats == NULL)
|
||
|
return;
|
||
|
|
||
|
p = buf;
|
||
|
for (i = 0; i < DBSTAT_CNT; i++) {
|
||
|
p += wsprintf(p, TEXT(" %d"), DBcStats[i]);
|
||
|
}
|
||
|
p += wsprintf(p, TEXT(" o=%d u=%d e=%d"),
|
||
|
DBcStats[DBSI_SPEC(DBSI_OVERFLOW)],
|
||
|
DBcStats[DBSI_SPEC(DBSI_IUNKNOWN)],
|
||
|
DBcStats[DBSI_SPEC(DBSI_FAIL)]);
|
||
|
|
||
|
TraceMsg(TF_QISTUB, "qi stats: %s", buf);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
#endif // }
|
||
|
|
||
|
|
||
|
//*** QISearch -- table-driven QI
|
||
|
// ENTRY/EXIT
|
||
|
// this IUnknown* of calling QI
|
||
|
// pqit QI table of IID,cast_offset pairs
|
||
|
// ppv the usual
|
||
|
// hr the usual S_OK/E_NOINTERFACE, plus other E_* for errors
|
||
|
// NOTES
|
||
|
// perf: shell split means FAST_IsEqIID often fails, so QI_EASY is off.
|
||
|
// perf: IUnknown v. rare, so goes last.
|
||
|
// PERF: explicit 'E_NOIFACE' entry in qitab for common miss(es)?
|
||
|
STDAPI_(void*) QIStub_CreateInstance(void* that, IUnknown* punk, REFIID riid); // qistub.cpp
|
||
|
|
||
|
STDAPI QISearch(void* that, LPCQITAB pqitab, REFIID riid, LPVOID* ppv)
|
||
|
{
|
||
|
// do *not* move this!!! (must be 1st on frame)
|
||
|
#ifdef DEBUG
|
||
|
#if (_X86_)
|
||
|
int var0; // *must* be 1st on frame
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
LPCQITAB pqit;
|
||
|
#ifdef DEBUG
|
||
|
TCHAR *pst;
|
||
|
|
||
|
DBEXEC(TRUE, DBcQITot++);
|
||
|
#if ( _X86_) // QIStub only works for X86
|
||
|
if (IsFlagSet(g_dwDumpFlags, DF_DEBUGQI)) {
|
||
|
if (DBpQIFuncTab == NULL)
|
||
|
DBpQIFuncTab = new CUniqueTab(SIZEOF(DWORD)); // LONG_PTR?
|
||
|
if (DBpQIFuncTab) {
|
||
|
int n;
|
||
|
int fp = (int) (1 + (int *)&var0);
|
||
|
struct DBstkback sbtab[1] = { 0 };
|
||
|
n = DBGetStackBack(&fp, sbtab, ARRAYSIZE(sbtab));
|
||
|
DBpQIFuncTab->Add(sbtab[n - 1].ret);
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if (DBQIuTrace)
|
||
|
pst = DBBreakGUID(&riid, DBBRK_ENTER);
|
||
|
#endif
|
||
|
|
||
|
if (ppv == NULL)
|
||
|
return E_POINTER;
|
||
|
|
||
|
#if QI_EASY
|
||
|
// 1st try the fast way
|
||
|
for (pqit = pqitab; pqit->piid != NULL; pqit++) {
|
||
|
DBEXEC(DBQIuTrace, (pst = DBBreakGUID(pqit->piid, DBBRK_TRACE)));
|
||
|
if (FAST_IsEqualIID(&riid, pqit->piid)) {
|
||
|
DBEXEC(TRUE, DBcQIEasy++);
|
||
|
goto Lhit;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// no luck, try the hard way
|
||
|
for (pqit = pqitab; pqit->piid != NULL; pqit++) {
|
||
|
DBEXEC(DBQIuTrace, (pst = DBBreakGUID(pqit->piid, DBBRK_TRACE)));
|
||
|
if (IsEqualIID(riid, *(pqit->piid))) {
|
||
|
DBEXEC(TRUE, DBcQIHard++);
|
||
|
#if QI_EASY
|
||
|
Lhit:
|
||
|
#else
|
||
|
// keep 'easy' stats anyway
|
||
|
DBEXEC(FAST_IsEqualIID(&riid, pqit->piid), DBcQIEasy++);
|
||
|
#endif
|
||
|
#ifdef DEBUG
|
||
|
DBEXEC(TRUE, DBQIStats(pqitab, pqit - pqitab));
|
||
|
#if ( _X86_) // QIStub only works for X86
|
||
|
if (IsFlagSet(g_dwDumpFlags, DF_DEBUGQI)) {
|
||
|
IUnknown* punk = (IUnknown*)((LONG_PTR)that + pqit->dwOffset);
|
||
|
*ppv = QIStub_CreateInstance(that, punk, riid);
|
||
|
if (*ppv) {
|
||
|
pst = DBBreakGUID(&riid, DBBRK_S_XXX);
|
||
|
return S_OK;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
#endif
|
||
|
Lcast:
|
||
|
IUnknown* punk = (IUnknown*)((LONG_PTR)that + pqit->dwOffset);
|
||
|
DBEXEC(TRUE, (pst = DBBreakGUID(&riid, DBBRK_S_XXX)));
|
||
|
punk->AddRef();
|
||
|
*ppv = punk;
|
||
|
return S_OK;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// no luck, try IUnknown (which is implicit in the table)
|
||
|
// we try IUnknown last not 1st since stats show it's rare
|
||
|
if (IsEqualIID(riid, IID_IUnknown)) {
|
||
|
// just use 1st table entry
|
||
|
pqit = pqitab;
|
||
|
DBEXEC(TRUE, DBcQIUnk++);
|
||
|
DBEXEC(TRUE, DBQIStats(pqitab, DBSI_IUNKNOWN));
|
||
|
|
||
|
goto Lcast;
|
||
|
}
|
||
|
|
||
|
DBEXEC(DBQIuTrace, (pst = DBBreakGUID(&riid, DBBRK_E_XXX)));
|
||
|
DBEXEC(TRUE, DBcQIErr++);
|
||
|
DBEXEC(TRUE, DBQIStats(pqitab, DBSI_FAIL));
|
||
|
*ppv = NULL;
|
||
|
return E_NOINTERFACE;
|
||
|
}
|
||
|
|
||
|
// }
|
||
|
|
||
|
#ifdef DEBUG // {
|
||
|
#if ( _X86_) // { QIStub only works for X86
|
||
|
|
||
|
//*** QIStub helpers {
|
||
|
|
||
|
class CQIStub
|
||
|
{
|
||
|
public:
|
||
|
virtual void thunk0();
|
||
|
// FEATURE: should AddRef/Release up _iSeq? don't recommend it.
|
||
|
virtual STDMETHODIMP_(ULONG) AddRef(void)
|
||
|
{ _cRef++; return _cRef; }
|
||
|
virtual STDMETHODIMP_(ULONG) Release(void)
|
||
|
{ _cRef--; if (_cRef>0) return _cRef; delete this; return 0; }
|
||
|
virtual void thunk3();
|
||
|
virtual void thunk4();
|
||
|
virtual void thunk5();
|
||
|
virtual void thunk6();
|
||
|
virtual void thunk7();
|
||
|
virtual void thunk8();
|
||
|
virtual void thunk9();
|
||
|
virtual void thunk10();
|
||
|
virtual void thunk11();
|
||
|
virtual void thunk12();
|
||
|
virtual void thunk13();
|
||
|
virtual void thunk14();
|
||
|
virtual void thunk15();
|
||
|
virtual void thunk16();
|
||
|
virtual void thunk17();
|
||
|
virtual void thunk18();
|
||
|
virtual void thunk19();
|
||
|
virtual void thunk20();
|
||
|
virtual void thunk21();
|
||
|
virtual void thunk22();
|
||
|
virtual void thunk23();
|
||
|
virtual void thunk24();
|
||
|
virtual void thunk25();
|
||
|
virtual void thunk26();
|
||
|
virtual void thunk27();
|
||
|
virtual void thunk28();
|
||
|
virtual void thunk29();
|
||
|
virtual void thunk30();
|
||
|
virtual void thunk31();
|
||
|
virtual void thunk32();
|
||
|
virtual void thunk33();
|
||
|
virtual void thunk34();
|
||
|
virtual void thunk35();
|
||
|
virtual void thunk36();
|
||
|
virtual void thunk37();
|
||
|
virtual void thunk38();
|
||
|
virtual void thunk39();
|
||
|
virtual void thunk40();
|
||
|
virtual void thunk41();
|
||
|
virtual void thunk42();
|
||
|
virtual void thunk43();
|
||
|
virtual void thunk44();
|
||
|
virtual void thunk45();
|
||
|
virtual void thunk46();
|
||
|
virtual void thunk47();
|
||
|
virtual void thunk48();
|
||
|
virtual void thunk49();
|
||
|
virtual void thunk50();
|
||
|
virtual void thunk51();
|
||
|
virtual void thunk52();
|
||
|
virtual void thunk53();
|
||
|
virtual void thunk54();
|
||
|
virtual void thunk55();
|
||
|
virtual void thunk56();
|
||
|
virtual void thunk57();
|
||
|
virtual void thunk58();
|
||
|
virtual void thunk59();
|
||
|
virtual void thunk60();
|
||
|
virtual void thunk61();
|
||
|
virtual void thunk62();
|
||
|
virtual void thunk63();
|
||
|
virtual void thunk64();
|
||
|
virtual void thunk65();
|
||
|
virtual void thunk66();
|
||
|
virtual void thunk67();
|
||
|
virtual void thunk68();
|
||
|
virtual void thunk69();
|
||
|
virtual void thunk70();
|
||
|
virtual void thunk71();
|
||
|
virtual void thunk72();
|
||
|
virtual void thunk73();
|
||
|
virtual void thunk74();
|
||
|
virtual void thunk75();
|
||
|
virtual void thunk76();
|
||
|
virtual void thunk77();
|
||
|
virtual void thunk78();
|
||
|
virtual void thunk79();
|
||
|
virtual void thunk80();
|
||
|
virtual void thunk81();
|
||
|
virtual void thunk82();
|
||
|
virtual void thunk83();
|
||
|
virtual void thunk84();
|
||
|
virtual void thunk85();
|
||
|
virtual void thunk86();
|
||
|
virtual void thunk87();
|
||
|
virtual void thunk88();
|
||
|
virtual void thunk89();
|
||
|
virtual void thunk90();
|
||
|
virtual void thunk91();
|
||
|
virtual void thunk92();
|
||
|
virtual void thunk93();
|
||
|
virtual void thunk94();
|
||
|
virtual void thunk95();
|
||
|
virtual void thunk96();
|
||
|
virtual void thunk97();
|
||
|
virtual void thunk98();
|
||
|
virtual void thunk99();
|
||
|
|
||
|
protected:
|
||
|
CQIStub(void *that, IUnknown* punk, REFIID riid);
|
||
|
friend void* QIStub_CreateInstance(void *that, IUnknown* punk, REFIID riid);
|
||
|
friend BOOL __stdcall DBIsQIStub(void *that);
|
||
|
friend void __stdcall DBDumpQIStub(void *that);
|
||
|
friend TCHAR *DBGetQIStubSymbolic(void *that);
|
||
|
|
||
|
private:
|
||
|
~CQIStub();
|
||
|
|
||
|
static void *_sar; // C (not C++) ptr to CQIStub::AddRef
|
||
|
|
||
|
int _cRef;
|
||
|
IUnknown* _punk; // vtable we hand off to
|
||
|
void* _that; // "this" pointer of object we stub (for reference)
|
||
|
IUnknown* _punkRef; // "punk" (for reference)
|
||
|
REFIID _riid; // iid of interface (for reference)
|
||
|
int _iSeq; // sequence #
|
||
|
TCHAR _szName[GUIDSTR_MAX]; // legible name of interface (for reference)
|
||
|
};
|
||
|
|
||
|
struct DBQISeq
|
||
|
{
|
||
|
GUID * pIid;
|
||
|
int iSeq;
|
||
|
};
|
||
|
//CASSERT(SIZEOF(GUID *) == SIZEOF(DWORD)); // CUniqueTab uses DWORD's
|
||
|
|
||
|
// FEATURE: todo: _declspec(thread)
|
||
|
CUniqueTab * DBpQISeqTab = NULL;
|
||
|
|
||
|
extern "C" void *Dbg_GetREFIIDAtom(REFIID riid); // lib/dump.c (priv.h?)
|
||
|
|
||
|
//***
|
||
|
// NOTES
|
||
|
// there's actually a race condition here -- another thread can come in
|
||
|
// and do seq++, then we do the reset, etc. -- but the assumption is that
|
||
|
// the developer has set the flag in a scenario where this isn't an issue.
|
||
|
void DBQIReset(void)
|
||
|
{
|
||
|
ASSERT(!DBQIfReset); // caller should do test-and-clear
|
||
|
if (DBpQISeqTab)
|
||
|
DBpQISeqTab->Reset();
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
void *DBGetVtblEnt(void *that, int i);
|
||
|
#define VFUNC_ADDREF 1 // AddRef is vtbl[1]
|
||
|
|
||
|
void * CQIStub::_sar = NULL;
|
||
|
|
||
|
CQIStub::CQIStub(void* that, IUnknown* punk, REFIID riid) : _cRef(1), _riid(riid)
|
||
|
{
|
||
|
_that = that;
|
||
|
|
||
|
_punk = punk;
|
||
|
if (_punk)
|
||
|
_punk->AddRef();
|
||
|
|
||
|
_punkRef = _punk; // for reference, so don't AddRef it!
|
||
|
|
||
|
// c++ won't let me get &CQIStub::AddRef as a 'real' ptr (!@#$),
|
||
|
// so we need to get it the hard way, viz. new'ing an object which
|
||
|
// we know inherits it.
|
||
|
if (_sar == NULL) {
|
||
|
_sar = DBGetVtblEnt((void *)this, VFUNC_ADDREF);
|
||
|
ASSERT(_sar != NULL);
|
||
|
}
|
||
|
|
||
|
StrCpyN(_szName, Dbg_GetREFIIDName(riid), ARRAYSIZE(_szName));
|
||
|
|
||
|
// generate sequence #
|
||
|
if (DBpQISeqTab == NULL)
|
||
|
DBpQISeqTab = new CUniqueTab(SIZEOF(struct DBQISeq));
|
||
|
if (DBpQISeqTab) {
|
||
|
struct DBQISeq *pqiseq;
|
||
|
|
||
|
if (InterlockedExchange(&DBQIfReset, FALSE))
|
||
|
DBQIReset();
|
||
|
|
||
|
pqiseq = (struct DBQISeq *) DBpQISeqTab->Add((DWORD) Dbg_GetREFIIDAtom(riid));
|
||
|
if (EVAL(pqiseq)) // (might fail on table overflow)
|
||
|
_iSeq = pqiseq->iSeq++;
|
||
|
}
|
||
|
|
||
|
TraceMsg(TF_QISTUB, "ctor QIStub %s seq=%d (that=%x punk=%x) %x", _szName, _iSeq, _that, _punk, this);
|
||
|
}
|
||
|
|
||
|
CQIStub::~CQIStub()
|
||
|
{
|
||
|
TraceMsg(TF_QISTUB, "dtor QIStub %s (that=%x punk=%x) %x", _szName, _that, _punk, this);
|
||
|
|
||
|
ATOMICRELEASE(_punk);
|
||
|
}
|
||
|
|
||
|
STDAPI_(void*) QIStub_CreateInstance(void* that, IUnknown* punk, REFIID riid)
|
||
|
{
|
||
|
CQIStub* pThis = new CQIStub(that, punk, riid);
|
||
|
|
||
|
if (DBQIiSeq == pThis->_iSeq && IsEqualIID(riid, *DBQIiid)) {
|
||
|
TCHAR *pst;
|
||
|
// BRKPT put brkpt here to brkpt on seq#'th call to 'DBQIiid' iface
|
||
|
pst = DBBreakGUID(&riid, DBBRK_BRKPT);
|
||
|
}
|
||
|
|
||
|
return(pThis);
|
||
|
}
|
||
|
|
||
|
//*** DBGetVtblEnt -- get vtable entry
|
||
|
// NOTES
|
||
|
// always uses 1st vtbl (so MI won't work...).
|
||
|
void *DBGetVtblEnt(void *that, int i)
|
||
|
{
|
||
|
void **vptr;
|
||
|
void *pfunc;
|
||
|
|
||
|
__try {
|
||
|
vptr = (void **) *(void **) that;
|
||
|
pfunc = (vptr == 0) ? 0 : vptr[i];
|
||
|
}
|
||
|
__except(EXCEPTION_EXECUTE_HANDLER) {
|
||
|
// since we're called from the DebMemLeak, we're only *guessing*
|
||
|
// that we have a vptr etc., so we might fault.
|
||
|
TraceMsg(TF_ALWAYS, "gve: GPF");
|
||
|
pfunc = 0;
|
||
|
}
|
||
|
|
||
|
return pfunc;
|
||
|
}
|
||
|
|
||
|
//*** DBIsQIStub -- is 'this' a ptr to a 'CQIStub' object?
|
||
|
// DESCRIPTION
|
||
|
// we look at the vtbl and assume that if we have a ptr to CQIStub::AddRef,
|
||
|
// then it's us.
|
||
|
// NOTES
|
||
|
// M00BUG we do a 'new' in here, which can cause pblms if we're in the middle
|
||
|
// of intelli-leak and we end up doing a ReAlloc which moves the heap (raymondc
|
||
|
// found such a case).
|
||
|
// M00BUG in a release build (w/ identical COMDAT folding) we'll get false
|
||
|
// hits since most/all AddRefs are identical and folded. if we ever need to
|
||
|
// be more exact we can add a signature and key off that.
|
||
|
// M00BUG hack hack we actually return a void *, just in case you want to
|
||
|
// know the 'real' object. if that turns out to be useful, we should change
|
||
|
// to return a void * instead of a BOOL.
|
||
|
|
||
|
BOOL DBIsQIStub(void* that)
|
||
|
{
|
||
|
void *par;
|
||
|
|
||
|
#if 0
|
||
|
if (_sar == NULL)
|
||
|
TraceMsg(DM_TRACE, "qis: _sar == NULL");
|
||
|
#endif
|
||
|
|
||
|
par = DBGetVtblEnt(that, VFUNC_ADDREF);
|
||
|
|
||
|
#if 0
|
||
|
TraceMsg(TF_ALWAYS, "IsQIStub(%x): par=%x _sar=%x", that, _sar, par);
|
||
|
#endif
|
||
|
|
||
|
return (CQIStub::_sar == par && CQIStub::_sar != NULL) ? (BOOL)((CQIStub *)that)->_punk : 0;
|
||
|
#undef VFUNC_ADDREF
|
||
|
}
|
||
|
|
||
|
TCHAR *DBGetQIStubSymbolic(void* that)
|
||
|
{
|
||
|
class CQIStub *pqis = (CQIStub *) that;
|
||
|
return pqis->_szName;
|
||
|
}
|
||
|
|
||
|
//*** DBDumpQIStub -- pretty-print a 'CQIStub'
|
||
|
//
|
||
|
STDAPI_(void) DBDumpQIStub(void* that)
|
||
|
{
|
||
|
class CQIStub *pqis = (CQIStub *) that;
|
||
|
TraceMsg(TF_ALWAYS, "\tqistub(%x): cRef=0x%x iSeq=%x iid=%s", that, pqis->_cRef, pqis->_iSeq, pqis->_szName);
|
||
|
}
|
||
|
|
||
|
// Memory layout of CQIStub is:
|
||
|
// lpVtbl // offset 0
|
||
|
// _cRef // offset 4
|
||
|
// _punk // offset 8
|
||
|
//
|
||
|
// "this" pointer stored in stack
|
||
|
//
|
||
|
// mov eax, ss:4[esp] ; get pThis
|
||
|
// mov ecx, 8[eax] ; get real object (_punk)
|
||
|
// mov eax, [ecx] ; load the real vtable (_punk->lpVtbl)
|
||
|
// ; the above will fault if referenced after we're freed
|
||
|
// mov ss:4[esp], ecx ; fix up stack object (_punk)
|
||
|
// jmp dword ptr cs:(4*i)[eax] ; jump to the real function
|
||
|
//
|
||
|
#define QIStubThunk(i) \
|
||
|
void _declspec(naked) CQIStub::thunk##i() \
|
||
|
{ \
|
||
|
_asm mov eax, ss:4[esp] \
|
||
|
_asm mov ecx, 8[eax] \
|
||
|
_asm mov eax, [ecx] \
|
||
|
_asm mov ss:4[esp], ecx \
|
||
|
_asm jmp dword ptr cs:(4*i)[eax] \
|
||
|
}
|
||
|
|
||
|
QIStubThunk(0);
|
||
|
QIStubThunk(3);
|
||
|
QIStubThunk(4);
|
||
|
QIStubThunk(5);
|
||
|
QIStubThunk(6);
|
||
|
QIStubThunk(7);
|
||
|
QIStubThunk(8);
|
||
|
QIStubThunk(9);
|
||
|
QIStubThunk(10);
|
||
|
QIStubThunk(11);
|
||
|
QIStubThunk(12);
|
||
|
QIStubThunk(13);
|
||
|
QIStubThunk(14);
|
||
|
QIStubThunk(15);
|
||
|
QIStubThunk(16);
|
||
|
QIStubThunk(17);
|
||
|
QIStubThunk(18);
|
||
|
QIStubThunk(19);
|
||
|
QIStubThunk(20);
|
||
|
QIStubThunk(21);
|
||
|
QIStubThunk(22);
|
||
|
QIStubThunk(23);
|
||
|
QIStubThunk(24);
|
||
|
QIStubThunk(25);
|
||
|
QIStubThunk(26);
|
||
|
QIStubThunk(27);
|
||
|
QIStubThunk(28);
|
||
|
QIStubThunk(29);
|
||
|
QIStubThunk(30);
|
||
|
QIStubThunk(31);
|
||
|
QIStubThunk(32);
|
||
|
QIStubThunk(33);
|
||
|
QIStubThunk(34);
|
||
|
QIStubThunk(35);
|
||
|
QIStubThunk(36);
|
||
|
QIStubThunk(37);
|
||
|
QIStubThunk(38);
|
||
|
QIStubThunk(39);
|
||
|
QIStubThunk(40);
|
||
|
QIStubThunk(41);
|
||
|
QIStubThunk(42);
|
||
|
QIStubThunk(43);
|
||
|
QIStubThunk(44);
|
||
|
QIStubThunk(45);
|
||
|
QIStubThunk(46);
|
||
|
QIStubThunk(47);
|
||
|
QIStubThunk(48);
|
||
|
QIStubThunk(49);
|
||
|
QIStubThunk(50);
|
||
|
QIStubThunk(51);
|
||
|
QIStubThunk(52);
|
||
|
QIStubThunk(53);
|
||
|
QIStubThunk(54);
|
||
|
QIStubThunk(55);
|
||
|
QIStubThunk(56);
|
||
|
QIStubThunk(57);
|
||
|
QIStubThunk(58);
|
||
|
QIStubThunk(59);
|
||
|
QIStubThunk(60);
|
||
|
QIStubThunk(61);
|
||
|
QIStubThunk(62);
|
||
|
QIStubThunk(63);
|
||
|
QIStubThunk(64);
|
||
|
QIStubThunk(65);
|
||
|
QIStubThunk(66);
|
||
|
QIStubThunk(67);
|
||
|
QIStubThunk(68);
|
||
|
QIStubThunk(69);
|
||
|
QIStubThunk(70);
|
||
|
QIStubThunk(71);
|
||
|
QIStubThunk(72);
|
||
|
QIStubThunk(73);
|
||
|
QIStubThunk(74);
|
||
|
QIStubThunk(75);
|
||
|
QIStubThunk(76);
|
||
|
QIStubThunk(77);
|
||
|
QIStubThunk(78);
|
||
|
QIStubThunk(79);
|
||
|
QIStubThunk(80);
|
||
|
QIStubThunk(81);
|
||
|
QIStubThunk(82);
|
||
|
QIStubThunk(83);
|
||
|
QIStubThunk(84);
|
||
|
QIStubThunk(85);
|
||
|
QIStubThunk(86);
|
||
|
QIStubThunk(87);
|
||
|
QIStubThunk(88);
|
||
|
QIStubThunk(89);
|
||
|
QIStubThunk(90);
|
||
|
QIStubThunk(91);
|
||
|
QIStubThunk(92);
|
||
|
QIStubThunk(93);
|
||
|
QIStubThunk(94);
|
||
|
QIStubThunk(95);
|
||
|
QIStubThunk(96);
|
||
|
QIStubThunk(97);
|
||
|
QIStubThunk(98);
|
||
|
QIStubThunk(99);
|
||
|
|
||
|
// }
|
||
|
|
||
|
#endif // }
|
||
|
#endif // }
|