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.
This commit is contained in:
Rejdukien
2026-01-16 16:06:38 +01:00
committed by LGUG2Z
parent 95838fc896
commit dbde351e22
2 changed files with 6 additions and 7 deletions

View File

@@ -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)
}

View File

@@ -767,12 +767,6 @@ fn remove_border(
fn destroy_border(border: Box<Border>) -> 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()?;
}