/* Unit test suite for property sheet control. * * Copyright 2006 Huw Davies * Copyright 2009 Jan de Mooij * * 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 #include #include "msg.h" #include "resources.h" #include "wine/test.h" #include "v6util.h" static HWND parenthwnd; static HWND sheethwnd; static BOOL rtl; static LONG active_page = -1; static BOOL is_v6, is_theme_active; #define IDC_APPLY_BUTTON 12321 static HPROPSHEETPAGE (WINAPI *pCreatePropertySheetPageA)(const PROPSHEETPAGEA *desc); static HPROPSHEETPAGE (WINAPI *pCreatePropertySheetPageW)(const PROPSHEETPAGEW *desc); static BOOL (WINAPI *pDestroyPropertySheetPage)(HPROPSHEETPAGE proppage); static INT_PTR (WINAPI *pPropertySheetA)(const PROPSHEETHEADERA *header); static BOOL (WINAPI *pIsThemeActive)(void); static BOOL (WINAPI *pIsThemeDialogTextureEnabled)(HWND); static void detect_locale(void) { DWORD reading_layout; rtl = GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_IREADINGLAYOUT | LOCALE_RETURN_NUMBER, (void *)&reading_layout, sizeof(reading_layout)) && reading_layout == 1; } /* try to make sure pending X events have been processed before continuing */ static void flush_events(void) { MSG msg; int diff = 200; int min_timeout = 100; DWORD time = GetTickCount() + diff; while (diff > 0) { if (MsgWaitForMultipleObjects( 0, NULL, FALSE, min_timeout, QS_ALLINPUT ) == WAIT_TIMEOUT) break; while (PeekMessageA( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessageA( &msg ); diff = time - GetTickCount(); } } static int CALLBACK sheet_callback(HWND hwnd, UINT msg, LPARAM lparam) { switch(msg) { case PSCB_PRECREATE: { HMODULE module = GetModuleHandleA("comctl32.dll"); DWORD size, buffer_size; HRSRC hrsrc; hrsrc = FindResourceA(module, MAKEINTRESOURCEA(1006 /* IDD_PROPSHEET */), (LPSTR)RT_DIALOG); size = SizeofResource(module, hrsrc); ok(size != 0, "Failed to get size of propsheet dialog resource\n"); buffer_size = HeapSize(GetProcessHeap(), 0, (void *)lparam); /* Hebrew Windows 10 allocates 2 * size + 8, * Arabic Windows 10 allocates 2 * size - 32, * all others allocate exactly 2 * size */ ok(buffer_size >= 2 * size || broken(buffer_size == 2 * size - 32), "Unexpected template buffer size %lu, resource size %lu\n", buffer_size, size); break; } case PSCB_INITIALIZED: { char caption[256]; GetWindowTextA(hwnd, caption, sizeof(caption)); ok(!strcmp(caption,"test caption"), "caption: %s\n", caption); sheethwnd = hwnd; return 0; } } return 0; } static INT_PTR CALLBACK page_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { switch(msg) { case WM_INITDIALOG: { HWND sheet = GetParent(hwnd); char caption[256]; GetWindowTextA(sheet, caption, sizeof(caption)); ok(!strcmp(caption,"test caption"), "caption: %s\n", caption); return TRUE; } case WM_NOTIFY: { NMHDR *nmhdr = (NMHDR *)lparam; switch(nmhdr->code) { case PSN_APPLY: return TRUE; default: return FALSE; } } case WM_NCDESTROY: ok(!SendMessageA(sheethwnd, PSM_INDEXTOHWND, 400, 0),"Should always be 0\n"); return TRUE; default: return FALSE; } } static void test_title(void) { HPROPSHEETPAGE hpsp[1]; PROPSHEETPAGEA psp; PROPSHEETHEADERA psh; HWND hdlg; DWORD style; memset(&psp, 0, sizeof(psp)); psp.dwSize = sizeof(psp); psp.dwFlags = 0; psp.hInstance = GetModuleHandleA(NULL); U(psp).pszTemplate = "prop_page1"; U2(psp).pszIcon = NULL; psp.pfnDlgProc = page_dlg_proc; psp.lParam = 0; hpsp[0] = pCreatePropertySheetPageA(&psp); memset(&psh, 0, sizeof(psh)); psh.dwSize = PROPSHEETHEADERA_V1_SIZE; psh.dwFlags = PSH_MODELESS | PSH_USECALLBACK; psh.pszCaption = "test caption"; psh.nPages = 1; psh.hwndParent = GetDesktopWindow(); U3(psh).phpage = hpsp; psh.pfnCallback = sheet_callback; hdlg = (HWND)pPropertySheetA(&psh); ok(hdlg != INVALID_HANDLE_VALUE, "got invalid handle value %p\n", hdlg); style = GetWindowLongA(hdlg, GWL_STYLE); ok(style == (WS_POPUP|WS_VISIBLE|WS_CLIPSIBLINGS|WS_CAPTION|WS_SYSMENU| DS_CONTEXTHELP|DS_MODALFRAME|DS_SETFONT|DS_3DLOOK), "got unexpected style: %lx\n", style); DestroyWindow(hdlg); } static void test_nopage(void) { HPROPSHEETPAGE hpsp[1]; PROPSHEETPAGEA psp; PROPSHEETHEADERA psh; HWND hdlg, hpage; MSG msg; memset(&psp, 0, sizeof(psp)); psp.dwSize = sizeof(psp); psp.dwFlags = 0; psp.hInstance = GetModuleHandleA(NULL); U(psp).pszTemplate = "prop_page1"; U2(psp).pszIcon = NULL; psp.pfnDlgProc = page_dlg_proc; psp.lParam = 0; hpsp[0] = pCreatePropertySheetPageA(&psp); memset(&psh, 0, sizeof(psh)); psh.dwSize = PROPSHEETHEADERA_V1_SIZE; psh.dwFlags = PSH_MODELESS | PSH_USECALLBACK; psh.pszCaption = "test caption"; psh.nPages = 1; psh.hwndParent = GetDesktopWindow(); U3(psh).phpage = hpsp; psh.pfnCallback = sheet_callback; hdlg = (HWND)pPropertySheetA(&psh); ok(hdlg != INVALID_HANDLE_VALUE, "got invalid handle value %p\n", hdlg); ShowWindow(hdlg,SW_NORMAL); SendMessageA(hdlg, PSM_REMOVEPAGE, 0, 0); hpage = /* PropSheet_GetCurrentPageHwnd(hdlg); */ (HWND)SendMessageA(hdlg, PSM_GETCURRENTPAGEHWND, 0, 0); active_page = /* PropSheet_HwndToIndex(hdlg, hpage)); */ (int)SendMessageA(hdlg, PSM_HWNDTOINDEX, (WPARAM)hpage, 0); ok(hpage == NULL, "expected no current page, got %p, index=%ld\n", hpage, active_page); flush_events(); RedrawWindow(hdlg,NULL,NULL,RDW_UPDATENOW|RDW_ERASENOW); /* Check that the property sheet was fully redrawn */ ok(!PeekMessageA(&msg, 0, WM_PAINT, WM_PAINT, PM_NOREMOVE), "expected no pending WM_PAINT messages\n"); DestroyWindow(hdlg); } static int CALLBACK disableowner_callback(HWND hwnd, UINT msg, LPARAM lparam) { switch(msg) { case PSCB_INITIALIZED: { ok(IsWindowEnabled(parenthwnd) == 0, "parent window should be disabled\n"); PostQuitMessage(0); return FALSE; } } return FALSE; } static void register_parent_wnd_class(void) { WNDCLASSA cls; cls.style = 0; cls.lpfnWndProc = DefWindowProcA; cls.cbClsExtra = 0; cls.cbWndExtra = 0; cls.hInstance = GetModuleHandleA(NULL); cls.hIcon = 0; cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW); cls.hbrBackground = GetStockObject(WHITE_BRUSH); cls.lpszMenuName = NULL; cls.lpszClassName = "parent class"; RegisterClassA(&cls); } static void test_disableowner(void) { HPROPSHEETPAGE hpsp[1]; PROPSHEETPAGEA psp; PROPSHEETHEADERA psh; INT_PTR p; register_parent_wnd_class(); parenthwnd = CreateWindowA("parent class", "", WS_CAPTION | WS_SYSMENU | WS_VISIBLE, 100, 100, 100, 100, GetDesktopWindow(), NULL, GetModuleHandleA(NULL), 0); memset(&psp, 0, sizeof(psp)); psp.dwSize = sizeof(psp); psp.dwFlags = 0; psp.hInstance = GetModuleHandleA(NULL); U(psp).pszTemplate = "prop_page1"; U2(psp).pszIcon = NULL; psp.pfnDlgProc = NULL; psp.lParam = 0; hpsp[0] = pCreatePropertySheetPageA(&psp); memset(&psh, 0, sizeof(psh)); psh.dwSize = PROPSHEETHEADERA_V1_SIZE; psh.dwFlags = PSH_USECALLBACK; psh.pszCaption = "test caption"; psh.nPages = 1; psh.hwndParent = parenthwnd; U3(psh).phpage = hpsp; psh.pfnCallback = disableowner_callback; p = pPropertySheetA(&psh); ok(p == 0, "Expected 0, got %Id\n", p); ok(IsWindowEnabled(parenthwnd) != 0, "parent window should be enabled\n"); DestroyWindow(parenthwnd); } static INT_PTR CALLBACK nav_page_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { switch(msg){ case WM_NOTIFY: { LPNMHDR hdr = (LPNMHDR)lparam; switch(hdr->code){ case PSN_SETACTIVE: active_page = /* PropSheet_HwndToIndex(hdr->hwndFrom, hwnd); */ (int)SendMessageA(hdr->hwndFrom, PSM_HWNDTOINDEX, (WPARAM)hwnd, 0); return TRUE; case PSN_KILLACTIVE: /* prevent navigation away from the fourth page */ if(active_page == 3){ SetWindowLongPtrA(hwnd, DWLP_MSGRESULT, TRUE); return TRUE; } } break; } } return FALSE; } static WNDPROC old_nav_dialog_proc; static LRESULT CALLBACK new_nav_dialog_proc( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp ) { switch (msg) { case DM_SETDEFID: ok( IsWindowEnabled( GetDlgItem(hwnd, wp) ), "button is not enabled\n" ); break; } return CallWindowProcW( old_nav_dialog_proc, hwnd, msg, wp, lp ); } static LRESULT CALLBACK hook_proc( int code, WPARAM wp, LPARAM lp ) { static BOOL done; if (code == HCBT_CREATEWND) { CBT_CREATEWNDW *c = (CBT_CREATEWNDW *)lp; /* The first dialog created will be the parent dialog */ if (!done && c->lpcs->lpszClass == (LPWSTR)WC_DIALOG) { old_nav_dialog_proc = (WNDPROC)SetWindowLongPtrW( (HWND)wp, GWLP_WNDPROC, (LONG_PTR)new_nav_dialog_proc ); done = TRUE; } } return CallNextHookEx( NULL, code, wp, lp ); } static void test_wiznavigation(void) { HPROPSHEETPAGE hpsp[4]; PROPSHEETPAGEA psp[4]; PROPSHEETHEADERA psh; HWND hdlg, control; LONG_PTR controlID; DWORD style; LRESULT defidres; BOOL hwndtoindex_supported = TRUE; const INT nextID = 12324; const INT backID = 12323; HHOOK hook; /* set up a hook proc in order to subclass the main dialog early on */ hook = SetWindowsHookExW( WH_CBT, hook_proc, NULL, GetCurrentThreadId() ); /* create the property sheet pages */ memset(psp, 0, sizeof(PROPSHEETPAGEA) * 4); psp[0].dwSize = sizeof(PROPSHEETPAGEA); psp[0].hInstance = GetModuleHandleA(NULL); U(psp[0]).pszTemplate = (LPCSTR)MAKEINTRESOURCE(IDD_PROP_PAGE_INTRO); psp[0].pfnDlgProc = nav_page_proc; hpsp[0] = pCreatePropertySheetPageA(&psp[0]); psp[1].dwSize = sizeof(PROPSHEETPAGEA); psp[1].hInstance = GetModuleHandleA(NULL); U(psp[1]).pszTemplate = (LPCSTR)MAKEINTRESOURCE(IDD_PROP_PAGE_EDIT); psp[1].pfnDlgProc = nav_page_proc; hpsp[1] = pCreatePropertySheetPageA(&psp[1]); psp[2].dwSize = sizeof(PROPSHEETPAGEA); psp[2].hInstance = GetModuleHandleA(NULL); U(psp[2]).pszTemplate = (LPCSTR)MAKEINTRESOURCE(IDD_PROP_PAGE_RADIO); psp[2].pfnDlgProc = nav_page_proc; hpsp[2] = pCreatePropertySheetPageA(&psp[2]); psp[3].dwSize = sizeof(PROPSHEETPAGEA); psp[3].hInstance = GetModuleHandleA(NULL); U(psp[3]).pszTemplate = (LPCSTR)MAKEINTRESOURCE(IDD_PROP_PAGE_EXIT); psp[3].pfnDlgProc = nav_page_proc; hpsp[3] = pCreatePropertySheetPageA(&psp[3]); /* set up the property sheet dialog */ memset(&psh, 0, sizeof(psh)); psh.dwSize = PROPSHEETHEADERA_V1_SIZE; psh.dwFlags = PSH_MODELESS | PSH_WIZARD; psh.pszCaption = "A Wizard"; psh.nPages = 4; psh.hwndParent = GetDesktopWindow(); U3(psh).phpage = hpsp; hdlg = (HWND)pPropertySheetA(&psh); ok(hdlg != INVALID_HANDLE_VALUE, "got invalid handle %p\n", hdlg); ok(active_page == 0, "Active page should be 0. Is: %ld\n", active_page); style = GetWindowLongA(hdlg, GWL_STYLE) & ~(DS_CONTEXTHELP|WS_SYSMENU); ok(style == (WS_POPUP|WS_VISIBLE|WS_CLIPSIBLINGS|WS_CAPTION| DS_MODALFRAME|DS_SETFONT|DS_3DLOOK), "got unexpected style: %lx\n", style); control = GetFocus(); controlID = GetWindowLongPtrA(control, GWLP_ID); ok(controlID == nextID, "Focus should have been set to the Next button. Expected: %d, Found: %Id\n", nextID, controlID); /* simulate pressing the Next button */ SendMessageA(hdlg, PSM_PRESSBUTTON, PSBTN_NEXT, 0); if (!active_page) hwndtoindex_supported = FALSE; if (hwndtoindex_supported) ok(active_page == 1, "Active page should be 1 after pressing Next. Is: %ld\n", active_page); control = GetFocus(); controlID = GetWindowLongPtrA(control, GWLP_ID); ok(controlID == IDC_PS_EDIT1, "Focus should be set to the first item on the second page. Expected: %d, Found: %Id\n", IDC_PS_EDIT1, controlID); defidres = SendMessageA(hdlg, DM_GETDEFID, 0, 0); ok(defidres == MAKELRESULT(nextID, DC_HASDEFID), "Expected default button ID to be %d, is %d\n", nextID, LOWORD(defidres)); /* set the focus to the second edit box on this page */ SetFocus(GetNextDlgTabItem(hdlg, control, FALSE)); /* press next again */ SendMessageA(hdlg, PSM_PRESSBUTTON, PSBTN_NEXT, 0); if (hwndtoindex_supported) ok(active_page == 2, "Active page should be 2 after pressing Next. Is: %ld\n", active_page); control = GetFocus(); controlID = GetWindowLongPtrA(control, GWLP_ID); ok(controlID == IDC_PS_RADIO1, "Focus should have been set to item on third page. Expected: %d, Found %Id\n", IDC_PS_RADIO1, controlID); /* back button */ SendMessageA(hdlg, PSM_PRESSBUTTON, PSBTN_BACK, 0); if (hwndtoindex_supported) ok(active_page == 1, "Active page should be 1 after pressing Back. Is: %ld\n", active_page); control = GetFocus(); controlID = GetWindowLongPtrA(control, GWLP_ID); ok(controlID == IDC_PS_EDIT1, "Focus should have been set to the first item on second page. Expected: %d, Found %Id\n", IDC_PS_EDIT1, controlID); defidres = SendMessageA(hdlg, DM_GETDEFID, 0, 0); ok(defidres == MAKELRESULT(backID, DC_HASDEFID), "Expected default button ID to be %d, is %d\n", backID, LOWORD(defidres)); /* press next twice */ SendMessageA(hdlg, PSM_PRESSBUTTON, PSBTN_NEXT, 0); if (hwndtoindex_supported) ok(active_page == 2, "Active page should be 2 after pressing Next. Is: %ld\n", active_page); SendMessageA(hdlg, PSM_PRESSBUTTON, PSBTN_NEXT, 0); if (hwndtoindex_supported) ok(active_page == 3, "Active page should be 3 after pressing Next. Is: %ld\n", active_page); else active_page = 3; control = GetFocus(); controlID = GetWindowLongPtrA(control, GWLP_ID); ok(controlID == nextID, "Focus should have been set to the Next button. Expected: %d, Found: %Id\n", nextID, controlID); /* try to navigate away, but shouldn't be able to */ SendMessageA(hdlg, PSM_PRESSBUTTON, PSBTN_BACK, 0); ok(active_page == 3, "Active page should still be 3 after pressing Back. Is: %ld\n", active_page); defidres = SendMessageA(hdlg, DM_GETDEFID, 0, 0); ok(defidres == MAKELRESULT(nextID, DC_HASDEFID), "Expected default button ID to be %d, is %d\n", nextID, LOWORD(defidres)); DestroyWindow(hdlg); UnhookWindowsHookEx( hook ); } static void test_buttons(void) { HPROPSHEETPAGE hpsp[1]; PROPSHEETPAGEA psp; PROPSHEETHEADERA psh; HWND hdlg; HWND button; RECT rc; int prevRight, top; memset(&psp, 0, sizeof(psp)); psp.dwSize = sizeof(psp); psp.dwFlags = 0; psp.hInstance = GetModuleHandleA(NULL); U(psp).pszTemplate = "prop_page1"; U2(psp).pszIcon = NULL; psp.pfnDlgProc = page_dlg_proc; psp.lParam = 0; hpsp[0] = pCreatePropertySheetPageA(&psp); memset(&psh, 0, sizeof(psh)); psh.dwSize = PROPSHEETHEADERA_V1_SIZE; psh.dwFlags = PSH_MODELESS | PSH_USECALLBACK; psh.pszCaption = "test caption"; psh.nPages = 1; psh.hwndParent = GetDesktopWindow(); U3(psh).phpage = hpsp; psh.pfnCallback = sheet_callback; hdlg = (HWND)pPropertySheetA(&psh); ok(hdlg != INVALID_HANDLE_VALUE, "got null handle\n"); /* OK button */ button = GetDlgItem(hdlg, IDOK); GetWindowRect(button, &rc); prevRight = rc.right; top = rc.top; /* Cancel button */ button = GetDlgItem(hdlg, IDCANCEL); GetWindowRect(button, &rc); ok(rc.top == top, "Cancel button should have same top as OK button\n"); if (rtl) ok(rc.left < prevRight, "Cancel button should be to the left of OK button\n"); else ok(rc.left > prevRight, "Cancel button should be to the right of OK button\n"); prevRight = rc.right; button = GetDlgItem(hdlg, IDC_APPLY_BUTTON); GetWindowRect(button, &rc); ok(rc.top == top, "Apply button should have same top as OK button\n"); if (rtl) ok(rc.left < prevRight, "Apply button should be to the left of Cancel button\n"); else ok(rc.left > prevRight, "Apply button should be to the right of Cancel button\n"); prevRight = rc.right; button = GetDlgItem(hdlg, IDHELP); GetWindowRect(button, &rc); ok(rc.top == top, "Help button should have same top as OK button\n"); if (rtl) ok(rc.left < prevRight, "Help button should be to the left of Apply button\n"); else ok(rc.left > prevRight, "Help button should be to the right of Apply button\n"); DestroyWindow(hdlg); } static BOOL add_button_has_been_pressed; static INT_PTR CALLBACK page_with_custom_default_button_dlg_proc(HWND hdlg, UINT msg, WPARAM wparam, LPARAM lparam) { switch (msg) { case WM_COMMAND: switch(LOWORD(wparam)) { case IDC_PS_PUSHBUTTON1: switch(HIWORD(wparam)) { case BN_CLICKED: add_button_has_been_pressed = TRUE; return TRUE; } break; } break; } return FALSE; } static void test_custom_default_button(void) { HWND hdlg, page; PROPSHEETPAGEA psp[1]; PROPSHEETHEADERA psh; MSG msg; LRESULT result; psp[0].dwSize = sizeof (PROPSHEETPAGEA); psp[0].dwFlags = PSP_USETITLE; psp[0].hInstance = GetModuleHandleA(NULL); U(psp[0]).pszTemplate = (LPCSTR)MAKEINTRESOURCE(IDD_PROP_PAGE_WITH_CUSTOM_DEFAULT_BUTTON); U2(psp[0]).pszIcon = NULL; psp[0].pfnDlgProc = page_with_custom_default_button_dlg_proc; psp[0].pszTitle = "Page1"; psp[0].lParam = 0; psh.dwSize = PROPSHEETHEADERA_V1_SIZE; psh.dwFlags = PSH_PROPSHEETPAGE | PSH_MODELESS; psh.hwndParent = GetDesktopWindow(); psh.hInstance = GetModuleHandleA(NULL); U(psh).pszIcon = NULL; psh.pszCaption = "PropertySheet1"; psh.nPages = 1; U3(psh).ppsp = psp; U2(psh).nStartPage = 0; /* The goal of the test is to make sure that the Add button is pressed * when the ENTER key is pressed and a different control, a combobox, * has the keyboard focus. */ add_button_has_been_pressed = FALSE; /* Create the modeless property sheet. */ hdlg = (HWND)pPropertySheetA(&psh); ok(hdlg != INVALID_HANDLE_VALUE, "Cannot create the property sheet\n"); /* Set the Add button as the default button. */ SendMessageA(hdlg, DM_SETDEFID, (WPARAM)IDC_PS_PUSHBUTTON1, 0); /* Make sure the default button is the Add button. */ result = SendMessageA(hdlg, DM_GETDEFID, 0, 0); ok(DC_HASDEFID == HIWORD(result), "The property sheet does not have a default button\n"); ok(IDC_PS_PUSHBUTTON1 == LOWORD(result), "The default button is not the Add button\n"); /* At this point, the combobox should have keyboard focus, so we press ENTER. * Pull the lever, Kronk! */ page = (HWND)SendMessageW(hdlg, PSM_GETCURRENTPAGEHWND, 0, 0); PostMessageW(GetDlgItem(page, IDC_PS_COMBO1), WM_KEYDOWN, VK_RETURN, 0); /* Process all the messages in the queue for this thread. */ while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) { /* (!PropSheet_IsDialogMessage(hdlg, &msg)) */ if (!((BOOL)SendMessageA(hdlg, PSM_ISDIALOGMESSAGE, 0, (LPARAM)&msg))) { TranslateMessage(&msg); DispatchMessageA(&msg); } } ok(add_button_has_been_pressed, "The Add button has not been pressed!\n"); DestroyWindow(hdlg); } #define RECEIVER_SHEET_CALLBACK 0 #define RECEIVER_SHEET_WINPROC 1 #define RECEIVER_PAGE 2 #define NUM_MSG_SEQUENCES 1 #define PROPSHEET_SEQ_INDEX 0 static struct msg_sequence *sequences[NUM_MSG_SEQUENCES]; static WNDPROC oldWndProc; static const struct message property_sheet_seq[] = { { PSCB_PRECREATE, sent|id, 0, 0, RECEIVER_SHEET_CALLBACK }, { PSCB_INITIALIZED, sent|id, 0, 0, RECEIVER_SHEET_CALLBACK }, { WM_WINDOWPOSCHANGING, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, /*{ WM_NCCALCSIZE, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },*/ { WM_WINDOWPOSCHANGED, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_MOVE, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_SIZE, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, /*{ WM_GETTEXT, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_GETICON, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_GETICON, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_GETICON, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_NCCALCSIZE, sent|id|optional, 0, 0, RECEIVER_SHEET_WINPROC }, { DM_REPOSITION, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },*/ { WM_WINDOWPOSCHANGING, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_WINDOWPOSCHANGING, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_WINDOWPOSCHANGED, sent|id|optional, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_ACTIVATEAPP, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, /*{ WM_NCACTIVATE, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_GETTEXT, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_GETICON, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_GETICON, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_GETICON, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_GETTEXT, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },*/ { WM_ACTIVATE, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, /*{ WM_IME_SETCONTEXT, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_IME_NOTIFY, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },*/ { WM_SETFOCUS, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_KILLFOCUS, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, /*{ WM_IME_SETCONTEXT, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },*/ { WM_PARENTNOTIFY, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_INITDIALOG, sent|id, 0, 0, RECEIVER_PAGE }, { WM_WINDOWPOSCHANGING, sent|id, 0, 0, RECEIVER_PAGE }, /*{ WM_NCCALCSIZE, sent|id, 0, 0, RECEIVER_PAGE },*/ { WM_CHILDACTIVATE, sent|id, 0, 0, RECEIVER_PAGE }, { WM_WINDOWPOSCHANGED, sent|id, 0, 0, RECEIVER_PAGE }, { WM_MOVE, sent|id, 0, 0, RECEIVER_PAGE }, { WM_SIZE, sent|id, 0, 0, RECEIVER_PAGE }, { WM_NOTIFY, sent|id, 0, 0, RECEIVER_PAGE }, { WM_STYLECHANGING, sent|id|optional, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_STYLECHANGED, sent|id|optional, 0, 0, RECEIVER_SHEET_WINPROC }, /*{ WM_GETTEXT, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_GETICON, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_GETICON, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_GETICON, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },*/ { WM_SETTEXT, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_SHOWWINDOW, sent|id, 0, 0, RECEIVER_PAGE }, /*{ 0x00000401, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, { 0x00000400, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },*/ { WM_CHANGEUISTATE, sent|id|optional, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_UPDATEUISTATE, sent|id|optional, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_UPDATEUISTATE, sent|id|optional, 0, 0, RECEIVER_PAGE }, { WM_SHOWWINDOW, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_WINDOWPOSCHANGING, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, /*{ WM_NCPAINT, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_GETICON, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_GETICON, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_GETICON, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_ERASEBKGND, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },*/ { WM_CTLCOLORDLG, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_WINDOWPOSCHANGED, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, /*{ WM_GETICON, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_GETICON, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_GETICON, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_PAINT, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_PAINT, sent|id, 0, 0, RECEIVER_PAGE }, { WM_NCPAINT, sent|id, 0, 0, RECEIVER_PAGE }, { WM_ERASEBKGND, sent|id, 0, 0, RECEIVER_PAGE },*/ { WM_CTLCOLORDLG, sent|id, 0, 0, RECEIVER_PAGE }, { WM_CTLCOLORSTATIC, sent|id, 0, 0, RECEIVER_PAGE }, { WM_CTLCOLORSTATIC, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_CTLCOLORBTN, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_CTLCOLORBTN, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_CTLCOLORBTN, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, /*{ WM_GETICON, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_GETICON, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_GETICON, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },*/ { WM_COMMAND, sent|id|optional, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_NOTIFY, sent|id|optional, 0, 0, RECEIVER_PAGE }, { WM_NOTIFY, sent|id|optional, 0, 0, RECEIVER_PAGE }, { WM_WINDOWPOSCHANGING, sent|id|optional, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_WINDOWPOSCHANGED, sent|id|optional, 0, 0, RECEIVER_SHEET_WINPROC }, /*{ WM_NCACTIVATE, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_GETICON, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_GETICON, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_GETICON, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },*/ /*{ WM_ACTIVATE, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_ACTIVATE, sent|id, 0, 0, RECEIVER_PAGE }, { WM_ACTIVATEAPP, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_ACTIVATEAPP, sent|id, 0, 0, RECEIVER_PAGE }, { WM_DESTROY, sent|id, 0, 0, RECEIVER_SHEET_WINPROC }, { WM_DESTROY, sent|id, 0, 0, RECEIVER_PAGE },*/ /*{ WM_NCDESTROY, sent|id, 0, 0, RECEIVER_PAGE }, { WM_NCDESTROY, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },*/ { 0 } }; static void save_message(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, INT receiver) { struct message msg = { 0 }; if (message < WM_USER && message != WM_GETICON && message != WM_GETTEXT && message != WM_IME_SETCONTEXT && message != WM_IME_NOTIFY && message != WM_PAINT && message != WM_ERASEBKGND && message != WM_SETCURSOR && (message < WM_NCCREATE || message > WM_NCMBUTTONDBLCLK) && (message < WM_MOUSEFIRST || message > WM_MOUSEHWHEEL) && message != 0x90) { msg.message = message; msg.flags = sent|wparam|lparam|id; msg.wParam = wParam; msg.lParam = lParam; msg.id = receiver; add_message(sequences, PROPSHEET_SEQ_INDEX, &msg); } } static LRESULT CALLBACK sheet_callback_messages_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { save_message(hwnd, msg, wParam, lParam, RECEIVER_SHEET_WINPROC); return CallWindowProcA(oldWndProc, hwnd, msg, wParam, lParam); } static int CALLBACK sheet_callback_messages(HWND hwnd, UINT msg, LPARAM lParam) { save_message(hwnd, msg, 0, lParam, RECEIVER_SHEET_CALLBACK); switch (msg) { case PSCB_INITIALIZED: oldWndProc = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_WNDPROC); SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (LONG_PTR)&sheet_callback_messages_proc); return TRUE; } return TRUE; } static INT_PTR CALLBACK page_dlg_proc_messages(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { save_message(hwnd, msg, wParam, lParam, RECEIVER_PAGE); return FALSE; } static void test_messages(void) { HPROPSHEETPAGE hpsp[1]; PROPSHEETPAGEA psp; PROPSHEETHEADERA psh; HWND hdlg; init_msg_sequences(sequences, NUM_MSG_SEQUENCES); memset(&psp, 0, sizeof(psp)); psp.dwSize = sizeof(psp); psp.dwFlags = 0; psp.hInstance = GetModuleHandleA(NULL); U(psp).pszTemplate = (LPCSTR)MAKEINTRESOURCE(IDD_PROP_PAGE_MESSAGE_TEST); U2(psp).pszIcon = NULL; psp.pfnDlgProc = page_dlg_proc_messages; psp.lParam = 0; hpsp[0] = pCreatePropertySheetPageA(&psp); memset(&psh, 0, sizeof(psh)); psh.dwSize = PROPSHEETHEADERA_V1_SIZE; psh.dwFlags = PSH_NOAPPLYNOW | PSH_WIZARD | PSH_USECALLBACK | PSH_MODELESS | PSH_USEICONID; psh.pszCaption = "test caption"; psh.nPages = 1; psh.hwndParent = GetDesktopWindow(); U3(psh).phpage = hpsp; psh.pfnCallback = sheet_callback_messages; hdlg = (HWND)pPropertySheetA(&psh); ok(hdlg != INVALID_HANDLE_VALUE, "got invalid handle %p\n", hdlg); ShowWindow(hdlg,SW_NORMAL); ok_sequence(sequences, PROPSHEET_SEQ_INDEX, property_sheet_seq, "property sheet with custom window proc", TRUE); DestroyWindow(hdlg); } static void test_PSM_ADDPAGE(void) { HPROPSHEETPAGE hpsp[5]; PROPSHEETPAGEA psp; PROPSHEETHEADERA psh; HWND hdlg, tab; BOOL ret; DWORD r; memset(&psp, 0, sizeof(psp)); psp.dwSize = sizeof(psp); psp.dwFlags = 0; psp.hInstance = GetModuleHandleA(NULL); U(psp).pszTemplate = (LPCSTR)MAKEINTRESOURCE(IDD_PROP_PAGE_MESSAGE_TEST); U2(psp).pszIcon = NULL; psp.pfnDlgProc = page_dlg_proc_messages; psp.lParam = 0; /* multiple pages with the same data */ hpsp[0] = pCreatePropertySheetPageA(&psp); hpsp[1] = pCreatePropertySheetPageA(&psp); hpsp[2] = pCreatePropertySheetPageA(&psp); U(psp).pszTemplate = (LPCSTR)MAKEINTRESOURCE(IDD_PROP_PAGE_ERROR); hpsp[3] = pCreatePropertySheetPageA(&psp); psp.dwFlags = PSP_PREMATURE; hpsp[4] = pCreatePropertySheetPageA(&psp); memset(&psh, 0, sizeof(psh)); psh.dwSize = PROPSHEETHEADERA_V1_SIZE; psh.dwFlags = PSH_MODELESS; psh.pszCaption = "test caption"; psh.nPages = 1; psh.hwndParent = GetDesktopWindow(); U3(psh).phpage = hpsp; hdlg = (HWND)pPropertySheetA(&psh); ok(hdlg != INVALID_HANDLE_VALUE, "got invalid handle %p\n", hdlg); /* add pages one by one */ ret = SendMessageA(hdlg, PSM_ADDPAGE, 0, (LPARAM)hpsp[1]); ok(ret == TRUE, "got %d\n", ret); /* try with null and invalid value */ ret = SendMessageA(hdlg, PSM_ADDPAGE, 0, 0); ok(ret == FALSE, "got %d\n", ret); if (0) { /* crashes on native */ ret = SendMessageA(hdlg, PSM_ADDPAGE, 0, (LPARAM)INVALID_HANDLE_VALUE); } /* check item count */ tab = (HWND)SendMessageA(hdlg, PSM_GETTABCONTROL, 0, 0); r = SendMessageA(tab, TCM_GETITEMCOUNT, 0, 0); ok(r == 2, "got %ld\n", r); ret = SendMessageA(hdlg, PSM_ADDPAGE, 0, (LPARAM)hpsp[2]); ok(ret == TRUE, "got %d\n", ret); r = SendMessageA(tab, TCM_GETITEMCOUNT, 0, 0); ok(r == 3, "got %ld\n", r); /* add property sheet page that can't be created */ ret = SendMessageA(hdlg, PSM_ADDPAGE, 0, (LPARAM)hpsp[3]); ok(ret == TRUE, "got %d\n", ret); r = SendMessageA(tab, TCM_GETITEMCOUNT, 0, 0); ok(r == 4, "got %ld\n", r); /* select page that can't be created */ ret = SendMessageA(hdlg, PSM_SETCURSEL, 3, 1); ok(ret == TRUE, "got %d\n", ret); r = SendMessageA(tab, TCM_GETITEMCOUNT, 0, 0); ok(r == 3, "got %ld\n", r); /* test PSP_PREMATURE flag with incorrect property sheet page */ ret = SendMessageA(hdlg, PSM_ADDPAGE, 0, (LPARAM)hpsp[4]); ok(ret == FALSE, "got %d\n", ret); r = SendMessageA(tab, TCM_GETITEMCOUNT, 0, 0); ok(r == 3, "got %ld\n", r); pDestroyPropertySheetPage(hpsp[4]); DestroyWindow(hdlg); } static void test_PSM_INSERTPAGE(void) { HPROPSHEETPAGE hpsp[5]; PROPSHEETPAGEA psp; PROPSHEETHEADERA psh; HWND hdlg, tab; BOOL ret; DWORD r; memset(&psp, 0, sizeof(psp)); psp.dwSize = sizeof(psp); psp.dwFlags = 0; psp.hInstance = GetModuleHandleA(NULL); U(psp).pszTemplate = (LPCSTR)MAKEINTRESOURCE(IDD_PROP_PAGE_MESSAGE_TEST); U2(psp).pszIcon = NULL; psp.pfnDlgProc = page_dlg_proc_messages; psp.lParam = 0; /* multiple pages with the same data */ hpsp[0] = pCreatePropertySheetPageA(&psp); hpsp[1] = pCreatePropertySheetPageA(&psp); hpsp[2] = pCreatePropertySheetPageA(&psp); U(psp).pszTemplate = (LPCSTR)MAKEINTRESOURCE(IDD_PROP_PAGE_ERROR); hpsp[3] = pCreatePropertySheetPageA(&psp); psp.dwFlags = PSP_PREMATURE; hpsp[4] = pCreatePropertySheetPageA(&psp); memset(&psh, 0, sizeof(psh)); psh.dwSize = PROPSHEETHEADERA_V1_SIZE; psh.dwFlags = PSH_MODELESS; psh.pszCaption = "test caption"; psh.nPages = 1; psh.hwndParent = GetDesktopWindow(); U3(psh).phpage = hpsp; hdlg = (HWND)pPropertySheetA(&psh); ok(hdlg != INVALID_HANDLE_VALUE, "got invalid handle %p\n", hdlg); /* add pages one by one */ ret = SendMessageA(hdlg, PSM_INSERTPAGE, 5, (LPARAM)hpsp[1]); ok(ret == TRUE, "got %d\n", ret); /* try with invalid values */ ret = SendMessageA(hdlg, PSM_INSERTPAGE, 0, 0); ok(ret == FALSE, "got %d\n", ret); if (0) { /* crashes on native */ ret = SendMessageA(hdlg, PSM_INSERTPAGE, 0, (LPARAM)INVALID_HANDLE_VALUE); } ret = SendMessageA(hdlg, PSM_INSERTPAGE, (WPARAM)INVALID_HANDLE_VALUE, (LPARAM)hpsp[2]); ok(ret == FALSE, "got %d\n", ret); /* check item count */ tab = (HWND)SendMessageA(hdlg, PSM_GETTABCONTROL, 0, 0); r = SendMessageA(tab, TCM_GETITEMCOUNT, 0, 0); ok(r == 2, "got %ld\n", r); ret = SendMessageA(hdlg, PSM_INSERTPAGE, (WPARAM)hpsp[1], (LPARAM)hpsp[2]); ok(ret == TRUE, "got %d\n", ret); r = SendMessageA(tab, TCM_GETITEMCOUNT, 0, 0); ok(r == 3, "got %ld\n", r); /* add property sheet page that can't be created */ ret = SendMessageA(hdlg, PSM_INSERTPAGE, 1, (LPARAM)hpsp[3]); ok(ret == TRUE, "got %d\n", ret); r = SendMessageA(tab, TCM_GETITEMCOUNT, 0, 0); ok(r == 4, "got %ld\n", r); /* select page that can't be created */ ret = SendMessageA(hdlg, PSM_SETCURSEL, 1, 0); ok(ret == TRUE, "got %d\n", ret); r = SendMessageA(tab, TCM_GETITEMCOUNT, 0, 0); ok(r == 3, "got %ld\n", r); /* test PSP_PREMATURE flag with incorrect property sheet page */ ret = SendMessageA(hdlg, PSM_INSERTPAGE, 0, (LPARAM)hpsp[4]); ok(ret == FALSE, "got %d\n", ret); r = SendMessageA(tab, TCM_GETITEMCOUNT, 0, 0); ok(r == 3, "got %ld\n", r); pDestroyPropertySheetPage(hpsp[4]); DestroyWindow(hdlg); } struct custom_proppage { union { PROPSHEETPAGEA pageA; PROPSHEETPAGEW pageW; } u; unsigned int addref_called; unsigned int release_called; }; static UINT CALLBACK proppage_callback_a(HWND hwnd, UINT msg, PROPSHEETPAGEA *psp) { struct custom_proppage *cpage = (struct custom_proppage *)psp->lParam; PROPSHEETPAGEA *psp_orig = &cpage->u.pageA; ok(hwnd == NULL, "Expected NULL hwnd, got %p\n", hwnd); ok(psp->lParam && psp->lParam != (LPARAM)psp, "Expected newly allocated page description, got %Ix, %p\n", psp->lParam, psp); ok(psp_orig->pszTitle == psp->pszTitle, "Expected same page title pointer\n"); ok(!lstrcmpA(psp_orig->pszTitle, psp->pszTitle), "Expected same page title string\n"); switch (msg) { case PSPCB_ADDREF: ok(psp->dwSize > PROPSHEETPAGEA_V1_SIZE, "Expected ADDREF for V2+ only, got size %lu\n", psp->dwSize); cpage->addref_called++; break; case PSPCB_RELEASE: ok(psp->dwSize >= PROPSHEETPAGEA_V1_SIZE, "Unexpected RELEASE, got size %lu\n", psp->dwSize); cpage->release_called++; break; default: ok(0, "Unexpected message %u\n", msg); } return 1; } static UINT CALLBACK proppage_callback_w(HWND hwnd, UINT msg, PROPSHEETPAGEW *psp) { struct custom_proppage *cpage = (struct custom_proppage *)psp->lParam; PROPSHEETPAGEW *psp_orig = &cpage->u.pageW; ok(hwnd == NULL, "Expected NULL hwnd, got %p\n", hwnd); ok(psp->lParam && psp->lParam != (LPARAM)psp, "Expected newly allocated page description, got %Ix, %p\n", psp->lParam, psp); ok(psp_orig->pszTitle == psp->pszTitle, "Expected same page title pointer\n"); ok(!lstrcmpW(psp_orig->pszTitle, psp->pszTitle), "Expected same page title string\n"); switch (msg) { case PSPCB_ADDREF: ok(psp->dwSize > PROPSHEETPAGEW_V1_SIZE, "Expected ADDREF for V2+ only, got size %lu\n", psp->dwSize); cpage->addref_called++; break; case PSPCB_RELEASE: ok(psp->dwSize >= PROPSHEETPAGEW_V1_SIZE, "Unexpected RELEASE, got size %lu\n", psp->dwSize); cpage->release_called++; break; default: ok(0, "Unexpected message %u\n", msg); } return 1; } static void test_CreatePropertySheetPage(void) { struct custom_proppage page; HPROPSHEETPAGE hpsp; BOOL ret; memset(&page.u.pageA, 0, sizeof(page.u.pageA)); page.u.pageA.dwFlags = PSP_USECALLBACK; page.u.pageA.pfnDlgProc = page_dlg_proc_messages; page.u.pageA.pfnCallback = proppage_callback_a; page.u.pageA.lParam = (LPARAM)&page; page.u.pageA.pszTitle = "Title"; /* Only minimal size validation is performed */ for (page.u.pageA.dwSize = PROPSHEETPAGEA_V1_SIZE - 1; page.u.pageA.dwSize <= PROPSHEETPAGEA_V4_SIZE + 1; page.u.pageA.dwSize++) { page.addref_called = 0; hpsp = pCreatePropertySheetPageA(&page.u.pageA); if (page.u.pageA.dwSize < PROPSHEETPAGEA_V1_SIZE) ok(hpsp == NULL, "Expected failure, size %lu\n", page.u.pageA.dwSize); else { ok(hpsp != NULL, "Failed to create a page, size %lu\n", page.u.pageA.dwSize); ok(page.addref_called == (page.u.pageA.dwSize > PROPSHEETPAGEA_V1_SIZE) ? 1 : 0, "Expected ADDREF callback message\n"); } if (hpsp) { page.release_called = 0; ret = pDestroyPropertySheetPage(hpsp); ok(ret, "Failed to destroy a page\n"); ok(page.release_called == 1, "Expected RELEASE callback message\n"); } } memset(&page.u.pageW, 0, sizeof(page.u.pageW)); page.u.pageW.dwFlags = PSP_USECALLBACK; page.u.pageW.pfnDlgProc = page_dlg_proc_messages; page.u.pageW.pfnCallback = proppage_callback_w; page.u.pageW.lParam = (LPARAM)&page; page.u.pageW.pszTitle = L"Title"; for (page.u.pageW.dwSize = PROPSHEETPAGEW_V1_SIZE - 1; page.u.pageW.dwSize <= PROPSHEETPAGEW_V4_SIZE + 1; page.u.pageW.dwSize++) { page.addref_called = 0; hpsp = pCreatePropertySheetPageW(&page.u.pageW); if (page.u.pageW.dwSize < PROPSHEETPAGEW_V1_SIZE) ok(hpsp == NULL, "Expected failure, size %lu\n", page.u.pageW.dwSize); else { ok(hpsp != NULL, "Failed to create a page, size %lu\n", page.u.pageW.dwSize); ok(page.addref_called == (page.u.pageW.dwSize > PROPSHEETPAGEW_V1_SIZE) ? 1 : 0, "Expected ADDREF callback message\n"); } if (hpsp) { page.release_called = 0; ret = pDestroyPropertySheetPage(hpsp); ok(ret, "Failed to destroy a page\n"); ok(page.release_called == 1, "Expected RELEASE callback message\n"); } } } static void test_bad_control_class(void) { PROPSHEETPAGEA psp; PROPSHEETHEADERA psh; HPROPSHEETPAGE hpsp; INT_PTR ret; memset(&psp, 0, sizeof(psp)); psp.dwSize = sizeof(psp); psp.hInstance = GetModuleHandleA(NULL); U(psp).pszTemplate = (LPCSTR)MAKEINTRESOURCE(IDD_PROP_PAGE_BAD_CONTROL); psp.pfnDlgProc = page_dlg_proc; hpsp = pCreatePropertySheetPageA(&psp); ok(hpsp != 0, "CreatePropertySheetPage failed\n"); memset(&psh, 0, sizeof(psh)); psh.dwSize = PROPSHEETHEADERA_V1_SIZE; psh.nPages = 1; psh.hwndParent = GetDesktopWindow(); U3(psh).phpage = &hpsp; ret = pPropertySheetA(&psh); ok(ret == 0, "got %Id\n", ret); /* Need to recreate hpsp otherwise the test fails under Windows */ hpsp = pCreatePropertySheetPageA(&psp); ok(hpsp != 0, "CreatePropertySheetPage failed\n"); U3(psh).phpage = &hpsp; psh.dwFlags = PSH_MODELESS; ret = pPropertySheetA(&psh); ok(ret != 0, "got %Id\n", ret); ok(IsWindow((HWND)ret), "bad window handle %#Ix\n", ret); DestroyWindow((HWND)ret); } static INT_PTR CALLBACK test_WM_CTLCOLORSTATIC_proc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { switch(msg) { case WM_INITDIALOG: sheethwnd = hwnd; return FALSE; default: return FALSE; } } static void test_page_dialog_texture(void) { HPROPSHEETPAGE hpsp[1]; PROPSHEETHEADERA psh; PROPSHEETPAGEA psp; ULONG_PTR dlgproc; HWND hdlg, hwnd; HBRUSH hbrush; BOOL ret; HDC hdc; memset(&psp, 0, sizeof(psp)); psp.dwSize = sizeof(psp); psp.hInstance = GetModuleHandleA(NULL); U(psp).pszTemplate = "prop_page1"; psp.pfnDlgProc = test_WM_CTLCOLORSTATIC_proc; hpsp[0] = pCreatePropertySheetPageA(&psp); memset(&psh, 0, sizeof(psh)); psh.dwSize = PROPSHEETHEADERA_V1_SIZE; psh.dwFlags = PSH_MODELESS; psh.pszCaption = "caption"; psh.nPages = 1; psh.hwndParent = GetDesktopWindow(); U3(psh).phpage = hpsp; hdlg = (HWND)pPropertySheetA(&psh); ok(hdlg != INVALID_HANDLE_VALUE, "Got invalid handle value %p.\n", hdlg); flush_events(); /* Test that page dialog procedure is unchanged */ dlgproc = GetWindowLongPtrA(sheethwnd, DWLP_DLGPROC); ok(dlgproc == (ULONG_PTR)test_WM_CTLCOLORSTATIC_proc, "Unexpected dlgproc %#Ix.\n", dlgproc); /* 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"); hwnd = CreateWindowA(WC_EDITA, "child", WS_POPUP | WS_VISIBLE, 1, 2, 50, 50, 0, 0, 0, NULL); ok(hwnd != NULL, "CreateWindowA failed, error %ld.\n", GetLastError()); hdc = GetDC(hwnd); hbrush = (HBRUSH)SendMessageW(sheethwnd, WM_CTLCOLORSTATIC, (WPARAM)hdc, (LPARAM)hwnd); if (is_v6 && is_theme_active) { /* 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"); } ReleaseDC(hwnd, hdc); DestroyWindow(hwnd); DestroyWindow(hdlg); } static void init_comctl32_functions(void) { HMODULE hComCtl32 = LoadLibraryA("comctl32.dll"); #define X(f) p##f = (void*)GetProcAddress(hComCtl32, #f); X(CreatePropertySheetPageA); X(CreatePropertySheetPageW); X(DestroyPropertySheetPage); X(PropertySheetA); #undef X } static void init_uxtheme_functions(void) { HMODULE uxtheme = LoadLibraryA("uxtheme.dll"); #define X(f) p##f = (void *)GetProcAddress(uxtheme, #f); X(IsThemeActive) X(IsThemeDialogTextureEnabled) #undef X } START_TEST(propsheet) { ULONG_PTR ctx_cookie; HANDLE ctx; detect_locale(); if (rtl) { /* use locale-specific RTL resources when on an RTL locale */ /* without this, propsheets on RTL locales use English LTR resources */ trace("RTL locale detected\n"); SetProcessDefaultLayout(LAYOUT_RTL); } init_comctl32_functions(); init_uxtheme_functions(); is_theme_active = pIsThemeActive(); test_bad_control_class(); test_title(); test_nopage(); test_disableowner(); test_wiznavigation(); test_buttons(); test_custom_default_button(); test_messages(); test_PSM_ADDPAGE(); test_PSM_INSERTPAGE(); test_CreatePropertySheetPage(); test_page_dialog_texture(); if (!load_v6_module(&ctx_cookie, &ctx)) return; init_comctl32_functions(); is_v6 = TRUE; test_page_dialog_texture(); unload_v6_module(ctx_cookie, ctx); }