Hook registry: list installed hooks + call counts in Injection panel
Add a process-wide hook registry (hook/src/hook_registry) that every hook module registers its hooks with and bumps a counter from each detour. The XInput, focus-spoof, and audio render-hooks now register their individual hooks (XInputGetState/Ex/Caps/SetState; GetForegroundWindow/GetActiveWindow/ GetFocus/WndProc guard; IMMDevice::Activate, IAudioClient::Initialize/ GetService, IAudioRenderClient::GetBuffer/ReleaseBuffer) and count calls. The worker publishes the table to the host each tick over a new HookStatus field (protocol v4 -> v5: HookEntry[] + count). The Injection panel shows it as a collapsible table grouped by subsystem with an installed flag and call count per hook; coop_audio_probe prints the same table headless. Verified against Phantom Brave: 13 hooks listed with live counts (focus APIs polled heavily, GetBuffer/ReleaseBuffer ticking with the audio render loop). All four tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
103
hook/src/hook_registry.cpp
Normal file
103
hook/src/hook_registry.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
#include "hook_registry.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
|
||||
namespace coop::hook
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
struct Slot
|
||||
{
|
||||
char name[40] = {};
|
||||
std::atomic<std::uint32_t> subsystem{0};
|
||||
std::atomic<std::uint32_t> installed{0};
|
||||
std::atomic<std::uint64_t> calls{0};
|
||||
std::atomic<std::uint32_t> used{0};
|
||||
};
|
||||
|
||||
Slot g_slots[kMaxHookEntries];
|
||||
std::atomic<std::uint32_t> g_count{0}; // high-water mark of allocated slots
|
||||
std::mutex g_register_mutex; // registration only (rare); calls are lock-free
|
||||
|
||||
} // namespace
|
||||
|
||||
int hook_register(const char* name, std::uint32_t subsystem)
|
||||
{
|
||||
std::scoped_lock lock(g_register_mutex);
|
||||
const std::uint32_t count = g_count.load(std::memory_order_relaxed);
|
||||
for (std::uint32_t i = 0; i < count; ++i)
|
||||
{
|
||||
if (g_slots[i].used.load(std::memory_order_relaxed) && std::strcmp(g_slots[i].name, name) == 0)
|
||||
{
|
||||
return static_cast<int>(i); // already registered
|
||||
}
|
||||
}
|
||||
if (count >= kMaxHookEntries)
|
||||
{
|
||||
return -1; // table full
|
||||
}
|
||||
Slot& s = g_slots[count];
|
||||
std::strncpy(s.name, name, sizeof(s.name) - 1);
|
||||
s.name[sizeof(s.name) - 1] = '\0';
|
||||
s.subsystem.store(subsystem, std::memory_order_relaxed);
|
||||
s.installed.store(0, std::memory_order_relaxed);
|
||||
s.calls.store(0, std::memory_order_relaxed);
|
||||
s.used.store(1, std::memory_order_release);
|
||||
g_count.store(count + 1, std::memory_order_release);
|
||||
return static_cast<int>(count);
|
||||
}
|
||||
|
||||
void hook_set_installed(int id, bool installed)
|
||||
{
|
||||
if (id >= 0 && id < static_cast<int>(kMaxHookEntries))
|
||||
{
|
||||
g_slots[id].installed.store(installed ? 1u : 0u, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
void hook_note_call(int id)
|
||||
{
|
||||
if (id >= 0 && id < static_cast<int>(kMaxHookEntries))
|
||||
{
|
||||
g_slots[id].calls.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
void hook_publish(IpcClient& ipc)
|
||||
{
|
||||
const std::uint32_t count = g_count.load(std::memory_order_acquire);
|
||||
HookEntry entries[kMaxHookEntries];
|
||||
std::uint32_t n = 0;
|
||||
for (std::uint32_t i = 0; i < count && i < kMaxHookEntries; ++i)
|
||||
{
|
||||
if (!g_slots[i].used.load(std::memory_order_acquire))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
HookEntry& e = entries[n];
|
||||
std::memcpy(e.name, g_slots[i].name, sizeof(e.name));
|
||||
e.subsystem = g_slots[i].subsystem.load(std::memory_order_relaxed);
|
||||
e.installed = g_slots[i].installed.load(std::memory_order_relaxed);
|
||||
e.calls = g_slots[i].calls.load(std::memory_order_relaxed);
|
||||
++n;
|
||||
}
|
||||
ipc.publish_hook_entries(entries, n);
|
||||
}
|
||||
|
||||
void hook_registry_reset()
|
||||
{
|
||||
std::scoped_lock lock(g_register_mutex);
|
||||
for (auto& s : g_slots)
|
||||
{
|
||||
s.used.store(0, std::memory_order_relaxed);
|
||||
s.installed.store(0, std::memory_order_relaxed);
|
||||
s.calls.store(0, std::memory_order_relaxed);
|
||||
}
|
||||
g_count.store(0, std::memory_order_release);
|
||||
}
|
||||
|
||||
} // namespace coop::hook
|
||||
Reference in New Issue
Block a user