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