Phase 1a: input forwarding via DLL injection + XInput hook
The host can now inject coop_hook.dll into a running game and forward controller state to it over shared memory, so the game reads the host's (eventually the guest's) input and nothing else. - hook/: coop_hook.dll. DllMain spawns a worker that opens the shared-memory channel (named by the game's pid) and installs SafetyHook inline hooks on XInputGetState/GetStateEx/GetCapabilities/SetState. Detours synthesize state from shared memory; unmanaged slots report disconnected, hiding physical pads. - host/: process picker (Toolhelp32), CreateRemoteThread(LoadLibraryW) injector with an IsWow64Process2 bitness guard, IPC server publishing pads each frame, and an ImGui Injection panel wiring it together. - tests/: hook_selftest exercises the IPC seqlock + hook detours in-process (no game/controller needed); passes. Build: SafetyHook wired in (COOP_BUILD_HOOK=ON), Zydis via FetchContent. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
60
hook/src/dllmain.cpp
Normal file
60
hook/src/dllmain.cpp
Normal file
@@ -0,0 +1,60 @@
|
||||
// coop_hook.dll -- injected into the target game by the host.
|
||||
//
|
||||
// On load it opens the host's shared-memory channel (named by this process's
|
||||
// pid), then hooks XInput so the game reads the forwarded controller state. All
|
||||
// real work happens on a worker thread; DllMain only kicks it off to stay clear
|
||||
// of the loader lock.
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "ipc_client.hpp"
|
||||
#include "xinput_hook.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
coop::hook::IpcClient g_ipc;
|
||||
|
||||
DWORD WINAPI init_thread(LPVOID)
|
||||
{
|
||||
// The host creates the mapping around injection time; give it a few seconds.
|
||||
if (!g_ipc.connect(/*attempts=*/200, /*delay_ms=*/25))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// XInput may not be loaded yet at this point (games often load it lazily on
|
||||
// first controller use), so keep retrying until a module appears.
|
||||
for (int i = 0; i < 400 && !coop::hook::install_xinput_hooks(g_ipc); ++i)
|
||||
{
|
||||
Sleep(25);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE module, DWORD reason, LPVOID reserved)
|
||||
{
|
||||
switch (reason)
|
||||
{
|
||||
case DLL_PROCESS_ATTACH:
|
||||
DisableThreadLibraryCalls(module);
|
||||
if (HANDLE thread = CreateThread(nullptr, 0, &init_thread, nullptr, 0, nullptr))
|
||||
{
|
||||
CloseHandle(thread);
|
||||
}
|
||||
break;
|
||||
case DLL_PROCESS_DETACH:
|
||||
// Skip cleanup when the process is tearing down (reserved != null): the
|
||||
// loader is already unwinding and touching other modules is unsafe.
|
||||
if (reserved == nullptr)
|
||||
{
|
||||
coop::hook::remove_xinput_hooks();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
Reference in New Issue
Block a user