[PATCH v4 0/2] MR10324: win32u: Fix deadlock when setting hdc_src to the window's self DC.
Supersed !9180 and !9177 When hdc_src is the window's self DC, Within the NtUserUpdateLayeredWindow function, there exists the following call sequence: window_surface_lock()-\>NtGdiAlphaBlend()-\>window_surface_unlock(). However, the implementation of the NtGdiAlphaBlend() function calls the windrv_GetImage() function, which internally invokes lock_surface() and unlock_surface(). In this situation, window_surface_lock() is called first to acquire a lock. When lock_surface() is subsequently called to acquire the same lock, the initial lock has not yet been released, ultimately resulting in a deadlock. -- v4: win32u/tests: Add tests for hdc_src is window's self dc when calling UpdateLayeredWindow. win32u: Fix deadlock when setting hdc_src to the window's self DC. https://gitlab.winehq.org/wine/wine/-/merge_requests/10324
From: Zhao Yi <zhaoyi@uniontech.com> Signed-off-by: Zhao Yi <zhaoyi@uniontech.com> --- dlls/win32u/window.c | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/dlls/win32u/window.c b/dlls/win32u/window.c index fc129df0785..bc68d175302 100644 --- a/dlls/win32u/window.c +++ b/dlls/win32u/window.c @@ -2688,27 +2688,57 @@ BOOL WINAPI NtUserUpdateLayeredWindow( HWND hwnd, HDC hdc_dst, const POINT *pts_ BLENDFUNCTION src_blend = { AC_SRC_OVER, 0, 255, 0 }; RECT rect = new_rects.window, src_rect; HDC hdc = NULL; + HDC hdc_src_tmp = hdc_src; + BOOL use_tmp_dc = FALSE; + HDC hdc_tmp = NULL; + HBITMAP hbm_tmp = NULL; OffsetRect( &rect, -rect.left, -rect.top ); intersect_rect( &rect, &rect, &surface_rect ); + if (dirty) intersect_rect( &rect, &rect, dirty ); + src_rect = rect; + if (pts_src) OffsetRect( &src_rect, pts_src->x, pts_src->y ); + + if (NtUserWindowFromDC( hdc_src ) == hwnd) + { + if (!(hdc_tmp = NtGdiCreateCompatibleDC( 0 ))) goto done; + if (!(hbm_tmp = NtGdiCreateCompatibleBitmap( hdc_src, rect.right - rect.left, rect.bottom - rect.top ))) + { + NtGdiDeleteObjectApp( hdc_tmp ); + goto done; + } + NtGdiSelectBitmap( hdc_tmp, hbm_tmp ); + NtGdiBitBlt( hdc_tmp, 0, 0, rect.right - rect.left, rect.bottom - rect.top, hdc_src, src_rect.left, src_rect.top, SRCCOPY, 0, 0 ); + hdc_src_tmp = hdc_tmp; + use_tmp_dc = TRUE; + } - if (!(hdc = NtGdiCreateCompatibleDC( 0 ))) goto done; + if (!(hdc = NtGdiCreateCompatibleDC( 0 ))) + { + if (use_tmp_dc) + { + NtGdiDeleteObjectApp( hbm_tmp ); + NtGdiDeleteObjectApp( hdc_tmp ); + } + goto done; + } window_surface_lock( surface ); NtGdiSelectBitmap( hdc, surface->color_bitmap ); - if (dirty) intersect_rect( &rect, &rect, dirty ); NtGdiPatBlt( hdc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, BLACKNESS ); - - src_rect = rect; - if (pts_src) OffsetRect( &src_rect, pts_src->x, pts_src->y ); NtGdiTransformPoints( hdc_src, (POINT *)&src_rect, (POINT *)&src_rect, 2, NtGdiDPtoLP ); ret = NtGdiAlphaBlend( hdc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, - hdc_src, src_rect.left, src_rect.top, src_rect.right - src_rect.left, src_rect.bottom - src_rect.top, + hdc_src_tmp, src_rect.left, src_rect.top, src_rect.right - src_rect.left, src_rect.bottom - src_rect.top, *(DWORD *)&src_blend, 0 ); if (ret) add_bounds_rect( &surface->bounds, &rect ); NtGdiDeleteObjectApp( hdc ); + if (use_tmp_dc) + { + NtGdiDeleteObjectApp( hbm_tmp ); + NtGdiDeleteObjectApp( hdc_tmp ); + } window_surface_unlock( surface ); if (!(flags & ULW_COLORKEY)) key = CLR_INVALID; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10324
From: Zhao Yi <zhaoyi@uniontech.com> Signed-off-by: Zhao Yi <zhaoyi@uniontech.com> --- dlls/user32/tests/win.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/dlls/user32/tests/win.c b/dlls/user32/tests/win.c index 135b11ddac4..6ef13eaeaa3 100644 --- a/dlls/user32/tests/win.c +++ b/dlls/user32/tests/win.c @@ -9297,6 +9297,12 @@ static void test_layered_window(void) HBITMAP hbm; BOOL ret; MSG msg; + HDC hdc_from_hwnd; + BLENDFUNCTION bf; + bf.AlphaFormat = 0; + bf.BlendFlags = 0; + bf.BlendOp = 0; + bf.SourceConstantAlpha = 180; if (!pGetLayeredWindowAttributes || !pSetLayeredWindowAttributes || !pUpdateLayeredWindow) { @@ -9320,10 +9326,18 @@ static void test_layered_window(void) ret = pSetLayeredWindowAttributes( hwnd, 0, 0, LWA_ALPHA ); ok( !ret, "SetLayeredWindowAttributes should fail on non-layered window\n" ); SetWindowLongA( hwnd, GWL_EXSTYLE, GetWindowLongA(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED ); + hdc_from_hwnd = GetDC( hwnd ); + ret = pUpdateLayeredWindow( hwnd, 0, NULL, &sz, hdc_from_hwnd, &pt, 0, &bf, ULW_ALPHA ); + ReleaseDC ( hwnd, hdc_from_hwnd ); + ok( ret, "UpdateLayeredWindow should succeed on layered window\n" ); ret = pGetLayeredWindowAttributes( hwnd, &key, &alpha, &flags ); ok( !ret, "GetLayeredWindowAttributes should fail on layered but not initialized window\n" ); ret = pUpdateLayeredWindow( hwnd, 0, NULL, &sz, hdc, &pt, 0, NULL, ULW_OPAQUE ); ok( ret, "UpdateLayeredWindow should succeed on layered window\n" ); + hdc_from_hwnd = GetDC( hwnd ); + ret = pUpdateLayeredWindow( hwnd, 0, NULL, &sz, hdc_from_hwnd, &pt, 0, &bf, ULW_ALPHA ); + ReleaseDC ( hwnd, hdc_from_hwnd ); + ok( ret, "UpdateLayeredWindow should succeed on layered window\n" ); ret = pGetLayeredWindowAttributes( hwnd, &key, &alpha, &flags ); ok( !ret, "GetLayeredWindowAttributes should fail on layered but not initialized window\n" ); ret = pSetLayeredWindowAttributes( hwnd, 0x123456, 44, LWA_ALPHA ); @@ -9459,6 +9473,10 @@ static void test_layered_window(void) SetWindowLongA( hwnd, GWL_EXSTYLE, GetWindowLongA(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED ); ret = pUpdateLayeredWindow( hwnd, 0, NULL, &sz, hdc, &pt, 0, NULL, ULW_OPAQUE ); ok( ret, "UpdateLayeredWindow should succeed on layered window\n" ); + hdc_from_hwnd = GetDC( hwnd ); + ret = pUpdateLayeredWindow( hwnd, 0, NULL, &sz, hdc_from_hwnd, &pt, 0, &bf, ULW_ALPHA ); + ReleaseDC ( hwnd, hdc_from_hwnd ); + ok( ret, "UpdateLayeredWindow should succeed on layered window\n" ); ret = pSetLayeredWindowAttributes( hwnd, 0, 255, LWA_ALPHA ); ok( ret, "SetLayeredWindowAttributes should succeed on layered window\n" ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10324
On Wed Mar 25 13:49:09 2026 +0000, Zhao Yi wrote:
But I think this has little to do with the bug. We cannot confirm whether hdc_dst is consistent with hdc_src when hdc_src is the window's own DC. So I think the solution here is to introduce a temporary DC to store hdc_src when hdc_src is the window's own DC. This is unrelated to hdc_dst, I only meant to ask what happens visually when hdc_src is a DC of the window itself. After a quick test it seems that, this for instance doesn't modify or shift the window contents at all:
``` pos.x = 10; pos.y = 10; hdc = GetDC( hwnd ); UpdateLayeredWindow( hwnd, NULL, NULL, NULL, hdc, &pos, 0, &blend, ULW_ALPHA ); ReleaseDC( hwnd, hdc ); ``` If that's true, we can make things simpler by simply ignoring surface content updates when hdc is the window DC. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10324#note_134644
Zhao Yi (@Zhaoyi) commented about dlls/win32u/window.c:
apply_window_pos( hwnd, 0, swp_flags, surface, &new_rects, NULL ); if (!surface) return FALSE;
if (!hdc_src || surface == &dummy_surface)
I agree with your view regarding the scenario you tested above: we can ignore surface content updates when hdc is the window DC. And we may probably fix this bug as follws: ```suggestion:-0+0 if (!hdc_src || surface == &dummy_surface || NtUserWindowFromDC( hdc_src ) == hwnd) ``` -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10324#note_134745
participants (3)
-
Rémi Bernon (@rbernon) -
Zhao Yi -
Zhao Yi (@Zhaoyi)