Wine-Devel
Threads by month
- ----- 2026 -----
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2004 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2003 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2002 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2001 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- 11 participants
- 84520 discussions
[PATCH 5/5] uxtheme: Make IsThemeDialogTextureEnabled() return TRUE for all valid flags without ETDT_DISABLE.
by Zhiyi Zhang Jan. 21, 2022
by Zhiyi Zhang Jan. 21, 2022
Jan. 21, 2022
Signed-off-by: Zhiyi Zhang <zzhang(a)codeweavers.com>
---
dlls/uxtheme/draw.c | 7 ++-----
dlls/uxtheme/tests/system.c | 17 ++++++-----------
2 files changed, 8 insertions(+), 16 deletions(-)
diff --git a/dlls/uxtheme/draw.c b/dlls/uxtheme/draw.c
index a7942b03f97..b6e5f0230e5 100644
--- a/dlls/uxtheme/draw.c
+++ b/dlls/uxtheme/draw.c
@@ -69,14 +69,11 @@ HRESULT WINAPI EnableThemeDialogTexture(HWND hwnd, DWORD dwFlags)
BOOL WINAPI IsThemeDialogTextureEnabled(HWND hwnd)
{
DWORD dwDialogTextureFlags;
+
TRACE("(%p)\n", hwnd);
dwDialogTextureFlags = HandleToUlong( GetPropW( hwnd, (LPCWSTR)MAKEINTATOM(atDialogThemeEnabled) ));
- if (dwDialogTextureFlags == 0)
- /* Means EnableThemeDialogTexture wasn't called for this dialog */
- return TRUE;
-
- return (dwDialogTextureFlags & ETDT_ENABLE) && !(dwDialogTextureFlags & ETDT_DISABLE);
+ return dwDialogTextureFlags && !(dwDialogTextureFlags & ETDT_DISABLE);
}
/***********************************************************************
diff --git a/dlls/uxtheme/tests/system.c b/dlls/uxtheme/tests/system.c
index 03e435eb463..93994fa8364 100644
--- a/dlls/uxtheme/tests/system.c
+++ b/dlls/uxtheme/tests/system.c
@@ -1649,12 +1649,12 @@ static void test_EnableThemeDialogTexture(void)
}
invalid_flag_tests[] =
{
- {0, FALSE},
+ {0, FALSE, TRUE},
{ETDT_DISABLE | ETDT_ENABLE, FALSE},
{ETDT_ENABLETAB | ETDT_ENABLEAEROWIZARDTAB, TRUE},
- {ETDT_USETABTEXTURE | ETDT_USEAEROWIZARDTABTEXTURE, TRUE, TRUE},
+ {ETDT_USETABTEXTURE | ETDT_USEAEROWIZARDTABTEXTURE, TRUE},
{ETDT_VALIDBITS, FALSE},
- {~ETDT_VALIDBITS, FALSE},
+ {~ETDT_VALIDBITS, FALSE, TRUE},
{~ETDT_VALIDBITS | ETDT_DISABLE, FALSE}
};
@@ -1746,7 +1746,6 @@ static void test_EnableThemeDialogTexture(void)
/* Test dialog texture is disabled by default. EnableThemeDialogTexture() needs to be called */
ret = IsThemeDialogTextureEnabled(dialog);
- todo_wine
ok(!ret, "Expected theme dialog texture disabled.\n");
ok(GetWindowTheme(dialog) == NULL, "Expected NULL theme handle.\n");
@@ -2021,7 +2020,7 @@ static void test_EnableThemeDialogTexture(void)
"EnableThemeDialogTexture first flag", FALSE);
ret = IsThemeDialogTextureEnabled(dialog);
/* Non-zero flags without ETDT_DISABLE enables dialog texture */
- todo_wine_if(flags[i] == ETDT_USETABTEXTURE || flags[i] == ETDT_USEAEROWIZARDTABTEXTURE)
+ todo_wine_if(flags[i] == 0)
ok(ret == (!(flags[i] & ETDT_DISABLE) && flags[i]), "Wrong dialog texture status.\n");
child = GetDlgItem(dialog, 100);
@@ -2044,10 +2043,9 @@ static void test_EnableThemeDialogTexture(void)
ret = IsThemeDialogTextureEnabled(dialog);
/* If the flag is zero, it will have previous dialog texture status */
if (flags[j])
- todo_wine_if(flags[j] == ETDT_USETABTEXTURE || flags[j] == ETDT_USEAEROWIZARDTABTEXTURE)
ok(ret == !(flags[j] & ETDT_DISABLE), "Wrong dialog texture status.\n");
else
- todo_wine_if((!(flags[i] & ETDT_DISABLE) && flags[i]))
+ todo_wine_if(!flags[i] || flags[i] == ETDT_DISABLE || flags[i] == (ETDT_DISABLE | ETDT_ENABLE))
ok(ret == (!(flags[i] & ETDT_DISABLE) && flags[i]), "Wrong dialog texture status.\n");
lr = SendMessageA(dialog, WM_ERASEBKGND, (WPARAM)child_hdc, 0);
ok(lr != 0, "WM_ERASEBKGND failed.\n");
@@ -2159,12 +2157,12 @@ static void test_EnableThemeDialogTexture(void)
0, 0, NULL);
ok(hwnd != NULL, "CreateWindowA failed, error %d.\n", GetLastError());
ret = IsThemeDialogTextureEnabled(hwnd);
- todo_wine
ok(!ret, "Wrong dialog texture status.\n");
child = CreateWindowA(WC_STATICA, "child", WS_CHILD | WS_VISIBLE, 0, 0, 50, 50, hwnd, 0, 0,
NULL);
ok(child != NULL, "CreateWindowA failed, error %d.\n", GetLastError());
ret = IsThemeDialogTextureEnabled(hwnd);
+ todo_wine
ok(ret, "Wrong dialog texture status.\n");
/* Test that if you move the child control to another window, it doesn't enables tab texture for
@@ -2173,18 +2171,15 @@ static void test_EnableThemeDialogTexture(void)
0, 0, NULL);
ok(hwnd2 != NULL, "CreateWindowA failed, error %d.\n", GetLastError());
ret = IsThemeDialogTextureEnabled(hwnd2);
- todo_wine
ok(!ret, "Wrong dialog texture status.\n");
SetParent(child, hwnd2);
ok(GetParent(child) == hwnd2, "Wrong parent.\n");
ret = IsThemeDialogTextureEnabled(hwnd2);
- todo_wine
ok(!ret, "Wrong dialog texture status.\n");
InvalidateRect(child, NULL, TRUE);
flush_events();
ret = IsThemeDialogTextureEnabled(hwnd2);
- todo_wine
ok(!ret, "Wrong dialog texture status.\n");
DestroyWindow(hwnd2);
--
2.32.0
1
0
[PATCH 4/5] uxtheme: Do not call SetWindowTheme() in EnableThemeDialogTexture().
by Zhiyi Zhang Jan. 21, 2022
by Zhiyi Zhang Jan. 21, 2022
Jan. 21, 2022
It's no longer necessary to call SetWindowTheme() in EnableThemeDialogTexture() because dialog
theming doesn't need to open theme handles associated with the dialog. Calling SetWindowTheme() also
sends extra messages.
Signed-off-by: Zhiyi Zhang <zzhang(a)codeweavers.com>
---
dlls/uxtheme/draw.c | 7 +------
dlls/uxtheme/tests/system.c | 4 ++--
2 files changed, 3 insertions(+), 8 deletions(-)
diff --git a/dlls/uxtheme/draw.c b/dlls/uxtheme/draw.c
index 04ea1e79df9..a7942b03f97 100644
--- a/dlls/uxtheme/draw.c
+++ b/dlls/uxtheme/draw.c
@@ -60,12 +60,7 @@ HRESULT WINAPI EnableThemeDialogTexture(HWND hwnd, DWORD dwFlags)
res = SetPropW (hwnd, (LPCWSTR)MAKEINTATOM(atDialogThemeEnabled),
UlongToHandle(dwFlags|0x80000000));
/* 0x80000000 serves as a "flags set" flag */
- if (!res)
- return HRESULT_FROM_WIN32(GetLastError());
- if (dwFlags & ETDT_USETABTEXTURE)
- return SetWindowTheme (hwnd, NULL, L"Tab");
- else
- return SetWindowTheme (hwnd, NULL, NULL);
+ return res ? S_OK : HRESULT_FROM_WIN32(GetLastError());
}
/***********************************************************************
diff --git a/dlls/uxtheme/tests/system.c b/dlls/uxtheme/tests/system.c
index 3f8f32a9a7b..03e435eb463 100644
--- a/dlls/uxtheme/tests/system.c
+++ b/dlls/uxtheme/tests/system.c
@@ -2018,7 +2018,7 @@ static void test_EnableThemeDialogTexture(void)
hr = EnableThemeDialogTexture(dialog, flags[i]);
ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#x.\n", hr);
ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq,
- "EnableThemeDialogTexture first flag", TRUE);
+ "EnableThemeDialogTexture first flag", FALSE);
ret = IsThemeDialogTextureEnabled(dialog);
/* Non-zero flags without ETDT_DISABLE enables dialog texture */
todo_wine_if(flags[i] == ETDT_USETABTEXTURE || flags[i] == ETDT_USEAEROWIZARDTABTEXTURE)
@@ -2040,7 +2040,7 @@ static void test_EnableThemeDialogTexture(void)
hr = EnableThemeDialogTexture(dialog, flags[j]);
ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#x.\n", hr);
ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq,
- "EnableThemeDialogTexture second flag", TRUE);
+ "EnableThemeDialogTexture second flag", FALSE);
ret = IsThemeDialogTextureEnabled(dialog);
/* If the flag is zero, it will have previous dialog texture status */
if (flags[j])
--
2.32.0
1
0
[PATCH 3/5] comctl32/tests: Remove WM_CTLCOLORSTATIC tests for property sheets.
by Zhiyi Zhang Jan. 21, 2022
by Zhiyi Zhang Jan. 21, 2022
Jan. 21, 2022
The removed tests are covered by test_EnableThemeDialogTexture() in uxtheme/tests/system.c.
Signed-off-by: Zhiyi Zhang <zzhang(a)codeweavers.com>
---
dlls/comctl32/tests/propsheet.c | 229 ++------------------------------
1 file changed, 14 insertions(+), 215 deletions(-)
diff --git a/dlls/comctl32/tests/propsheet.c b/dlls/comctl32/tests/propsheet.c
index affd21da825..0284311c81b 100644
--- a/dlls/comctl32/tests/propsheet.c
+++ b/dlls/comctl32/tests/propsheet.c
@@ -20,8 +20,6 @@
#include <windows.h>
#include <commctrl.h>
-#include <uxtheme.h>
-#include <vsstyle.h>
#include "msg.h"
#include "resources.h"
@@ -45,13 +43,8 @@ static HPROPSHEETPAGE (WINAPI *pCreatePropertySheetPageW)(const PROPSHEETPAGEW *
static BOOL (WINAPI *pDestroyPropertySheetPage)(HPROPSHEETPAGE proppage);
static INT_PTR (WINAPI *pPropertySheetA)(const PROPSHEETHEADERA *header);
-static HRESULT (WINAPI *pCloseThemeData)(HTHEME);
-static HRESULT (WINAPI *pEnableThemeDialogTexture)(HWND, DWORD);
-static HRESULT (WINAPI *pGetThemePartSize)(HTHEME, HDC, int, int, RECT *, THEMESIZE, SIZE *);
-static HTHEME (WINAPI *pGetWindowTheme)(HWND);
static BOOL (WINAPI *pIsThemeActive)(void);
static BOOL (WINAPI *pIsThemeDialogTextureEnabled)(HWND);
-static HTHEME (WINAPI *pOpenThemeData)(HWND, LPCWSTR);
static void detect_locale(void)
{
@@ -1198,26 +1191,12 @@ static void test_bad_control_class(void)
DestroyWindow((HWND)ret);
}
-static BOOL handle_WM_CTLCOLORSTATIC;
-
static INT_PTR CALLBACK test_WM_CTLCOLORSTATIC_proc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
- static HWND child;
-
switch(msg)
{
case WM_INITDIALOG:
sheethwnd = hwnd;
- child = CreateWindowA(WC_STATICA, "child", WS_CHILD | WS_VISIBLE, 1, 2, 50, 50, hwnd,
- (HMENU)100, 0, NULL);
- ok(child != NULL, "CreateWindowA failed, error %d.\n", GetLastError());
- return FALSE;
-
- case WM_CTLCOLORSTATIC:
- return (INT_PTR)(handle_WM_CTLCOLORSTATIC ? GetSysColorBrush(COLOR_MENU) : 0);
-
- case WM_CLOSE:
- DestroyWindow(child);
return FALSE;
default:
@@ -1225,26 +1204,16 @@ static INT_PTR CALLBACK test_WM_CTLCOLORSTATIC_proc(HWND hwnd, UINT msg, WPARAM
}
}
-static void test_WM_CTLCOLORSTATIC(void)
+static void test_page_dialog_texture(void)
{
- HWND hdlg, child, parent, hwnd;
- COLORREF color, old_color;
- int mode, old_mode, count;
- HBRUSH hbrush, hbrush2;
HPROPSHEETPAGE hpsp[1];
PROPSHEETHEADERA psh;
- HDC child_hdc, hdc;
- LOGBRUSH log_brush;
PROPSHEETPAGEA psp;
ULONG_PTR dlgproc;
- WNDCLASSA cls;
- HTHEME theme;
- DWORD error;
- BITMAP bmp;
- HRESULT hr;
- POINT org;
- SIZE size;
+ HWND hdlg, hwnd;
+ HBRUSH hbrush;
BOOL ret;
+ HDC hdc;
memset(&psp, 0, sizeof(psp));
psp.dwSize = sizeof(psp);
@@ -1265,193 +1234,28 @@ static void test_WM_CTLCOLORSTATIC(void)
ok(hdlg != INVALID_HANDLE_VALUE, "Got invalid handle value %p.\n", hdlg);
flush_events();
- child = GetDlgItem(sheethwnd, 100);
- ok(child != NULL, "Failed to get child static control, error %d.\n", GetLastError());
- parent = GetParent(child);
- ok(parent == sheethwnd, "Expected parent %p, got %p.\n", sheethwnd, parent);
- ok(sheethwnd != hdlg, "Expected handles not equal.\n");
-
/* Test that page dialog procedure is unchanged */
dlgproc = GetWindowLongPtrA(sheethwnd, DWLP_DLGPROC);
ok(dlgproc == (ULONG_PTR)test_WM_CTLCOLORSTATIC_proc, "Unexpected dlgproc %#lx.\n", dlgproc);
- child_hdc = GetDC(child);
- old_mode = SetBkMode(child_hdc, OPAQUE);
- ok(old_mode != 0, "SetBkMode failed.\n");
- old_color = SetBkColor(child_hdc, 0xaa5511);
- ok(old_color != CLR_INVALID, "SetBkColor failed.\n");
-
- /* Test that brush origin is at (0,0) */
- ret = GetBrushOrgEx(child_hdc, &org);
- ok(ret, "GetBrushOrgEx failed, error %u.\n", GetLastError());
- ok(org.x == 0 && org.y == 0, "Expected (0,0), got %s.\n", wine_dbgstr_point(&org));
-
/* Test that theme dialog texture is enabled for comctl32 v6, even when theming is off */
ret = pIsThemeDialogTextureEnabled(sheethwnd);
todo_wine_if(!is_v6)
ok(ret == is_v6, "Wrong theme dialog texture status.\n");
- hbrush = (HBRUSH)SendMessageW(sheethwnd, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
+ hwnd = CreateWindowA(WC_EDITA, "child", WS_POPUP | WS_VISIBLE, 1, 2, 50, 50, 0, 0, 0, NULL);
+ ok(hwnd != NULL, "CreateWindowA failed, error %d.\n", GetLastError());
+ hdc = GetDC(hwnd);
+
+ hbrush = (HBRUSH)SendMessageW(sheethwnd, WM_CTLCOLORSTATIC, (WPARAM)hdc, (LPARAM)hwnd);
if (is_v6 && is_theme_active)
{
- /* Test that brush origin is changed after WM_CTLCOLORSTATIC */
- ret = GetBrushOrgEx(child_hdc, &org);
- ok(ret, "GetBrushOrgEx failed, error %u.\n", GetLastError());
- ok(org.x == -1 && org.y == -2, "Expected (-1,-2), got %s.\n", wine_dbgstr_point(&org));
-
- /* Test that device context is set to transparent after WM_CTLCOLORSTATIC */
- mode = SetBkMode(child_hdc, old_mode);
- ok(mode == TRANSPARENT, "Expected mode %#x, got %#x.\n", TRANSPARENT, mode);
-
- /* Test that the brush is a pattern brush created from the tab body bitmap in the theme */
+ /* Test that dialog tab texture is enabled even without any child controls in the dialog */
ok(hbrush != GetSysColorBrush(COLOR_BTNFACE), "Expected a different brush.\n");
- ok(hbrush != GetStockObject(NULL_BRUSH), "Expected a different brush.\n");
- hbrush2 = SelectObject(child_hdc, GetSysColorBrush(COLOR_BTNFACE));
- ok(hbrush2 != hbrush, "Expected a different brush.\n");
-
- memset(&log_brush, 0, sizeof(log_brush));
- count = GetObjectA(hbrush, sizeof(log_brush), &log_brush);
- ok(count == sizeof(log_brush), "GetObjectA failed, error %u.\n", GetLastError());
- ok(log_brush.lbColor == 0, "Expected brush color %#x, got %#x.\n", 0, log_brush.lbColor);
- ok(log_brush.lbStyle == BS_PATTERN, "Expected brush style %#x, got %#x.\n", BS_PATTERN,
- log_brush.lbStyle);
-
- memset(&bmp, 0, sizeof(bmp));
- count = GetObjectA((HBITMAP)log_brush.lbHatch, sizeof(bmp), &bmp);
- ok(count == sizeof(bmp), "GetObjectA failed, error %u.\n", GetLastError());
-
- ok(pGetWindowTheme(hdlg) == NULL, "Expected NULL theme handle.\n");
- ok(pGetWindowTheme(sheethwnd) == NULL, "Expected NULL theme handle.\n");
-
- memset(&cls, 0, sizeof(cls));
- cls.lpfnWndProc = DefWindowProcA;
- cls.hInstance = GetModuleHandleA(NULL);
- cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
- cls.hbrBackground = GetStockObject(WHITE_BRUSH);
- cls.lpszClassName = "TestClass";
- RegisterClassA(&cls);
-
- hwnd = CreateWindowA("TestClass", "test", WS_POPUP | WS_VISIBLE, 0, 0, 4, 4, 0, 0, 0, NULL);
- ok(hwnd != NULL, "CreateWindowA failed, error %d.\n", GetLastError());
- theme = pOpenThemeData(hwnd, L"Tab");
- ok(theme != NULL, "OpenThemeData failed.\n");
-
- size.cx = 0;
- size.cy = 0;
- hr = pGetThemePartSize(theme, NULL, TABP_BODY, 0, NULL, TS_TRUE, &size);
- ok(hr == S_OK, "GetThemePartSize failed, hr %#x.\n", hr);
- ok(bmp.bmWidth == size.cx, "Expected width %d, got %d.\n", size.cx, bmp.bmWidth);
- ok(bmp.bmHeight == size.cy, "Expected height %d, got %d.\n", size.cy, bmp.bmHeight);
-
- pCloseThemeData(theme);
-
- /* Test that other controls besides static controls also get the same brush */
- hdc = GetDC(hwnd);
- old_mode = SetBkMode(hdc, OPAQUE);
- ok(old_mode != 0, "SetBkMode failed.\n");
- hbrush2 = (HBRUSH)SendMessageW(sheethwnd, WM_CTLCOLORSTATIC, (WPARAM)hdc, (LPARAM)hwnd);
- ok(hbrush2 == hbrush, "Expected the same brush.\n");
- mode = SetBkMode(hdc, old_mode);
- ok(mode == TRANSPARENT, "Expected mode %#x, got %#x.\n", TRANSPARENT, mode);
- ReleaseDC(hwnd, hdc);
- DestroyWindow(hwnd);
- UnregisterClassA("TestClass", GetModuleHandleA(NULL));
-
- /* Test disabling theme dialog texture should change the brush */
- hbrush = (HBRUSH)SendMessageW(sheethwnd, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
- hr = pEnableThemeDialogTexture(sheethwnd, ETDT_DISABLE);
- ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#x.\n", hr);
- hbrush2 = (HBRUSH)SendMessageW(sheethwnd, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
- ok(hbrush2 != hbrush, "Expected a different brush.\n");
- ok(hbrush2 == GetSysColorBrush(COLOR_BTNFACE), "Expected brush %p, got %p.\n",
- GetSysColorBrush(COLOR_BTNFACE), hbrush2);
-
- /* Test re-enabling theme dialog texture with ETDT_ENABLE doesn't change the brush*/
- hbrush = (HBRUSH)SendMessageW(sheethwnd, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
- hr = pEnableThemeDialogTexture(sheethwnd, ETDT_ENABLE);
- ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#x.\n", hr);
- hbrush2 = (HBRUSH)SendMessageW(sheethwnd, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
- todo_wine_if(hbrush2 != hbrush)
- ok(hbrush2 == hbrush, "Expected the same brush.\n");
-
- /* Test ETDT_ENABLE | ETDT_USETABTEXTURE should change the brush */
- hbrush = (HBRUSH)SendMessageW(sheethwnd, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
- hr = pEnableThemeDialogTexture(sheethwnd, ETDT_ENABLE | ETDT_USETABTEXTURE);
- ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#x.\n", hr);
- hbrush2 = (HBRUSH)SendMessageW(sheethwnd, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
- todo_wine_if(hbrush2 == hbrush)
- ok(hbrush2 != hbrush, "Expected a different brush.\n");
-
- /* Test ETDT_ENABLE | ETDT_USEAEROWIZARDTABTEXTURE should change the brush */
- hbrush = (HBRUSH)SendMessageW(sheethwnd, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
- hr = pEnableThemeDialogTexture(sheethwnd, ETDT_ENABLE | ETDT_USEAEROWIZARDTABTEXTURE);
- ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#x.\n", hr);
- hbrush2 = (HBRUSH)SendMessageW(sheethwnd, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
- /* ETDT_USEAEROWIZARDTABTEXTURE is supported only on Vista+ */
- if (LOBYTE(LOWORD(GetVersion())) < 6)
- ok(hbrush2 == hbrush, "Expected the same brush.\n");
- else
- todo_wine_if(hbrush2 == hbrush)
- ok(hbrush2 != hbrush, "Expected a different brush.\n");
-
- hr = pEnableThemeDialogTexture(sheethwnd, ETDT_DISABLE);
- ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#x.\n", hr);
- hr = pEnableThemeDialogTexture(sheethwnd, ETDT_ENABLE | ETDT_USETABTEXTURE);
- ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#x.\n", hr);
-
- /* Test that the page procedure should take precedence if WM_CTLCOLORSTATIC is handled */
- handle_WM_CTLCOLORSTATIC = TRUE;
- hbrush = (HBRUSH)SendMessageW(sheethwnd, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
- ok(hbrush == GetSysColorBrush(COLOR_MENU), "Expected brush %p, got %p.\n",
- GetSysColorBrush(COLOR_MENU), hbrush);
- handle_WM_CTLCOLORSTATIC = FALSE;
-
- /* Test that the brush is not a system object and has only one reference and shouldn't be freed */
- hbrush = (HBRUSH)SendMessageW(sheethwnd, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
- ret = DeleteObject(hbrush);
- ok(ret, "DeleteObject failed, error %u.\n", GetLastError());
- SetLastError(0xdeadbeef);
- ret = GetObjectA(hbrush, sizeof(log_brush), &log_brush);
- error = GetLastError();
- ok(!ret, "GetObjectA succeeded.\n");
- todo_wine
- ok(error == ERROR_INVALID_PARAMETER, "Expected error %u, got %u.\n",
- ERROR_INVALID_PARAMETER, error);
- ret = DeleteObject(hbrush);
- ok(!ret, "DeleteObject succeeded.\n");
-
- /* Should still report the same brush handle after the brush handle was freed */
- hbrush2 = (HBRUSH)SendMessageW(sheethwnd, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
- ok(hbrush2 == hbrush, "Expected the same brush.\n");
-
- /* Test that WM_THEMECHANGED should recreate the brush */
- hbrush = (HBRUSH)SendMessageW(sheethwnd, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
- SendMessageW(sheethwnd, WM_THEMECHANGED, 0, 0);
- hbrush2 = (HBRUSH)SendMessageW(sheethwnd, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
- ok(hbrush2 != hbrush, "Expected a different brush.\n");
- ret = GetObjectA(hbrush, sizeof(log_brush), &log_brush);
- ok(!ret, "GetObjectA succeeded.\n");
}
- else
- {
- /* Test that brush origin is at (0,0) */
- ret = GetBrushOrgEx(child_hdc, &org);
- ok(ret, "GetBrushOrgEx failed, error %u.\n", GetLastError());
- todo_wine_if(is_theme_active)
- ok(org.x == 0 && org.y == 0, "Expected (0,0), got %s.\n", wine_dbgstr_point(&org));
- todo_wine_if(is_theme_active)
- ok(hbrush == GetSysColorBrush(COLOR_BTNFACE), "Expected brush %p, got %p.\n",
- GetSysColorBrush(COLOR_BTNFACE), hbrush);
- mode = SetBkMode(child_hdc, old_mode);
- todo_wine_if(is_theme_active)
- ok(mode == OPAQUE, "Expected mode %#x, got %#x.\n", OPAQUE, mode);
- }
- color = SetBkColor(child_hdc, old_color);
- ok(color == GetSysColor(COLOR_BTNFACE), "Expected background color %#x, got %#x.\n",
- GetSysColor(COLOR_BTNFACE), color);
-
- ReleaseDC(child, child_hdc);
+ ReleaseDC(hwnd, hdc);
+ DestroyWindow(hwnd);
DestroyWindow(hdlg);
}
@@ -1472,13 +1276,8 @@ static void init_uxtheme_functions(void)
HMODULE uxtheme = LoadLibraryA("uxtheme.dll");
#define X(f) p##f = (void *)GetProcAddress(uxtheme, #f);
- X(CloseThemeData)
- X(EnableThemeDialogTexture)
- X(GetThemePartSize)
- X(GetWindowTheme)
X(IsThemeActive)
X(IsThemeDialogTextureEnabled)
- X(OpenThemeData)
#undef X
}
@@ -1511,7 +1310,7 @@ START_TEST(propsheet)
test_PSM_ADDPAGE();
test_PSM_INSERTPAGE();
test_CreatePropertySheetPage();
- test_WM_CTLCOLORSTATIC();
+ test_page_dialog_texture();
if (!load_v6_module(&ctx_cookie, &ctx))
return;
@@ -1519,7 +1318,7 @@ START_TEST(propsheet)
init_comctl32_functions();
is_v6 = TRUE;
- test_WM_CTLCOLORSTATIC();
+ test_page_dialog_texture();
unload_v6_module(ctx_cookie, ctx);
}
--
2.32.0
1
0
[PATCH 2/5] comctl32/tests: Remove WM_CTLCOLORSTATIC tests for static controls.
by Zhiyi Zhang Jan. 21, 2022
by Zhiyi Zhang Jan. 21, 2022
Jan. 21, 2022
These tests are covered by test_EnableThemeDialogTexture() in uxtheme/tests/system.c. These tests
are also misleading because a WM_ERASEBKGND message is needed to actually activate dialog theming.
Without the message, they give the impression that EnableThemeDialogTexture() does nothing and
DefDlgProcA/W() are not hooked.
Signed-off-by: Zhiyi Zhang <zzhang(a)codeweavers.com>
---
dlls/comctl32/tests/static.c | 120 -----------------------------------
1 file changed, 120 deletions(-)
diff --git a/dlls/comctl32/tests/static.c b/dlls/comctl32/tests/static.c
index 529396b3f0d..40a337b97b0 100644
--- a/dlls/comctl32/tests/static.c
+++ b/dlls/comctl32/tests/static.c
@@ -27,7 +27,6 @@
#include "winuser.h"
#include "commctrl.h"
#include "resources.h"
-#include "uxtheme.h"
#include "wine/test.h"
@@ -40,10 +39,6 @@
static HWND hMainWnd;
static int g_nReceivedColorStatic;
-static HRESULT (WINAPI *pEnableThemeDialogTexture)(HWND, DWORD);
-static HTHEME (WINAPI *pGetWindowTheme)(HWND);
-static BOOL (WINAPI *pIsThemeDialogTextureEnabled)(HWND);
-
/* try to make sure pending X events have been processed before continuing */
static void flush_events(void)
{
@@ -382,118 +377,6 @@ static void test_STM_SETIMAGE(void)
DeleteEnhMetaFile(emf);
}
-static INT_PTR CALLBACK test_WM_CTLCOLORSTATIC_proc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
-{
- static HWND child;
-
- switch (msg)
- {
- case WM_INITDIALOG:
- child = CreateWindowA(WC_STATICA, "child", WS_CHILD | WS_VISIBLE, 1, 2, 50, 50, hwnd,
- (HMENU)100, 0, NULL);
- ok(child != NULL, "CreateWindowA failed, error %d.\n", GetLastError());
- return FALSE;
-
- case WM_CLOSE:
- DestroyWindow(child);
- return TRUE;
-
- default:
- return FALSE;
- }
-}
-
-static void test_WM_CTLCOLORSTATIC(void)
-{
- HWND parent, dialog, child;
- COLORREF color, old_color;
- HDC child_hdc, dialog_hdc;
- int mode, old_mode;
- HBRUSH brush;
- HRESULT hr;
- POINT org;
- BOOL ret;
-
- struct
- {
- DLGTEMPLATE tmplate;
- WORD menu;
- WORD class;
- WORD title;
- } temp = {{0}};
-
- parent = CreateWindowA(WC_STATICA, "parent", WS_POPUP | WS_VISIBLE, 100, 100, 200, 200, 0, 0, 0,
- NULL);
- ok(parent != NULL, "CreateWindowA failed, error %d.\n", GetLastError());
-
- temp.tmplate.style = WS_CHILD | WS_VISIBLE;
- temp.tmplate.cx = 80;
- temp.tmplate.cy = 80;
- dialog = CreateDialogIndirectParamA(NULL, &temp.tmplate, parent, test_WM_CTLCOLORSTATIC_proc, 0);
- ok(dialog != NULL, "CreateDialogIndirectParamA failed, error %d.\n", GetLastError());
- child = GetDlgItem(dialog, 100);
- ok(child != NULL, "Failed to get child static control, error %d.\n", GetLastError());
-
- dialog_hdc = GetDC(dialog);
- child_hdc = GetDC(child);
- PatBlt(dialog_hdc, 0, 0, 80, 80, BLACKNESS);
-
- old_mode = SetBkMode(child_hdc, OPAQUE);
- ok(old_mode != 0, "SetBkMode failed.\n");
- old_color = SetBkColor(child_hdc, 0xaa5511);
- ok(old_color != CLR_INVALID, "SetBkColor failed.\n");
-
- ret = pIsThemeDialogTextureEnabled(dialog);
- ok(ret, "Expected theme dialog texture supported.\n");
- ok(pGetWindowTheme(dialog) == NULL, "Expected NULL theme handle.\n");
-
- brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
- ok(brush == GetSysColorBrush(COLOR_BTNFACE), "Expected brush %p, got %p.\n",
- GetSysColorBrush(COLOR_BTNFACE), brush);
- color = SetBkColor(child_hdc, old_color);
- ok(color == GetSysColor(COLOR_BTNFACE), "Expected background color %#x, got %#x.\n",
- GetSysColor(COLOR_BTNFACE), color);
- mode = SetBkMode(child_hdc, old_mode);
- ok(mode == OPAQUE, "Expected mode %#x, got %#x.\n", OPAQUE, mode);
- color = GetPixel(dialog_hdc, 40, 40);
- ok(color == 0, "Expected pixel %#x, got %#x.\n", 0, color);
-
- /* Test that EnableThemeDialogTexture() doesn't make WM_CTLCOLORSTATIC return a different brush */
- hr = pEnableThemeDialogTexture(dialog, ETDT_DISABLE);
- ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#x.\n", hr);
- ret = pIsThemeDialogTextureEnabled(dialog);
- ok(!ret, "Expected theme dialog texture disabled.\n");
- hr = pEnableThemeDialogTexture(dialog, ETDT_ENABLETAB);
- ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#x.\n", hr);
- ret = pIsThemeDialogTextureEnabled(dialog);
- ok(ret, "Expected theme dialog texture enabled.\n");
-
- brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
- ok(brush == GetSysColorBrush(COLOR_BTNFACE), "Expected brush %p, got %p.\n",
- GetSysColorBrush(COLOR_BTNFACE), brush);
-
- /* Test that WM_CTLCOLORSTATIC doesn't change brush origin */
- ret = GetBrushOrgEx(child_hdc, &org);
- ok(ret, "GetBrushOrgEx failed, error %u.\n", GetLastError());
- ok(org.x == 0 && org.y == 0, "Expected (0,0), got %s.\n", wine_dbgstr_point(&org));
-
- ReleaseDC(child, child_hdc);
- ReleaseDC(dialog, dialog_hdc);
- EndDialog(dialog, 0);
- DestroyWindow(parent);
-}
-
-static void init_functions(void)
-{
- HMODULE uxtheme = LoadLibraryA("uxtheme.dll");
-
-#define X(f) p##f = (void *)GetProcAddress(uxtheme, #f);
- X(EnableThemeDialogTexture)
- X(GetWindowTheme)
- X(IsThemeDialogTextureEnabled)
-#undef X
-}
-
START_TEST(static)
{
static const char classname[] = "testclass";
@@ -504,8 +387,6 @@ START_TEST(static)
if (!load_v6_module(&ctx_cookie, &hCtx))
return;
- init_functions();
-
wndclass.cbSize = sizeof(wndclass);
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
@@ -536,7 +417,6 @@ START_TEST(static)
test_set_text();
test_set_image();
test_STM_SETIMAGE();
- test_WM_CTLCOLORSTATIC();
DestroyWindow(hMainWnd);
--
2.32.0
1
0
Jan. 21, 2022
These tests show that DefDlgProcA/W() are hooked to implemented dialog theming, using a pattern
brush created from the tab body part. For dialogs that need theming, EnableThemeDialogTexture(ETDT_USETABTEXTURE)
or EnableThemeDialogTexture(ETDT_USEAEROWIZARDTABTEXTURE) is called for the dialog. And then
static or button controls in comctl32 v6 call EnableThemeDialogTexture(ETDT_ENABLE) to activate it.
A WM_ERASEBKGND is also needed to activate dialog theming. test_WM_CTLCOLORSTATIC() in dlls/comctl32/tests/static.c
doesn't send this message after EnableThemeDialogTexture() calls, which misdirected me to think that
DefDlgProcA/W() are not hooked.
Signed-off-by: Zhiyi Zhang <zzhang(a)codeweavers.com>
---
dlls/uxtheme/tests/system.c | 658 ++++++++++++++++++++++++++++++++++++
dlls/uxtheme/tests/v6util.h | 131 +++++++
2 files changed, 789 insertions(+)
create mode 100644 dlls/uxtheme/tests/v6util.h
diff --git a/dlls/uxtheme/tests/system.c b/dlls/uxtheme/tests/system.c
index 0385af663e3..3f8f32a9a7b 100644
--- a/dlls/uxtheme/tests/system.c
+++ b/dlls/uxtheme/tests/system.c
@@ -1,6 +1,7 @@
/* Unit test suite for uxtheme API functions
*
* Copyright 2006 Paul Vriens
+ * Copyright 2021-2022 Zhiyi Zhang for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -33,6 +34,8 @@
#include "msg.h"
#include "wine/test.h"
+#include "v6util.h"
+
static HTHEME (WINAPI * pOpenThemeDataEx)(HWND, LPCWSTR, DWORD);
static HTHEME (WINAPI *pOpenThemeDataForDpi)(HWND, LPCWSTR, UINT);
static HPAINTBUFFER (WINAPI *pBeginBufferedPaint)(HDC, const RECT *, BP_BUFFERFORMAT, BP_PAINTPARAMS *, HDC *);
@@ -1545,8 +1548,656 @@ static void test_DrawThemeParentBackground(void)
UnregisterClassA("TestDrawThemeParentBackgroundClass", GetModuleHandleA(0));
}
+struct test_EnableThemeDialogTexture_param
+{
+ const CHAR *class_name;
+ DWORD style;
+};
+
+static const struct message empty_seq[] =
+{
+ {0}
+};
+
+static HWND dialog_child;
+static DWORD dialog_init_flag;
+static BOOL handle_WM_CTLCOLORSTATIC;
+
+static INT_PTR CALLBACK test_EnableThemeDialogTexture_proc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
+{
+ struct test_EnableThemeDialogTexture_param *param;
+ struct message message = {0};
+
+ message.message = msg;
+ message.flags = sent | wparam | lparam;
+ message.wParam = wp;
+ message.lParam = lp;
+ add_message(sequences, PARENT_SEQ_INDEX, &message);
+
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ param = (struct test_EnableThemeDialogTexture_param *)lp;
+ dialog_child = CreateWindowA(param->class_name, "child",
+ param->style | WS_CHILD | WS_VISIBLE, 1, 2, 50, 50, hwnd,
+ (HMENU)100, 0, NULL);
+ ok(dialog_child != NULL, "CreateWindowA failed, error %d.\n", GetLastError());
+ if (dialog_init_flag)
+ EnableThemeDialogTexture(hwnd, dialog_init_flag);
+ return TRUE;
+
+ case WM_CTLCOLORSTATIC:
+ return (INT_PTR)(handle_WM_CTLCOLORSTATIC ? GetSysColorBrush(COLOR_MENU) : 0);
+
+ case WM_CLOSE:
+ DestroyWindow(dialog_child);
+ dialog_child = NULL;
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+}
+
+static void test_EnableThemeDialogTexture(void)
+{
+ struct test_EnableThemeDialogTexture_param param;
+ HWND dialog, child, hwnd, hwnd2;
+ int mode, old_mode, count, i, j;
+ COLORREF color, old_color;
+ HBRUSH brush, brush2;
+ HDC child_hdc, hdc;
+ LOGBRUSH log_brush;
+ ULONG_PTR proc;
+ WNDCLASSA cls;
+ HTHEME theme;
+ DWORD error;
+ BITMAP bmp;
+ HRESULT hr;
+ LRESULT lr;
+ POINT org;
+ SIZE size;
+ BOOL ret;
+
+ struct
+ {
+ DLGTEMPLATE template;
+ WORD menu;
+ WORD class;
+ WORD title;
+ } temp = {{0}};
+
+ static const DWORD flags[] =
+ {
+ ETDT_DISABLE,
+ ETDT_ENABLE,
+ ETDT_USETABTEXTURE,
+ ETDT_USEAEROWIZARDTABTEXTURE,
+ ETDT_ENABLETAB,
+ ETDT_ENABLEAEROWIZARDTAB,
+ /* Bad flags */
+ 0,
+ ETDT_DISABLE | ETDT_ENABLE,
+ ETDT_ENABLETAB | ETDT_ENABLEAEROWIZARDTAB
+ };
+
+ static const struct invalid_flag_test
+ {
+ DWORD flag;
+ BOOL enabled;
+ BOOL todo;
+ }
+ invalid_flag_tests[] =
+ {
+ {0, FALSE},
+ {ETDT_DISABLE | ETDT_ENABLE, FALSE},
+ {ETDT_ENABLETAB | ETDT_ENABLEAEROWIZARDTAB, TRUE},
+ {ETDT_USETABTEXTURE | ETDT_USEAEROWIZARDTABTEXTURE, TRUE, TRUE},
+ {ETDT_VALIDBITS, FALSE},
+ {~ETDT_VALIDBITS, FALSE},
+ {~ETDT_VALIDBITS | ETDT_DISABLE, FALSE}
+ };
+
+ static const struct class_test
+ {
+ struct test_EnableThemeDialogTexture_param param;
+ BOOL texture_enabled;
+ }
+ class_tests[] =
+ {
+ {{ANIMATE_CLASSA}},
+ {{WC_BUTTONA, BS_PUSHBUTTON}, TRUE},
+ {{WC_BUTTONA, BS_DEFPUSHBUTTON}, TRUE},
+ {{WC_BUTTONA, BS_CHECKBOX}, TRUE},
+ {{WC_BUTTONA, BS_AUTOCHECKBOX}, TRUE},
+ {{WC_BUTTONA, BS_RADIOBUTTON}, TRUE},
+ {{WC_BUTTONA, BS_3STATE}, TRUE},
+ {{WC_BUTTONA, BS_AUTO3STATE}, TRUE},
+ {{WC_BUTTONA, BS_GROUPBOX}, TRUE},
+ {{WC_BUTTONA, BS_USERBUTTON}, TRUE},
+ {{WC_BUTTONA, BS_AUTORADIOBUTTON}, TRUE},
+ {{WC_BUTTONA, BS_PUSHBOX}, TRUE},
+ {{WC_BUTTONA, BS_OWNERDRAW}, TRUE},
+ {{WC_BUTTONA, BS_SPLITBUTTON}, TRUE},
+ {{WC_BUTTONA, BS_DEFSPLITBUTTON}, TRUE},
+ {{WC_BUTTONA, BS_COMMANDLINK}, TRUE},
+ {{WC_BUTTONA, BS_DEFCOMMANDLINK}, TRUE},
+ {{WC_COMBOBOXA}},
+ {{WC_COMBOBOXEXA}},
+ {{DATETIMEPICK_CLASSA}},
+ {{WC_EDITA}},
+ {{WC_HEADERA}},
+ {{HOTKEY_CLASSA}},
+ {{WC_IPADDRESSA}},
+ {{WC_LISTBOXA}},
+ {{WC_LISTVIEWA}},
+ {{MONTHCAL_CLASSA}},
+ {{WC_NATIVEFONTCTLA}},
+ {{WC_PAGESCROLLERA}},
+ {{PROGRESS_CLASSA}},
+ {{REBARCLASSNAMEA}},
+ {{WC_STATICA, SS_LEFT}, TRUE},
+ {{WC_STATICA, SS_ICON}, TRUE},
+ {{WC_STATICA, SS_BLACKRECT}, TRUE},
+ {{WC_STATICA, SS_OWNERDRAW}, TRUE},
+ {{WC_STATICA, SS_BITMAP}, TRUE},
+ {{WC_STATICA, SS_ENHMETAFILE}, TRUE},
+ {{WC_STATICA, SS_ETCHEDHORZ}, TRUE},
+ {{STATUSCLASSNAMEA}},
+ {{"SysLink"}},
+ {{WC_TABCONTROLA}},
+ {{TOOLBARCLASSNAMEA}},
+ {{TOOLTIPS_CLASSA}},
+ {{TRACKBAR_CLASSA}},
+ {{WC_TREEVIEWA}},
+ {{UPDOWN_CLASSA}},
+ {{WC_SCROLLBARA}},
+ };
+
+ if (!IsThemeActive())
+ {
+ skip("Theming is inactive.\n");
+ return;
+ }
+
+ memset(&cls, 0, sizeof(cls));
+ cls.lpfnWndProc = DefWindowProcA;
+ cls.hInstance = GetModuleHandleA(NULL);
+ cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
+ cls.hbrBackground = GetStockObject(GRAY_BRUSH);
+ cls.lpszClassName = "TestEnableThemeDialogTextureClass";
+ RegisterClassA(&cls);
+
+ temp.template.style = WS_CHILD | WS_VISIBLE;
+ temp.template.cx = 100;
+ temp.template.cy = 100;
+ param.class_name = cls.lpszClassName;
+ param.style = 0;
+ dialog = CreateDialogIndirectParamA(NULL, &temp.template, GetDesktopWindow(),
+ test_EnableThemeDialogTexture_proc, (LPARAM)¶m);
+ ok(dialog != NULL, "CreateDialogIndirectParamA failed, error %d.\n", GetLastError());
+ child = GetDlgItem(dialog, 100);
+ ok(child != NULL, "Failed to get child control, error %d.\n", GetLastError());
+ child_hdc = GetDC(child);
+
+ /* Test that dialog procedure is unchanged */
+ proc = GetWindowLongPtrA(dialog, DWLP_DLGPROC);
+ ok(proc == (ULONG_PTR)test_EnableThemeDialogTexture_proc, "Unexpected proc %#lx.\n", proc);
+
+ /* Test dialog texture is disabled by default. EnableThemeDialogTexture() needs to be called */
+ ret = IsThemeDialogTextureEnabled(dialog);
+ todo_wine
+ ok(!ret, "Expected theme dialog texture disabled.\n");
+ ok(GetWindowTheme(dialog) == NULL, "Expected NULL theme handle.\n");
+
+ /* Test ETDT_ENABLE | ETDT_USETABTEXTURE doesn't take effect immediately */
+ hr = EnableThemeDialogTexture(dialog, ETDT_ENABLE | ETDT_USETABTEXTURE);
+ ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#x.\n", hr);
+ ret = IsThemeDialogTextureEnabled(dialog);
+ ok(ret, "Expected theme dialog texture enabled.\n");
+
+ brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
+ ok(brush == GetSysColorBrush(COLOR_BTNFACE), "Expected brush %p, got %p.\n",
+ GetSysColorBrush(COLOR_BTNFACE), brush);
+ ret = GetBrushOrgEx(child_hdc, &org);
+ ok(ret, "GetBrushOrgEx failed, error %u.\n", GetLastError());
+ ok(org.x == 0 && org.y == 0, "Expected (0,0), got %s.\n", wine_dbgstr_point(&org));
+
+ /* Test WM_THEMECHANGED doesn't make ETDT_ENABLE | ETDT_USETABTEXTURE take effect */
+ lr = SendMessageA(dialog, WM_THEMECHANGED, 0, 0);
+ ok(lr == 0, "WM_THEMECHANGED failed.\n");
+ brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
+ ok(brush == GetSysColorBrush(COLOR_BTNFACE), "Expected brush %p, got %p.\n",
+ GetSysColorBrush(COLOR_BTNFACE), brush);
+
+ /* Test WM_ERASEBKGND make ETDT_ENABLE | ETDT_USETABTEXTURE take effect */
+ lr = SendMessageA(dialog, WM_ERASEBKGND, (WPARAM)child_hdc, 0);
+ ok(lr != 0, "WM_ERASEBKGND failed.\n");
+ brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
+ todo_wine
+ ok(brush != GetSysColorBrush(COLOR_BTNFACE), "Expected brush changed.\n");
+
+ /* Test disabling theme dialog texture should change the brush immediately */
+ brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
+ hr = EnableThemeDialogTexture(dialog, ETDT_DISABLE);
+ ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#x.\n", hr);
+ brush2 = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
+ todo_wine
+ ok(brush2 != brush, "Expected a different brush.\n");
+ ok(brush2 == GetSysColorBrush(COLOR_BTNFACE), "Expected brush %p, got %p.\n",
+ GetSysColorBrush(COLOR_BTNFACE), brush2);
+
+ /* Test re-enabling theme dialog texture with ETDT_ENABLE doesn't change the brush */
+ brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
+ hr = EnableThemeDialogTexture(dialog, ETDT_ENABLE);
+ ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#x.\n", hr);
+ brush2 = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
+ ok(brush2 == brush, "Expected the same brush.\n");
+ ok(brush2 == GetSysColorBrush(COLOR_BTNFACE), "Expected brush %p, got %p.\n",
+ GetSysColorBrush(COLOR_BTNFACE), brush2);
+
+ /* Test adding ETDT_USETABTEXTURE should change the brush immediately */
+ brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
+ hr = EnableThemeDialogTexture(dialog, ETDT_USETABTEXTURE);
+ ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#x.\n", hr);
+ brush2 = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
+ todo_wine
+ ok(brush2 != brush, "Expected a different brush.\n");
+
+ /* Test ETDT_ENABLE | ETDT_USEAEROWIZARDTABTEXTURE should change the brush immediately */
+ brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
+ hr = EnableThemeDialogTexture(dialog, ETDT_ENABLE | ETDT_USEAEROWIZARDTABTEXTURE);
+ ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#x.\n", hr);
+ brush2 = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
+ /* ETDT_USEAEROWIZARDTABTEXTURE is supported only on Vista+ */
+ if (LOBYTE(LOWORD(GetVersion())) < 6)
+ ok(brush2 == brush, "Expected the same brush.\n");
+ else
+ todo_wine
+ ok(brush2 != brush, "Expected a different brush.\n");
+
+ hr = EnableThemeDialogTexture(dialog, ETDT_DISABLE);
+ ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#x.\n", hr);
+ hr = EnableThemeDialogTexture(dialog, ETDT_ENABLE | ETDT_USETABTEXTURE);
+ ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#x.\n", hr);
+
+ /* Test that the dialog procedure should take precedence over DefDlgProc() for WM_CTLCOLORSTATIC */
+ handle_WM_CTLCOLORSTATIC = TRUE;
+ brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
+ ok(brush == GetSysColorBrush(COLOR_MENU), "Expected brush %p, got %p.\n",
+ GetSysColorBrush(COLOR_MENU), brush);
+ handle_WM_CTLCOLORSTATIC = FALSE;
+
+ /* Test that WM_CTLCOLORSTATIC changes brush origin when dialog texture is on */
+ ret = SetBrushOrgEx(child_hdc, 0, 0, NULL);
+ ok(ret, "SetBrushOrgEx failed, error %u.\n", GetLastError());
+ SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
+ ret = GetBrushOrgEx(child_hdc, &org);
+ ok(ret, "GetBrushOrgEx failed, error %u.\n", GetLastError());
+ todo_wine
+ ok(org.x == -1 && org.y == -2, "Expected (-1,-2), got %s.\n", wine_dbgstr_point(&org));
+
+ /* Test that WM_CTLCOLORSTATIC changes background mode when dialog texture is on */
+ old_mode = SetBkMode(child_hdc, OPAQUE);
+ ok(old_mode != 0, "SetBkMode failed.\n");
+ SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
+ mode = SetBkMode(child_hdc, old_mode);
+ todo_wine
+ ok(mode == TRANSPARENT, "Expected mode %#x, got %#x.\n", TRANSPARENT, mode);
+
+ /* Test that WM_CTLCOLORSTATIC changes background color when dialog texture is on */
+ old_color = SetBkColor(child_hdc, 0xaa5511);
+ ok(old_color != CLR_INVALID, "SetBkColor failed.\n");
+ SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
+ color = SetBkColor(child_hdc, old_color);
+ ok(color == GetSysColor(COLOR_BTNFACE), "Expected background color %#x, got %#x.\n",
+ GetSysColor(COLOR_BTNFACE), color);
+
+ /* Test that dialog doesn't have theme handle opened for itself */
+ ok(GetWindowTheme(dialog) == NULL, "Expected NULL theme handle.\n");
+
+ /* Test that the returned brush is a pattern brush created from the tab body */
+ brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
+ memset(&log_brush, 0, sizeof(log_brush));
+ count = GetObjectA(brush, sizeof(log_brush), &log_brush);
+ ok(count == sizeof(log_brush), "GetObjectA failed, error %u.\n", GetLastError());
+ todo_wine
+ ok(log_brush.lbColor == 0, "Expected brush color %#x, got %#x.\n", 0, log_brush.lbColor);
+ todo_wine
+ ok(log_brush.lbStyle == BS_PATTERN, "Expected brush style %#x, got %#x.\n", BS_PATTERN,
+ log_brush.lbStyle);
+
+ memset(&bmp, 0, sizeof(bmp));
+ count = GetObjectA((HBITMAP)log_brush.lbHatch, sizeof(bmp), &bmp);
+ todo_wine
+ ok(count == sizeof(bmp), "GetObjectA failed, error %u.\n", GetLastError());
+
+ theme = OpenThemeData(NULL, L"Tab");
+ ok(theme != NULL, "OpenThemeData failed.\n");
+
+ size.cx = 0;
+ size.cy = 0;
+ hr = GetThemePartSize(theme, NULL, TABP_BODY, 0, NULL, TS_TRUE, &size);
+ ok(hr == S_OK, "GetThemePartSize failed, hr %#x.\n", hr);
+ todo_wine
+ ok(bmp.bmWidth == size.cx, "Expected width %d, got %d.\n", size.cx, bmp.bmWidth);
+ todo_wine
+ ok(bmp.bmHeight == size.cy, "Expected height %d, got %d.\n", size.cy, bmp.bmHeight);
+
+ CloseThemeData(theme);
+
+ /* Test that DefDlgProcA/W() are hooked for WM_CTLCOLORSTATIC */
+ brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
+ todo_wine
+ ok(brush != GetSysColorBrush(COLOR_BTNFACE), "Expected a different brush.\n");
+ brush2 = (HBRUSH)DefDlgProcW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
+ ok(brush2 == brush, "Expected the same brush.\n");
+ brush2 = (HBRUSH)DefDlgProcA(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
+ ok(brush2 == brush, "Expected the same brush.\n");
+
+ /* Test that DefWindowProcA/W() are also hooked for WM_CTLCOLORSTATIC */
+ brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
+ todo_wine
+ ok(brush != GetSysColorBrush(COLOR_BTNFACE), "Expected a different brush.\n");
+ brush2 = (HBRUSH)DefWindowProcW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
+ ok(brush2 == brush, "Expected the same brush.\n");
+ brush2 = (HBRUSH)DefWindowProcA(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
+ ok(brush2 == brush, "Expected the same brush.\n");
+
+ /* Test that DefWindowProcA/W() are not hooked for WM_ERASEBKGND. So the background is still
+ * drawn with hbrBackground, which in this case, is GRAY_BRUSH.
+ *
+ * This test means it could be that both DefWindowProc() and DefDlgProc() are hooked for
+ * WM_CTLCOLORSTATIC and only DefDlgProc() is hooked for WM_ERASEBKGND. Or it could mean
+ * DefWindowProc() is hooked for WM_CTLCOLORSTATIC and DefDlgProc() is hooked for WM_ERASEBKGND.
+ * Considering the dialog theming needs a WM_ERASEBKGND to activate, it would be weird for let
+ * only DefWindowProc() to hook WM_CTLCOLORSTATIC. For example, what's the point of hooking
+ * WM_CTLCOLORSTATIC in DefWindowProc() for a feature that can only be activated in
+ * DefDlgProc()? So I tend to believe both DefWindowProc() and DefDlgProc() are hooked for
+ * WM_CTLCOLORSTATIC */
+ hwnd = CreateWindowA(cls.lpszClassName, "parent", WS_POPUP | WS_VISIBLE, 0, 0, 100, 100, 0, 0,
+ 0, NULL);
+ ok(hwnd != NULL, "CreateWindowA failed, error %d.\n", GetLastError());
+ hwnd2 = CreateWindowA(WC_STATICA, "child", WS_CHILD | WS_VISIBLE, 10, 10, 20, 20, hwnd, NULL, 0,
+ NULL);
+ hr = EnableThemeDialogTexture(hwnd, ETDT_ENABLETAB);
+ ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#x.\n", hr);
+ ret = IsThemeDialogTextureEnabled(hwnd);
+ ok(ret, "Wrong dialog texture status.\n");
+ flush_events();
+
+ hdc = GetDC(hwnd);
+ color = GetPixel(hdc, 0, 0);
+ ok(color == 0x808080 || broken(color == 0xffffffff), /* Win 7 may report 0xffffffff */
+ "Expected color %#x, got %#x.\n", 0x808080, color);
+ color = GetPixel(hdc, 50, 50);
+ ok(color == 0x808080 || broken(color == 0xffffffff), /* Win 7 may report 0xffffffff */
+ "Expected color %#x, got %#x.\n", 0x808080, color);
+ color = GetPixel(hdc, 99, 99);
+ ok(color == 0x808080 || broken(color == 0xffffffff), /* Win 7 may report 0xffffffff */
+ "Expected color %#x, got %#x.\n", 0x808080, color);
+ ReleaseDC(hwnd, hdc);
+
+ /* Test EnableThemeDialogTexture() doesn't work for non-dialog windows */
+ hdc = GetDC(hwnd2);
+ brush = (HBRUSH)SendMessageW(hwnd, WM_CTLCOLORSTATIC, (WPARAM)hdc, (LPARAM)hwnd2);
+ ok(brush == GetSysColorBrush(COLOR_BTNFACE), "Expected a different brush.\n");
+ ReleaseDC(hwnd2, hdc);
+
+ DestroyWindow(hwnd);
+
+ /* Test that the brush is not a system object and has only one reference and shouldn't be freed */
+ brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
+ ret = DeleteObject(brush);
+ ok(ret, "DeleteObject failed, error %u.\n", GetLastError());
+ SetLastError(0xdeadbeef);
+ ret = GetObjectA(brush, sizeof(log_brush), &log_brush);
+ error = GetLastError();
+ todo_wine
+ ok(!ret || broken(ret) /* XP */, "GetObjectA succeeded.\n");
+ todo_wine
+ ok(error == ERROR_INVALID_PARAMETER || broken(error == 0xdeadbeef) /* XP */,
+ "Expected error %u, got %u.\n", ERROR_INVALID_PARAMETER, error);
+ ret = DeleteObject(brush);
+ todo_wine
+ ok(!ret || broken(ret) /* XP */, "DeleteObject succeeded.\n");
+
+ /* Should still report the same brush handle after the brush handle was freed */
+ brush2 = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
+ ok(brush2 == brush, "Expected the same brush.\n");
+
+ /* Test WM_THEMECHANGED can update the brush now that ETDT_ENABLE | ETDT_USETABTEXTURE is in
+ * effect. This test needs to be ran last as it affect other tests for the same dialog for
+ * unknown reason, causing the brush not to update */
+ brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
+ lr = SendMessageA(dialog, WM_THEMECHANGED, 0, 0);
+ ok(lr == 0, "WM_THEMECHANGED failed.\n");
+ brush2 = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
+ todo_wine
+ ok(brush2 != brush, "Expected a different brush.\n");
+
+ ReleaseDC(child, child_hdc);
+ EndDialog(dialog, 0);
+
+ /* Test invalid flags */
+ for (i = 0; i < ARRAY_SIZE(invalid_flag_tests); ++i)
+ {
+ winetest_push_context("%d flag %#x", i, invalid_flag_tests[i].flag);
+
+ dialog = CreateDialogIndirectParamA(NULL, &temp.template, GetDesktopWindow(),
+ test_EnableThemeDialogTexture_proc, (LPARAM)¶m);
+ ok(dialog != NULL, "CreateDialogIndirectParamA failed, error %d.\n", GetLastError());
+ hr = EnableThemeDialogTexture(dialog, invalid_flag_tests[i].flag);
+ ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#x.\n", hr);
+ ret = IsThemeDialogTextureEnabled(dialog);
+ todo_wine_if(invalid_flag_tests[i].todo)
+ ok(ret == invalid_flag_tests[i].enabled, "Wrong dialog texture status.\n");
+ EndDialog(dialog, 0);
+
+ winetest_pop_context();
+ }
+
+ /* Test different flag combinations */
+ for (i = 0; i < ARRAY_SIZE(flags); ++i)
+ {
+ for (j = 0; j < ARRAY_SIZE(flags); ++j)
+ {
+ /* ETDT_USEAEROWIZARDTABTEXTURE is supported only on Vista+ */
+ if (LOBYTE(LOWORD(GetVersion())) < 6
+ && ((flags[i] | flags[j]) & ETDT_USEAEROWIZARDTABTEXTURE))
+ continue;
+
+ winetest_push_context("%#x to %#x", flags[i], flags[j]);
+
+ dialog = CreateDialogIndirectParamA(NULL, &temp.template, GetDesktopWindow(),
+ test_EnableThemeDialogTexture_proc, (LPARAM)¶m);
+ ok(dialog != NULL, "CreateDialogIndirectParamA failed, error %d.\n", GetLastError());
+ flush_events();
+ flush_sequences(sequences, NUM_MSG_SEQUENCES);
+
+ hr = EnableThemeDialogTexture(dialog, flags[i]);
+ ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#x.\n", hr);
+ ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq,
+ "EnableThemeDialogTexture first flag", TRUE);
+ ret = IsThemeDialogTextureEnabled(dialog);
+ /* Non-zero flags without ETDT_DISABLE enables dialog texture */
+ todo_wine_if(flags[i] == ETDT_USETABTEXTURE || flags[i] == ETDT_USEAEROWIZARDTABTEXTURE)
+ ok(ret == (!(flags[i] & ETDT_DISABLE) && flags[i]), "Wrong dialog texture status.\n");
+
+ child = GetDlgItem(dialog, 100);
+ ok(child != NULL, "Failed to get child control, error %d.\n", GetLastError());
+ child_hdc = GetDC(child);
+ lr = SendMessageA(dialog, WM_ERASEBKGND, (WPARAM)child_hdc, 0);
+ ok(lr != 0, "WM_ERASEBKGND failed.\n");
+ brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
+ if (flags[i] == ETDT_ENABLETAB || flags[i] == ETDT_ENABLEAEROWIZARDTAB)
+ todo_wine
+ ok(brush != GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture enabled.\n");
+ else
+ ok(brush == GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture disabled.\n");
+ flush_sequences(sequences, NUM_MSG_SEQUENCES);
+
+ hr = EnableThemeDialogTexture(dialog, flags[j]);
+ ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#x.\n", hr);
+ ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq,
+ "EnableThemeDialogTexture second flag", TRUE);
+ ret = IsThemeDialogTextureEnabled(dialog);
+ /* If the flag is zero, it will have previous dialog texture status */
+ if (flags[j])
+ todo_wine_if(flags[j] == ETDT_USETABTEXTURE || flags[j] == ETDT_USEAEROWIZARDTABTEXTURE)
+ ok(ret == !(flags[j] & ETDT_DISABLE), "Wrong dialog texture status.\n");
+ else
+ todo_wine_if((!(flags[i] & ETDT_DISABLE) && flags[i]))
+ ok(ret == (!(flags[i] & ETDT_DISABLE) && flags[i]), "Wrong dialog texture status.\n");
+ lr = SendMessageA(dialog, WM_ERASEBKGND, (WPARAM)child_hdc, 0);
+ ok(lr != 0, "WM_ERASEBKGND failed.\n");
+ brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
+ /* Dialog texture is turned on when the flag contains ETDT_ENABLETAB or
+ * ETDT_ENABLEAEROWIZARDTAB. The flag can be turned on in multiple steps, but you can't
+ * do things like set ETDT_ENABLETAB and then ETDT_USEAEROWIZARDTABTEXTURE */
+ if (((flags[j] == ETDT_ENABLETAB || flags[j] == ETDT_ENABLEAEROWIZARDTAB)
+ || ((((flags[i] | flags[j]) & ETDT_ENABLETAB) == ETDT_ENABLETAB
+ || ((flags[i] | flags[j]) & ETDT_ENABLEAEROWIZARDTAB) == ETDT_ENABLEAEROWIZARDTAB)
+ && !((flags[i] | flags[j]) & ETDT_DISABLE)))
+ && (((flags[i] | flags[j]) & (ETDT_ENABLETAB | ETDT_ENABLEAEROWIZARDTAB)) != (ETDT_ENABLETAB | ETDT_ENABLEAEROWIZARDTAB)))
+ todo_wine
+ ok(brush != GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture enabled.\n");
+ else
+ ok(brush == GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture disabled.\n");
+
+ ReleaseDC(child, child_hdc);
+ EndDialog(dialog, 0);
+
+ winetest_pop_context();
+ }
+ }
+
+ /* Test that the dialog procedure should set ETDT_USETABTEXTURE/ETDT_USEAEROWIZARDTABTEXTURE and
+ * find out which comctl32 class should set ETDT_ENABLE to turn on dialog texture */
+ for (i = 0; i < ARRAY_SIZE(class_tests); ++i)
+ {
+ winetest_push_context("%s %#x", wine_dbgstr_a(class_tests[i].param.class_name),
+ class_tests[i].param.style);
+
+ dialog = CreateDialogIndirectParamA(NULL, &temp.template, GetDesktopWindow(),
+ test_EnableThemeDialogTexture_proc,
+ (LPARAM)&class_tests[i].param);
+ ok(dialog != NULL, "CreateDialogIndirectParamA failed, error %d.\n", GetLastError());
+ /* GetDlgItem() fails to get the child control if the child is a tooltip */
+ child = dialog_child;
+ ok(child != NULL, "Failed to get child control, error %d.\n", GetLastError());
+ child_hdc = GetDC(child);
+
+ brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
+ ok(brush == GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture disabled.\n");
+
+ ReleaseDC(child, child_hdc);
+ EndDialog(dialog, 0);
+
+ dialog_init_flag = ETDT_ENABLE;
+ dialog = CreateDialogIndirectParamA(NULL, &temp.template, GetDesktopWindow(),
+ test_EnableThemeDialogTexture_proc,
+ (LPARAM)&class_tests[i].param);
+ ok(dialog != NULL, "CreateDialogIndirectParamA failed, error %d.\n", GetLastError());
+ child = dialog_child;
+ ok(child != NULL, "Failed to get child control, error %d.\n", GetLastError());
+ child_hdc = GetDC(child);
+
+ brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
+ ok(brush == GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture disabled.\n");
+
+ ReleaseDC(child, child_hdc);
+ EndDialog(dialog, 0);
+
+ dialog_init_flag = ETDT_USETABTEXTURE;
+ dialog = CreateDialogIndirectParamA(NULL, &temp.template, GetDesktopWindow(),
+ test_EnableThemeDialogTexture_proc,
+ (LPARAM)&class_tests[i].param);
+ ok(dialog != NULL, "CreateDialogIndirectParamA failed, error %d.\n", GetLastError());
+ child = dialog_child;
+ ok(child != NULL, "Failed to get child control, error %d.\n", GetLastError());
+ child_hdc = GetDC(child);
+ brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
+ if (class_tests[i].texture_enabled)
+ todo_wine
+ ok(brush != GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture enabled.\n");
+ else
+ ok(brush == GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture disabled.\n");
+ ReleaseDC(child, child_hdc);
+ EndDialog(dialog, 0);
+
+ if (LOBYTE(LOWORD(GetVersion())) < 6)
+ {
+ dialog_init_flag = 0;
+ winetest_pop_context();
+ continue;
+ }
+
+ dialog_init_flag = ETDT_USEAEROWIZARDTABTEXTURE;
+ dialog = CreateDialogIndirectParamA(NULL, &temp.template, GetDesktopWindow(),
+ test_EnableThemeDialogTexture_proc,
+ (LPARAM)&class_tests[i].param);
+ ok(dialog != NULL, "CreateDialogIndirectParamA failed, error %d.\n", GetLastError());
+ child = dialog_child;
+ ok(child != NULL, "Failed to get child control, error %d.\n", GetLastError());
+ child_hdc = GetDC(child);
+ brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
+ if (class_tests[i].texture_enabled)
+ todo_wine
+ ok(brush != GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture enabled.\n");
+ else
+ ok(brush == GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture disabled.\n");
+ ReleaseDC(child, child_hdc);
+ EndDialog(dialog, 0);
+ dialog_init_flag = 0;
+
+ winetest_pop_context();
+ }
+
+ /* Test that EnableThemeDialogTexture() is called from child controls for its parent */
+ hwnd = CreateWindowA(cls.lpszClassName, "parent", WS_POPUP | WS_VISIBLE, 100, 100, 200, 200, 0,
+ 0, 0, NULL);
+ ok(hwnd != NULL, "CreateWindowA failed, error %d.\n", GetLastError());
+ ret = IsThemeDialogTextureEnabled(hwnd);
+ todo_wine
+ ok(!ret, "Wrong dialog texture status.\n");
+ child = CreateWindowA(WC_STATICA, "child", WS_CHILD | WS_VISIBLE, 0, 0, 50, 50, hwnd, 0, 0,
+ NULL);
+ ok(child != NULL, "CreateWindowA failed, error %d.\n", GetLastError());
+ ret = IsThemeDialogTextureEnabled(hwnd);
+ ok(ret, "Wrong dialog texture status.\n");
+
+ /* Test that if you move the child control to another window, it doesn't enables tab texture for
+ * the new parent */
+ hwnd2 = CreateWindowA(cls.lpszClassName, "parent", WS_POPUP | WS_VISIBLE, 100, 100, 200, 200, 0,
+ 0, 0, NULL);
+ ok(hwnd2 != NULL, "CreateWindowA failed, error %d.\n", GetLastError());
+ ret = IsThemeDialogTextureEnabled(hwnd2);
+ todo_wine
+ ok(!ret, "Wrong dialog texture status.\n");
+
+ SetParent(child, hwnd2);
+ ok(GetParent(child) == hwnd2, "Wrong parent.\n");
+ ret = IsThemeDialogTextureEnabled(hwnd2);
+ todo_wine
+ ok(!ret, "Wrong dialog texture status.\n");
+ InvalidateRect(child, NULL, TRUE);
+ flush_events();
+ ret = IsThemeDialogTextureEnabled(hwnd2);
+ todo_wine
+ ok(!ret, "Wrong dialog texture status.\n");
+
+ DestroyWindow(hwnd2);
+ DestroyWindow(hwnd);
+
+ UnregisterClassA(cls.lpszClassName, GetModuleHandleA(NULL));
+}
+
START_TEST(system)
{
+ ULONG_PTR ctx_cookie;
+ HANDLE ctx;
+
init_funcs();
init_msg_sequences(sequences, NUM_MSG_SEQUENCES);
@@ -1568,6 +2219,13 @@ START_TEST(system)
test_GetThemeTransitionDuration();
test_DrawThemeParentBackground();
+ if (load_v6_module(&ctx_cookie, &ctx))
+ {
+ test_EnableThemeDialogTexture();
+
+ unload_v6_module(ctx_cookie, ctx);
+ }
+
/* Test EnableTheming() in the end because it may disable theming */
test_EnableTheming();
}
diff --git a/dlls/uxtheme/tests/v6util.h b/dlls/uxtheme/tests/v6util.h
new file mode 100644
index 00000000000..626f6e61255
--- /dev/null
+++ b/dlls/uxtheme/tests/v6util.h
@@ -0,0 +1,131 @@
+/*
+ * Utility routines for comctl32 v6 tests
+ *
+ * Copyright 2006 Mike McCormack for CodeWeavers
+ * Copyright 2007 George Gov
+ * Copyright 2009 Owen Rudge for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifdef __i386__
+#define ARCH "x86"
+#elif defined __x86_64__
+#define ARCH "amd64"
+#elif defined __arm__
+#define ARCH "arm"
+#elif defined __aarch64__
+#define ARCH "arm64"
+#else
+#define ARCH "none"
+#endif
+
+static const CHAR manifest_name[] = "cc6.manifest";
+
+static const CHAR manifest[] =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n"
+ "<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\">\n"
+ " <assemblyIdentity\n"
+ " type=\"win32\"\n"
+ " name=\"Wine.ComCtl32.Tests\"\n"
+ " version=\"1.0.0.0\"\n"
+ " processorArchitecture=\"" ARCH "\"\n"
+ " />\n"
+ "<description>Wine comctl32 test suite</description>\n"
+ "<dependency>\n"
+ " <dependentAssembly>\n"
+ " <assemblyIdentity\n"
+ " type=\"win32\"\n"
+ " name=\"microsoft.windows.common-controls\"\n"
+ " version=\"6.0.0.0\"\n"
+ " processorArchitecture=\"" ARCH "\"\n"
+ " publicKeyToken=\"6595b64144ccf1df\"\n"
+ " language=\"*\"\n"
+ " />\n"
+ "</dependentAssembly>\n"
+ "</dependency>\n"
+ "</assembly>\n";
+
+static void unload_v6_module(ULONG_PTR cookie, HANDLE hCtx)
+{
+ DeactivateActCtx(0, cookie);
+ ReleaseActCtx(hCtx);
+
+ DeleteFileA(manifest_name);
+}
+
+static BOOL load_v6_module(ULONG_PTR *pcookie, HANDLE *hCtx)
+{
+ ACTCTX_SECTION_KEYED_DATA data;
+ DWORD written;
+ HMODULE hmod;
+ ACTCTXA ctx;
+ HANDLE file;
+ BOOL ret;
+
+ /* create manifest */
+ file = CreateFileA( manifest_name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL );
+ if (file != INVALID_HANDLE_VALUE)
+ {
+ ret = (WriteFile( file, manifest, sizeof(manifest)-1, &written, NULL ) &&
+ written == sizeof(manifest)-1);
+ CloseHandle( file );
+ if (!ret)
+ {
+ DeleteFileA( manifest_name );
+ skip("Failed to fill manifest file. Skipping comctl32 V6 tests.\n");
+ return FALSE;
+ }
+ else
+ trace("created %s\n", manifest_name);
+ }
+ else
+ {
+ skip("Failed to create manifest file. Skipping comctl32 V6 tests.\n");
+ return FALSE;
+ }
+
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.cbSize = sizeof(ctx);
+ ctx.lpSource = manifest_name;
+
+ *hCtx = CreateActCtxA(&ctx);
+ ok(*hCtx != 0, "Expected context handle\n");
+
+ hmod = GetModuleHandleA("comctl32.dll");
+
+ ret = ActivateActCtx(*hCtx, pcookie);
+ ok(ret, "Failed to activate context, error %d.\n", GetLastError());
+
+ if (!ret)
+ {
+ win_skip("A problem during context activation occurred.\n");
+ DeleteFileA(manifest_name);
+ }
+
+ data.cbSize = sizeof(data);
+ ret = FindActCtxSectionStringA(0, NULL, ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION,
+ "comctl32.dll", &data);
+ ok(ret, "failed to find comctl32.dll in active context, %u\n", GetLastError());
+ if (ret)
+ {
+ FreeLibrary(hmod);
+ LoadLibraryA("comctl32.dll");
+ }
+
+ return ret;
+}
+
+#undef ARCH
--
2.32.0
1
0
[PATCH] msdasql: Connect to DSN in IDBInitialize Initialize.
by Alistair Leslie-Hughes Jan. 21, 2022
by Alistair Leslie-Hughes Jan. 21, 2022
Jan. 21, 2022
Signed-off-by: Alistair Leslie-Hughes <leslie_alistair(a)hotmail.com>
---
dlls/msdasql/Makefile.in | 2 +-
dlls/msdasql/msdasql_main.c | 63 ++++++++++++++++++++++++++++++++++++-
2 files changed, 63 insertions(+), 2 deletions(-)
diff --git a/dlls/msdasql/Makefile.in b/dlls/msdasql/Makefile.in
index ee8fa672623..0f5453131f6 100644
--- a/dlls/msdasql/Makefile.in
+++ b/dlls/msdasql/Makefile.in
@@ -1,5 +1,5 @@
MODULE = msdasql.dll
-IMPORTS = uuid ole32 oleaut32
+IMPORTS = uuid ole32 oleaut32 odbc32
EXTRADLLFLAGS = -Wb,--prefer-native
diff --git a/dlls/msdasql/msdasql_main.c b/dlls/msdasql/msdasql_main.c
index bc208a74cc5..cf69195baf7 100644
--- a/dlls/msdasql/msdasql_main.c
+++ b/dlls/msdasql/msdasql_main.c
@@ -30,6 +30,9 @@
#include "initguid.h"
#include "msdasql.h"
+#include "odbcinst.h"
+#include "sqlext.h"
+#include "sqlucode.h"
#include "msdasql_private.h"
@@ -40,6 +43,22 @@ DEFINE_GUID(DBPROPSET_DBINIT, 0xc8b522bc, 0x5cf3, 0x11ce, 0xad, 0xe5, 0x00, 0
DEFINE_GUID(DBGUID_DEFAULT, 0xc8b521fb, 0x5cf3, 0x11ce, 0xad, 0xe5, 0x00, 0xaa, 0x00, 0x44, 0x77, 0x3d);
+static void dump_sql_diag_records(SQLSMALLINT type, SQLHANDLE handle)
+{
+ SQLCHAR state[6], msg[SQL_MAX_MESSAGE_LENGTH];
+ SQLINTEGER native;
+ SQLSMALLINT i = 1, len;
+
+ if (!TRACE_ON(msdasql))
+ return;
+
+ while(SQLGetDiagRec(type, handle, i, state, &native, msg, sizeof(msg), &len) != SQL_SUCCESS)
+ {
+ WARN("%d: %s: %s\n", i, state, msg);
+ i++;
+ }
+}
+
static HRESULT WINAPI ClassFactory_QueryInterface(IClassFactory *iface, REFIID riid, void **ppv)
{
*ppv = NULL;
@@ -210,6 +229,10 @@ struct msdasql
LONG ref;
struct msdasql_prop properties[14];
+
+ /* ODBC Support */
+ HENV henv;
+ HDBC hdbc;
};
static inline struct msdasql *impl_from_IUnknown(IUnknown *iface)
@@ -294,6 +317,10 @@ static ULONG WINAPI msdsql_Release(IUnknown *iface)
if (!ref)
{
+ SQLDisconnect(provider->hdbc);
+
+ SQLFreeHandle(SQL_HANDLE_DBC, provider->hdbc);
+ SQLFreeHandle(SQL_HANDLE_ENV, provider->henv);
free(provider);
}
@@ -502,8 +529,33 @@ static ULONG WINAPI dbinit_Release(IDBInitialize *iface)
static HRESULT WINAPI dbinit_Initialize(IDBInitialize *iface)
{
struct msdasql *provider = impl_from_IDBInitialize(iface);
+ int i;
+ SQLRETURN ret;
- FIXME("%p stub\n", provider);
+ FIXME("%p semi-stub\n", provider);
+
+ for(i=0; i < sizeof(provider->properties); i++)
+ {
+ if (provider->properties[i].id == DBPROP_INIT_DATASOURCE)
+ break;
+ }
+
+ if (i >= sizeof(provider->properties))
+ {
+ ERR("Datasource not found\n");
+ return E_FAIL;
+ }
+
+ ret = SQLConnectW( provider->hdbc, (SQLWCHAR *)V_BSTR(&provider->properties[i].value),
+ SQL_NTS, (SQLWCHAR *)NULL, SQL_NTS, (SQLWCHAR *)NULL, SQL_NTS );
+ TRACE("SQLConnectW ret %d\n", ret);
+ if (ret != SQL_SUCCESS)
+ {
+ dump_sql_diag_records(SQL_HANDLE_DBC, provider->hdbc);
+
+ if (ret != SQL_SUCCESS_WITH_INFO)
+ return E_FAIL;
+ }
return S_OK;
}
@@ -653,6 +705,15 @@ static HRESULT create_msdasql_provider(REFIID riid, void **ppv)
}
}
+ SQLAllocHandle(SQL_HANDLE_ENV, NULL, &provider->henv );
+ if (SQLSetEnvAttr(provider->henv, SQL_ATTR_ODBC_VERSION, (void *)SQL_OV_ODBC3_80, 0) == SQL_ERROR)
+ {
+ WARN("Falling back to SQL_OV_ODBC3\n");
+ SQLSetEnvAttr(provider->henv, SQL_ATTR_ODBC_VERSION, (void *)SQL_OV_ODBC3, 0);
+ }
+
+ SQLAllocHandle(SQL_HANDLE_DBC, provider->henv, &provider->hdbc);
+
hr = IUnknown_QueryInterface(&provider->MSDASQL_iface, riid, ppv);
IUnknown_Release(&provider->MSDASQL_iface);
return hr;
--
2.34.1
1
0
[PATCH v2 1/2] winmm: Test that a callback passed to `waveOutOpen` can call `waveOutWrite` without getting called reentrantly.
by Liam Murphy Jan. 21, 2022
by Liam Murphy Jan. 21, 2022
Jan. 21, 2022
This behaviour is relied upon by Teardown, see https://github.com/ValveSoftware/Proton/issues/4332.
Signed-off-by: Liam Murphy <liampm32(a)gmail.com>
---
v2: Add `todo_wine` to avoid test failure
---
dlls/winmm/tests/wave.c | 159 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 159 insertions(+)
diff --git a/dlls/winmm/tests/wave.c b/dlls/winmm/tests/wave.c
index 2f0c8443363..05e5e3b2d71 100644
--- a/dlls/winmm/tests/wave.c
+++ b/dlls/winmm/tests/wave.c
@@ -865,6 +865,163 @@ EXIT:
HeapFree(GetProcessHeap(), 0, frags);
}
+struct reentrancy_callback_data {
+ HANDLE hevent;
+ // Which thread is currently running the callback, or 0 if it isn't.
+ DWORD running_thread;
+ // How many times the callback's been called.
+ BOOL call_num;
+};
+
+static void CALLBACK reentrancy_callback_func(HWAVEOUT hwo, UINT uMsg,
+ DWORD_PTR dwInstance,
+ DWORD_PTR dwParam1, DWORD_PTR dwParam2)
+{
+ struct reentrancy_callback_data *data = (struct reentrancy_callback_data *)dwInstance;
+
+ data->call_num += 1;
+
+ todo_wine_if(data->call_num == 3)
+ ok(data->running_thread != GetCurrentThreadId(), "winmm callback called reentrantly, with message %u\n", uMsg);
+ if (data->running_thread) {
+ if (data->running_thread != GetCurrentThreadId()) trace("Callback running on two threads simultaneously\n");
+
+ // Don't mess with what the original call was doing.
+ return;
+ }
+ data->running_thread = GetCurrentThreadId();
+
+ // We have to wait for a `WOM_DONE` event rather than just using the `WOM_OPEN` event
+ // so that we're running on the winmm audio thread, so that we can block it
+ // to prevent the next `WOM_DONE` event just getting fired in the background
+ // (and then presumably calling the callback on another thread?)
+ if (uMsg == WOM_DONE) {
+ if (data->call_num == 2) {
+ MMRESULT rc;
+ // Since this is the first `WOM_DONE`, and the first frame is always `frags[0]`, we know that this is actually a pointer to `frags`.
+ WAVEHDR *frags = (WAVEHDR *)dwParam1;
+
+ rc = waveOutWrite(hwo, &frags[0], sizeof(frags[0]));
+ ok(rc==MMSYSERR_NOERROR,"waveOutWrite(): rc=%s\n", wave_out_error(rc));
+
+ // Each frame is supposed to last 100ms, so 150ms should be enough to guarantee that the last one has finished.
+ Sleep(150);
+
+ // This is needed to trigger the bug in wine,
+ // since winmm checks for `WOM_DONE` within `WINMM_PushData`, which this calls.
+ rc = waveOutWrite(hwo, &frags[1], sizeof(frags[0]));
+ ok(rc==MMSYSERR_NOERROR,"waveOutWrite(): rc=%s\n", wave_out_error(rc));
+ }
+ }
+
+ data->running_thread = 0;
+ // Only do this at the end of the function so that it isn't possible for the main thread
+ // to mistakenly call `waveOutClose` while the test is running.
+ SetEvent(data->hevent);
+}
+
+// Test whether the callback gets called reentrantly when `waveOutWrite` is called
+// from within the callback passed to `waveOutOpen`.
+static void wave_out_test_no_reentrancy(int device)
+{
+ HWAVEOUT wout;
+ HANDLE hevent = CreateEventW(NULL, FALSE, FALSE, NULL);
+ WAVEHDR *frags = 0;
+ MMRESULT rc;
+ int headers = 2;
+ int loops = 0;
+ WAVEFORMATEX format;
+ WAVEFORMATEX* pwfx = &format;
+ DWORD flags = CALLBACK_FUNCTION;
+ double duration = 0.2;
+ DWORD_PTR callback = 0;
+ struct reentrancy_callback_data callback_data;
+ DWORD_PTR callback_instance = 0;
+ char * buffer;
+ DWORD length;
+ DWORD frag_length;
+ int i;
+ BOOL done;
+
+ format.wFormatTag=WAVE_FORMAT_PCM;
+ format.nChannels=1;
+ format.wBitsPerSample=8;
+ format.nSamplesPerSec=22050;
+ format.nBlockAlign=format.nChannels*format.wBitsPerSample/8;
+ format.nAvgBytesPerSec=format.nSamplesPerSec*format.nBlockAlign;
+ format.cbSize=0;
+
+ callback = (DWORD_PTR)reentrancy_callback_func;
+ callback_data.hevent = hevent;
+ callback_data.running_thread = 0;
+ callback_data.call_num = 0;
+ callback_instance = (DWORD_PTR)(&callback_data);
+
+ wout=NULL;
+ rc=waveOutOpen(&wout,device,pwfx,callback,callback_instance,flags);
+ if (rc!=MMSYSERR_NOERROR) {
+ trace("`waveOutOpen` failed with rc %s, skipping", mmsys_error(rc));
+ goto EXIT;
+ }
+
+ rc=WaitForSingleObject(hevent,9000);
+ ok(rc==WAIT_OBJECT_0, "missing WOM_OPEN notification\n");
+
+ frags = HeapAlloc(GetProcessHeap(), 0, headers * sizeof(WAVEHDR));
+
+ buffer=wave_generate_silence(pwfx,duration / (loops + 1),&length);
+
+ /* make sure fragment length is a multiple of block size */
+ frag_length = ((length / headers) / pwfx->nBlockAlign) * pwfx->nBlockAlign;
+
+ for (i = 0; i < headers; i++) {
+ frags[i].lpData=buffer + (i * frag_length);
+ if (i != (headers-1))
+ frags[i].dwBufferLength=frag_length;
+ else {
+ /* use remainder of buffer for last fragment */
+ frags[i].dwBufferLength=length - (i * frag_length);
+ }
+ frags[i].dwFlags=0;
+ frags[i].dwLoops=0;
+ rc=waveOutPrepareHeader(wout, &frags[i], sizeof(frags[0]));
+ ok(rc==MMSYSERR_NOERROR,
+ "waveOutPrepareHeader(%s): rc=%s\n",dev_name(device),wave_out_error(rc));
+ }
+
+ if (rc==MMSYSERR_NOERROR) {
+ // Queue up the first fragment.
+ rc=waveOutWrite(wout, &frags[0], sizeof(frags[0]));
+ ok(rc==MMSYSERR_NOERROR,"waveOutWrite(%s): rc=%s\n",
+ dev_name(device),wave_out_error(rc));
+ }
+
+ done = FALSE;
+ while (!done) {
+ rc=WaitForSingleObject(hevent,8000);
+ ok(rc==WAIT_OBJECT_0, "missing WOM_DONE notification\n");
+
+ done = TRUE;
+ for (i = 0; i < headers; i++) {
+ if (!(frags[i].dwFlags & WHDR_DONE)) {
+ done = FALSE;
+ }
+ }
+ }
+
+ // Calling `waveOutClose` from within the callback hangs on Windows, so we have to make sure to do it here instead.
+ rc = waveOutClose(wout);
+ ok(rc==MMSYSERR_NOERROR,"waveOutClose(): rc=%s\n", wave_out_error(rc));
+
+ rc=WaitForSingleObject(hevent,1500);
+ ok(rc==WAIT_OBJECT_0, "missing WOM_CLOSE notification\n");
+
+ HeapFree(GetProcessHeap(), 0, buffer);
+EXIT:
+ CloseHandle(hevent);
+ HeapFree(GetProcessHeap(), 0, frags);
+}
+
static void wave_out_test_device(UINT_PTR device)
{
WAVEOUTCAPSA capsA;
@@ -984,6 +1141,8 @@ static void wave_out_test_device(UINT_PTR device)
trace(" %s\n",wave_out_caps(capsA.dwSupport));
HeapFree(GetProcessHeap(), 0, nameA);
+ wave_out_test_no_reentrancy(device);
+
if (winetest_interactive && (device != WAVE_MAPPER))
{
trace("Playing a 5 seconds reference tone.\n");
--
2.34.1
1
1
[PATCH] gdiplus: Handle NULL elementdata.combine.left/right in delete_element
by Konstantin Romanov Jan. 21, 2022
by Konstantin Romanov Jan. 21, 2022
Jan. 21, 2022
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=52423
Signed-off-by: Konstantin Romanov <incubusrk(a)gmail.com>
---
dlls/gdiplus/gdiplus.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/dlls/gdiplus/gdiplus.c b/dlls/gdiplus/gdiplus.c
index 7c4c68f162f..7b0592c184c 100644
--- a/dlls/gdiplus/gdiplus.c
+++ b/dlls/gdiplus/gdiplus.c
@@ -473,10 +473,14 @@ void delete_element(region_element* element)
case RegionDataInfiniteRect:
break;
default:
- delete_element(element->elementdata.combine.left);
- delete_element(element->elementdata.combine.right);
- heap_free(element->elementdata.combine.left);
- heap_free(element->elementdata.combine.right);
+ if(element->elementdata.combine.left){
+ delete_element(element->elementdata.combine.left);
+ heap_free(element->elementdata.combine.left);
+ }
+ if(element->elementdata.combine.right){
+ delete_element(element->elementdata.combine.right);
+ heap_free(element->elementdata.combine.right);
+ }
break;
}
}
--
2.33.0.windows.1
3
3
[PATCH 1/2] winmm: Test that a callback passed to `waveOutOpen` can call `waveOutWrite` without getting called reentrantly.
by Liam Murphy Jan. 21, 2022
by Liam Murphy Jan. 21, 2022
Jan. 21, 2022
This behaviour is relied upon by Teardown, see https://github.com/ValveSoftware/Proton/issues/4332.
Signed-off-by: Liam Murphy <liampm32(a)gmail.com>
---
dlls/winmm/tests/wave.c | 158 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 158 insertions(+)
diff --git a/dlls/winmm/tests/wave.c b/dlls/winmm/tests/wave.c
index 2f0c8443363..986714bcbde 100644
--- a/dlls/winmm/tests/wave.c
+++ b/dlls/winmm/tests/wave.c
@@ -865,6 +865,162 @@ EXIT:
HeapFree(GetProcessHeap(), 0, frags);
}
+struct reentrancy_callback_data {
+ HANDLE hevent;
+ // Which thread is currently running the callback, or 0 if it isn't.
+ DWORD running_thread;
+ // Whether we've finished our test.
+ BOOL test_done;
+};
+
+static void CALLBACK reentrancy_callback_func(HWAVEOUT hwo, UINT uMsg,
+ DWORD_PTR dwInstance,
+ DWORD_PTR dwParam1, DWORD_PTR dwParam2)
+{
+ struct reentrancy_callback_data *data = (struct reentrancy_callback_data *)dwInstance;
+
+ ok(data->running_thread != GetCurrentThreadId(), "winmm callback called reentrantly, with message %u\n", uMsg);
+ if (data->running_thread) {
+ if (data->running_thread != GetCurrentThreadId()) trace("Callback running on two threads simultaneously\n");
+
+ // Don't mess with what the original call was doing.
+ return;
+ }
+ data->running_thread = GetCurrentThreadId();
+
+ // We have to wait for a `WOM_DONE` event rather than just using the `WOM_OPEN` event
+ // so that we're running on the winmm audio thread, so that we can block it
+ // to prevent the next `WOM_DONE` event just getting fired in the background
+ // (and then presumably calling the callback on another thread?)
+ if (uMsg == WOM_DONE) {
+ if (!data->test_done) {
+ MMRESULT rc;
+ // Since this is the first `WOM_DONE`, and the first frame is always `frags[0]`, we know that this is actually a pointer to `frags`.
+ WAVEHDR *frags = (WAVEHDR *)dwParam1;
+
+ rc = waveOutWrite(hwo, &frags[0], sizeof(frags[0]));
+ ok(rc==MMSYSERR_NOERROR,"waveOutWrite(): rc=%s\n", wave_out_error(rc));
+
+ // Each frame is supposed to last 100ms, so 150ms should be enough to guarantee that the last one has finished.
+ Sleep(150);
+
+ // This is needed to trigger the bug in wine,
+ // since winmm checks for `WOM_DONE` within `WINMM_PushData`, which this calls.
+ rc = waveOutWrite(hwo, &frags[1], sizeof(frags[0]));
+ ok(rc==MMSYSERR_NOERROR,"waveOutWrite(): rc=%s\n", wave_out_error(rc));
+
+ data->test_done = TRUE;
+ }
+ }
+
+ data->running_thread = 0;
+ // Only do this at the end of the function so that it isn't possible for the main thread
+ // to mistakenly call `waveOutClose` while the test is running.
+ SetEvent(data->hevent);
+}
+
+// Test whether the callback gets called reentrantly when `waveOutWrite` is called
+// from within the callback passed to `waveOutOpen`.
+static void wave_out_test_no_reentrancy(int device)
+{
+ HWAVEOUT wout;
+ HANDLE hevent = CreateEventW(NULL, FALSE, FALSE, NULL);
+ WAVEHDR *frags = 0;
+ MMRESULT rc;
+ int headers = 2;
+ int loops = 0;
+ WAVEFORMATEX format;
+ WAVEFORMATEX* pwfx = &format;
+ DWORD flags = CALLBACK_FUNCTION;
+ double duration = 0.2;
+ DWORD_PTR callback = 0;
+ struct reentrancy_callback_data callback_data;
+ DWORD_PTR callback_instance = 0;
+ char * buffer;
+ DWORD length;
+ DWORD frag_length;
+ int i;
+ BOOL done;
+
+ format.wFormatTag=WAVE_FORMAT_PCM;
+ format.nChannels=1;
+ format.wBitsPerSample=8;
+ format.nSamplesPerSec=22050;
+ format.nBlockAlign=format.nChannels*format.wBitsPerSample/8;
+ format.nAvgBytesPerSec=format.nSamplesPerSec*format.nBlockAlign;
+ format.cbSize=0;
+
+ callback = (DWORD_PTR)reentrancy_callback_func;
+ callback_data.hevent = hevent;
+ callback_data.running_thread = 0;
+ callback_data.test_done = FALSE;
+ callback_instance = (DWORD_PTR)(&callback_data);
+
+ wout=NULL;
+ rc=waveOutOpen(&wout,device,pwfx,callback,callback_instance,flags);
+ if (rc!=MMSYSERR_NOERROR) {
+ trace("`waveOutOpen` failed with rc %s, skipping", mmsys_error(rc));
+ goto EXIT;
+ }
+
+ rc=WaitForSingleObject(hevent,9000);
+ ok(rc==WAIT_OBJECT_0, "missing WOM_OPEN notification\n");
+
+ frags = HeapAlloc(GetProcessHeap(), 0, headers * sizeof(WAVEHDR));
+
+ buffer=wave_generate_silence(pwfx,duration / (loops + 1),&length);
+
+ /* make sure fragment length is a multiple of block size */
+ frag_length = ((length / headers) / pwfx->nBlockAlign) * pwfx->nBlockAlign;
+
+ for (i = 0; i < headers; i++) {
+ frags[i].lpData=buffer + (i * frag_length);
+ if (i != (headers-1))
+ frags[i].dwBufferLength=frag_length;
+ else {
+ /* use remainder of buffer for last fragment */
+ frags[i].dwBufferLength=length - (i * frag_length);
+ }
+ frags[i].dwFlags=0;
+ frags[i].dwLoops=0;
+ rc=waveOutPrepareHeader(wout, &frags[i], sizeof(frags[0]));
+ ok(rc==MMSYSERR_NOERROR,
+ "waveOutPrepareHeader(%s): rc=%s\n",dev_name(device),wave_out_error(rc));
+ }
+
+ if (rc==MMSYSERR_NOERROR) {
+ // Queue up the first fragment.
+ rc=waveOutWrite(wout, &frags[0], sizeof(frags[0]));
+ ok(rc==MMSYSERR_NOERROR,"waveOutWrite(%s): rc=%s\n",
+ dev_name(device),wave_out_error(rc));
+ }
+
+ done = FALSE;
+ while (!done) {
+ rc=WaitForSingleObject(hevent,8000);
+ ok(rc==WAIT_OBJECT_0, "missing WOM_DONE notification\n");
+
+ done = TRUE;
+ for (i = 0; i < headers; i++) {
+ if (!(frags[i].dwFlags & WHDR_DONE)) {
+ done = FALSE;
+ }
+ }
+ }
+
+ // Calling `waveOutClose` from within the callback hangs on Windows, so we have to make sure to do it here instead.
+ rc = waveOutClose(wout);
+ ok(rc==MMSYSERR_NOERROR,"waveOutClose(): rc=%s\n", wave_out_error(rc));
+
+ rc=WaitForSingleObject(hevent,1500);
+ ok(rc==WAIT_OBJECT_0, "missing WOM_CLOSE notification\n");
+
+ HeapFree(GetProcessHeap(), 0, buffer);
+EXIT:
+ CloseHandle(hevent);
+ HeapFree(GetProcessHeap(), 0, frags);
+}
+
static void wave_out_test_device(UINT_PTR device)
{
WAVEOUTCAPSA capsA;
@@ -984,6 +1140,8 @@ static void wave_out_test_device(UINT_PTR device)
trace(" %s\n",wave_out_caps(capsA.dwSupport));
HeapFree(GetProcessHeap(), 0, nameA);
+ wave_out_test_no_reentrancy(device);
+
if (winetest_interactive && (device != WAVE_MAPPER))
{
trace("Playing a 5 seconds reference tone.\n");
--
2.34.1
2
1
[PATCH 2/2] winmm: Buffer any WOM_DONE/WIM_DATA events triggered from within a waveOut/waveIn callback.
by Liam Murphy Jan. 21, 2022
by Liam Murphy Jan. 21, 2022
Jan. 21, 2022
The events are then fired after the callback completes.
Signed-off-by: Liam Murphy <liampm32(a)gmail.com>
---
dlls/winmm/waveform.c | 165 +++++++++++++++++++++++++++++++-----------
1 file changed, 124 insertions(+), 41 deletions(-)
diff --git a/dlls/winmm/waveform.c b/dlls/winmm/waveform.c
index 1159b48b483..55b18b3e4dd 100644
--- a/dlls/winmm/waveform.c
+++ b/dlls/winmm/waveform.c
@@ -67,12 +67,20 @@ WINE_DEFAULT_DEBUG_CHANNEL(winmm);
#define AC_BUFLEN (10 * 100000)
#define MAX_DEVICES 256
#define MAPPER_INDEX 0x3F
+#define CB_RUNNING 0x10
typedef struct _WINMM_CBInfo {
DWORD_PTR callback;
DWORD_PTR user;
+ // Layout: 0b00000000_000r0ttt
+ // where t: callback type (`DCB_*` constants)
+ // r: Whether the callback is currently running, to avoid reentrancy.
+ // The 0 in the middle is `DCB_NOSWITCH`.
DWORD flags;
HWAVE hwave;
+ // Used to buffer data from `WIM_DATA` or `WOM_DONE` if the callback is still running,
+ // depending on whether this is an input or output device respectively.
+ WAVEHDR *first, *last;
} WINMM_CBInfo;
struct _WINMM_MMDevice;
@@ -169,6 +177,12 @@ typedef struct _WINMM_OpenInfo {
WAVEFORMATEX *format;
DWORD_PTR callback;
DWORD_PTR cb_user;
+ // The high half of the bits are the same as `WINMM_CBInfo.flags`,
+ // and the low half have the layout 0b00000000_0000dmaq,
+ // where d: `WAVE_FORMAT_DIRECT`,
+ // m: `WAVE_MAPPED`,
+ // a: `WAVE_ALLOWSYNC`,
+ // q: `WAVE_FORMAT_QUERY`.
DWORD flags;
BOOL reset;
} WINMM_OpenInfo;
@@ -370,13 +384,103 @@ static WINMM_Device *WINMM_GetDeviceFromHWAVE(HWAVE hwave)
return device;
}
+// Note: the lock on `device` must already have been acquired before calling this function.
+static inline void WINMM_SendBufferedMessages(WINMM_Device *device) {
+ WINMM_CBInfo* info = &device->cb_info;
+ // Make a copy of this to pass to the callback while within the critical section,
+ // since it can be mutated once we leave the critical section.
+ DWORD cb_type = info->flags;
+
+ while (info->first) {
+ WAVEHDR* hdr;
+ WORD new_msg;
+
+ if (device->render) {
+ new_msg = WOM_DONE;
+ } else {
+ new_msg = WIM_DATA;
+ }
+
+ hdr = info->first;
+ info->first = hdr->lpNext;
+ hdr->lpNext = NULL;
+
+ LeaveCriticalSection(&device->lock);
+
+ DriverCallback(info->callback, cb_type, (HDRVR)info->hwave,
+ new_msg, info->user, (DWORD_PTR)hdr, 0);
+
+ EnterCriticalSection(&device->lock);
+ }
+}
+
/* Note: NotifyClient should never be called while holding the device lock
- * since the client may call wave* functions from within the callback. */
-static inline void WINMM_NotifyClient(WINMM_CBInfo *info, WORD msg, DWORD_PTR param1,
- DWORD_PTR param2)
+ * since the client may call wave* functions from within the callback.
+ *
+ * Any `WAVEHDR`s passed to this function must no longer be referenced by any other `WAVEHDR`s,
+ * so that their linked list can be reused to buffer them when this is called from within the callback. */
+static inline void WINMM_NotifyClient(WINMM_Device *device, WORD msg, WAVEHDR* param1,
+ WAVEHDR* param2)
{
- DriverCallback(info->callback, info->flags, (HDRVR)info->hwave,
- msg, info->user, param1, param2);
+ WINMM_CBInfo *info;
+ DWORD cb_type;
+
+ EnterCriticalSection(&device->lock);
+ info = &device->cb_info;
+ // Make a copy of this to pass to the callback while within the critical section,
+ // since it can be mutated once we leave the critical section.
+ cb_type = info->flags;
+
+ if (info->flags & CB_RUNNING) {
+ switch (msg) {
+ case WIM_DATA:
+ case WOM_DONE:
+ param1->lpNext = NULL;
+ if (!info->first) {
+ info->first = info->last = param1;
+ } else {
+ info->last->lpNext = param1;
+ info->last = param1;
+ }
+ break;
+ case WIM_CLOSE:
+ case WOM_CLOSE:
+ // If this device is closing, it might get reopened as another kind of device
+ // with all the buffered messages wiped, causing them to never get sent.
+ // So we need to send them all now.
+ //
+ // On Windows, calling `waveOutClose` from within `DriverCallback` hangs anyway,
+ // so this causing reentrancy isn't really a problem.
+ WINMM_SendBufferedMessages(device);
+
+ LeaveCriticalSection(&device->lock);
+
+ DriverCallback(info->callback, cb_type, (HDRVR)info->hwave,
+ msg, info->user, (DWORD_PTR)param1, (DWORD_PTR)param2);
+
+ EnterCriticalSection(&device->lock);
+ break;
+ }
+ } else {
+ if ((info->flags & DCB_TYPEMASK) == DCB_FUNCTION) {
+ info->flags |= CB_RUNNING;
+ }
+
+ LeaveCriticalSection(&device->lock);
+
+ DriverCallback(info->callback, cb_type, (HDRVR)info->hwave,
+ msg, info->user, (DWORD_PTR)param1, (DWORD_PTR)param2);
+
+ EnterCriticalSection(&device->lock);
+
+ if ((info->flags & DCB_TYPEMASK) == DCB_FUNCTION) {
+ WINMM_SendBufferedMessages(device);
+
+ info->flags &= ~CB_RUNNING;
+ }
+ }
+
+ LeaveCriticalSection(&device->lock);
}
static MMRESULT hr2mmr(HRESULT hr)
@@ -1202,6 +1306,8 @@ static LRESULT WINMM_OpenDevice(WINMM_Device *device, WINMM_OpenInfo *info,
device->cb_info.callback = info->callback;
device->cb_info.user = info->cb_user;
device->cb_info.hwave = device->handle;
+ device->cb_info.first = NULL;
+ device->cb_info.last = NULL;
info->handle = device->handle;
@@ -1623,7 +1729,6 @@ static WAVEHDR *WOD_MarkDoneHeaders(WINMM_Device *device)
static void WOD_PushData(WINMM_Device *device)
{
- WINMM_CBInfo cb_info;
HRESULT hr;
UINT32 pad, bufsize, avail_frames, queue_frames, written, ofs;
UINT32 queue_bytes, nloops;
@@ -1752,15 +1857,13 @@ static void WOD_PushData(WINMM_Device *device)
device->played_frames += avail_frames;
exit:
- cb_info = device->cb_info;
-
LeaveCriticalSection(&device->lock);
while(first){
WAVEHDR *next = first->lpNext;
first->dwFlags &= ~WHDR_INQUEUE;
first->dwFlags |= WHDR_DONE;
- WINMM_NotifyClient(&cb_info, WOM_DONE, (DWORD_PTR)first, 0);
+ WINMM_NotifyClient(device, WOM_DONE, first, 0);
first = next;
}
}
@@ -1860,7 +1963,6 @@ static void WID_PullACMData(WINMM_Device *device)
static void WID_PullData(WINMM_Device *device)
{
- WINMM_CBInfo cb_info;
WAVEHDR *queue, *first = NULL, *last = NULL;
HRESULT hr;
@@ -1926,8 +2028,6 @@ static void WID_PullData(WINMM_Device *device)
}
exit:
- cb_info = device->cb_info;
-
LeaveCriticalSection(&device->lock);
if(last){
@@ -1936,7 +2036,7 @@ exit:
WAVEHDR *next = first->lpNext;
first->dwFlags &= ~WHDR_INQUEUE;
first->dwFlags |= WHDR_DONE;
- WINMM_NotifyClient(&cb_info, WIM_DATA, (DWORD_PTR)first, 0);
+ WINMM_NotifyClient(device, WIM_DATA, first, 0);
first = next;
}
}
@@ -1985,7 +2085,6 @@ static LRESULT WINMM_Pause(WINMM_Device *device)
static LRESULT WINMM_Reset(HWAVE hwave)
{
- WINMM_CBInfo cb_info;
WINMM_Device *device = WINMM_GetDeviceFromHWAVE(hwave);
BOOL is_out;
WAVEHDR *first;
@@ -2012,7 +2111,6 @@ static LRESULT WINMM_Reset(HWAVE hwave)
device->last_clock_pos = 0;
IAudioClient_Reset(device->client);
- cb_info = device->cb_info;
is_out = device->render != NULL;
LeaveCriticalSection(&device->lock);
@@ -2022,9 +2120,9 @@ static LRESULT WINMM_Reset(HWAVE hwave)
first->dwFlags &= ~WHDR_INQUEUE;
first->dwFlags |= WHDR_DONE;
if(is_out)
- WINMM_NotifyClient(&cb_info, WOM_DONE, (DWORD_PTR)first, 0);
+ WINMM_NotifyClient(device, WOM_DONE, first, 0);
else
- WINMM_NotifyClient(&cb_info, WIM_DATA, (DWORD_PTR)first, 0);
+ WINMM_NotifyClient(device, WIM_DATA, first, 0);
first = next;
}
@@ -2732,7 +2830,7 @@ MMRESULT WINAPI waveOutOpen(LPHWAVEOUT lphWaveOut, UINT uDeviceID,
{
LRESULT res;
WINMM_OpenInfo info;
- WINMM_CBInfo cb_info;
+ WINMM_Device* device;
TRACE("(%p, %u, %p, %lx, %lx, %08x)\n", lphWaveOut, uDeviceID, lpFormat,
dwCallback, dwInstance, dwFlags);
@@ -2763,12 +2861,9 @@ MMRESULT WINAPI waveOutOpen(LPHWAVEOUT lphWaveOut, UINT uDeviceID,
if(lphWaveOut)
*lphWaveOut = (HWAVEOUT)info.handle;
- cb_info.flags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
- cb_info.callback = dwCallback;
- cb_info.user = dwInstance;
- cb_info.hwave = info.handle;
+ device = WINMM_GetDeviceFromHWAVE(info.handle);
- WINMM_NotifyClient(&cb_info, WOM_OPEN, 0, 0);
+ WINMM_NotifyClient(device, WOM_OPEN, 0, 0);
return res;
}
@@ -2780,7 +2875,6 @@ UINT WINAPI waveOutClose(HWAVEOUT hWaveOut)
{
UINT res;
WINMM_Device *device;
- WINMM_CBInfo cb_info;
TRACE("(%p)\n", hWaveOut);
@@ -2789,14 +2883,12 @@ UINT WINAPI waveOutClose(HWAVEOUT hWaveOut)
if(!WINMM_ValidateAndLock(device))
return MMSYSERR_INVALHANDLE;
- cb_info = device->cb_info;
-
LeaveCriticalSection(&device->lock);
res = SendMessageW(g_devices_hwnd, WODM_CLOSE, (WPARAM)hWaveOut, 0);
if(res == MMSYSERR_NOERROR)
- WINMM_NotifyClient(&cb_info, WOM_CLOSE, 0, 0);
+ WINMM_NotifyClient(device, WOM_CLOSE, 0, 0);
return res;
}
@@ -3388,7 +3480,7 @@ MMRESULT WINAPI waveInOpen(HWAVEIN* lphWaveIn, UINT uDeviceID,
{
LRESULT res;
WINMM_OpenInfo info;
- WINMM_CBInfo cb_info;
+ WINMM_Device* device;
TRACE("(%p, %x, %p, %lx, %lx, %08x)\n", lphWaveIn, uDeviceID, lpFormat,
dwCallback, dwInstance, dwFlags);
@@ -3419,12 +3511,9 @@ MMRESULT WINAPI waveInOpen(HWAVEIN* lphWaveIn, UINT uDeviceID,
if(lphWaveIn)
*lphWaveIn = (HWAVEIN)info.handle;
- cb_info.flags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
- cb_info.callback = dwCallback;
- cb_info.user = dwInstance;
- cb_info.hwave = info.handle;
+ device = WINMM_GetDeviceFromHWAVE(info.handle);
- WINMM_NotifyClient(&cb_info, WIM_OPEN, 0, 0);
+ WINMM_NotifyClient(device, WIM_OPEN, 0, 0);
return res;
}
@@ -3435,7 +3524,6 @@ MMRESULT WINAPI waveInOpen(HWAVEIN* lphWaveIn, UINT uDeviceID,
UINT WINAPI waveInClose(HWAVEIN hWaveIn)
{
WINMM_Device *device;
- WINMM_CBInfo cb_info;
UINT res;
TRACE("(%p)\n", hWaveIn);
@@ -3445,14 +3533,12 @@ UINT WINAPI waveInClose(HWAVEIN hWaveIn)
if(!WINMM_ValidateAndLock(device))
return MMSYSERR_INVALHANDLE;
- cb_info = device->cb_info;
-
LeaveCriticalSection(&device->lock);
res = SendMessageW(g_devices_hwnd, WIDM_CLOSE, (WPARAM)hWaveIn, 0);
if(res == MMSYSERR_NOERROR)
- WINMM_NotifyClient(&cb_info, WIM_CLOSE, 0, 0);
+ WINMM_NotifyClient(device, WIM_CLOSE, 0, 0);
return res;
}
@@ -3568,7 +3654,6 @@ UINT WINAPI waveInStart(HWAVEIN hWaveIn)
*/
UINT WINAPI waveInStop(HWAVEIN hWaveIn)
{
- WINMM_CBInfo cb_info;
WINMM_Device *device;
WAVEHDR *buf;
HRESULT hr;
@@ -3593,14 +3678,12 @@ UINT WINAPI waveInStop(HWAVEIN hWaveIn)
}else
buf = NULL;
- cb_info = device->cb_info;
-
LeaveCriticalSection(&device->lock);
if(buf){
buf->dwFlags &= ~WHDR_INQUEUE;
buf->dwFlags |= WHDR_DONE;
- WINMM_NotifyClient(&cb_info, WIM_DATA, (DWORD_PTR)buf, 0);
+ WINMM_NotifyClient(device, WIM_DATA, buf, 0);
}
return MMSYSERR_NOERROR;
--
2.34.1
1
0