diff --git a/README.md b/README.md index 100d7f8..8c6bd07 100644 --- a/README.md +++ b/README.md @@ -113,12 +113,6 @@ default** and covers anything the hooked path doesn't. From an in-depth review pass. Each item is fixed test-first (a failing test, then the fix) and lands as its own commit; "verify" items are confirmed real before any change, and dropped if not. -Robustness (verify, then fix if real): -- **Permissive injector bitness gate** — `IsWow64Process2` failure is treated as 64-bit; fall back to - `IsWow64Process` instead of mis-injecting. -- **Ignored HRESULT/BOOL returns** — `CreateShaderResourceView`, `GetClientRect`/`ClientToScreen`, - `XInputSetState`, `GetExitCodeProcess`: check and surface/handle. - Cross-process / ABI: - **Tie `kProtocolVersion` to the layout** — `static_assert` `sizeof(SharedBlock)` + the full offset set, and make `hook_selftest`'s `dump_layout` assert instead of only printing. diff --git a/host/src/capture/window_capture.cpp b/host/src/capture/window_capture.cpp index 5c1661c..0999312 100644 --- a/host/src/capture/window_capture.cpp +++ b/host/src/capture/window_capture.cpp @@ -188,9 +188,18 @@ void WindowCapture::draw_latest(FrameRenderer& renderer, ID3D11DeviceContext* ct dst.MiscFlags = 0; if (SUCCEEDED(device_->CreateTexture2D(&dst, nullptr, latest_.GetAddressOf()))) { - device_->CreateShaderResourceView(latest_.Get(), nullptr, latest_srv_.GetAddressOf()); - width_ = desc.Width; - height_ = desc.Height; + if (SUCCEEDED(device_->CreateShaderResourceView(latest_.Get(), nullptr, + latest_srv_.GetAddressOf()))) + { + width_ = desc.Width; + height_ = desc.Height; + } + else + { + // Drop the texture so the (latest_ == nullptr) guard retries next frame instead + // of leaving a null SRV (a silently black mirror) until the next resize. + latest_.Reset(); + } } } diff --git a/host/src/inject/injector.cpp b/host/src/inject/injector.cpp index 8639646..95b6d2b 100644 --- a/host/src/inject/injector.cpp +++ b/host/src/inject/injector.cpp @@ -50,7 +50,15 @@ bool is_wow64_process(HANDLE process) { return process_machine != IMAGE_FILE_MACHINE_UNKNOWN; } - return false; // be permissive if the query is unavailable + // IsWow64Process2 failed -- fall back to the legacy query rather than guessing "native", since + // guessing wrong sends the x64 DLL into a 32-bit target (which can't load it). Only if BOTH + // queries fail do we fall back to permissive. + BOOL wow64 = FALSE; + if (IsWow64Process(process, &wow64)) + { + return wow64 != FALSE; + } + return false; // both queries failed; best-effort assume native } // Replace the trailing file name of `path` with `name` (same directory). @@ -86,12 +94,13 @@ InjectResult inject_via_helper(unsigned long pid, const std::wstring& dll_path) } WaitForSingleObject(pi.hProcess, INFINITE); DWORD exit_code = 1; - GetExitCodeProcess(pi.hProcess, &exit_code); + const BOOL got = GetExitCodeProcess(pi.hProcess, &exit_code); + const DWORD err = got ? exit_code : GetLastError(); // on a failed query, surface the OS error CloseHandle(pi.hThread); CloseHandle(pi.hProcess); - if (exit_code != 0) + if (!got || exit_code != 0) { - return InjectResult{InjectStatus::HelperFailed, exit_code}; + return InjectResult{InjectStatus::HelperFailed, err}; } return InjectResult{InjectStatus::Ok, 0}; } diff --git a/host/src/input/xinput_source.cpp b/host/src/input/xinput_source.cpp index 7125920..d1eaf65 100644 --- a/host/src/input/xinput_source.cpp +++ b/host/src/input/xinput_source.cpp @@ -46,7 +46,9 @@ void XInputSource::set_rumble(int slot, std::uint16_t left, std::uint16_t right) return; } XINPUT_VIBRATION v{left, right}; - XInputSetState(static_cast(slot), &v); + // Best-effort: if the device just disconnected this returns ERROR_DEVICE_NOT_CONNECTED, but rumble + // is fire-and-forget (the next connected-pad refresh re-syncs state), so the result isn't acted on. + (void)XInputSetState(static_cast(slot), &v); } } // namespace coop