From dbde351e2234b471cc222f95cef10052d7d64a25 Mon Sep 17 00:00:00 2001 From: Rejdukien Date: Fri, 16 Jan 2026 16:06:38 +0100 Subject: [PATCH] fix(border): release resources before destroying window to prevent access violations When a border is destroyed, the main thread was forcefully releasing D2D resources, while the border window thread might still be trying to use them in its message loop. Releasing the RenderTarget on one thread while another is calling EndDraw on it leads to a use-after-free scenario or invalid state for the COM object, resulting in the crash. This commit applies a fix that moves the resource cleanup logic from the main thread to the window thread. Now, resources are only released during the WM_DESTROY message processing, which guarantees synchronization with other window messages like WM_PAINT. --- komorebi/src/border_manager/border.rs | 7 ++++++- komorebi/src/border_manager/mod.rs | 6 ------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/komorebi/src/border_manager/border.rs b/komorebi/src/border_manager/border.rs index 2d0d1e51..4e13970a 100644 --- a/komorebi/src/border_manager/border.rs +++ b/komorebi/src/border_manager/border.rs @@ -548,7 +548,12 @@ impl Border { LRESULT(0) } WM_DESTROY => { - SetWindowLongPtrW(window, GWLP_USERDATA, 0); + let border_pointer: *mut Border = GetWindowLongPtrW(window, GWLP_USERDATA) as _; + if !border_pointer.is_null() { + (*border_pointer).render_target = None; + (*border_pointer).brushes.clear(); + SetWindowLongPtrW(window, GWLP_USERDATA, 0); + } PostQuitMessage(0); LRESULT(0) } diff --git a/komorebi/src/border_manager/mod.rs b/komorebi/src/border_manager/mod.rs index 0280164d..6326f826 100644 --- a/komorebi/src/border_manager/mod.rs +++ b/komorebi/src/border_manager/mod.rs @@ -767,12 +767,6 @@ fn remove_border( fn destroy_border(border: Box) -> color_eyre::Result<()> { let raw_pointer = Box::into_raw(border); unsafe { - // release d2d resources **BEFORE** destroying window - // this drops render_target and brushes while HWND is still valid - // prevents EndDraw() from accessing freed HWND resources - (*raw_pointer).render_target = None; - (*raw_pointer).brushes.clear(); - // Now safe to destroy window (*raw_pointer).destroy()?; }