Theme
Shared hooks
The one MinHook state and the one-hook-per-target rule, owned by the framework, so no plugin carries MinHook of its own.
MinHook keeps one hook per target per module: a second installer of a target is refused from inside the library and silently does nothing, and the first one to shut MinHook down takes every hook in the DLL with it. Both are invisible until they are not. This group is the answer to both - a target is recorded by tag the moment it is hooked, a second request for the same target is answered #SH_HOOK_DUP with the owner named in the log, and MinHook's state is initialized here once (already initialized being the normal answer) and never shut down.
The plugin owns the detour and the feature; this group owns the install. Every entry point is safe from any thread - but not from inside a hook detour, because creating a hook allocates and writes code pages.
| Export | Summary |
|---|---|
ShHookAdd | Install and enable a hook over mod's export fn, atomically. |
ShHookAddLate | ShHookAdd for a module that is not loaded yet. |
ShHookAddAt | Release one reference to tag's hook; the hook leaves when the last one does. |
ShHookClaim | — |
ShHookEnable | Enable or disable a hook without giving it back: hidden, reference counted, and the trampoline stays valid across both - MinHook does not write the jump while a hook is disabled, so a hot-path hook that is off costs nothing per call. |
ShHookRemove | — |
ShHookAddAt (again) | ShHookAdd for a target that has no export name: the playmode and ammocap targets are addresses inside the game image, reached through the framework's own RVA machinery. |
ShHookClaim (again) | Record that target is owned, without hooking it. |
ShHookStatus | One line about what is hooked, for a log or a status row: how many are hooked, how many are claimed by hand-written patches, and which tags. |
ShHookAdd
c
SH_API int ShHookAdd(const char *tag, HMODULE mod, const char *fn,
void *detour, void **orig);Install and enable a hook over mod's export fn, atomically.
tag is the caller's name for the hook - it is what the log and ShHookStatus show, and it is the handle ShHookRemove takes back. The same tag twice is idempotent and reference counted: one hook, counted twice, gone when the last caller removes it.
On #SH_HOOK_OK, *orig is the trampoline to call through, valid from the moment the call returns. On #SH_HOOK_DUP the detour is not in the chain at all and *orig is NULL: back off, or the feature quietly does nothing. On #SH_HOOK_ERR *orig is NULL as well.
Never MH_ALL_HOOKS: each target is enabled by its own address, so a hook another module of this DLL created and deliberately left disabled stays disabled.
scripthook.h, line 2696
ShHookAddLate
c
SH_API int ShHookAddLate(const char *tag, const char *modname,
const char *fn, void *detour, void **orig);ShHookAdd for a module that is not loaded yet.
Waits for modname on the calling thread - a quarter second for the first minute, a second after that, about five minutes in total - and then does what ShHookAdd does. It exists for the loader DLLs (uplay_r1_loader64.dll and friends) that arrive whenever the game gets round to them: a hook over the function's own entry point works whether or not the game already holds the pointer, which is the only kind that is not a race.
The result is the install's, with one addition: #SH_HOOK_ERR also means the module never arrived.
scripthook.h, line 2711
ShHookAddAt
c
SH_API int ShHookAddAt(const char *tag, void *target, const char *label,
void *detour, void **orig);Release one reference to tag's hook; the hook leaves when the last one does. Teardown, not a switch: a detour that checks its own flag and calls through is the cheap way to be off, and removing a hook that is mid-call is not supported. Returns #SH_HOOK_ERR when the tag is not installed.
scripthook.h, line 2719
ShHookClaim
c
SH_API int ShHookClaim(const char *tag, void *target, const char *label);No doc comment in the header.
scripthook.h, line 2722
ShHookEnable
c
SH_API int ShHookEnable(const char *tag, int on);Enable or disable a hook without giving it back: hidden, reference counted, and the trampoline stays valid across both - MinHook does not write the jump while a hook is disabled, so a hot-path hook that is off costs nothing per call. What this is for: a detour that is only worth having while a feature is on (NvFilter's PSSetShader, which runs for every draw when armed and for none while off).
A claimed tag (the hand-patched sites) has nothing to enable and is answered #SH_HOOK_ERR.
scripthook.h, line 2733
ShHookRemove
c
SH_API int ShHookRemove(const char *tag);No doc comment in the header.
scripthook.h, line 2735
ShHookAddAt (declared again)
Duplicate declaration
The header declares ShHookAddAt more than once; this is the repeated declaration. The doc comment shown is the one placed above it in the header and may belong to a neighbouring function.
c
SH_API int ShHookAddAt(const char *tag, void *target, const char *label,
void *detour, void **orig);ShHookAdd for a target that has no export name: the playmode and ammocap targets are addresses inside the game image, reached through the framework's own RVA machinery. label is what the log and ShHookStatus show ("playmode:mode_mgr" and the like); pass NULL and the address itself is shown.
scripthook.h, line 2742
ShHookClaim (declared again)
Duplicate declaration
The header declares ShHookClaim more than once; this is the repeated declaration. The doc comment shown is the one placed above it in the header and may belong to a neighbouring function.
c
SH_API int ShHookClaim(const char *tag, void *target, const char *label);Record that target is owned, without hooking it.
For the nine framework modules that patch game code by hand - an E9 into a stub of their own, no trampoline, no call-through. The patch stays exactly where it is; the claim takes the address out of circulation, so the next caller asking for the same target is told who owns it instead of writing a second jump over the first one's. A claim is removed by ShHookRemove like any other tag.
scripthook.h, line 2753
ShHookStatus
c
SH_API int ShHookStatus(char *buf, int n);One line about what is hooked, for a log or a status row: how many are hooked, how many are claimed by hand-written patches, and which tags. Returns the length written, 0 when buf is too small.
scripthook.h, line 2758