📄 unkhook.h
字号:
#ifndef __UNKHOOK_H__
#define __UNKHOOK_H__
#include "blinddel.h"
class _declspec(novtable) CBaseUnkHook :
public CBaseUnk<UnknownHook>
{
typedef void (_stdcall *VTBL_ENTRY)();
public:
static HRESULT CreateInstance(IUnknown* pUnk, IQIHook* pQIHook, UnknownHook** ppHook);
// Functions for UnknownHook interface
STDMETHOD(get_Flags)(UnkHookFlags *puhFlags);
STDMETHOD(put_Flags)(UnkHookFlags uhFlags);
~CBaseUnkHook(){m_Hook.Unhook();}
protected:
class CHook
{
friend class CBaseUnkHook;
public:
CHook() : m_pOuter(NULL){}
void HookUnk(IProvideClassInfo* pUnk, IQIARHook* pQIHook, UnkHookFlags uhFlags, bool fWeakRefQIHook, bool fUseARHook, CBaseUnkHook* pOwner);
void Unhook();
// Functions for vtable population
static HRESULT _stdcall QueryInterface(CHook** ppThis, REFIID riid, void** ppvObj);
static ULONG _stdcall AddRef(CHook** ppThis);
static ULONG _stdcall Release(CHook** ppThis);
static ULONG _stdcall AddRefHooked(CHook** ppThis);
static ULONG _stdcall ReleaseHooked(CHook** ppThis);
static HRESULT _stdcall GetClassInfo(CHook** ppThis, ITypeInfo** ppTI);
private:
// The actual VTABLE
VTBL_ENTRY m_VTable[4];
IProvideClassInfo* m_pOuter;
IQIARHook* m_pQIHook;
void* m_lpVTableStart;
CBaseUnkHook* m_pOwner;
union
{
bool m_fAllFlags;
struct
{
bool m_fHookQIBefore : 1; // Maintain order on first five items
bool m_fHookQIAfter : 1; // to line up with UnkHookFlags
bool m_fCallMapIID : 1;
bool m_fReportAddRef : 1;
bool m_fReportRelease : 1;
bool m_fWeakQIHook : 1;
bool m_fWeakQIHookExplicit : 1;
bool m_fARHook : 1;
//More than 8 of these requires adjustment to
//m_fAllFlags, current count: 8
};
};
} m_Hook;
};
class _declspec(novtable) CAggregator
{
public:
~CAggregator(); // Be careful with directly derived objects, this is non-virtual
protected:
// A structure to figure out element counts before the
// memory for CAggregator (and its derived classes) is
// actually pulled off the heap. This lets us allocate
// all required elements in a single shot.
struct PreAllocData
{
friend class CAggregator;
PreAllocData(AggregateData* pAggData, ULONG cAggElems, ULONG cIIDElems, HRESULT& hrLaunch);
void SetExtraBytesLocation(void* pExtra){pExtraBytes = pExtra;}
size_t GetExtraBytesCount(){return cExtraBytes;}
private:
void* pExtraBytes; // The starting offset of the extra data
size_t cExtraBytes; // Filled in by constructor
ULONG cIIDs; // Total IID count
ULONG cUnfiltered; // The number of unfiltered items (should be total - cMaps)
ULONG cUnfilteredNoDelegator; // The number of unfiltered items that have NoDelegator set
ULONG cUnfilteredBefore; // The number of unfiltered objects with a before flag
ULONG cIIDMapAndBlockElems; // The number of items that turn into an IID map or a block
ULONG cIIDMaps; // The total number of mapped IIDs, one per map
ULONG cIIDBlocks; // The total number of blocked IIDs, >=1 per map
bool fHaveDispHook; // Whether or not we've seen the primary dispatch flag
};
friend struct PreAllocData;
// The actual constructor
CAggregator(AggregateData* pAggData, ULONG cAggElems, IID* pIIDs, PreAllocData& rPAD, HRESULT& hrLaunch, IUnknown* punkControlling);
// A node to hold the IID information, as well as the location
// of the blind delegator structure. This lets us do multiple things:
// 1) (Most importantly) The blind delegators must be able to outlive
// the aggregator because the aggregator can be released before all
// interfaces retrieved through it have been destroyed. The aggregator
// needs to know when an interface is already outstanding, so there must
// be some means of communication from the delegator back into the aggregator
// when an interface is no longer in use. Because the aggregator may be
// gone when the notification happens, the only way to do this is to essentially
// have joint control over the memory. When the aggregator dies, it
// walks all outstanding interfaces (those with Del.m_dwRefs > 0),
// AddRefs the memory allocator, and swaps in a different destruction function.
// 2) This gives us a means of maintaining the ObjPtr for all interfaces retrieved
// from the aggregator. Even if the interface is fully released, it is nice
// if a subsequent QI for the same interface retrieves the same object.
// 3) We can search the list for punkInner values to determine the external pointer
// to the given interface from inside the object, similar to SafeRef in MTS land.
// 4) For an unfiltered object, when we find the IID, we permanently attach the
// node to the end of the filtered list of IIDs when a QI succeeds. This
// cache enables us to jump directly to the correct object on a subsequent
// QI for the same IID.
struct IIDHookNode
{
friend struct IIDHookNodeList;
_BlindDelegator BlindDel; // Blind delegator to attach to this IID
IID iid; // IID to hook
IIDHookNode* pNext; // Next node in the chain
FixedSizeMemoryManager* pFSMMOwner; // Reference to owning node
short iUnk; // Index of object in m_pUnks array
// This is swapped into the delegator as the destructor to use
// after the aggregator is no longer alive.
static ULONG _stdcall PostMortemDestruct(_BlindDelegator* pBlindDel, IUnknown** ppInner, IUnknown** ppOuter);
};
struct IIDHookNodeList
{
IIDHookNodeList() : pHead(NULL){ppTail = &pHead;}
// Walks all items, does work as described in 1) as needed.
void AddTail(IIDHookNode *pNode)
{
*ppTail = pNode;
ppTail = &pNode->pNext;
}
IIDHookNode* pHead;
IIDHookNode** ppTail;
};
void OnTerminateAggregator(IIDHookNodeList& NodeList);
// A structure to hold IID mapping information. These are
// preallocated with the CAggregator object.
struct IIDMapEntry
{
IID IIDFrom;
IID IIDTo;
};
class CHoldUnk
{
public:
// If this changes to require anything other than
// zeroed memory, then change the corresponding commented
// out code in CAggregator::CAggregator as well.
// CHoldUnk() : m_pUnk(NULL), m_fAllFlags(false){}
~CHoldUnk()
{
if (m_pUnk && !m_fWeakRefRaw)
{
if (m_fWeakRef)
{
// Add a reference back to the controlling IUnknown
IUnknown* punkControl;
m_pUnk->QueryInterface(IID_IUnknown, (void**)&punkControl);
}
m_pUnk->Release();
}
}
void operator=(IUnknown* pUnk){(m_pUnk = pUnk)->AddRef();}
IUnknown* operator->(){return m_pUnk;}
void AssignRawWeakRef(IUnknown* pUnk){m_pUnk = pUnk; m_fWeakRefRaw = true;}
void SetWeakRef(){m_fWeakRef = true;}
void SetRawWeakRef(){m_fWeakRefRaw = true;}
void SetExplicitWeakRef(){m_fWeakRefExplicit = true;}
void SetDelayedCreation(){m_fDelayedCreation = true;}
bool GetDelayedCreation(){return m_fDelayedCreation;}
void SetKeepDelayed(){m_fKeepDelayed = true;}
void SetNoDelegator(){m_fNoDelegator = true;}
bool GetNoDelegator(){return m_fNoDelegator;}
void SetFullyResolved(){m_fFullyResolved = true;}
bool GetFullyResolved(){return m_fFullyResolved;}
// Call IDelayCreation::Create, replacing m_pUnk with
// the resolved value if successful. If m_fKeepDelayed
// is true, then *ppResolved returns an AddRefed pointer
// to the resolved interface. Otherwise, use operator->
// directly to get at the IUnknown.
HRESULT ResolveDelayedCreation(REFIID riid, IUnknown** ppResolved);
private:
IUnknown* m_pUnk;
union
{
bool m_fAllFlags;
struct
{
// Don't generate a blind delegator around this item
bool m_fNoDelegator : 1;
// This is held as a weak ref, which means that a ref
// has been released on the controlling IUnknown. This
// ref must be replaced before the final release.
bool m_fWeakRef : 1;
// m_pUnk is a reference to an IDelayCreation interface,
// not the real object we return
bool m_fDelayedCreation : 1;
// We should keep this as a delayed creator instead of cache
// the returned interface pointer
bool m_fKeepDelayed : 1;
// The stored pointer is fully resolved to the correct IID and
// shouldn't be QI'ed. Just AddRef and return it. This is
// done for overriding the primary dispatch, where a QI would
// actually return the wrong interface.
bool m_fFullyResolved : 1;
// The WeakRef quality of this object was explicitly set in the
// AggregateData structure.
bool m_fWeakRefExplicit : 1;
// The stored pointer is a raw weak reference. It is not balanced
// by a Release on the controlling IUnknown.
bool m_fWeakRefRaw : 1;
//More than 8 of these requires adjustment to
//m_fAllFlags, current count: 7
};
};
};
// Protected functions
HRESULT DoQIHook(REFIID riid, IIDHookNode*& pNodeToWrap, bool fAfter, IUnknown** ppResult);
HRESULT DoMapIID(IID* pIID);
UnkHookFlags GetFlags();
// Member variables
FixedSizeMemoryManager* m_pFSMMNodes; // The memory manager for all nodes
IIDHookNodeList m_BeforeNodes; // A list of nodes for the before QI hook
IIDHookNodeList m_AfterNodes; // A list of nodes for the after QI hook
ULONG m_cIIDMaps; // A count of items in m_pIIDMaps
IIDMapEntry* m_pIIDMaps; // A list of mappings. Don't delete these, they're preallocated with the object.
ULONG m_cBlockIIDs; // A count of items in the m_pBlockIIDs mapping.
IID* m_pBlockIIDs; // A list of IIDs to block. Don't delete these, they're preallocated with the object.
ULONG m_cUnks; // A count of items in the m_pUnks array
CHoldUnk* m_pUnks; // Array of unknown objects. The unfiltered are stored first, then all the rest.
short m_cUnfilteredBefore; // The total count of unfiltered items in the m_pUnks array for a BeforeQI
short m_cUnfilteredAfter; // The total count of unfiltered items in the m_pUnks array for an AfterQI
};
class CUnkHook : public CBaseUnkHook
{
public:
UNKNOWN_IMPL
static HRESULT CreateInstance(IUnknown* pUnk, IQIARHook* pQIHook, UnkHookFlags uhFlags, bool fUseARHook, UnknownHook** ppHook);
};
class CUnkHookAggregate :
// Keep these CAggregator, then CBaseUnkHook. This forces
// an Unhook on teardown before CAggregator is destroyed, which
// stops it from crashing.
public CAggregator,
public CBaseUnkHook,
public IQIHook
{
public:
UNKNOWN_IMPL
CUnkHookAggregate(AggregateData* pAggData, ULONG cAggElems, IID* pIIDs, PreAllocData& rPAD, HRESULT& hrLaunch, IUnknown* punkControlling) : CAggregator(pAggData, cAggElems, pIIDs, rPAD, hrLaunch, punkControlling){}
//~CUnkHookAggregate(){m_Hook.Unhook();}
static HRESULT CreateInstance(IUnknown* pUnk, SAFEARRAY* pAggData, SAFEARRAY* pIIDs, UnknownHook** ppHook);
STDMETHOD(QIHook)(VBGUID* iid, UnkHookFlags uhFlags, IUnknown** pResult, IUnknown* HookedUnknown);
STDMETHOD(MapIID)(VBGUID *iid);
STDMETHOD(get_Flags)(UnkHookFlags* puhFlags);
STDMETHOD(put_Flags)(UnkHookFlags uhFlags);
};
class CAggregateObject :
public IUnknown,
public CAggregator
{
public:
static HRESULT CreateInstance(SAFEARRAY* pAggData, SAFEARRAY* pIIDs, IUnknown** ppOwner, IUnknown** ppUnk);
//~CAggregateObject(); // Watch out, CAggregator doesn't have a virtual destructor
CAggregateObject(AggregateData* pAggData, ULONG cAggElems, IID* pIIDs, PreAllocData& rPAD, HRESULT& hrLaunch) : m_dwRefs(0), m_ppThis(NULL), CAggregator(pAggData, cAggElems, pIIDs, rPAD, hrLaunch, NULL)
{
ADD_OBJECT;
}
// IUnknown methods
STDMETHOD(QueryInterface)(REFIID riid, void** ppv);
STDMETHOD_(ULONG, AddRef)(void)
{
return ++m_dwRefs;
}
STDMETHOD_(ULONG, Release)(void)
{
if (--m_dwRefs == 0)
{
if (m_ppThis)
{
*m_ppThis = NULL;
}
delete this;
REMOVE_OBJECT;
return 0;
}
return m_dwRefs;
}
private:
ULONG m_dwRefs;
UnkHookFlags m_UHFlags; // Cache flags, GetFlags isn't free
IUnknown** m_ppThis;
};
#endif //__UNKHOOK_H__
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -