[PATCH 1/5] uxtheme/tests: Add EnableThemeDialogTexture() tests.

Zhiyi Zhang zzhang at codeweavers.com
Fri Jan 21 01:47:30 CST 2022


These tests show that DefDlgProcA/W() are hooked to implemented dialog theming, using a pattern
brush created from the tab body part. For dialogs that need theming, EnableThemeDialogTexture(ETDT_USETABTEXTURE)
or EnableThemeDialogTexture(ETDT_USEAEROWIZARDTABTEXTURE) is called for the dialog. And then
static or button controls in comctl32 v6 call EnableThemeDialogTexture(ETDT_ENABLE) to activate it.
A WM_ERASEBKGND is also needed to activate dialog theming. test_WM_CTLCOLORSTATIC() in dlls/comctl32/tests/static.c
doesn't send this message after EnableThemeDialogTexture() calls, which misdirected me to think that
DefDlgProcA/W() are not hooked.

Signed-off-by: Zhiyi Zhang <zzhang at 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)&param);
+    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)&param);
+        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)&param);
+            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




More information about the wine-devel mailing list