Fix 32-bit game crash: call hooked __stdcall functions with stdcall()
SafetyHook's InlineHook::call() invokes the trampoline through a __cdecl pointer (the compiler default on x86). The functions we hook are __stdcall (IDXGISwapChain::Present/Present1, the WASAPI render interfaces, and the WINAPI SwapBuffers/wglSwapBuffers), so on 32-bit both sides cleaned the stack -> ESP imbalance -> Run-Time Check Failure #0 and an instant crash. On x64 every convention collapses to one, so it only bit 32-bit games: Slaps and Beans (Unity/Rewired, 32-bit D3D11) froze the moment the Present hook ran. The user's "crashes as soon as a button is pressed" was the Present, not the button. Switch every __stdcall trampoline call to SafetyHook's stdcall() (a no-op on x64). The XInput/focus hooks were unaffected because they never call the trampoline -- they return synthesized data. Reproduction + regression coverage: - tools/input_probe (coop_input_probe): injects, reports a connected pad, toggles a button, and takes a disable_mask to bisect which subsystem affects a game. Isolated the freeze to the video subsystem live. - hook_selftest_x86 + present_hook_test_x86: the x86 sub-build now builds and runs these (the x64 present_hook_test can't see a one-convention bug). present_hook_test_x86 drives a real swapchain through the trampoline -- it would hit RTC #0 before this fix. - hook_selftest strengthened to exercise every loaded xinput DLL's full export set (GetState, ordinal-100 GetStateEx, GetCapabilities, rumble SetState) and to dump the SharedBlock layout. - protocol.hpp: static_asserts lock the cross-bitness front-of-block offsets (verified byte-identical on x86 and x64). README roadmap trimmed (this milestone done) and a lessons-learned note added on the call()/stdcall() convention trap. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -232,7 +232,12 @@ HRESULT STDMETHODCALLTYPE hk_Present(IDXGISwapChain* sc, UINT sync_interval, UIN
|
||||
{
|
||||
capture_backbuffer(sc);
|
||||
}
|
||||
return g_hk_present.call<HRESULT>(sc, sync_interval, flags);
|
||||
// stdcall(), NOT call(): IDXGISwapChain::Present is __stdcall, but SafetyHook's
|
||||
// call() invokes the trampoline through a __cdecl pointer (the default on x86).
|
||||
// On 32-bit that double-cleans the stack -> ESP imbalance -> Run-Time Check
|
||||
// Failure #0 and an instant crash. On x64 the conventions collapse, so it only
|
||||
// bit 32-bit games (e.g. Slaps and Beans froze the moment it presented).
|
||||
return g_hk_present.stdcall<HRESULT>(sc, sync_interval, flags);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE hk_Present1(IDXGISwapChain1* sc, UINT sync_interval, UINT flags,
|
||||
@@ -248,7 +253,7 @@ HRESULT STDMETHODCALLTYPE hk_Present1(IDXGISwapChain1* sc, UINT sync_interval, U
|
||||
{
|
||||
capture_backbuffer(sc); // IDXGISwapChain1 derives from IDXGISwapChain
|
||||
}
|
||||
return g_hk_present1.call<HRESULT>(sc, sync_interval, flags, params);
|
||||
return g_hk_present1.stdcall<HRESULT>(sc, sync_interval, flags, params); // __stdcall, see hk_Present
|
||||
}
|
||||
|
||||
// Create a throwaway device + swapchain purely to read IDXGISwapChain::Present
|
||||
|
||||
Reference in New Issue
Block a user