Added manual hooks. I hope that I haven't broken binary compatibility - someone might want to test it.

--HG--
extra : convert_revision : svn%3Ac2935e3e-5518-0410-8daf-afa5dab7d4e3/trunk%40158
This commit is contained in:
Pavol Marko 2005-12-10 22:37:53 +00:00
parent 20beff0636
commit 8cc68b35fb
7 changed files with 2766 additions and 564 deletions

File diff suppressed because it is too large Load Diff

View File

@ -133,6 +133,17 @@ namespace SourceHook
*/
typedef int Plugin;
struct ProtoInfo
{
ProtoInfo(int rtsz, int nop, const int *p) : beginningNull(0), retTypeSize(rtsz), numOfParams(nop), params(p)
{
}
int beginningNull; //!< To distinguish from old protos (which never begin with 0)
int retTypeSize; //!< 0 if void
int numOfParams; //!< number of parameters
const int *params; //!< params[0]=0 (or -1 for vararg), params[1]=size of first param, ...
};
/**
* @brief Specifies the actions for hook managers
*/
@ -363,6 +374,14 @@ namespace SourceHook
virtual void SetOrigRetPtr(const void *ptr) = 0; //!< Sets the original return pointer
virtual void SetOverrideRetPtr(const void *ptr) = 0; //!< Sets the override result pointer
virtual bool ShouldContinue() = 0; //!< Returns false if the hook loop should exit
/**
* @brief Remove a hook manager. Auto-removes all hooks attached to it from plugin plug.
*
* @param plug The owner of the hook manager
* @param pubFunc The hook manager's info function
*/
virtual void RemoveHookManager(Plugin plug, HookManagerPubFunc pubFunc) = 0;
};
}
@ -398,6 +417,14 @@ inline void SH_RELEASE_CALLCLASS_R(SourceHook::ISourceHook *shptr, SourceHook::C
shptr->ReleaseCallClass(reinterpret_cast<SourceHook::GenericCallClass*>(ptr));
}
#define SH_MANUALHOOK_RECONFIGURE(hookname, pvtblindex, pvtbloffs, pthisptroffs) \
do { \
SH_GLOB_SHPTR->RemoveHookManager(SH_GLOB_PLUGPTR, SH_MFHCls(hookname)::HookManPubFunc); \
SH_MFHCls(hookname)::ms_MFI.thisptroffs = pthisptroffs; \
SH_MFHCls(hookname)::ms_MFI.vtblindex = pvtblindex; \
SH_MFHCls(hookname)::ms_MFI.vtbloffs = pvtbloffs; \
} while (0)
#define SH_GET_CALLCLASS(ptr) SH_GET_CALLCLASS_R(SH_GLOB_SHPTR, ptr)
#define SH_RELEASE_CALLCLASS(ptr) SH_RELEASE_CALLCLASS_R(SH_GLOB_SHPTR, ptr)
@ -417,6 +444,22 @@ inline void SH_RELEASE_CALLCLASS_R(SourceHook::ISourceHook *shptr, SourceHook::C
#define SH_REMOVE_HOOK_MEMFUNC(ifacetype, ifacefunc, ifaceptr, handler_inst, handler_func, post) \
SH_REMOVE_HOOK(ifacetype, ifacefunc, ifaceptr, fastdelegate::MakeDelegate(handler_inst, handler_func), post)
#define SH_ADD_MANUALHOOK(hookname, ifaceptr, handler, post) \
__SourceHook_FHMAdd##hookname(reinterpret_cast<void*>(ifaceptr), post, handler)
#define SH_ADD_MANUALHOOK_STATICFUNC(hookname, ifaceptr, handler, post) \
SH_ADD_MANUALHOOK(hookname, ifaceptr, fastdelegate::MakeDelegate(handler), post)
#define SH_ADD_MANUALHOOK_MEMFUNC(hookname, ifaceptr, handler_inst, handler_func, post) \
SH_ADD_MANUALHOOK(hookname, ifaceptr, fastdelegate::MakeDelegate(handler_inst, handler_func), post)
#define SH_REMOVE_MANUALHOOK(hookname, ifaceptr, handler, post) \
__SourceHook_FHMRemove##hookname(reinterpret_cast<void*>(ifaceptr), post, handler)
#define SH_REMOVE_MANUALHOOK_STATICFUNC(hookname, ifaceptr, handler, post) \
SH_REMOVE_MANUALHOOK(hookname, ifaceptr, fastdelegate::MakeDelegate(handler), post)
#define SH_REMOVE_MANUALHOOK_MEMFUNC(hookname, ifaceptr, handler_inst, handler_func, post) \
SH_REMOVE_MANUALHOOK(hookname, ifaceptr, fastdelegate::MakeDelegate(handler_inst, handler_func), post)
#define SH_NOATTRIB
@ -435,6 +478,7 @@ inline void SH_RELEASE_CALLCLASS_R(SourceHook::ISourceHook *shptr, SourceHook::C
//////////////////////////////////////////////////////////////////////////
#define SH_FHCls(ift, iff, ov) __SourceHook_FHCls_##ift##iff##ov
#define SH_MFHCls(hookname) __SourceHook_MFHCls_##hookname
#define SHINT_MAKE_HOOKMANPUBFUNC(ifacetype, ifacefunc, overload, funcptr) \
SH_FHCls(ifacetype,ifacefunc,overload)() \
@ -452,7 +496,8 @@ inline void SH_RELEASE_CALLCLASS_R(SourceHook::ISourceHook *shptr, SourceHook::C
\
if (action == HA_GetInfo) \
{ \
param->SetInfo(ms_MFI.vtbloffs, ms_MFI.vtblindex, ms_Proto); \
param->SetInfo(ms_MFI.vtbloffs, ms_MFI.vtblindex, \
reinterpret_cast<const char*>(&ms_Proto)); \
\
MemFuncInfo mfi; \
GetFuncInfo(&SH_FHCls(ifacetype,ifacefunc,overload)::Func, mfi); \
@ -483,15 +528,14 @@ inline void SH_RELEASE_CALLCLASS_R(SourceHook::ISourceHook *shptr, SourceHook::C
static SH_FHCls(ifacetype,ifacefunc,overload) ms_Inst; \
static ::SourceHook::MemFuncInfo ms_MFI; \
static ::SourceHook::IHookManagerInfo *ms_HI; \
static const char *ms_Proto; \
static ::SourceHook::ProtoInfo ms_Proto; \
SHINT_MAKE_HOOKMANPUBFUNC(ifacetype, ifacefunc, overload, funcptr)
#define SHINT_MAKE_GENERICSTUFF_END(ifacetype, ifacefunc, overload, proto, funcptr) \
#define SHINT_MAKE_GENERICSTUFF_END(ifacetype, ifacefunc, overload, funcptr) \
}; \
SH_FHCls(ifacetype,ifacefunc,overload) SH_FHCls(ifacetype,ifacefunc,overload)::ms_Inst; \
::SourceHook::MemFuncInfo SH_FHCls(ifacetype,ifacefunc,overload)::ms_MFI; \
::SourceHook::IHookManagerInfo *SH_FHCls(ifacetype,ifacefunc,overload)::ms_HI; \
const char *SH_FHCls(ifacetype,ifacefunc,overload)::ms_Proto = proto; \
bool __SourceHook_FHAdd##ifacetype##ifacefunc(void *iface, bool post, \
SH_FHCls(ifacetype,ifacefunc,overload)::FD handler) \
{ \
@ -519,6 +563,77 @@ inline void SH_RELEASE_CALLCLASS_R(SourceHook::ISourceHook *shptr, SourceHook::C
SH_FHCls(ifacetype,ifacefunc,overload)::HookManPubFunc, &tmp, post); \
} \
#define SHINT_MAKE_GENERICSTUFF_BEGIN_MANUAL(hookname, pvtbloffs, pvtblidx, pthisptroffs) \
struct SH_MFHCls(hookname) \
{ \
static SH_MFHCls(hookname) ms_Inst; \
static ::SourceHook::MemFuncInfo ms_MFI; \
static ::SourceHook::IHookManagerInfo *ms_HI; \
static ::SourceHook::ProtoInfo ms_Proto; \
\
SH_MFHCls(hookname)() \
{ \
ms_MFI.isVirtual = true; \
ms_MFI.thisptroffs = pthisptroffs; \
ms_MFI.vtblindex = pvtblidx; \
ms_MFI.vtbloffs = pvtbloffs; \
} \
static int HookManPubFunc(::SourceHook::HookManagerAction action, ::SourceHook::IHookManagerInfo *param) \
{ \
using namespace ::SourceHook; \
/* we don't set ms_MFI here because manual hookmans can be reconfigured */ \
/* :FIXME: possible problem: someone adding a hook from a constructor of a global entity */ \
/* which is construced before SH_MFHCls(hookname)() gets called? */ \
/* Verify interface version */ \
if (SH_GLOB_SHPTR->GetIfaceVersion() != SH_IFACE_VERSION) \
return 1; \
\
if (action == HA_GetInfo) \
{ \
param->SetInfo(ms_MFI.vtbloffs, ms_MFI.vtblindex, \
reinterpret_cast<const char*>(&ms_Proto)); \
\
MemFuncInfo mfi; \
GetFuncInfo(&SH_MFHCls(hookname)::Func, mfi); \
param->SetHookfuncVfnptr( \
reinterpret_cast<void**>(reinterpret_cast<char*>(&ms_Inst) + mfi.vtbloffs)[mfi.vtblindex]); \
return 0; \
} \
else if (action == HA_Register) \
{ \
ms_HI = param; \
return 0; \
} \
else if (action == HA_Unregister) \
{ \
ms_HI = NULL; \
return 0; \
} \
else \
return 1; \
}
#define SHINT_MAKE_GENERICSTUFF_END_MANUAL(hookname, pvtbloffs, pvtblidx, pthisptroffs) \
}; \
SH_MFHCls(hookname) SH_MFHCls(hookname)::ms_Inst; \
::SourceHook::MemFuncInfo SH_MFHCls(hookname)::ms_MFI; \
::SourceHook::IHookManagerInfo *SH_MFHCls(hookname)::ms_HI; \
bool __SourceHook_FHMAdd##hookname(void *iface, bool post, \
SH_MFHCls(hookname)::FD handler) \
{ \
return SH_GLOB_SHPTR->AddHook(SH_GLOB_PLUGPTR, iface, pthisptroffs, \
SH_MFHCls(hookname)::HookManPubFunc, \
new ::SourceHook::CSHDelegate<SH_MFHCls(hookname)::FD>(handler), post); \
} \
bool __SourceHook_FHMRemove##hookname(void *iface, bool post, \
SH_MFHCls(hookname)::FD handler) \
{ \
::SourceHook::CSHDelegate<SH_MFHCls(hookname)::FD> tmp(handler); \
return SH_GLOB_SHPTR->RemoveHook(SH_GLOB_PLUGPTR, iface, pthisptroffs, \
SH_MFHCls(hookname)::HookManPubFunc, &tmp, post); \
} \
#define SH_SETUPCALLS(rettype, paramtypes, params) \
/* 1) Find the vfnptr */ \
using namespace ::SourceHook; \
@ -580,7 +695,7 @@ inline void SH_RELEASE_CALLCLASS_R(SourceHook::ISourceHook *shptr, SourceHook::C
} \
}
#define SH_CALL_ORIG(ifacetype, ifacefunc, rettype, paramtypes, params) \
#define SH_CALL_ORIG(rettype, paramtypes, params) \
if (status != MRES_SUPERCEDE) \
{ \
rettype (EmptyClass::*mfp)paramtypes; \
@ -594,10 +709,10 @@ inline void SH_RELEASE_CALLCLASS_R(SourceHook::ISourceHook *shptr, SourceHook::C
SH_GLOB_SHPTR->HookLoopEnd(); \
return status >= MRES_OVERRIDE ? override_ret : orig_ret;
#define SH_HANDLEFUNC(ifacetype, ifacefunc, paramtypes, params, rettype) \
#define SH_HANDLEFUNC(paramtypes, params, rettype) \
SH_SETUPCALLS(rettype, paramtypes, params) \
SH_CALL_HOOKS(pre, params) \
SH_CALL_ORIG(ifacetype, ifacefunc, rettype, paramtypes, params) \
SH_CALL_ORIG(rettype, paramtypes, params) \
SH_CALL_HOOKS(post, params) \
SH_RETURN()
@ -656,7 +771,7 @@ inline void SH_RELEASE_CALLCLASS_R(SourceHook::ISourceHook *shptr, SourceHook::C
} \
}
#define SH_CALL_ORIG_void(ifacetype, ifacefunc, paramtypes, params) \
#define SH_CALL_ORIG_void(paramtypes, params) \
if (status != MRES_SUPERCEDE) \
{ \
void (EmptyClass::*mfp)paramtypes; \
@ -667,26 +782,26 @@ inline void SH_RELEASE_CALLCLASS_R(SourceHook::ISourceHook *shptr, SourceHook::C
#define SH_RETURN_void() \
SH_GLOB_SHPTR->HookLoopEnd();
#define SH_HANDLEFUNC_void(ifacetype, ifacefunc, paramtypes, params) \
#define SH_HANDLEFUNC_void(paramtypes, params) \
SH_SETUPCALLS_void(paramtypes, params) \
SH_CALL_HOOKS_void(pre, params) \
SH_CALL_ORIG_void(ifacetype, ifacefunc, paramtypes, params) \
SH_CALL_ORIG_void(paramtypes, params) \
SH_CALL_HOOKS_void(post, params) \
SH_RETURN_void()
// Special vafmt handlers
#define SH_HANDLEFUNC_vafmt(ifacetype, ifacefunc, paramtypes, params_orig, params_plug, rettype) \
#define SH_HANDLEFUNC_vafmt(paramtypes, params_orig, params_plug, rettype) \
SH_SETUPCALLS(rettype, paramtypes, params_orig) \
SH_CALL_HOOKS(pre, params_plug) \
SH_CALL_ORIG(ifacetype, ifacefunc, rettype, paramtypes, params_orig) \
SH_CALL_ORIG(rettype, paramtypes, params_orig) \
SH_CALL_HOOKS(post, params_plug) \
SH_RETURN()
#define SH_HANDLEFUNC_void_vafmt(ifacetype, ifacefunc, paramtypes, params_orig, params_plug) \
#define SH_HANDLEFUNC_void_vafmt(paramtypes, params_orig, params_plug) \
SH_SETUPCALLS_void(paramtypes, params_orig) \
SH_CALL_HOOKS_void(pre, params_plug) \
SH_CALL_ORIG_void(ifacetype, ifacefunc, paramtypes, params_orig) \
SH_CALL_ORIG_void(paramtypes, params_orig) \
SH_CALL_HOOKS_void(post, params_plug) \
SH_RETURN_void()
@ -699,18 +814,27 @@ inline void SH_RELEASE_CALLCLASS_R(SourceHook::ISourceHook *shptr, SourceHook::C
(&ifacetype::ifacefunc))) \
typedef fastdelegate::FastDelegate@$@<@param%%|, @@, @rettype> FD; \
virtual rettype Func(@param%% p%%|, @) \
{ SH_HANDLEFUNC(ifacetype, ifacefunc, (@param%%|, @), (@p%%|, @), rettype); } \
SHINT_MAKE_GENERICSTUFF_END(ifacetype, ifacefunc, overload, #attr "|" #rettype @"|" #param%%| @, \
(static_cast<rettype (ifacetype::*)(@param%%|, @) attr>(&ifacetype::ifacefunc)))
{ SH_HANDLEFUNC((@param%%|, @), (@p%%|, @), rettype); } \
SHINT_MAKE_GENERICSTUFF_END(ifacetype, ifacefunc, overload, \
(static_cast<rettype (ifacetype::*)(@param%%|, @) attr>(&ifacetype::ifacefunc))) \
\
const int __SourceHook_ParamSizes_##ifacetype##ifacefunc##overload[] = { 0@, sizeof(param%%)@ }; \
::SourceHook::ProtoInfo SH_FHCls(ifacetype, ifacefunc, overload)::ms_Proto(sizeof(rettype), \
@$@, __SourceHook_ParamSizes_##ifacetype##ifacefunc##overload);
#define SH_DECL_HOOK@$@_void(ifacetype, ifacefunc, attr, overload@, param%%@) \
SHINT_MAKE_GENERICSTUFF_BEGIN(ifacetype, ifacefunc, overload, (static_cast<void (ifacetype::*)(@param%%|, @) attr> \
(&ifacetype::ifacefunc))) \
typedef fastdelegate::FastDelegate@$@<@param%%|, @> FD; \
virtual void Func(@param%% p%%|, @) \
{ SH_HANDLEFUNC_void(ifacetype, ifacefunc, (@param%%|, @), (@p%%|, @)); } \
SHINT_MAKE_GENERICSTUFF_END(ifacetype, ifacefunc, overload, #attr @"|" #param%%| @, \
(static_cast<void (ifacetype::*)(@param%%|, @) attr>(&ifacetype::ifacefunc)))
{ SH_HANDLEFUNC_void((@param%%|, @), (@p%%|, @)); } \
SHINT_MAKE_GENERICSTUFF_END(ifacetype, ifacefunc, overload, \
(static_cast<void (ifacetype::*)(@param%%|, @) attr>(&ifacetype::ifacefunc))) \
\
\
const int __SourceHook_ParamSizes_##ifacetype##ifacefunc##overload[] = { 0@, sizeof(param%%)@ }; \
::SourceHook::ProtoInfo SH_FHCls(ifacetype, ifacefunc, overload)::ms_Proto(0, \
@$@, __SourceHook_ParamSizes_##ifacetype##ifacefunc##overload);
#define SH_DECL_HOOK@$@_vafmt(ifacetype, ifacefunc, attr, overload, rettype@, param%%@) \
SHINT_MAKE_GENERICSTUFF_BEGIN(ifacetype, ifacefunc, overload, (static_cast<rettype (ifacetype::*)(@param%%|, @@, @const char *, ...) attr> \
@ -723,10 +847,14 @@ inline void SH_RELEASE_CALLCLASS_R(SourceHook::ISourceHook *shptr, SourceHook::C
va_start(ap, fmt); \
vsnprintf(buf, sizeof(buf), fmt, ap); \
va_end(ap); \
SH_HANDLEFUNC_vafmt(ifacetype, ifacefunc, (@param%%|, @@, @...), (@p%%|, @@, @"%s", buf), (@p%%|, @@, @buf), rettype); \
SH_HANDLEFUNC_vafmt((@param%%|, @@, @...), (@p%%|, @@, @"%s", buf), (@p%%|, @@, @buf), rettype); \
} \
SHINT_MAKE_GENERICSTUFF_END(ifacetype, ifacefunc, overload, #attr "|" #rettype @"|" #param%%| @ "|const char*|...", \
(static_cast<rettype (ifacetype::*)(@param%%|, @@, @const char *, ...) attr>(&ifacetype::ifacefunc)))
SHINT_MAKE_GENERICSTUFF_END(ifacetype, ifacefunc, overload, \
(static_cast<rettype (ifacetype::*)(@param%%|, @@, @const char *, ...) attr>(&ifacetype::ifacefunc))) \
\
const int __SourceHook_ParamSizes_##ifacetype##ifacefunc##overload[] = { -1@, sizeof(param%%)@ }; \
::SourceHook::ProtoInfo SH_FHCls(ifacetype, ifacefunc, overload)::ms_Proto(sizeof(rettype), \
@$@, __SourceHook_ParamSizes_##ifacetype##ifacefunc##overload);
#define SH_DECL_HOOK@$@_void_vafmt(ifacetype, ifacefunc, attr, overload@, param%%@) \
SHINT_MAKE_GENERICSTUFF_BEGIN(ifacetype, ifacefunc, overload, (static_cast<void (ifacetype::*)(@param%%|, @@, @const char *, ...) attr> \
@ -739,10 +867,36 @@ inline void SH_RELEASE_CALLCLASS_R(SourceHook::ISourceHook *shptr, SourceHook::C
va_start(ap, fmt); \
vsnprintf(buf, sizeof(buf), fmt, ap); \
va_end(ap); \
SH_HANDLEFUNC_void_vafmt(ifacetype, ifacefunc, (@param%%|, @@, @...), (@p%%|, @@, @"%s", buf), (@p%%|, @@, @buf)); \
SH_HANDLEFUNC_void_vafmt((@param%%|, @@, @...), (@p%%|, @@, @"%s", buf), (@p%%|, @@, @buf)); \
} \
SHINT_MAKE_GENERICSTUFF_END(ifacetype, ifacefunc, overload, #attr @"|" #param%%| @ "|const char*|...", \
(static_cast<void (ifacetype::*)(@param%%|, @@, @const char *, ...) attr>(&ifacetype::ifacefunc)))
SHINT_MAKE_GENERICSTUFF_END(ifacetype, ifacefunc, overload, \
(static_cast<void (ifacetype::*)(@param%%|, @@, @const char *, ...) attr>(&ifacetype::ifacefunc))) \
\
const int __SourceHook_ParamSizes_##ifacetype##ifacefunc##overload[] = { -1@, sizeof(param%%)@ }; \
::SourceHook::ProtoInfo SH_FHCls(ifacetype, ifacefunc, overload)::ms_Proto(0, \
@$@, __SourceHook_ParamSizes_##ifacetype##ifacefunc##overload);
#define SH_DECL_MANUALHOOK@$@(hookname, vtblidx, vtbloffs, thisptroffs, rettype@, param%%@) \
SHINT_MAKE_GENERICSTUFF_BEGIN_MANUAL(hookname, vtbloffs, vtblidx, thisptroffs) \
typedef fastdelegate::FastDelegate@$@<@param%%|, @@, @rettype> FD; \
virtual rettype Func(@param%% p%%|, @) \
{ SH_HANDLEFUNC((@param%%|, @), (@p%%|, @), rettype); } \
SHINT_MAKE_GENERICSTUFF_END_MANUAL(hookname, vtbloffs, vtblidx, thisptroffs) \
\
const int __SourceHook_ParamSizesM_##hookname[] = { 0@, sizeof(param%%)@ }; \
::SourceHook::ProtoInfo SH_MFHCls(hookname)::ms_Proto(sizeof(rettype), \
@$@, __SourceHook_ParamSizesM_##hookname);
#define SH_DECL_MANUALHOOK@$@_void(hookname, vtblidx, vtbloffs, thisptroffs@, param%%@) \
SHINT_MAKE_GENERICSTUFF_BEGIN_MANUAL(hookname, vtbloffs, vtblidx, thisptroffs) \
typedef fastdelegate::FastDelegate@$@<@param%%|, @> FD; \
virtual void Func(@param%% p%%|, @) \
{ SH_HANDLEFUNC_void((@param%%|, @), (@p%%|, @)); } \
SHINT_MAKE_GENERICSTUFF_END_MANUAL(hookname, vtbloffs, vtblidx, thisptroffs) \
\
const int __SourceHook_ParamSizesM_##hookname[] = { 0@, sizeof(param%%)@ }; \
::SourceHook::ProtoInfo SH_MFHCls(hookname)::ms_Proto(0, \
@$@, __SourceHook_ParamSizesM_##hookname);
@ENDARGS@

View File

@ -182,6 +182,93 @@ namespace SourceHook
}
}
void CSourceHookImpl::RemoveHookManager(Plugin plug, HookManagerPubFunc pubFunc)
{
// Moo!
CHookManagerInfo tmp;
if (pubFunc(HA_GetInfo, &tmp) != 0)
return;
HookManInfoList::iterator hmil_iter = FindHookMan(m_HookMans.begin(), m_HookMans.end(),
tmp.m_Proto, tmp.m_VtblOffs, tmp.m_VtblIdx);
if (hmil_iter == m_HookMans.end())
{
// Moo ?
return;
}
bool stillInUse = false;
List<RemoveHookInfo> hookstoremove;
// Iterate through all of the hook manager's hooks. Remove all hooks from the hookman's plugin.
#define TMP_CHECK_LIST(name, ispost) \
for (hook_iter = iface_iter->name.m_List.begin(); hook_iter != iface_iter->name.m_List.end(); ++hook_iter) \
if (hook_iter->plug == plug) \
hookstoremove.push_back(RemoveHookInfo(hook_iter->plug, iface_iter->m_Ptr, \
hook_iter->thisptr_offs, hmil_iter->m_Func, hook_iter->handler, ispost)); \
else \
stillInUse = true; \
for (CHookManagerInfo::VfnPtrListIter vfnptr_iter = hmil_iter->m_VfnPtrs.begin();
vfnptr_iter != hmil_iter->m_VfnPtrs.end(); ++vfnptr_iter)
{
for (CVfnPtr::IfaceListIter iface_iter = vfnptr_iter->m_Ifaces.begin();
iface_iter != vfnptr_iter->m_Ifaces.end(); ++iface_iter)
{
List<HookInfo>::iterator hook_iter;
TMP_CHECK_LIST(m_PreHooks, false);
TMP_CHECK_LIST(m_PostHooks, true);
}
}
#undef TMP_CHECK_LIST
for (List<RemoveHookInfo>::iterator rmiter = hookstoremove.begin(); rmiter != hookstoremove.end(); ++rmiter)
RemoveHook(*rmiter);
CHookManagerInfo info = *hmil_iter;
// Unlink the hook manager from the list
m_HookMans.erase(hmil_iter);
// If there were any hooks from other plugins, find a new suitable hook manager.
if (stillInUse)
{
// Find a suitable hook manager in an other plugin
HookManInfoList::iterator newHookMan = FindHookMan(m_HookMans.begin(), m_HookMans.end(),
info.m_Proto, info.m_VtblOffs, info.m_VtblIdx);
// This should _never_ happen.
// If there is a hook from an other plugin, the plugin must have provided a hook manager as well.
SH_ASSERT(newHookMan != m_HookMans.end(),
("Could not find a suitable hook manager in an other plugin!"));
// AddHook should make sure that every plugin only has _one_ hook manager for _one_ proto/vi/vo
SH_ASSERT(newHookMan->m_Plug != plug, ("New hook manager from same plugin!"));
// The first hook manager should be always used - so the new hook manager has to be empty
SH_ASSERT(newHookMan->m_VfnPtrs.empty(), ("New hook manager not empty!"));
// Move the vfnptrs from the old hook manager to the new one
newHookMan->m_VfnPtrs = info.m_VfnPtrs;
// Unregister the old one, register the new one
info.m_Func(HA_Unregister, NULL);
newHookMan->m_Func(HA_Register, &(*newHookMan));
// Go through all vfnptrs in this hookman and patch them to point to the new manager's handler!
// or whatever
for (CHookManagerInfo::VfnPtrListIter vfnptr_iter = newHookMan->m_VfnPtrs.begin();
vfnptr_iter != newHookMan->m_VfnPtrs.end(); ++vfnptr_iter)
{
// And DEREFERENCE newHookMan->m_HookfuncVfnptr!
// otherwise it will be executing the vtable... had to find out the hard way
*reinterpret_cast<void**>(vfnptr_iter->m_Ptr) = *reinterpret_cast<void**>(newHookMan->m_HookfuncVfnptr);
}
}
}
void CSourceHookImpl::CompleteShutdown()
{
List<RemoveHookInfo> hookstoremove;
@ -214,17 +301,22 @@ namespace SourceHook
bool CSourceHookImpl::AddHook(Plugin plug, void *iface, int thisptr_offs, HookManagerPubFunc myHookMan, ISHDelegate *handler, bool post)
{
void *adjustediface = reinterpret_cast<void*>(reinterpret_cast<char*>(iface) + thisptr_offs);
// 1) Get info about the hook manager
CHookManagerInfo tmp;
if (myHookMan(HA_GetInfo, &tmp) != 0)
return false;
void **cur_vtptr = *reinterpret_cast<void***>(
reinterpret_cast<char*>(adjustediface) + tmp.m_VtblOffs);
void *cur_vfnptr = reinterpret_cast<void*>(cur_vtptr + tmp.m_VtblIdx);
// Add the proposed hook manager to the _end_ of the list if the plugin doesn't have a hook manager
// with this proto/vo/vi registered
HookManInfoList::iterator hkmi_iter;
for (hkmi_iter = m_HookMans.begin(); hkmi_iter != m_HookMans.end(); ++hkmi_iter)
{
if (hkmi_iter->m_Plug == plug && strcmp(hkmi_iter->m_Proto, tmp.m_Proto) == 0 &&
if (hkmi_iter->m_Plug == plug && ProtosEquiv(hkmi_iter->m_Proto, tmp.m_Proto) &&
hkmi_iter->m_VtblOffs == tmp.m_VtblOffs && hkmi_iter->m_VtblIdx == tmp.m_VtblIdx)
break;
}
@ -241,14 +333,21 @@ namespace SourceHook
tmp.m_VtblOffs, tmp.m_VtblIdx);
SH_ASSERT(hookman != m_HookMans.end(), ("No hookman found - but if there was none, we've just added one!"));
// Check whether there is already an other hook manager for the same vfnptr but with other values before
// If yes, it means that the two are incompatible, so return false.
for (hkmi_iter = m_HookMans.begin(); hkmi_iter != m_HookMans.end(); ++hkmi_iter)
{
if (hkmi_iter != hookman)
{
if (hkmi_iter->m_VfnPtrs.find(cur_vfnptr) != hkmi_iter->m_VfnPtrs.end())
return false;
}
}
// Tell it to store the pointer if it's not already active
if (hookman->m_VfnPtrs.empty())
hookman->m_Func(HA_Register, &(*hookman));
void **cur_vtptr = *reinterpret_cast<void***>(
reinterpret_cast<char*>(adjustediface) + tmp.m_VtblOffs);
void *cur_vfnptr = reinterpret_cast<void*>(cur_vtptr + tmp.m_VtblIdx);
CHookManagerInfo::VfnPtrListIter vfnptr_iter = hookman->m_VfnPtrs.find(cur_vfnptr);
if (vfnptr_iter == hookman->m_VfnPtrs.end())
@ -486,7 +585,7 @@ namespace SourceHook
HookManInfoList::iterator hookmaniter;
for (hookmaniter = m_HookMans.begin(); hookmaniter != m_HookMans.end(); ++hookmaniter)
{
if (strcmp(hookmaniter->m_Proto, proto) == 0 && hookmaniter->m_VtblOffs == vtblofs &&
if (ProtosEquiv(hookmaniter->m_Proto, proto) && hookmaniter->m_VtblOffs == vtblofs &&
hookmaniter->m_VtblIdx == vtblidx)
break;
}
@ -658,6 +757,65 @@ namespace SourceHook
}
}
bool CSourceHookImpl::ProtosEquiv(const char *p1, const char *p2)
{
/*
Old protos look like this:
0_void:
"attrib"
1_void:
"attrib|param1_type"
2_void:
"attrib|param1_type|param2_type
0:
"attrib|ret_type"
1:
"attrib|ret_type|param1_type"
2:
"attrib|ret_type|param2_type"
New protos are in fact pointers to the ProtoInfo sturcture (see sourcehook.h for details)
Old protos _never_ begin with a null character
New protos _always_ begin with a null character
*/
if (*p1 && *p2) // Case1: Both old
{
// As in old versions
return strcmp(p1, p2) == 0;
}
else if (!*p1 && !*p2) // Case2: Both new
{
const ProtoInfo *pi1 = reinterpret_cast<const ProtoInfo*>(p1);
const ProtoInfo *pi2 = reinterpret_cast<const ProtoInfo*>(p2);
if (pi1->retTypeSize == pi2->retTypeSize &&
pi1->numOfParams == pi2->numOfParams)
{
// params[0] is 0 for "normal" functions and -1 for vararg functions
// params[1] is size of first parameter
// params[2] is size of second parameter ...
for (int i = 0; i <= pi1->numOfParams; ++i)
{
if (pi1->params[i] != pi2->params[i])
return false;
}
return true;
}
else
{
return false;
}
}
else // Case3: Mixed old/new
{
// Trust the user
return true;
}
}
////////////////////////////
// CHookManagerInfo
////////////////////////////
@ -686,6 +844,8 @@ namespace SourceHook
////////////////////////////
// If you get a crash here, the ptr passed is invalid
// This usually means a SH_DECL_MANUALHOOK* with wrong thisptroffs/vtbloffs/vtblidx
CSourceHookImpl::CVfnPtr::CVfnPtr(void *ptr) : m_Ptr(ptr), m_OrigEntry(*reinterpret_cast<void**>(ptr))
{
}

File diff suppressed because it is too large Load Diff

View File

@ -241,6 +241,8 @@ namespace SourceHook
void SetPluginPaused(Plugin plug, bool paused);
bool ProtosEquiv(const char *p1, const char *p2);
HookLoopInfoStack m_HLIStack;
public:
CSourceHookImpl();
@ -357,6 +359,14 @@ namespace SourceHook
void SetOrigRetPtr(const void *ptr); //!< Sets the original return pointer
void SetOverrideRetPtr(const void *ptr); //!< Sets the override result pointer
bool ShouldContinue(); //!< Returns false if the hook loop should exit
/**
* @brief Remove a hook manager. Auto-removes all hooks attached to it from plugin plug.
*
* @param plug The owner of the hook manager
* @param pubFunc The hook manager's info function
*/
virtual void RemoveHookManager(Plugin plug, HookManagerPubFunc pubFunc);
};
}

View File

@ -74,6 +74,7 @@ DO_TEST(ThisPtrOffs);
DO_TEST(PlugSys);
DO_TEST(Bail);
DO_TEST(Reentr);
DO_TEST(Manual);
int main(int argc, char *argv[])
{

View File

@ -268,6 +268,15 @@
<File
RelativePath=".\testlist.cpp">
</File>
<File
RelativePath=".\testmanual.cpp">
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
GeneratePreprocessedFile="0"/>
</FileConfiguration>
</File>
<File
RelativePath=".\testreentr.cpp">
</File>