From b96a5595003909c634e3dc4f4dd9653488d8d474 Mon Sep 17 00:00:00 2001 From: BlackMark Date: Wed, 24 Jun 2026 02:22:06 +0200 Subject: [PATCH] Clean up a stale Vulkan-layer registration at host startup register_vk_layer writes an HKCU implicit-layer entry that makes the loader pull our DLL into every Vulkan app; it's session-scoped (unregistered on clean exit). If the host crashed or was killed while registered, the entry leaked and kept loading our DLL into every Vulkan process until the next clean run. Add cleanup_stale_vk_layer(), called once at the top of run(): since registration is opt-in per session, anything registered at startup is a crash leftover, so it removes it (delegates to unregister_vk_layer). No-op when nothing is registered. Co-Authored-By: Claude Opus 4.8 --- README.md | 4 ---- host/src/main.cpp | 5 +++++ host/src/vk_layer_setup.cpp | 7 +++++++ host/src/vk_layer_setup.hpp | 6 ++++++ 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0277326..7c1c54e 100644 --- a/README.md +++ b/README.md @@ -113,10 +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. -Features: -- **Stale Vulkan-layer registration cleanup** — remove a leftover `HKCU` implicit-layer entry from a - crashed prior run on host startup. - UX: - **Auto re-attach control** — keep it visible/controllable when not connected (today it can be enabled-but-invisible). diff --git a/host/src/main.cpp b/host/src/main.cpp index 78e0d36..e4ae5d4 100644 --- a/host/src/main.cpp +++ b/host/src/main.cpp @@ -35,6 +35,7 @@ #include "log_panel.hpp" #include "test_harness.hpp" #include "ui/app_chrome.hpp" +#include "vk_layer_setup.hpp" namespace { @@ -316,6 +317,10 @@ bool wait_for_hooked_frame(coop::D3D11Window& window, coop::InjectionPanel& inje int run() { + // Clear any leftover Vulkan-layer registration from a host that crashed while registered, before + // it can keep loading our DLL into every Vulkan app this session. + coop::cleanup_stale_vk_layer(); + coop::D3D11Window window; if (!window.create(L"CoopAllTheThings")) { diff --git a/host/src/vk_layer_setup.cpp b/host/src/vk_layer_setup.cpp index f6b0d61..caea6f3 100644 --- a/host/src/vk_layer_setup.cpp +++ b/host/src/vk_layer_setup.cpp @@ -76,4 +76,11 @@ void unregister_vk_layer() } } +void cleanup_stale_vk_layer() +{ + // At startup nothing of ours should be registered yet (registration is opt-in, this session). So + // a registration present now is a crash leftover -- removing it is exactly unregister_vk_layer(). + unregister_vk_layer(); +} + } // namespace coop diff --git a/host/src/vk_layer_setup.hpp b/host/src/vk_layer_setup.hpp index 3ca53e3..72768e8 100644 --- a/host/src/vk_layer_setup.hpp +++ b/host/src/vk_layer_setup.hpp @@ -21,4 +21,10 @@ bool register_vk_layer(const std::wstring& target_image); // on host shutdown as a belt-and-suspenders cleanup). void unregister_vk_layer(); +// Call once at host startup. The layer is session-scoped (registered only while the tool runs, +// unregistered on clean exit), so any registration present at startup is a leftover from a host that +// crashed/was killed while registered -- which would otherwise keep loading our DLL into EVERY Vulkan +// app until the next clean run. This removes that stale registration. No-op if nothing is registered. +void cleanup_stale_vk_layer(); + } // namespace coop