Direct VP hooks, new SH_CALL

--HG--
extra : convert_revision : svn%3Ac2935e3e-5518-0410-8daf-afa5dab7d4e3/trunk%40389
This commit is contained in:
Pavol Marko 2007-05-12 08:08:01 +00:00
parent 499e6359f0
commit 50926ce4e6
13 changed files with 4136 additions and 3784 deletions

File diff suppressed because it is too large Load Diff

View File

@ -27,7 +27,9 @@
// ??? // ???
// 4 - addition of hook ids and vp hooks (with them, AddHookNew and RemoveHookNew) // 4 - addition of hook ids and vp hooks (with them, AddHookNew and RemoveHookNew)
// This is not a SH_IFACE_VERSION change so that old plugins continue working! // This is not a SH_IFACE_VERSION change so that old plugins continue working!
#define SH_IMPL_VERSION 4 // 5 - addition of direct vp hooks (new hook mode; from now on AddHookNew checks for
// invalid hookmode -> impl version won't have to change because of things like this)
#define SH_IMPL_VERSION 5
// Hookman version: // Hookman version:
// 1 - Support for recalls, performance optimisations // 1 - Support for recalls, performance optimisations
@ -490,7 +492,8 @@ namespace SourceHook
enum AddHookMode enum AddHookMode
{ {
Hook_Normal, Hook_Normal,
Hook_VP Hook_VP,
Hook_DVP
}; };
/** /**
@ -518,6 +521,22 @@ namespace SourceHook
* @param hookid The hook id (returned by AddHookNew) * @param hookid The hook id (returned by AddHookNew)
*/ */
virtual bool RemoveHookByID(Plugin plug, int hookid) = 0; virtual bool RemoveHookByID(Plugin plug, int hookid) = 0;
/**
* @brief Makes sure that hooks are going to be ignored on the next call of vfnptr
*
* @param plug The unique identifier of the plugin that calls this function
* @param vfnptr The virtual function pointer of the function in question
*/
virtual void SetIgnoreHooks(Plugin plug, void *vfnptr) = 0;
/**
* @brief Reverses SetIgnoreHooks' effect
*
* @param plug The unique identifier of the plugin that calls this function
* @param vfnptr The virtual function pointer of the function in question
*/
virtual void ResetIgnoreHooks(Plugin plug, void *vfnptr) = 0;
}; };
// For META_RESULT_ORIG_RET and META_RESULT_OVERRIDE_RET: // For META_RESULT_ORIG_RET and META_RESULT_OVERRIDE_RET:
@ -548,6 +567,33 @@ namespace SourceHook
return &ref; return &ref;
} }
}; };
// For source-level compatibility
template <class T> struct LegacyCallClass
{
T *ptr;
LegacyCallClass(T *p) : ptr(p)
{
}
operator T*()
{
return ptr;
}
};
template <class T>
LegacyCallClass<T> *GetLegacyCallClass(T *p)
{
return new LegacyCallClass<T>(p);
}
template <class T>
void ReleaseLegacyCallClass(LegacyCallClass<T> *p)
{
delete p;
}
} }
/************************************************************************/ /************************************************************************/
@ -652,31 +698,6 @@ namespace SourceHook
RETURN_META_VALUE(MRES_SUPERCEDE, (thisptr->*(u.mfp)) newparams); \ RETURN_META_VALUE(MRES_SUPERCEDE, (thisptr->*(u.mfp)) newparams); \
} while (0) } while (0)
/**
* @brief Get/generate callclass for an interface pointer
*
* @param ifaceptr The interface pointer
*/
template<class ifacetype>
inline SourceHook::CallClass<ifacetype> *SH_GET_CALLCLASS_R(SourceHook::ISourceHook *shptr, ifacetype *ptr)
{
return reinterpret_cast<SourceHook::CallClass<ifacetype>*>(
shptr->GetCallClass(reinterpret_cast<void*>(ptr), sizeof(ifacetype)));
}
template<class ifacetype>
inline SourceHook::CallClass<ifacetype> *SH_GET_MCALLCLASS_R(SourceHook::ISourceHook *shptr, ifacetype *ptr, int ifacesize)
{
return reinterpret_cast<SourceHook::CallClass<ifacetype>*>(
shptr->GetCallClass(reinterpret_cast<void*>(ptr), ifacesize));
}
template<class ifacetype>
inline void SH_RELEASE_CALLCLASS_R(SourceHook::ISourceHook *shptr, SourceHook::CallClass<ifacetype> *ptr)
{
shptr->ReleaseCallClass(reinterpret_cast<SourceHook::GenericCallClass*>(ptr));
}
#define SH_MANUALHOOK_RECONFIGURE(hookname, pvtblindex, pvtbloffs, pthisptroffs) \ #define SH_MANUALHOOK_RECONFIGURE(hookname, pvtblindex, pvtbloffs, pthisptroffs) \
do { \ do { \
SH_GLOB_SHPTR->RemoveHookManager(SH_GLOB_PLUGPTR, SH_MFHCls(hookname)::HookManPubFunc); \ SH_GLOB_SHPTR->RemoveHookManager(SH_GLOB_PLUGPTR, SH_MFHCls(hookname)::HookManPubFunc); \
@ -685,9 +706,13 @@ inline void SH_RELEASE_CALLCLASS_R(SourceHook::ISourceHook *shptr, SourceHook::C
SH_MFHCls(hookname)::ms_MFI.vtbloffs = pvtbloffs; \ SH_MFHCls(hookname)::ms_MFI.vtbloffs = pvtbloffs; \
} while (0) } while (0)
#define SH_GET_CALLCLASS(ptr) SH_GET_CALLCLASS_R(SH_GLOB_SHPTR, ptr) // For source-level compatibility
#define SH_GET_MCALLCLASS(ptr, size) SH_GET_MCALLCLASS_R(SH_GLOB_SHPTR, reinterpret_cast<SourceHook::EmptyClass*>(ptr), size) #define CallClass LegacyCallClass
#define SH_RELEASE_CALLCLASS(ptr) SH_RELEASE_CALLCLASS_R(SH_GLOB_SHPTR, ptr) #define ManualCallClass LegacyCallClass<SourceHook::EmptyClass>
#define SH_GET_CALLCLASS(ptr) SourceHook::GetLegacyCallClass(ptr)
#define SH_GET_MCALLCLASS(ptr, size) SourceHook::GetLegacyCallClass(reinterpret_cast<SourceHook::EmptyClass*>(ptr))
#define SH_RELEASE_CALLCLASS(ptr) SourceHook::ReleaseLegacyCallClass(ptr)
// New ADD / REMOVE macros. // New ADD / REMOVE macros.
#define SH_STATIC(func) fastdelegate::MakeDelegate(func) #define SH_STATIC(func) fastdelegate::MakeDelegate(func)
@ -709,10 +734,17 @@ inline void SH_RELEASE_CALLCLASS_R(SourceHook::ISourceHook *shptr, SourceHook::C
#define SH_ADD_VPHOOK(ifacetype, ifacefunc, ifaceptr, handler, post) \ #define SH_ADD_VPHOOK(ifacetype, ifacefunc, ifaceptr, handler, post) \
__SourceHook_FHVPAdd##ifacetype##ifacefunc((void*)SourceHook::implicit_cast<ifacetype*>(ifaceptr), \ __SourceHook_FHVPAdd##ifacetype##ifacefunc((void*)SourceHook::implicit_cast<ifacetype*>(ifaceptr), \
post, handler) post, handler, false)
#define SH_ADD_DVPHOOK(ifacetype, ifacefunc, vtableptr, handler, post) \
__SourceHook_FHVPAdd##ifacetype##ifacefunc(reinterpret_cast<void*>(vtableptr), \
post, handler, true)
#define SH_ADD_MANUALVPHOOK(hookname, ifaceptr, handler, post) \ #define SH_ADD_MANUALVPHOOK(hookname, ifaceptr, handler, post) \
__SourceHook_FHMVPAdd##hookname(reinterpret_cast<void*>(ifaceptr), post, handler) __SourceHook_FHMVPAdd##hookname(reinterpret_cast<void*>(ifaceptr), post, handler, false)
#define SH_ADD_MANUALDVPHOOK(hookname, vtableptr, handler, post) \
__SourceHook_FHMVPAdd##hookname(reinterpret_cast<void*>(vtableptr), post, handler, true)
#define SH_REMOVE_HOOK_ID(hookid) \ #define SH_REMOVE_HOOK_ID(hookid) \
(SH_GLOB_SHPTR->RemoveHookByID(SH_GLOB_PLUGPTR, hookid)) (SH_GLOB_SHPTR->RemoveHookByID(SH_GLOB_PLUGPTR, hookid))
@ -844,7 +876,7 @@ inline void SH_RELEASE_CALLCLASS_R(SourceHook::ISourceHook *shptr, SourceHook::C
new CSHDelegate<SH_FHCls(ifacetype,ifacefunc,overload)::FD>(handler), post); \ new CSHDelegate<SH_FHCls(ifacetype,ifacefunc,overload)::FD>(handler), post); \
} \ } \
int __SourceHook_FHVPAdd##ifacetype##ifacefunc(void *iface, bool post, \ int __SourceHook_FHVPAdd##ifacetype##ifacefunc(void *iface, bool post, \
SH_FHCls(ifacetype,ifacefunc,overload)::FD handler) \ SH_FHCls(ifacetype,ifacefunc,overload)::FD handler, bool direct) \
{ \ { \
using namespace ::SourceHook; \ using namespace ::SourceHook; \
MemFuncInfo mfi = {true, -1, 0, 0}; \ MemFuncInfo mfi = {true, -1, 0, 0}; \
@ -852,8 +884,9 @@ inline void SH_RELEASE_CALLCLASS_R(SourceHook::ISourceHook *shptr, SourceHook::C
if (mfi.thisptroffs < 0 || !mfi.isVirtual) \ if (mfi.thisptroffs < 0 || !mfi.isVirtual) \
return false; /* No non-virtual functions / virtual inheritance supported */ \ return false; /* No non-virtual functions / virtual inheritance supported */ \
\ \
return SH_GLOB_SHPTR->AddHookNew(SH_GLOB_PLUGPTR, ::SourceHook::ISourceHook::Hook_VP, iface, mfi.thisptroffs, \ return SH_GLOB_SHPTR->AddHookNew(SH_GLOB_PLUGPTR, \
SH_FHCls(ifacetype,ifacefunc,overload)::HookManPubFunc, \ direct ? ::SourceHook::ISourceHook::Hook_DVP : ::SourceHook::ISourceHook::Hook_VP, \
iface, mfi.thisptroffs, SH_FHCls(ifacetype,ifacefunc,overload)::HookManPubFunc, \
new CSHDelegate<SH_FHCls(ifacetype,ifacefunc,overload)::FD>(handler), post); \ new CSHDelegate<SH_FHCls(ifacetype,ifacefunc,overload)::FD>(handler), post); \
} \ } \
bool __SourceHook_FHRemove##ifacetype##ifacefunc(void *iface, bool post, \ bool __SourceHook_FHRemove##ifacetype##ifacefunc(void *iface, bool post, \
@ -936,10 +969,11 @@ inline void SH_RELEASE_CALLCLASS_R(SourceHook::ISourceHook *shptr, SourceHook::C
new ::SourceHook::CSHDelegate<SH_MFHCls(hookname)::FD>(handler), post); \ new ::SourceHook::CSHDelegate<SH_MFHCls(hookname)::FD>(handler), post); \
} \ } \
int __SourceHook_FHMVPAdd##hookname(void *iface, bool post, \ int __SourceHook_FHMVPAdd##hookname(void *iface, bool post, \
SH_MFHCls(hookname)::FD handler) \ SH_MFHCls(hookname)::FD handler, bool direct) \
{ \ { \
return SH_GLOB_SHPTR->AddHookNew(SH_GLOB_PLUGPTR, ::SourceHook::ISourceHook::Hook_VP, iface, pthisptroffs, \ return SH_GLOB_SHPTR->AddHookNew(SH_GLOB_PLUGPTR, \
SH_MFHCls(hookname)::HookManPubFunc, \ direct ? ::SourceHook::ISourceHook::Hook_DVP : ::SourceHook::ISourceHook::Hook_VP, \
iface, pthisptroffs, SH_MFHCls(hookname)::HookManPubFunc, \
new ::SourceHook::CSHDelegate<SH_MFHCls(hookname)::FD>(handler), post); \ new ::SourceHook::CSHDelegate<SH_MFHCls(hookname)::FD>(handler), post); \
} \ } \
bool __SourceHook_FHMRemove##hookname(void *iface, bool post, \ bool __SourceHook_FHMRemove##hookname(void *iface, bool post, \
@ -1219,136 +1253,103 @@ inline void SH_RELEASE_CALLCLASS_R(SourceHook::ISourceHook *shptr, SourceHook::C
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// SH_CALL // SH_CALL
#if SH_COMP == SH_COMP_MSVC #define SH_MAKE_EXECUTABLECLASS_OB(call, prms) \
# define SH_MAKE_EXECUTABLECLASS_OB(call, prms) \
{ \ { \
using namespace ::SourceHook; \ using namespace ::SourceHook; \
MemFuncInfo mfi = {true, -1, 0, 0}; \
GetFuncInfo(m_CC->GetThisPtr(), m_MFP, mfi); \
void *origfunc = m_CC->GetOrigFunc(mfi.thisptroffs + mfi.vtbloffs, mfi.vtblindex); \
if (!origfunc) \
return (m_CC->GetThisPtr()->*m_MFP)call; \
\ \
/* It's hooked. Call the original function. */ \ SH_GLOB_SHPTR->SetIgnoreHooks(SH_GLOB_PLUGPTR, m_VfnPtr); \
union \ RetType tmpret = (m_ThisPtr->*m_MFP)call; \
{ \ SH_GLOB_SHPTR->ResetIgnoreHooks(SH_GLOB_PLUGPTR, m_VfnPtr); \
RetType(EmptyClass::*mfpnew)prms; \ return tmpret; \
void *addr; \
} u; \
u.addr = origfunc; \
\
void *adjustedthisptr = reinterpret_cast<void*>(reinterpret_cast<char*>(m_CC->GetThisPtr()) + mfi.thisptroffs); \
return (reinterpret_cast<EmptyClass*>(adjustedthisptr)->*u.mfpnew)call; \
} }
# define SH_MAKE_MEXECUTABLECLASS_OB(call, prms) \ #define SH_MAKE_EXECUTABLECLASS_OB_void(call, prms) \
{ \ { \
using namespace ::SourceHook; \ using namespace ::SourceHook; \
char *adjustedthisptr = reinterpret_cast<char*>(m_CC->GetThisPtr()) + m_ThisPtrOffs; \
union \
{ \
RetType(EmptyClass::*mfpnew)prms; \
void *addr; \
} u; \
u.addr = m_CC->GetOrigFunc(m_ThisPtrOffs + m_VtblOffs, m_VtblIdx); \
if (!u.addr) \
u.addr = (*reinterpret_cast<void***>(adjustedthisptr + m_VtblOffs))[m_VtblIdx]; \
\ \
return (reinterpret_cast<EmptyClass*>(adjustedthisptr)->*u.mfpnew)call; \ SH_GLOB_SHPTR->SetIgnoreHooks(SH_GLOB_PLUGPTR, m_VfnPtr); \
(m_ThisPtr->*m_MFP)call; \
SH_GLOB_SHPTR->ResetIgnoreHooks(SH_GLOB_PLUGPTR, m_VfnPtr); \
} }
#elif SH_COMP == SH_COMP_GCC
# define SH_MAKE_EXECUTABLECLASS_OB(call, prms) \
{ \
using namespace ::SourceHook; \
MemFuncInfo mfi = {true, -1, 0, 0}; \
GetFuncInfo(m_CC->GetThisPtr(), m_MFP, mfi); \
void *origfunc = m_CC->GetOrigFunc(mfi.thisptroffs + mfi.vtbloffs, mfi.vtblindex); \
if (!origfunc) \
return (m_CC->GetThisPtr()->*m_MFP)call; \
\
/* It's hooked. Call the original function. */ \
union \
{ \
RetType(EmptyClass::*mfpnew)prms; \
struct \
{ \
void *addr; \
intptr_t adjustor; \
} s; \
} u; \
u.s.addr = origfunc; \
u.s.adjustor = mfi.thisptroffs; \
\
return (reinterpret_cast<EmptyClass*>(m_CC->GetThisPtr())->*u.mfpnew)call; \
}
# define SH_MAKE_MEXECUTABLECLASS_OB(call, prms) \
{ \
using namespace ::SourceHook; \
char *thisptr = reinterpret_cast<char*>(m_CC->GetThisPtr()); \
union \
{ \
RetType(EmptyClass::*mfpnew)prms; \
struct { \
void *addr; \
intptr_t adjustor; \
} s; \
} u; \
u.s.addr = m_CC->GetOrigFunc(m_ThisPtrOffs + m_VtblOffs, m_VtblIdx); \
if (!u.s.addr) \
u.s.addr = (*reinterpret_cast<void***>(thisptr + m_ThisPtrOffs + m_VtblOffs))[m_VtblIdx]; \
\
u.s.adjustor = m_ThisPtrOffs; \
return (reinterpret_cast<EmptyClass*>(thisptr)->*u.mfpnew)call; \
}
#endif
namespace SourceHook namespace SourceHook
{ {
@[$1,0,$a: // Call Class Wrapper!
// Support for $1 arguments template <class T> struct CCW
template<class CCType, class MFPType, class RetType@[$2,1,$1:, class Param$2@]> class ExecutableClass$1
{ {
CCType *m_CC; typedef T type;
MFPType m_MFP;
public:
ExecutableClass$1(CCType *cc, MFPType mfp) : m_CC(cc), m_MFP(mfp) { }
RetType operator()(@[$2,1,$1|, :Param$2 p$2@]) const // Get Real Pointer!
SH_MAKE_EXECUTABLECLASS_OB((@[$2,1,$1|, :p$2@]), (@[$2,1,$1|, :Param$2@])) static inline T *GRP(T *p)
{
@[$2,$1+1,$a: return p;
template <@[$3,$1+1,$2|, :class Param$3@]> RetType operator()(@[$3,1,$2|, :Param$3 p$3@]) const }
SH_MAKE_EXECUTABLECLASS_OB((@[$3,1,$2|, :p$3@]), (@[$3,1,$2|, :Param$3@]))
@]
}; };
template <class RetType@[$2,1,$1:, class Param$2@]> class MExecutableClass$1 template <class T> struct CCW< LegacyCallClass<T> >
{ {
ManualCallClass *m_CC; typedef T type;
int m_ThisPtrOffs;
int m_VtblIdx; // Get Real Pointer!
int m_VtblOffs; static inline T *GRP(LegacyCallClass<T> *p)
{
return p->ptr;
}
};
@[$1,0,$a:
// Support for $1 arguments
template<class ObjType, class MFP, class RetType@[$2,1,$1:, class Param$2@]> class ExecutableClass$1
{
ObjType *m_ThisPtr;
void *m_VfnPtr;
MFP m_MFP;
public: public:
MExecutableClass$1(ManualCallClass *cc, int vtbloffs, int vtblidx, int thisptroffs) : m_CC(cc), ExecutableClass$1(ObjType *tp, MFP mfp, void *vp) : m_ThisPtr(tp), m_MFP(mfp), m_VfnPtr(vp) { }
m_ThisPtrOffs(thisptroffs), m_VtblIdx(vtblidx), m_VtblOffs(vtbloffs) { }
RetType operator()(@[$2,1,$1|, :Param$2 p$2@]) const RetType operator()(@[$2,1,$1|, :Param$2 p$2@]) const
SH_MAKE_MEXECUTABLECLASS_OB((@[$2,1,$1|, :p$2@]), (@[$2,1,$1|, :Param$2@])) SH_MAKE_EXECUTABLECLASS_OB((@[$2,1,$1|, :p$2@]), (@[$2,1,$1|, :Param$2@]))
@[$2,$1+1,$a: @[$2,$1+1,$a:
template <@[$3,$1+1,$2|, :class Param$3@]> RetType operator()(@[$3,1,$2|, :Param$3 p$3@]) const template <@[$3,$1+1,$2|, :class Param$3@]> RetType operator()(@[$3,1,$2|, :Param$3 p$3@]) const
SH_MAKE_MEXECUTABLECLASS_OB((@[$3,1,$2|, :p$3@]), (@[$3,1,$2|, :Param$3@])) SH_MAKE_EXECUTABLECLASS_OB((@[$3,1,$2|, :p$3@]), (@[$3,1,$2|, :Param$3@]))
@] @]
}; };
template<class ObjType, class MFP@[$2,1,$1:, class Param$2@]> class ExecutableClass$1<ObjType, MFP, void@[$2,1,$1:, Param$2@]>
{
ObjType *m_ThisPtr;
void *m_VfnPtr;
MFP m_MFP;
public:
ExecutableClass$1(ObjType *tp, MFP mfp, void *vp) : m_ThisPtr(tp), m_MFP(mfp), m_VfnPtr(vp) { }
void operator()(@[$2,1,$1|, :Param$2 p$2@]) const
SH_MAKE_EXECUTABLECLASS_OB_void((@[$2,1,$1|, :p$2@]), (@[$2,1,$1|, :Param$2@]))
@[$2,$1+1,$a:
template <@[$3,$1+1,$2|, :class Param$3@]> void operator()(@[$3,1,$2|, :Param$3 p$3@]) const
SH_MAKE_EXECUTABLECLASS_OB_void((@[$3,1,$2|, :p$3@]), (@[$3,1,$2|, :Param$3@]))
@]
};
@] @]
} }
#define SH__CALL_GET_VFNPTR_NORMAL \
using namespace ::SourceHook; \
MemFuncInfo mfi = {true, -1, 0, 0}; \
GetFuncInfo(CCW<Y>::GRP(ptr), mfp, mfi); \
void *vfnptr = reinterpret_cast<void*>( \
*reinterpret_cast<void***>(reinterpret_cast<char*>(ptr) + mfi.thisptroffs + mfi.vtbloffs) + mfi.vtblindex);
#define SH__CALL_GET_VFNPTR_MANUAL \
using namespace ::SourceHook; \
void *vfnptr = reinterpret_cast<void*>( \
*reinterpret_cast<void***>( (reinterpret_cast<char*>(CCW<Y>::GRP(ptr)) + thisptroffs + vtbloffs) ) + vtblidx); \
/* patch mfp */ \
*reinterpret_cast<void**>(&mfp) = *reinterpret_cast<void**>(vfnptr);
// SH_CALL needs to deduce the return type -> it uses templates and function overloading // SH_CALL needs to deduce the return type -> it uses templates and function overloading
// That's why SH_CALL takes two parameters: "mfp2" of type RetType(X::*mfp)(params), and "mfp" of type MFP // That's why SH_CALL takes two parameters: "mfp2" of type RetType(X::*mfp)(params), and "mfp" of type MFP
// The only purpose of the mfp2 parameter is to extract the return type // The only purpose of the mfp2 parameter is to extract the return type
@ -1356,24 +1357,28 @@ namespace SourceHook
@[$1,0,$a: @[$1,0,$a:
// Support for $1 arguments // Support for $1 arguments
template <class X, class Y, class MFP, class RetType@[$2,1,$1:, class Param$2@]> template <class X, class Y, class MFP, class RetType@[$2,1,$1:, class Param$2@]>
SourceHook::ExecutableClass$1<SourceHook::CallClass<Y>, MFP, RetType@[$2,1,$1:, Param$2@]> SourceHook::ExecutableClass$1<typename SourceHook::CCW<Y>::type, MFP, RetType@[$2,1,$1:, Param$2@]>
SH_CALL2(SourceHook::CallClass<Y> *ptr, MFP mfp, RetType(X::*mfp2)(@[$2,1,$1|, :Param$2@])) SH_CALL2(Y *ptr, MFP mfp, RetType(X::*mfp2)(@[$2,1,$1|, :Param$2@]))
{ {
return SourceHook::ExecutableClass$1<SourceHook::CallClass<Y>, MFP, RetType@[$2,1,$1:, Param$2@]>(ptr, mfp); SH__CALL_GET_VFNPTR_NORMAL
return SourceHook::ExecutableClass$1<typename CCW<Y>::type, MFP, RetType@[$2,1,$1:, Param$2@]>(CCW<Y>::GRP(ptr), mfp, vfnptr);
} }
template <class X, class Y, class MFP, class RetType@[$2,1,$1:, class Param$2@]> template <class X, class Y, class MFP, class RetType@[$2,1,$1:, class Param$2@]>
SourceHook::ExecutableClass$1<SourceHook::CallClass<Y>, MFP, RetType@[$2,1,$1:, Param$2@]> SourceHook::ExecutableClass$1<typename SourceHook::CCW<Y>::type, MFP, RetType@[$2,1,$1:, Param$2@]>
SH_CALL2(SourceHook::CallClass<Y> *ptr, MFP mfp, RetType(X::*mfp2)(@[$2,1,$1|, :Param$2@])const) SH_CALL2(Y *ptr, MFP mfp, RetType(X::*mfp2)(@[$2,1,$1|, :Param$2@])const)
{ {
return SourceHook::ExecutableClass$1<SourceHook::CallClass<Y>, MFP, RetType@[$2,1,$1:, Param$2@]>(ptr, mfp); SH__CALL_GET_VFNPTR_NORMAL
return SourceHook::ExecutableClass$1<typename CCW<Y>::type, MFP, RetType@[$2,1,$1:, Param$2@]>(CCW<Y>::GRP(ptr), mfp, vfnptr);
} }
template <class X, class RetType@[$2,1,$1:, class Param$2@]> template <class X, class Y, class MFP, class RetType@[$2,1,$1:, class Param$2@]>
SourceHook::MExecutableClass$1<RetType@[$2,1,$1:, Param$2@]> SourceHook::ExecutableClass$1<SourceHook::EmptyClass, MFP, RetType@[$2,1,$1:, Param$2@]>
SH_MCALL2(SourceHook::ManualCallClass *ptr, RetType(X::*mfp)(@[$2,1,$1|, :Param$2@]), int vtblidx, int vtbloffs, int thisptroffs) SH_MCALL3(Y *ptr, MFP mfp, RetType(X::*mfp2)(@[$2,1,$1|, :Param$2@]), int vtblidx, int vtbloffs, int thisptroffs)
{ {
return SourceHook::MExecutableClass$1<RetType@[$2,1,$1:, Param$2@]>(ptr, vtbloffs, vtblidx, thisptroffs); SH__CALL_GET_VFNPTR_MANUAL
return SourceHook::ExecutableClass$1<EmptyClass, MFP, RetType@[$2,1,$1:, Param$2@]>(
reinterpret_cast<SourceHook::EmptyClass*>(CCW<Y>::GRP(ptr)), mfp, vfnptr);
} }
@] @]
@ -1383,17 +1388,19 @@ SH_MCALL2(SourceHook::ManualCallClass *ptr, RetType(X::*mfp)(@[$2,1,$1|, :Param$
@[$1,0,$a: @[$1,0,$a:
// Support for $1 arguments // Support for $1 arguments
template <class X, class Y, class MFP, class RetType@[$2,1,$1:, class Param$2@]> template <class X, class Y, class MFP, class RetType@[$2,1,$1:, class Param$2@]>
SourceHook::ExecutableClass$1<SourceHook::CallClass<Y>, MFP, RetType@[$2,1,$1:, Param$2@]> SourceHook::ExecutableClass$1<typename SourceHook::CCW<Y>::type, MFP, RetType@[$2,1,$1:, Param$2@]>
SH_CALL2(SourceHook::CallClass<Y> *ptr, MFP mfp, RetType(X::*mfp2)(@[$2,1,$1|, :Param$2@]@[$1!=0:, @]...)) SH_CALL2(Y *ptr, MFP mfp, RetType(X::*mfp2)(@[$2,1,$1|, :Param$2@]@[$1!=0:, @]...))
{ {
return SourceHook::ExecutableClass$1<SourceHook::CallClass<Y>, MFP, RetType@[$2,1,$1:, Param$2@]>(ptr, mfp); SH__CALL_GET_VFNPTR_NORMAL
return SourceHook::ExecutableClass$1<typename CCW<Y>::type, MFP, RetType@[$2,1,$1:, Param$2@]>(CCW<Y>::GRP(ptr), mfp, vfnptr);
} }
template <class X, class Y, class MFP, class RetType@[$2,1,$1:, class Param$2@]> template <class X, class Y, class MFP, class RetType@[$2,1,$1:, class Param$2@]>
SourceHook::ExecutableClass$1<SourceHook::CallClass<Y>, MFP, RetType@[$2,1,$1:, Param$2@]> SourceHook::ExecutableClass$1<typename SourceHook::CCW<Y>::type, MFP, RetType@[$2,1,$1:, Param$2@]>
SH_CALL2(SourceHook::CallClass<Y> *ptr, MFP mfp, RetType(X::*mfp2)(@[$2,1,$1|, :Param$2@]@[$1!=0:, @]...)const) SH_CALL2(Y *ptr, MFP mfp, RetType(X::*mfp2)(@[$2,1,$1|, :Param$2@]@[$1!=0:, @]...)const)
{ {
return SourceHook::ExecutableClass$1<SourceHook::CallClass<Y>, MFP, RetType@[$2,1,$1:, Param$2@]>(ptr, mfp); SH__CALL_GET_VFNPTR_NORMAL
return SourceHook::ExecutableClass$1<typename CCW<Y>::type, MFP, RetType@[$2,1,$1:, Param$2@]>(CCW<Y>::GRP(ptr), mfp, vfnptr);
} }
@] @]
@ -1401,6 +1408,7 @@ SH_CALL2(SourceHook::CallClass<Y> *ptr, MFP mfp, RetType(X::*mfp2)(@[$2,1,$1|, :
#endif #endif
#define SH_CALL(ptr, mfp) SH_CALL2((ptr), (mfp), (mfp)) #define SH_CALL(ptr, mfp) SH_CALL2((ptr), (mfp), (mfp))
#define SH_MCALL2(ptr, mfp, vtblidx, vtbloffs, thisptroffs) SH_MCALL3((ptr), (mfp), (mfp), (vtblidx), (vtbloffs), (thisptroffs))
#define SH_MCALL(ptr, mhookname) SH_MCALL2((ptr), SH_MFHCls(mhookname)::ECMFP(), SH_MFHCls(mhookname)::ms_MFI.vtblindex, \ #define SH_MCALL(ptr, mhookname) SH_MCALL2((ptr), SH_MFHCls(mhookname)::ECMFP(), SH_MFHCls(mhookname)::ms_MFI.vtblindex, \
SH_MFHCls(mhookname)::ms_MFI.vtbloffs, SH_MFHCls(mhookname)::ms_MFI.thisptroffs) SH_MFHCls(mhookname)::ms_MFI.vtbloffs, SH_MFHCls(mhookname)::ms_MFI.thisptroffs)

View File

@ -39,7 +39,7 @@ namespace SourceHook
return -1; return -1;
return 0; return 0;
} }
CSourceHookImpl::CSourceHookImpl() CSourceHookImpl::CSourceHookImpl() : m_OneIgnore(NULL), m_IgnoreActive(false)
{ {
} }
CSourceHookImpl::~CSourceHookImpl() CSourceHookImpl::~CSourceHookImpl()
@ -286,7 +286,12 @@ namespace SourceHook
int CSourceHookImpl::AddHookNew(Plugin plug, AddHookMode mode, void *iface, int thisptr_offs, HookManagerPubFunc myHookMan, int CSourceHookImpl::AddHookNew(Plugin plug, AddHookMode mode, void *iface, int thisptr_offs, HookManagerPubFunc myHookMan,
ISHDelegate *handler, bool post) ISHDelegate *handler, bool post)
{ {
void *adjustediface = reinterpret_cast<void*>(reinterpret_cast<char*>(iface) + thisptr_offs); void *adjustediface = NULL;
void **cur_vtptr = NULL;
void *cur_vfnptr = NULL;
if (mode != Hook_Normal && mode != Hook_VP && mode != Hook_DVP)
return 0;
// 1) Get info about the hook manager // 1) Get info about the hook manager
CHookManagerInfo tmp; CHookManagerInfo tmp;
@ -298,10 +303,6 @@ namespace SourceHook
CHookManagerContainer::HMCI hmci(tmp.m_Proto, tmp.m_VtblOffs, tmp.m_VtblIdx); CHookManagerContainer::HMCI hmci(tmp.m_Proto, tmp.m_VtblOffs, tmp.m_VtblIdx);
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 container if not already added // Add the container if not already added
HookManContList::iterator hmcl_iter = m_HookMans.find(hmci); HookManContList::iterator hmcl_iter = m_HookMans.find(hmci);
if (hmcl_iter == m_HookMans.end()) if (hmcl_iter == m_HookMans.end())
@ -336,12 +337,33 @@ namespace SourceHook
if (hookman->m_VfnPtrs.empty()) if (hookman->m_VfnPtrs.empty())
hookman->m_Func(HA_Register, &(*hookman)); hookman->m_Func(HA_Register, &(*hookman));
// find vfnptr
if (mode == Hook_Normal || mode == Hook_VP)
{
adjustediface = reinterpret_cast<void*>(reinterpret_cast<char*>(iface) + thisptr_offs);
cur_vtptr = *reinterpret_cast<void***>(
reinterpret_cast<char*>(adjustediface) + tmp.m_VtblOffs);
cur_vfnptr = reinterpret_cast<void*>(cur_vtptr + tmp.m_VtblIdx);
// For Hook_VP, adjustediface will be set to NULL later
// because we first have to patch callclasses (callclasses are deprecated but left in for backwards compat)
}
else if (mode == Hook_DVP)
{
adjustediface = NULL;
cur_vtptr = reinterpret_cast<void**>(iface);
cur_vfnptr = cur_vtptr + tmp.m_VtblIdx;
}
CHookManagerInfo::VfnPtrListIter vfnptr_iter = hookman->m_VfnPtrs.find(cur_vfnptr); CHookManagerInfo::VfnPtrListIter vfnptr_iter = hookman->m_VfnPtrs.find(cur_vfnptr);
if (vfnptr_iter == hookman->m_VfnPtrs.end()) if (vfnptr_iter == hookman->m_VfnPtrs.end())
{ {
// Add a new one // Add a new one
CVfnPtr vfp(cur_vfnptr); CVfnPtr vfp(cur_vfnptr, &m_OneIgnore);
// Alter vtable entry // Alter vtable entry
if (!SetMemAccess(cur_vtptr, sizeof(void*) * (tmp.m_VtblIdx + 1), SH_MEM_READ | SH_MEM_WRITE)) if (!SetMemAccess(cur_vtptr, sizeof(void*) * (tmp.m_VtblIdx + 1), SH_MEM_READ | SH_MEM_WRITE))
@ -885,6 +907,16 @@ namespace SourceHook
} }
} }
void CSourceHookImpl::SetIgnoreHooks(Plugin plug, void *vfnptr)
{
m_OneIgnore = vfnptr;
}
void CSourceHookImpl::ResetIgnoreHooks(Plugin plug, void *vfnptr)
{
m_OneIgnore = NULL;
}
//////////////////////////// ////////////////////////////
// CCallClassImpl // CCallClassImpl
//////////////////////////// ////////////////////////////
@ -980,7 +1012,8 @@ namespace SourceHook
// If you get a crash here, the ptr passed is invalid // If you get a crash here, the ptr passed is invalid
// This usually means a SH_DECL_MANUALHOOK* with wrong thisptroffs/vtbloffs/vtblidx // 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)) CSourceHookImpl::CVfnPtr::CVfnPtr(void *ptr, void **pOneIgnore) : m_Ptr(ptr),
m_pOneIgnore(pOneIgnore), m_OrigEntry(*reinterpret_cast<void**>(ptr))
{ {
} }
CSourceHookImpl::CVfnPtr::~CVfnPtr() CSourceHookImpl::CVfnPtr::~CVfnPtr()
@ -999,6 +1032,12 @@ namespace SourceHook
IIface *CSourceHookImpl::CVfnPtr::FindIface(void *ptr) IIface *CSourceHookImpl::CVfnPtr::FindIface(void *ptr)
{ {
if (m_Ptr == *m_pOneIgnore)
{
*m_pOneIgnore = NULL; // Only once!
return NULL;
}
IfaceListIter iter = m_Ifaces.find(ptr); IfaceListIter iter = m_Ifaces.find(ptr);
// If nothing is found, check for a NULL-interface (VP hooks only) // If nothing is found, check for a NULL-interface (VP hooks only)

File diff suppressed because it is too large Load Diff

View File

@ -90,6 +90,9 @@ Hooks
--------------------------------------- ---------------------------------------
Call classes Call classes
!! deprecated !! - see below (new SH_CALL)
Call classes are identified by a this pointer and an instance size Call classes are identified by a this pointer and an instance size
We use the instance size because a derived class instance and a base class instance could We use the instance size because a derived class instance and a base class instance could
@ -152,8 +155,25 @@ VP Hooks
I'm afraid that with the addition of Recalls and VP Hooks, SourceHook is now a pretty complex and hacked-together I'm afraid that with the addition of Recalls and VP Hooks, SourceHook is now a pretty complex and hacked-together
binary compatible beast which is pretty hard to maintain unless you've written it :) binary compatible beast which is pretty hard to maintain unless you've written it :)
New SH_CALL
The addition of VP hooks messed up the Call Classes concept (see above) - call classes are bound to an
instance pointer; they only work on one of the hooked instances. But VP hooks are called on all instances.
That's why now, SH_CALL takes an instance pointer instead of a callclass pointer. It basically does this:
1) call SH_GLOB_PTR->SetIgnoreHooks(vfnptr)
2) call this->*mfp
3) call SH_GLOB_PTR->ResetIgnoreHooks(vfnptr)
SourceHook stroes the "ignored vfnptr" and makes CVfnPtr::FindIface return NULL if the CVfnPtr instance
corresponds to the ignored vfnptr. This way the hook manager thinks that the instance isn't hooked, and calls
the original function. Everything works fine. This works even for VP hooks.
*/ */
// See sourcehook.hxx :)
#undef CallClass
#undef ManualCallClass
namespace SourceHook namespace SourceHook
{ {
/** /**
@ -416,8 +436,9 @@ namespace SourceHook
IfaceList m_Ifaces; IfaceList m_Ifaces;
void **m_pOneIgnore;
public: public:
CVfnPtr(void *ptr); CVfnPtr(void *ptr, void **pOneIgnore);
virtual ~CVfnPtr(); virtual ~CVfnPtr();
void *GetVfnPtr(); void *GetVfnPtr();
@ -609,6 +630,9 @@ namespace SourceHook
HookLoopInfoStack m_HLIStack; HookLoopInfoStack m_HLIStack;
CHookIDManager m_HookIDMan; CHookIDManager m_HookIDMan;
void *m_OneIgnore; //:TODO:
bool m_IgnoreActive;
public: public:
CSourceHookImpl(); CSourceHookImpl();
virtual ~CSourceHookImpl(); virtual ~CSourceHookImpl();
@ -748,6 +772,8 @@ namespace SourceHook
* @param plug The unique identifier of the plugin that calls this function * @param plug The unique identifier of the plugin that calls this function
* @param mode Can be either Hook_Normal or Hook_VP (vtable-wide hook) * @param mode Can be either Hook_Normal or Hook_VP (vtable-wide hook)
* @param iface The interface pointer * @param iface The interface pointer
* The representative interface pointer for VP hooks
* The vtable pointer for direct VP hooks !!!
* @param ifacesize The size of the class iface points to * @param ifacesize The size of the class iface points to
* @param myHookMan A hook manager function that should be capable of handling the function * @param myHookMan A hook manager function that should be capable of handling the function
* @param handler A pointer to a FastDelegate containing the hook handler * @param handler A pointer to a FastDelegate containing the hook handler
@ -765,6 +791,22 @@ namespace SourceHook
* @param hookid The hook id (returned by AddHookNew) * @param hookid The hook id (returned by AddHookNew)
*/ */
virtual bool RemoveHookByID(Plugin plug, int hookid); virtual bool RemoveHookByID(Plugin plug, int hookid);
/**
* @brief Makes sure that hooks are going to be ignored on the next call of vfnptr
*
* @param plug The unique identifier of the plugin that calls this function
* @param vfnptr The virtual function pointer of the function in question
*/
virtual void SetIgnoreHooks(Plugin plug, void *vfnptr);
/**
* @brief Reverses SetIgnoreHooks' effect
*
* @param plug The unique identifier of the plugin that calls this function
* @param vfnptr The virtual function pointer of the function in question
*/
virtual void ResetIgnoreHooks(Plugin plug, void *vfnptr);
}; };
} }

View File

@ -305,6 +305,12 @@
<File <File
RelativePath="..\..\sh_listcat.h"> RelativePath="..\..\sh_listcat.h">
</File> </File>
<File
RelativePath="..\..\sh_memfuncinfo.h">
</File>
<File
RelativePath="..\..\sh_memory.h">
</File>
<File <File
RelativePath="..\..\sh_stack.h"> RelativePath="..\..\sh_stack.h">
</File> </File>

View File

@ -424,40 +424,33 @@ bool TestBasic(std::string &error)
Whatever test; Whatever test;
Test *pTest = &test; Test *pTest = &test;
// 1) Get a call class and call the member through it and normally // 1) SH_CALL it and call it normally
SourceHook::CallClass<Test> *cc = SH_GET_CALLCLASS(pTest);
return true;
SH_CALL(pTest, &Test::F1)();
ADD_STATE(State_F1_CallClassGenerated);
SH_CALL(cc, &Test::F1)();
pTest->F1(); pTest->F1();
CHECK_STATES((&g_States, CHECK_STATES((&g_States,
new State_F1_CallClassGenerated,
new State_F1_Called, new State_F1_Called,
new State_F1_Called, new State_F1_Called,
NULL), "Part 1"); NULL), "Part 1");
// 2) Request a call class again // 2) Request a call class again
SourceHook::CallClass<Test> *cc2 = SH_GET_CALLCLASS(pTest);
ADD_STATE(State_F1_CallClassGenerated);
SH_CALL(cc, &Test::F1)(); SH_CALL(pTest, &Test::F1)();
SH_CALL(cc2, &Test::F1)(); SH_CALL(pTest, &Test::F1)();
pTest->F1(); pTest->F1();
SH_RELEASE_CALLCLASS(cc2); SH_CALL(pTest, &Test::F1)();
ADD_STATE(State_F1_CallClassReleased);
SH_CALL(cc, &Test::F1)();
pTest->F1(); pTest->F1();
CHECK_STATES((&g_States, CHECK_STATES((&g_States,
new State_F1_CallClassGenerated,
new State_F1_Called, new State_F1_Called,
new State_F1_Called, new State_F1_Called,
new State_F1_Called, new State_F1_Called,
new State_F1_CallClassReleased,
new State_F1_Called, new State_F1_Called,
new State_F1_Called, new State_F1_Called,
NULL), "Part 2"); NULL), "Part 2");
@ -467,7 +460,7 @@ bool TestBasic(std::string &error)
g_F1Pre_WhatToDo = MRES_SUPERCEDE; g_F1Pre_WhatToDo = MRES_SUPERCEDE;
ADD_STATE(State_F1_HookAdded(SH_ADD_HOOK_MEMFUNC(Test, F1, pTest, &f1_handlers, &HandlersF1::Pre, false) ? true : false)); ADD_STATE(State_F1_HookAdded(SH_ADD_HOOK_MEMFUNC(Test, F1, pTest, &f1_handlers, &HandlersF1::Pre, false) ? true : false));
SH_CALL(cc, &Test::F1)(); SH_CALL(pTest, &Test::F1)();
pTest->F1(); pTest->F1();
CHECK_STATES((&g_States, CHECK_STATES((&g_States,
@ -476,30 +469,26 @@ bool TestBasic(std::string &error)
new State_F1_PreHandler_Called(&f1_handlers), new State_F1_PreHandler_Called(&f1_handlers),
NULL), "Part 3"); NULL), "Part 3");
// 4) Rerequest the callclass // 4) Test source-level compat with callclasses
SH_RELEASE_CALLCLASS(cc); SourceHook::CallClass<Test> *pCC = SH_GET_CALLCLASS(pTest);
ADD_STATE(State_F1_CallClassReleased); SH_CALL(pCC, &Test::F1)();
cc2 = SH_GET_CALLCLASS(pTest);
ADD_STATE(State_F1_CallClassGenerated);
SH_CALL(cc, &Test::F1)();
pTest->F1(); pTest->F1();
SH_RELEASE_CALLCLASS(pCC);
CHECK_STATES((&g_States, CHECK_STATES((&g_States,
new State_F1_CallClassReleased,
new State_F1_CallClassGenerated,
new State_F1_Called, new State_F1_Called,
new State_F1_PreHandler_Called(&f1_handlers), new State_F1_PreHandler_Called(&f1_handlers),
NULL), "Part 4"); NULL), "Part 4");
// 5) Check ignore / supercede // 5) Check ignore / supercede
g_F1Pre_WhatToDo = MRES_SUPERCEDE; g_F1Pre_WhatToDo = MRES_SUPERCEDE;
SH_CALL(cc, &Test::F1)(); SH_CALL(pTest, &Test::F1)();
pTest->F1(); pTest->F1();
g_F1Pre_WhatToDo = MRES_IGNORED; g_F1Pre_WhatToDo = MRES_IGNORED;
SH_CALL(cc, &Test::F1)(); SH_CALL(pTest, &Test::F1)();
pTest->F1(); pTest->F1();
CHECK_STATES((&g_States, CHECK_STATES((&g_States,
@ -515,7 +504,7 @@ bool TestBasic(std::string &error)
SH_REMOVE_HOOK_MEMFUNC(Test, F1, pTest, &f1_handlers, &HandlersF1::Pre, false); SH_REMOVE_HOOK_MEMFUNC(Test, F1, pTest, &f1_handlers, &HandlersF1::Pre, false);
ADD_STATE(State_F1_HookRemoved); ADD_STATE(State_F1_HookRemoved);
SH_CALL(cc, &Test::F1)(); SH_CALL(pTest, &Test::F1)();
pTest->F1(); pTest->F1();
CHECK_STATES((&g_States, CHECK_STATES((&g_States,
@ -528,7 +517,7 @@ bool TestBasic(std::string &error)
g_F1Post_WhatToDo = MRES_IGNORED; g_F1Post_WhatToDo = MRES_IGNORED;
ADD_STATE(State_F1_HookAdded(SH_ADD_HOOK(Test, F1, pTest, SH_MEMBER(&f1_handlers, &HandlersF1::Post), true) ? true : false)); ADD_STATE(State_F1_HookAdded(SH_ADD_HOOK(Test, F1, pTest, SH_MEMBER(&f1_handlers, &HandlersF1::Post), true) ? true : false));
SH_CALL(cc, &Test::F1)(); SH_CALL(pTest, &Test::F1)();
pTest->F1(); pTest->F1();
CHECK_STATES((&g_States, CHECK_STATES((&g_States,
@ -542,11 +531,11 @@ bool TestBasic(std::string &error)
g_F1Pre_WhatToDo = MRES_IGNORED; g_F1Pre_WhatToDo = MRES_IGNORED;
ADD_STATE(State_F1_HookAdded(SH_ADD_HOOK(Test, F1, pTest, SH_MEMBER(&f1_handlers, &HandlersF1::Pre), false) ? true : false)); ADD_STATE(State_F1_HookAdded(SH_ADD_HOOK(Test, F1, pTest, SH_MEMBER(&f1_handlers, &HandlersF1::Pre), false) ? true : false));
SH_CALL(cc, &Test::F1)(); SH_CALL(pTest, &Test::F1)();
pTest->F1(); pTest->F1();
g_F1Pre_WhatToDo = MRES_SUPERCEDE; g_F1Pre_WhatToDo = MRES_SUPERCEDE;
SH_CALL(cc, &Test::F1)(); SH_CALL(pTest, &Test::F1)();
pTest->F1(); pTest->F1();
CHECK_STATES((&g_States, CHECK_STATES((&g_States,
@ -566,7 +555,7 @@ bool TestBasic(std::string &error)
SH_REMOVE_HOOK(Test, F1, pTest, SH_MEMBER(&f1_handlers, &HandlersF1::Post), true); SH_REMOVE_HOOK(Test, F1, pTest, SH_MEMBER(&f1_handlers, &HandlersF1::Post), true);
ADD_STATE(State_F1_HookRemoved); ADD_STATE(State_F1_HookRemoved);
SH_CALL(cc, &Test::F1)(); SH_CALL(pTest, &Test::F1)();
pTest->F1(); pTest->F1();
CHECK_STATES((&g_States, CHECK_STATES((&g_States,
@ -581,7 +570,7 @@ bool TestBasic(std::string &error)
g_F299Pre_WhatToRet = false; g_F299Pre_WhatToRet = false;
ADD_STATE(State_F299Ret(pTest->F299("hi"))); ADD_STATE(State_F299Ret(pTest->F299("hi")));
ADD_STATE(State_F299Ret(SH_CALL(cc, &Test::F299)("hi"))); ADD_STATE(State_F299Ret(SH_CALL(pTest, &Test::F299)("hi")));
CHECK_STATES((&g_States, CHECK_STATES((&g_States,
new State_F299_Called("hi"), new State_F299_Called("hi"),
@ -593,7 +582,7 @@ bool TestBasic(std::string &error)
// (one add staticfunc in old format) // (one add staticfunc in old format)
SH_ADD_HOOK_STATICFUNC(Test, F299, pTest, F299_Pre, false); SH_ADD_HOOK_STATICFUNC(Test, F299, pTest, F299_Pre, false);
ADD_STATE(State_F299Ret(pTest->F299("hi"))); ADD_STATE(State_F299Ret(pTest->F299("hi")));
ADD_STATE(State_F299Ret(SH_CALL(cc, &Test::F299)("hi"))); ADD_STATE(State_F299Ret(SH_CALL(pTest, &Test::F299)("hi")));
CHECK_STATES((&g_States, CHECK_STATES((&g_States,
new State_F299_PreHandlerCalled("hi"), new State_F299_PreHandlerCalled("hi"),
@ -605,7 +594,7 @@ bool TestBasic(std::string &error)
SH_ADD_HOOK(Test, F299, pTest, SH_STATIC(F299_Post), true); SH_ADD_HOOK(Test, F299, pTest, SH_STATIC(F299_Post), true);
ADD_STATE(State_F299Ret(pTest->F299("hi"))); ADD_STATE(State_F299Ret(pTest->F299("hi")));
ADD_STATE(State_F299Ret(SH_CALL(cc, &Test::F299)("hi"))); ADD_STATE(State_F299Ret(SH_CALL(pTest, &Test::F299)("hi")));
CHECK_STATES((&g_States, CHECK_STATES((&g_States,
new State_F299_PreHandlerCalled("hi"), new State_F299_PreHandlerCalled("hi"),
@ -618,7 +607,7 @@ bool TestBasic(std::string &error)
g_F299Pre_WhatToDo = MRES_OVERRIDE; g_F299Pre_WhatToDo = MRES_OVERRIDE;
ADD_STATE(State_F299Ret(pTest->F299("hi"))); ADD_STATE(State_F299Ret(pTest->F299("hi")));
ADD_STATE(State_F299Ret(SH_CALL(cc, &Test::F299)("hi"))); ADD_STATE(State_F299Ret(SH_CALL(pTest, &Test::F299)("hi")));
CHECK_STATES((&g_States, CHECK_STATES((&g_States,
new State_F299_PreHandlerCalled("hi"), new State_F299_PreHandlerCalled("hi"),
@ -631,7 +620,7 @@ bool TestBasic(std::string &error)
g_F299Pre_WhatToDo = MRES_SUPERCEDE; g_F299Pre_WhatToDo = MRES_SUPERCEDE;
ADD_STATE(State_F299Ret(pTest->F299("hi"))); ADD_STATE(State_F299Ret(pTest->F299("hi")));
ADD_STATE(State_F299Ret(SH_CALL(cc, &Test::F299)("hi"))); ADD_STATE(State_F299Ret(SH_CALL(pTest, &Test::F299)("hi")));
CHECK_STATES((&g_States, CHECK_STATES((&g_States,
new State_F299_PreHandlerCalled("hi"), new State_F299_PreHandlerCalled("hi"),
@ -644,7 +633,7 @@ bool TestBasic(std::string &error)
// (one remove staticfunc in old format) // (one remove staticfunc in old format)
SH_REMOVE_HOOK_STATICFUNC(Test, F299, pTest, F299_Pre, false); SH_REMOVE_HOOK_STATICFUNC(Test, F299, pTest, F299_Pre, false);
ADD_STATE(State_F299Ret(pTest->F299("hi"))); ADD_STATE(State_F299Ret(pTest->F299("hi")));
ADD_STATE(State_F299Ret(SH_CALL(cc, &Test::F299)("hi"))); ADD_STATE(State_F299Ret(SH_CALL(pTest, &Test::F299)("hi")));
CHECK_STATES((&g_States, CHECK_STATES((&g_States,
new State_F299_Called("hi"), new State_F299_Called("hi"),
@ -656,7 +645,7 @@ bool TestBasic(std::string &error)
SH_REMOVE_HOOK(Test, F299, pTest, SH_STATIC(F299_Post), true); SH_REMOVE_HOOK(Test, F299, pTest, SH_STATIC(F299_Post), true);
ADD_STATE(State_F299Ret(pTest->F299("hi"))); ADD_STATE(State_F299Ret(pTest->F299("hi")));
ADD_STATE(State_F299Ret(SH_CALL(cc, &Test::F299)("hi"))); ADD_STATE(State_F299Ret(SH_CALL(pTest, &Test::F299)("hi")));
CHECK_STATES((&g_States, CHECK_STATES((&g_States,
new State_F299_Called("hi"), new State_F299_Called("hi"),
@ -665,14 +654,6 @@ bool TestBasic(std::string &error)
new State_F299Ret(true), new State_F299Ret(true),
NULL), "Part 10.7"); NULL), "Part 10.7");
// 11) Release callclass
SH_RELEASE_CALLCLASS(cc);
ADD_STATE(State_F1_CallClassReleased);
CHECK_STATES((&g_States,
new State_F1_CallClassReleased,
NULL), "Part 11");
// 11 1/2) Test removing hook by id // 11 1/2) Test removing hook by id

View File

@ -100,11 +100,9 @@ bool TestVafmtAndOverload(std::string &error)
Whatever gabgab; Whatever gabgab;
IGaben *pGab = &gabgab; IGaben *pGab = &gabgab;
SourceHook::CallClass<IGaben> *cc = SH_GET_CALLCLASS(pGab);
// Part 1 // Part 1
SH_CALL(cc, static_cast<void (IGaben::*)()>(&IGaben::EatYams))(); SH_CALL(pGab, static_cast<void (IGaben::*)()>(&IGaben::EatYams))();
SH_CALL(cc, static_cast<bool (IGaben::*)(const char *) const>(&IGaben::EatYams))("Here!"); SH_CALL(pGab, static_cast<bool (IGaben::*)(const char *) const>(&IGaben::EatYams))("Here!");
SH_ADD_HOOK(IGaben, EatYams, pGab, EatYams0_Handler, false); SH_ADD_HOOK(IGaben, EatYams, pGab, EatYams0_Handler, false);
SH_ADD_HOOK(IGaben, EatYams, pGab, EatYams1_Handler, false); SH_ADD_HOOK(IGaben, EatYams, pGab, EatYams1_Handler, false);
@ -126,9 +124,9 @@ bool TestVafmtAndOverload(std::string &error)
// Part 2 // Part 2
pGab->Vafmt1(true, 55, "Hello %s%d%s", "BA", 1, "L"); pGab->Vafmt1(true, 55, "Hello %s%d%s", "BA", 1, "L");
SH_CALL(cc, &IGaben::Vafmt1)(true, 55, "Hello %s%d%s", "BA", 1, "L"); SH_CALL(pGab, &IGaben::Vafmt1)(true, 55, "Hello %s%d%s", "BA", 1, "L");
pGab->Vafmt2("Hello %s%d%s", "BA", 1, "LOPAN"); pGab->Vafmt2("Hello %s%d%s", "BA", 1, "LOPAN");
SH_CALL(cc, &IGaben::Vafmt2)("Hello %s%d%s", "BA", 1, "LOPAN"); SH_CALL(pGab, &IGaben::Vafmt2)("Hello %s%d%s", "BA", 1, "LOPAN");
CHECK_STATES((&g_States, CHECK_STATES((&g_States,
new State_Vafmt_Called(1, "Hello BA1L"), new State_Vafmt_Called(1, "Hello BA1L"),
@ -170,7 +168,5 @@ bool TestVafmtAndOverload(std::string &error)
new State_Vafmt_Called(2, "Hello BA1LOPAN"), new State_Vafmt_Called(2, "Hello BA1LOPAN"),
NULL), "Part 4"); NULL), "Part 4");
SH_RELEASE_CALLCLASS(cc);
return true; return true;
} }

View File

@ -87,11 +87,10 @@ bool TestThisPtrOffs(std::string &error)
// Get a callclass for pD // Get a callclass for pD
// Verify whether the this pointers are correct // Verify whether the this pointers are correct
// Also call them normally to make sure that we aren't messing it up ;) // Also call them normally to make sure that we aren't messing it up ;)
SourceHook::CallClass<Derived> *pD_CC = SH_GET_CALLCLASS(pD);
SH_CALL(pD_CC, &Derived::Func1)(); SH_CALL(pD, &Derived::Func1)();
SH_CALL(pD_CC, &Derived::Func2)(); SH_CALL(pD, &Derived::Func2)();
SH_CALL(pD_CC, &Derived::Func3)(); SH_CALL(pD, &Derived::Func3)();
pD->Func1(); pD->Func1();
pD->Func2(); pD->Func2();
pD->Func3(); pD->Func3();
@ -105,8 +104,8 @@ bool TestThisPtrOffs(std::string &error)
new State_Func3_Called(pD), new State_Func3_Called(pD),
NULL), "Part 1"); NULL), "Part 1");
SH_CALL(pD_CC, &Base1::Func1)(); SH_CALL(pD, &Base1::Func1)();
SH_CALL(pD_CC, &Base2::Func2)(); SH_CALL(pD, &Base2::Func2)();
CHECK_STATES((&g_States, CHECK_STATES((&g_States,
new State_Func1_Called(pB1), new State_Func1_Called(pB1),
@ -116,11 +115,8 @@ bool TestThisPtrOffs(std::string &error)
// 2) // 2)
// Get callclasses for the other ones and verify it as well // Get callclasses for the other ones and verify it as well
SourceHook::CallClass<Base1> *pB1_CC = SH_GET_CALLCLASS(pB1); SH_CALL(pB1, &Base1::Func1)();
SourceHook::CallClass<Base2> *pB2_CC = SH_GET_CALLCLASS(pB2); SH_CALL(pB2, &Base2::Func2)();
SH_CALL(pB1_CC, &Base1::Func1)();
SH_CALL(pB2_CC, &Base2::Func2)();
CHECK_STATES((&g_States, CHECK_STATES((&g_States,
new State_Func1_Called(pB1), new State_Func1_Called(pB1),
@ -214,11 +210,11 @@ bool TestThisPtrOffs(std::string &error)
new State_Func3_Called(pD), new State_Func3_Called(pD),
NULL), "Part 5.1"); NULL), "Part 5.1");
SH_CALL(pD_CC, &Derived::Func1)(); SH_CALL(pD, &Derived::Func1)();
SH_CALL(pD_CC, &Derived::Func2)(); SH_CALL(pD, &Derived::Func2)();
SH_CALL(pD_CC, &Derived::Func3)(); SH_CALL(pD, &Derived::Func3)();
SH_CALL(pB1_CC, &Base1::Func1)(); SH_CALL(pB1, &Base1::Func1)();
SH_CALL(pB2_CC, &Base2::Func2)(); SH_CALL(pB2, &Base2::Func2)();
CHECK_STATES((&g_States, CHECK_STATES((&g_States,
new State_Func1_Called(pB1), new State_Func1_Called(pB1),
@ -232,9 +228,5 @@ bool TestThisPtrOffs(std::string &error)
SH_REMOVE_HOOK(Derived, Func2, pD, SH_STATIC(Handler_Func2), false); SH_REMOVE_HOOK(Derived, Func2, pD, SH_STATIC(Handler_Func2), false);
SH_REMOVE_HOOK(Derived, Func3, pD, SH_STATIC(Handler_Func3), false); SH_REMOVE_HOOK(Derived, Func3, pD, SH_STATIC(Handler_Func3), false);
SH_RELEASE_CALLCLASS(pB1_CC);
SH_RELEASE_CALLCLASS(pB2_CC);
SH_RELEASE_CALLCLASS(pD_CC);
return true; return true;
} }

View File

@ -114,8 +114,6 @@ bool TestManual(std::string &error)
Whatever inst; Whatever inst;
TheWall *p = &inst; TheWall *p = &inst;
SourceHook::ManualCallClass *cc = SH_GET_MCALLCLASS(p, sizeof(void*));
// 1) // 1)
// Call each function // Call each function
p->Func1(); p->Func1();
@ -134,14 +132,14 @@ bool TestManual(std::string &error)
// 1.1) // 1.1)
// Now call each function through the manual call class, using the hook decl and manually // Now call each function through the manual call class, using the hook decl and manually
SH_MCALL(cc, TheWall_Func1)(); SH_MCALL(p, TheWall_Func1)();
SH_MCALL2(cc, MFP_Func1(), 0, 0, 0)(); SH_MCALL2(p, MFP_Func1(), 0, 0, 0)();
SH_MCALL(cc, TheWall_Func2)(200); SH_MCALL(p, TheWall_Func2)(200);
SH_MCALL2(cc, MFP_Func2(), 1, 0, 0)(200); SH_MCALL2(p, MFP_Func2(), 1, 0, 0)(200);
ADD_STATE(State_Return(SH_MCALL(cc, TheWall_Func3)())); ADD_STATE(State_Return(SH_MCALL(p, TheWall_Func3)()));
ADD_STATE(State_Return(SH_MCALL2(cc, MFP_Func3(), 2, 0, 0)())); ADD_STATE(State_Return(SH_MCALL2(p, MFP_Func3(), 2, 0, 0)()));
ADD_STATE(State_Return(SH_MCALL(cc, TheWall_Func4)(400))); ADD_STATE(State_Return(SH_MCALL(p, TheWall_Func4)(400)));
ADD_STATE(State_Return(SH_MCALL2(cc, MFP_Func4(), 3, 0, 0)(400))); ADD_STATE(State_Return(SH_MCALL2(p, MFP_Func4(), 3, 0, 0)(400)));
CHECK_STATES((&g_States, CHECK_STATES((&g_States,
new State_Func1_Called(p), new State_Func1_Called(p),
@ -158,6 +156,17 @@ bool TestManual(std::string &error)
new State_Return(4), new State_Return(4),
NULL), "Part 1.1"); NULL), "Part 1.1");
// Compat: really get a manual call class!
SourceHook::ManualCallClass *pCC = SH_GET_MCALLCLASS(p, sizeof(void*));
SH_MCALL(pCC, TheWall_Func1)();
SH_MCALL2(pCC, MFP_Func1(), 0, 0, 0)();
CHECK_STATES((&g_States,
new State_Func1_Called(p),
new State_Func1_Called(p),
NULL), "Part 1.2");
SH_RELEASE_CALLCLASS(pCC);
// 2) // 2)
// Hook each function normally, call them // Hook each function normally, call them
SH_ADD_HOOK(TheWall, Func1, p, SH_STATIC(Handler_Func1), false); SH_ADD_HOOK(TheWall, Func1, p, SH_STATIC(Handler_Func1), false);
@ -186,14 +195,14 @@ bool TestManual(std::string &error)
// Call them through the mcallclass // Call them through the mcallclass
// 2.1) // 2.1)
// Now call each function through the manual call class, using the hook decl and manually // Now call each function through the manual call class, using the hook decl and manually
SH_MCALL(cc, TheWall_Func1)(); SH_MCALL(p, TheWall_Func1)();
SH_MCALL2(cc, MFP_Func1(), 0, 0, 0)(); SH_MCALL2(p, MFP_Func1(), 0, 0, 0)();
SH_MCALL(cc, TheWall_Func2)(200); SH_MCALL(p, TheWall_Func2)(200);
SH_MCALL2(cc, MFP_Func2(), 1, 0, 0)(200); SH_MCALL2(p, MFP_Func2(), 1, 0, 0)(200);
ADD_STATE(State_Return(SH_MCALL(cc, TheWall_Func3)())); ADD_STATE(State_Return(SH_MCALL(p, TheWall_Func3)()));
ADD_STATE(State_Return(SH_MCALL2(cc, MFP_Func3(), 2, 0, 0)())); ADD_STATE(State_Return(SH_MCALL2(p, MFP_Func3(), 2, 0, 0)()));
ADD_STATE(State_Return(SH_MCALL(cc, TheWall_Func4)(400))); ADD_STATE(State_Return(SH_MCALL(p, TheWall_Func4)(400)));
ADD_STATE(State_Return(SH_MCALL2(cc, MFP_Func4(), 3, 0, 0)(400))); ADD_STATE(State_Return(SH_MCALL2(p, MFP_Func4(), 3, 0, 0)(400)));
CHECK_STATES((&g_States, CHECK_STATES((&g_States,
new State_Func1_Called(p), new State_Func1_Called(p),
@ -247,14 +256,14 @@ bool TestManual(std::string &error)
// Call them through the mcallclass // Call them through the mcallclass
// 3.1) // 3.1)
// Now call each function through the manual call class, using the hook decl and manually // Now call each function through the manual call class, using the hook decl and manually
SH_MCALL(cc, TheWall_Func1)(); SH_MCALL(p, TheWall_Func1)();
SH_MCALL2(cc, MFP_Func1(), 0, 0, 0)(); SH_MCALL2(p, MFP_Func1(), 0, 0, 0)();
SH_MCALL(cc, TheWall_Func2)(200); SH_MCALL(p, TheWall_Func2)(200);
SH_MCALL2(cc, MFP_Func2(), 1, 0, 0)(200); SH_MCALL2(p, MFP_Func2(), 1, 0, 0)(200);
ADD_STATE(State_Return(SH_MCALL(cc, TheWall_Func3)())); ADD_STATE(State_Return(SH_MCALL(p, TheWall_Func3)()));
ADD_STATE(State_Return(SH_MCALL2(cc, MFP_Func3(), 2, 0, 0)())); ADD_STATE(State_Return(SH_MCALL2(p, MFP_Func3(), 2, 0, 0)()));
ADD_STATE(State_Return(SH_MCALL(cc, TheWall_Func4)(400))); ADD_STATE(State_Return(SH_MCALL(p, TheWall_Func4)(400)));
ADD_STATE(State_Return(SH_MCALL2(cc, MFP_Func4(), 3, 0, 0)(400))); ADD_STATE(State_Return(SH_MCALL2(p, MFP_Func4(), 3, 0, 0)(400)));
CHECK_STATES((&g_States, CHECK_STATES((&g_States,
new State_Func1_Called(p), new State_Func1_Called(p),

View File

@ -104,8 +104,6 @@ bool TestRef(std::string &error)
CHello *pHello = &hello; CHello *pHello = &hello;
CHook hook; CHook hook;
SourceHook::CallClass<CHello> *cc = SH_GET_CALLCLASS(pHello);
ADD_STATE(State_Result(pHello->Func(base))); ADD_STATE(State_Result(pHello->Func(base)));
ADD_STATE(State_Result(pHello->Func(der))); ADD_STATE(State_Result(pHello->Func(der)));
ADD_STATE(State_Result(pHello->Func(der2))); ADD_STATE(State_Result(pHello->Func(der2)));
@ -118,10 +116,10 @@ bool TestRef(std::string &error)
new State_Result(12), new State_Result(12),
NULL), "Part 1"); NULL), "Part 1");
ADD_STATE(State_Result(SH_CALL(cc, &CHello::Func)(base))); ADD_STATE(State_Result(SH_CALL(pHello, &CHello::Func)(base)));
ADD_STATE(State_Result(SH_CALL(cc, &CHello::Func)(der))); ADD_STATE(State_Result(SH_CALL(pHello, &CHello::Func)(der)));
ADD_STATE(State_Result(SH_CALL(cc, &CHello::Func)(der2))); ADD_STATE(State_Result(SH_CALL(pHello, &CHello::Func)(der2)));
ADD_STATE(State_Result(SH_CALL(cc, &CHello::Func)(der3))); ADD_STATE(State_Result(SH_CALL(pHello, &CHello::Func)(der3)));
CHECK_STATES((&g_States, CHECK_STATES((&g_States,
new State_Result(0), new State_Result(0),
@ -148,10 +146,10 @@ bool TestRef(std::string &error)
new State_Result(20), new State_Result(20),
NULL), "Part 3"); NULL), "Part 3");
ADD_STATE(State_Result(SH_CALL(cc, &CHello::Func)(base))); ADD_STATE(State_Result(SH_CALL(pHello, &CHello::Func)(base)));
ADD_STATE(State_Result(SH_CALL(cc, &CHello::Func)(der))); ADD_STATE(State_Result(SH_CALL(pHello, &CHello::Func)(der)));
ADD_STATE(State_Result(SH_CALL(cc, &CHello::Func)(der2))); ADD_STATE(State_Result(SH_CALL(pHello, &CHello::Func)(der2)));
ADD_STATE(State_Result(SH_CALL(cc, &CHello::Func)(der3))); ADD_STATE(State_Result(SH_CALL(pHello, &CHello::Func)(der3)));
CHECK_STATES((&g_States, CHECK_STATES((&g_States,
new State_Result(0), new State_Result(0),

View File

@ -164,8 +164,7 @@ bool TestRefRet(std::string &error)
CHECK_COND(hook.m_Var == 1337, "Part 4.1"); CHECK_COND(hook.m_Var == 1337, "Part 4.1");
// Through a callclass // Through a callclass
SourceHook::CallClass<Test> *cc1 = SH_GET_CALLCLASS(pTest); int &ret5 = SH_CALL(pTest, &Test::Func1)();
int &ret5 = SH_CALL(cc1, &Test::Func1)();
ADD_STATE(State_Func1_Ret(&ret5)); ADD_STATE(State_Func1_Ret(&ret5));
CHECK_STATES((&g_States, CHECK_STATES((&g_States,
@ -173,7 +172,6 @@ bool TestRefRet(std::string &error)
new State_Func1_Ret(&test.m_Var1), new State_Func1_Ret(&test.m_Var1),
NULL), "Part 5"); NULL), "Part 5");
SH_RELEASE_CALLCLASS(cc1);
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
// Func2 tests // Func2 tests

View File

@ -5,6 +5,7 @@
// TEST VP HOOKS // TEST VP HOOKS
// Test vfnptr-wide hooks // Test vfnptr-wide hooks
// Also contains a test for removing hooks on deleted instances (by id)
namespace namespace
{ {
@ -138,6 +139,18 @@ bool TestVPHooks(std::string &error)
new State_D2_Func1(p_d2i1), new State_D2_Func1(p_d2i1),
NULL), "Part 1"); NULL), "Part 1");
SH_CALL(p_d1i1, &IBase::Func1)();
SH_CALL(p_d1i2, &IBase::Func1)();
SH_CALL(p_d2i1, &IBase::Func1)();
CHECK_STATES((&g_States,
new State_D1_Func1(p_d1i1),
new State_D1_Func1(p_d1i2),
new State_D2_Func1(p_d2i1),
NULL), "Part 1.1");
SH_REMOVE_HOOK_ID(hook1); SH_REMOVE_HOOK_ID(hook1);
p_d1i1->Func1(); p_d1i1->Func1();
@ -261,6 +274,60 @@ bool TestVPHooks(std::string &error)
SH_REMOVE_HOOK_ID(hook2); SH_REMOVE_HOOK_ID(hook2);
SH_REMOVE_HOOK_ID(hook3); SH_REMOVE_HOOK_ID(hook3);
// Test direct VP hook
p_d1i1->Func1();
p_d1i2->Func1();
// get vtable manually
void *pVtable = *reinterpret_cast<void**>(p_d1i1);
hook1 = SH_ADD_DVPHOOK(IBase, Func1, pVtable, SH_STATIC(Handler_Func1_Pre), false);
p_d1i1->Func1();
p_d1i2->Func1();
CHECK_STATES((&g_States,
new State_D1_Func1(p_d1i1),
new State_D1_Func1(p_d1i2),
new State_Func1_Pre(p_d1i1),
new State_D1_Func1(p_d1i1),
new State_Func1_Pre(p_d1i2),
new State_D1_Func1(p_d1i2),
NULL), "Part 8.1");
SH_REMOVE_HOOK_ID(hook1);
// Now try manual dvp hooks
p_d1i1->Func3(1);
p_d1i2->Func3(1);
hook1 = SH_ADD_MANUALDVPHOOK(IBase_Func3_Manual, pVtable, SH_STATIC(Handler_Func3_Pre), false);
p_d1i1->Func3(1);
p_d1i2->Func3(1);
CHECK_STATES((&g_States,
new State_D1_Func3(p_d1i1, 1),
new State_D1_Func3(p_d1i2, 1),
new State_Func3_Pre(p_d1i1, 1), // manual vp hook
new State_D1_Func3(p_d1i1, 2), // function
new State_Func3_Pre(p_d1i2, 1), // normal vp hook
new State_D1_Func3(p_d1i2, 2), // function
NULL), "Part 8.2");
// Test removing normal hooks even though the instance is deleted
p_d1i1 = new CDerived1;
SH_ADD_HOOK(IBase, Func1, p_d1i1, SH_STATIC(Handler_Func1_Pre), false);
delete p_d1i1;
// The following line may not crash!
SH_REMOVE_HOOK(IBase, Func1, p_d1i1, SH_STATIC(Handler_Func1_Pre), false);
return true; return true;
} }