Wine-devel
Threads by month
- ----- 2026 -----
- 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
January 2022
- 86 participants
- 717 discussions
[PATCH 3/5] comctl32/tests: Remove WM_CTLCOLORSTATIC tests for property sheets.
by Zhiyi Zhang 21 Jan '22
by Zhiyi Zhang 21 Jan '22
21 Jan '22
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 21 Jan '22
by Zhiyi Zhang 21 Jan '22
21 Jan '22
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
21 Jan '22
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
21 Jan '22
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 21 Jan '22
by Liam Murphy 21 Jan '22
21 Jan '22
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 21 Jan '22
by Konstantin Romanov 21 Jan '22
21 Jan '22
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 21 Jan '22
by Liam Murphy 21 Jan '22
21 Jan '22
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 21 Jan '22
by Liam Murphy 21 Jan '22
21 Jan '22
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
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=51850
Signed-off-by: Mohamad Al-Jaf <mohamadaljaf(a)gmail.com>
---
v3: - Swap operands for arm and aarch64 (Credit: Jinoh Kang)
- Use void * instead of int for CurrentIP
- Add useful test (Credit: Giovanni Mascellani)
- Remove unnecessary #include <stdint.h>
Note: In the test I had to remove WINAPI from the declaration
because it would result in an 'undefined reference' error only
in the 32-bit build of Wine. The 64-bit build compiled without
any problems.
I don't understand why this happens.
---
configure.ac | 1 +
dlls/wdscore/Makefile.in | 3 ++
dlls/wdscore/main.c | 54 ++++++++++++++++++++++++++++++++++
dlls/wdscore/tests/Makefile.in | 5 ++++
dlls/wdscore/tests/main.c | 41 ++++++++++++++++++++++++++
dlls/wdscore/wdscore.spec | 2 +-
6 files changed, 105 insertions(+), 1 deletion(-)
create mode 100644 dlls/wdscore/main.c
create mode 100644 dlls/wdscore/tests/Makefile.in
create mode 100644 dlls/wdscore/tests/main.c
diff --git a/configure.ac b/configure.ac
index b42cb8a5346..a50881cc2fb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3347,6 +3347,7 @@ WINE_CONFIG_MAKEFILE(dlls/wbemdisp/tests)
WINE_CONFIG_MAKEFILE(dlls/wbemprox)
WINE_CONFIG_MAKEFILE(dlls/wbemprox/tests)
WINE_CONFIG_MAKEFILE(dlls/wdscore)
+WINE_CONFIG_MAKEFILE(dlls/wdscore/tests)
WINE_CONFIG_MAKEFILE(dlls/webservices)
WINE_CONFIG_MAKEFILE(dlls/webservices/tests)
WINE_CONFIG_MAKEFILE(dlls/websocket)
diff --git a/dlls/wdscore/Makefile.in b/dlls/wdscore/Makefile.in
index 20ba1d3b1c9..2020e72c7bb 100644
--- a/dlls/wdscore/Makefile.in
+++ b/dlls/wdscore/Makefile.in
@@ -1,3 +1,6 @@
MODULE = wdscore.dll
EXTRADLLFLAGS = -Wb,--prefer-native
+
+C_SRCS = \
+ main.c
diff --git a/dlls/wdscore/main.c b/dlls/wdscore/main.c
new file mode 100644
index 00000000000..24371068980
--- /dev/null
+++ b/dlls/wdscore/main.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2022 Mohamad Al-Jaf
+ *
+ * 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
+ */
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+
+#include "wine/asm.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(wdscore);
+
+/***********************************************************************
+ * CurrentIP (wdscore.@)
+ */
+#ifdef __i386__
+__ASM_STDCALL_FUNC(CurrentIP, 0,
+ "movl 0(%esp), %eax\n\t"
+ "ret" )
+#elif defined(__x86_64__)
+__ASM_STDCALL_FUNC(CurrentIP, 0,
+ "movq 0(%rsp), %rax\n\t"
+ "ret" )
+#elif defined(__arm__)
+__ASM_STDCALL_FUNC(CurrentIP, 0,
+ "mov r0, lr\n\t"
+ "bx lr" )
+#elif defined(__aarch64__)
+__ASM_STDCALL_FUNC(CurrentIP, 0,
+ "mov x0, lr\n\t"
+ "ret" )
+#else
+void *WINAPI CurrentIP(void)
+{
+ FIXME( "not implemented\n" );
+ return 0;
+}
+#endif
diff --git a/dlls/wdscore/tests/Makefile.in b/dlls/wdscore/tests/Makefile.in
new file mode 100644
index 00000000000..f0606d902d5
--- /dev/null
+++ b/dlls/wdscore/tests/Makefile.in
@@ -0,0 +1,5 @@
+TESTDLL = wdscore.dll
+IMPORTS = wdscore
+
+C_SRCS = \
+ main.c
diff --git a/dlls/wdscore/tests/main.c b/dlls/wdscore/tests/main.c
new file mode 100644
index 00000000000..71de31a1a0a
--- /dev/null
+++ b/dlls/wdscore/tests/main.c
@@ -0,0 +1,41 @@
+/*
+ * Unit test suite for wdscore
+ *
+ * Copyright 2022 Mohamad Al-Jaf
+ *
+ * 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
+ *
+ */
+
+#include "wine/test.h"
+#include "winbase.h"
+
+extern void *CurrentIP(void);
+
+static void test_CurrentIP(void)
+{
+ char *cur;
+ char *ret;
+
+ cur = (char*)&test_CurrentIP;
+ ret = (char*)CurrentIP();
+
+ ok(cur <= ret && ret < (cur + 0x100), "Address %p not in function starting at %p.\n", ret, cur);
+}
+
+START_TEST(main)
+{
+ test_CurrentIP();
+}
diff --git a/dlls/wdscore/wdscore.spec b/dlls/wdscore/wdscore.spec
index 15958b86aba..8b6febe6b3b 100644
--- a/dlls/wdscore/wdscore.spec
+++ b/dlls/wdscore/wdscore.spec
@@ -71,7 +71,7 @@
@ stub ConstructPartialMsgIfW
@ stub ConstructPartialMsgVA
@ stub ConstructPartialMsgVW
-@ stub CurrentIP
+@ stdcall CurrentIP()
@ stub EndMajorTask
@ stub EndMinorTask
@ stub GetMajorTask
--
2.34.1
3
4
[PATCH] server: Allocate enough space for the backslash in dup_nt_name() (Valgrind).
by Zebediah Figura 20 Jan '22
by Zebediah Figura 20 Jan '22
20 Jan '22
Signed-off-by: Zebediah Figura <zfigura(a)codeweavers.com>
---
server/fd.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/server/fd.c b/server/fd.c
index c9a21186722..1b4b98b0e76 100644
--- a/server/fd.c
+++ b/server/fd.c
@@ -1867,7 +1867,7 @@ static WCHAR *dup_nt_name( struct fd *root, struct unicode_str name, data_size_t
name.str++;
name.len -= sizeof(WCHAR);
}
- if ((ret = malloc( retlen + name.len + 1 )))
+ if ((ret = malloc( retlen + name.len + sizeof(WCHAR) )))
{
memcpy( ret, root->nt_name, root->nt_namelen );
if (name.len && name.str[0] != '\\' &&
--
2.34.1
1
0