1 /* Unit test suite for property sheet control.
2  *
3  * Copyright 2006 Huw Davies
4  * Copyright 2009 Jan de Mooij
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #include <windows.h>
22 #include <commctrl.h>
23 #include "msg.h"
24 
25 #include "resources.h"
26 
27 #include "wine/test.h"
28 
29 #ifdef __REACTOS__
30 #include <reactos/undocuser.h>
31 #endif
32 
33 static HWND parenthwnd;
34 static HWND sheethwnd;
35 
36 static BOOL rtl;
37 static LONG active_page = -1;
38 
39 #define IDC_APPLY_BUTTON 12321
40 
41 static HPROPSHEETPAGE (WINAPI *pCreatePropertySheetPageA)(const PROPSHEETPAGEA *desc);
42 static HPROPSHEETPAGE (WINAPI *pCreatePropertySheetPageW)(const PROPSHEETPAGEW *desc);
43 static BOOL (WINAPI *pDestroyPropertySheetPage)(HPROPSHEETPAGE proppage);
44 static INT_PTR (WINAPI *pPropertySheetA)(const PROPSHEETHEADERA *header);
45 
46 static void detect_locale(void)
47 {
48     DWORD reading_layout;
49     rtl = GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_IREADINGLAYOUT | LOCALE_RETURN_NUMBER,
50             (void *)&reading_layout, sizeof(reading_layout)) && reading_layout == 1;
51 }
52 
53 /* try to make sure pending X events have been processed before continuing */
54 static void flush_events(void)
55 {
56     MSG msg;
57     int diff = 200;
58     int min_timeout = 100;
59     DWORD time = GetTickCount() + diff;
60 
61     while (diff > 0)
62     {
63         if (MsgWaitForMultipleObjects( 0, NULL, FALSE, min_timeout, QS_ALLINPUT ) == WAIT_TIMEOUT) break;
64         while (PeekMessageA( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessageA( &msg );
65         diff = time - GetTickCount();
66     }
67 }
68 
69 
70 static int CALLBACK sheet_callback(HWND hwnd, UINT msg, LPARAM lparam)
71 {
72     switch(msg)
73     {
74     case PSCB_PRECREATE:
75       {
76         HMODULE module = GetModuleHandleA("comctl32.dll");
77         DWORD size, buffer_size;
78         HRSRC hrsrc;
79 
80         hrsrc = FindResourceA(module, MAKEINTRESOURCEA(1006 /* IDD_PROPSHEET */),
81                 (LPSTR)RT_DIALOG);
82         size = SizeofResource(module, hrsrc);
83         ok(size != 0, "Failed to get size of propsheet dialog resource\n");
84         buffer_size = HeapSize(GetProcessHeap(), 0, (void *)lparam);
85         ok(buffer_size == 2 * size, "Unexpected template buffer size %u, resource size %u\n",
86                 buffer_size, size);
87         break;
88       }
89     case PSCB_INITIALIZED:
90       {
91         char caption[256];
92         GetWindowTextA(hwnd, caption, sizeof(caption));
93         ok(!strcmp(caption,"test caption"), "caption: %s\n", caption);
94         sheethwnd = hwnd;
95         return 0;
96       }
97     }
98     return 0;
99 }
100 
101 static INT_PTR CALLBACK page_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam,
102                                       LPARAM lparam)
103 {
104     switch(msg)
105     {
106     case WM_INITDIALOG:
107       {
108         HWND sheet = GetParent(hwnd);
109         char caption[256];
110         GetWindowTextA(sheet, caption, sizeof(caption));
111         ok(!strcmp(caption,"test caption"), "caption: %s\n", caption);
112         return TRUE;
113       }
114 
115     case WM_NOTIFY:
116       {
117         NMHDR *nmhdr = (NMHDR *)lparam;
118         switch(nmhdr->code)
119         {
120         case PSN_APPLY:
121             return TRUE;
122         default:
123             return FALSE;
124         }
125       }
126     case WM_NCDESTROY:
127         ok(!SendMessageA(sheethwnd, PSM_INDEXTOHWND, 400, 0),"Should always be 0\n");
128         return TRUE;
129 
130     default:
131         return FALSE;
132     }
133 }
134 
135 static void test_title(void)
136 {
137     HPROPSHEETPAGE hpsp[1];
138     PROPSHEETPAGEA psp;
139     PROPSHEETHEADERA psh;
140     HWND hdlg;
141     DWORD style;
142 
143     memset(&psp, 0, sizeof(psp));
144     psp.dwSize = sizeof(psp);
145     psp.dwFlags = 0;
146     psp.hInstance = GetModuleHandleA(NULL);
147     U(psp).pszTemplate = "prop_page1";
148     U2(psp).pszIcon = NULL;
149     psp.pfnDlgProc = page_dlg_proc;
150     psp.lParam = 0;
151 
152     hpsp[0] = pCreatePropertySheetPageA(&psp);
153 
154     memset(&psh, 0, sizeof(psh));
155     psh.dwSize = PROPSHEETHEADERA_V1_SIZE;
156     psh.dwFlags = PSH_MODELESS | PSH_USECALLBACK;
157     psh.pszCaption = "test caption";
158     psh.nPages = 1;
159     psh.hwndParent = GetDesktopWindow();
160     U3(psh).phpage = hpsp;
161     psh.pfnCallback = sheet_callback;
162 
163     hdlg = (HWND)pPropertySheetA(&psh);
164     ok(hdlg != INVALID_HANDLE_VALUE, "got invalid handle value %p\n", hdlg);
165 
166     style = GetWindowLongA(hdlg, GWL_STYLE);
167     ok(style == (WS_POPUP|WS_VISIBLE|WS_CLIPSIBLINGS|WS_CAPTION|WS_SYSMENU|
168                  DS_CONTEXTHELP|DS_MODALFRAME|DS_SETFONT|DS_3DLOOK),
169        "got unexpected style: %x\n", style);
170 
171     DestroyWindow(hdlg);
172 }
173 
174 static void test_nopage(void)
175 {
176     HPROPSHEETPAGE hpsp[1];
177     PROPSHEETPAGEA psp;
178     PROPSHEETHEADERA psh;
179     HWND hdlg, hpage;
180     MSG msg;
181 
182     memset(&psp, 0, sizeof(psp));
183     psp.dwSize = sizeof(psp);
184     psp.dwFlags = 0;
185     psp.hInstance = GetModuleHandleA(NULL);
186     U(psp).pszTemplate = "prop_page1";
187     U2(psp).pszIcon = NULL;
188     psp.pfnDlgProc = page_dlg_proc;
189     psp.lParam = 0;
190 
191     hpsp[0] = pCreatePropertySheetPageA(&psp);
192 
193     memset(&psh, 0, sizeof(psh));
194     psh.dwSize = PROPSHEETHEADERA_V1_SIZE;
195     psh.dwFlags = PSH_MODELESS | PSH_USECALLBACK;
196     psh.pszCaption = "test caption";
197     psh.nPages = 1;
198     psh.hwndParent = GetDesktopWindow();
199     U3(psh).phpage = hpsp;
200     psh.pfnCallback = sheet_callback;
201 
202     hdlg = (HWND)pPropertySheetA(&psh);
203     ok(hdlg != INVALID_HANDLE_VALUE, "got invalid handle value %p\n", hdlg);
204 
205     ShowWindow(hdlg,SW_NORMAL);
206     SendMessageA(hdlg, PSM_REMOVEPAGE, 0, 0);
207     hpage = /* PropSheet_GetCurrentPageHwnd(hdlg); */
208         (HWND)SendMessageA(hdlg, PSM_GETCURRENTPAGEHWND, 0, 0);
209     active_page = /* PropSheet_HwndToIndex(hdlg, hpage)); */
210         (int)SendMessageA(hdlg, PSM_HWNDTOINDEX, (WPARAM)hpage, 0);
211     ok(hpage == NULL, "expected no current page, got %p, index=%d\n", hpage, active_page);
212     flush_events();
213     RedrawWindow(hdlg,NULL,NULL,RDW_UPDATENOW|RDW_ERASENOW);
214 
215     /* Check that the property sheet was fully redrawn */
216     ok(!PeekMessageA(&msg, 0, WM_PAINT, WM_PAINT, PM_NOREMOVE),
217        "expected no pending WM_PAINT messages\n");
218     DestroyWindow(hdlg);
219 }
220 
221 static int CALLBACK disableowner_callback(HWND hwnd, UINT msg, LPARAM lparam)
222 {
223     switch(msg)
224     {
225     case PSCB_INITIALIZED:
226       {
227         ok(IsWindowEnabled(parenthwnd) == 0, "parent window should be disabled\n");
228         PostQuitMessage(0);
229         return FALSE;
230       }
231     }
232     return FALSE;
233 }
234 
235 static void register_parent_wnd_class(void)
236 {
237     WNDCLASSA cls;
238 
239     cls.style = 0;
240     cls.lpfnWndProc = DefWindowProcA;
241     cls.cbClsExtra = 0;
242     cls.cbWndExtra = 0;
243     cls.hInstance = GetModuleHandleA(NULL);
244     cls.hIcon = 0;
245     cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
246     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
247     cls.lpszMenuName = NULL;
248     cls.lpszClassName = "parent class";
249     RegisterClassA(&cls);
250 }
251 
252 static void test_disableowner(void)
253 {
254     HPROPSHEETPAGE hpsp[1];
255     PROPSHEETPAGEA psp;
256     PROPSHEETHEADERA psh;
257     INT_PTR p;
258 
259     register_parent_wnd_class();
260     parenthwnd = CreateWindowA("parent class", "", WS_CAPTION | WS_SYSMENU | WS_VISIBLE, 100, 100, 100, 100, GetDesktopWindow(), NULL, GetModuleHandleA(NULL), 0);
261 
262     memset(&psp, 0, sizeof(psp));
263     psp.dwSize = sizeof(psp);
264     psp.dwFlags = 0;
265     psp.hInstance = GetModuleHandleA(NULL);
266     U(psp).pszTemplate = "prop_page1";
267     U2(psp).pszIcon = NULL;
268     psp.pfnDlgProc = NULL;
269     psp.lParam = 0;
270 
271     hpsp[0] = pCreatePropertySheetPageA(&psp);
272 
273     memset(&psh, 0, sizeof(psh));
274     psh.dwSize = PROPSHEETHEADERA_V1_SIZE;
275     psh.dwFlags = PSH_USECALLBACK;
276     psh.pszCaption = "test caption";
277     psh.nPages = 1;
278     psh.hwndParent = parenthwnd;
279     U3(psh).phpage = hpsp;
280     psh.pfnCallback = disableowner_callback;
281 
282     p = pPropertySheetA(&psh);
283     ok(p == 0, "Expected 0, got %ld\n", p);
284     ok(IsWindowEnabled(parenthwnd) != 0, "parent window should be enabled\n");
285     DestroyWindow(parenthwnd);
286 }
287 
288 static INT_PTR CALLBACK nav_page_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
289 {
290     switch(msg){
291     case WM_NOTIFY:
292         {
293             LPNMHDR hdr = (LPNMHDR)lparam;
294             switch(hdr->code){
295             case PSN_SETACTIVE:
296                 active_page = /* PropSheet_HwndToIndex(hdr->hwndFrom, hwnd); */
297                     (int)SendMessageA(hdr->hwndFrom, PSM_HWNDTOINDEX, (WPARAM)hwnd, 0);
298                 return TRUE;
299             case PSN_KILLACTIVE:
300                 /* prevent navigation away from the fourth page */
301                 if(active_page == 3){
302                     SetWindowLongPtrA(hwnd, DWLP_MSGRESULT, TRUE);
303                     return TRUE;
304                 }
305             }
306             break;
307         }
308     }
309     return FALSE;
310 }
311 
312 static WNDPROC old_nav_dialog_proc;
313 
314 static LRESULT CALLBACK new_nav_dialog_proc( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp )
315 {
316     switch (msg)
317     {
318     case DM_SETDEFID:
319         ok( IsWindowEnabled( GetDlgItem(hwnd, wp) ), "button is not enabled\n" );
320         break;
321     }
322     return CallWindowProcW( old_nav_dialog_proc, hwnd, msg, wp, lp );
323 }
324 
325 static LRESULT CALLBACK hook_proc( int code, WPARAM wp, LPARAM lp )
326 {
327     static BOOL done;
328     if (code == HCBT_CREATEWND)
329     {
330         CBT_CREATEWNDW *c = (CBT_CREATEWNDW *)lp;
331 
332         /* The first dialog created will be the parent dialog */
333         if (!done && c->lpcs->lpszClass == (LPWSTR)WC_DIALOG)
334         {
335             old_nav_dialog_proc = (WNDPROC)SetWindowLongPtrW( (HWND)wp, GWLP_WNDPROC, (LONG_PTR)new_nav_dialog_proc );
336             done = TRUE;
337         }
338     }
339 
340     return CallNextHookEx( NULL, code, wp, lp );
341 }
342 
343 static void test_wiznavigation(void)
344 {
345     HPROPSHEETPAGE hpsp[4];
346     PROPSHEETPAGEA psp[4];
347     PROPSHEETHEADERA psh;
348     HWND hdlg, control;
349     LONG_PTR controlID;
350     DWORD style;
351     LRESULT defidres;
352     BOOL hwndtoindex_supported = TRUE;
353     const INT nextID = 12324;
354     const INT backID = 12323;
355     HHOOK hook;
356 
357     /* set up a hook proc in order to subclass the main dialog early on */
358     hook = SetWindowsHookExW( WH_CBT, hook_proc, NULL, GetCurrentThreadId() );
359 
360     /* create the property sheet pages */
361     memset(psp, 0, sizeof(PROPSHEETPAGEA) * 4);
362 
363     psp[0].dwSize = sizeof(PROPSHEETPAGEA);
364     psp[0].hInstance = GetModuleHandleA(NULL);
365     U(psp[0]).pszTemplate = (LPCSTR)MAKEINTRESOURCE(IDD_PROP_PAGE_INTRO);
366     psp[0].pfnDlgProc = nav_page_proc;
367     hpsp[0] = pCreatePropertySheetPageA(&psp[0]);
368 
369     psp[1].dwSize = sizeof(PROPSHEETPAGEA);
370     psp[1].hInstance = GetModuleHandleA(NULL);
371     U(psp[1]).pszTemplate = (LPCSTR)MAKEINTRESOURCE(IDD_PROP_PAGE_EDIT);
372     psp[1].pfnDlgProc = nav_page_proc;
373     hpsp[1] = pCreatePropertySheetPageA(&psp[1]);
374 
375     psp[2].dwSize = sizeof(PROPSHEETPAGEA);
376     psp[2].hInstance = GetModuleHandleA(NULL);
377     U(psp[2]).pszTemplate = (LPCSTR)MAKEINTRESOURCE(IDD_PROP_PAGE_RADIO);
378     psp[2].pfnDlgProc = nav_page_proc;
379     hpsp[2] = pCreatePropertySheetPageA(&psp[2]);
380 
381     psp[3].dwSize = sizeof(PROPSHEETPAGEA);
382     psp[3].hInstance = GetModuleHandleA(NULL);
383     U(psp[3]).pszTemplate = (LPCSTR)MAKEINTRESOURCE(IDD_PROP_PAGE_EXIT);
384     psp[3].pfnDlgProc = nav_page_proc;
385     hpsp[3] = pCreatePropertySheetPageA(&psp[3]);
386 
387     /* set up the property sheet dialog */
388     memset(&psh, 0, sizeof(psh));
389     psh.dwSize = PROPSHEETHEADERA_V1_SIZE;
390     psh.dwFlags = PSH_MODELESS | PSH_WIZARD;
391     psh.pszCaption = "A Wizard";
392     psh.nPages = 4;
393     psh.hwndParent = GetDesktopWindow();
394     U3(psh).phpage = hpsp;
395     hdlg = (HWND)pPropertySheetA(&psh);
396     ok(hdlg != INVALID_HANDLE_VALUE, "got invalid handle %p\n", hdlg);
397 
398     ok(active_page == 0, "Active page should be 0. Is: %d\n", active_page);
399 
400     style = GetWindowLongA(hdlg, GWL_STYLE) & ~(DS_CONTEXTHELP|WS_SYSMENU);
401     ok(style == (WS_POPUP|WS_VISIBLE|WS_CLIPSIBLINGS|WS_CAPTION|
402                  DS_MODALFRAME|DS_SETFONT|DS_3DLOOK),
403        "got unexpected style: %x\n", style);
404 
405     control = GetFocus();
406     controlID = GetWindowLongPtrA(control, GWLP_ID);
407     ok(controlID == nextID, "Focus should have been set to the Next button. Expected: %d, Found: %ld\n", nextID, controlID);
408 
409     /* simulate pressing the Next button */
410     SendMessageA(hdlg, PSM_PRESSBUTTON, PSBTN_NEXT, 0);
411     if (!active_page) hwndtoindex_supported = FALSE;
412     if (hwndtoindex_supported)
413         ok(active_page == 1, "Active page should be 1 after pressing Next. Is: %d\n", active_page);
414 
415     control = GetFocus();
416     controlID = GetWindowLongPtrA(control, GWLP_ID);
417     ok(controlID == IDC_PS_EDIT1, "Focus should be set to the first item on the second page. Expected: %d, Found: %ld\n", IDC_PS_EDIT1, controlID);
418 
419     defidres = SendMessageA(hdlg, DM_GETDEFID, 0, 0);
420     ok(defidres == MAKELRESULT(nextID, DC_HASDEFID), "Expected default button ID to be %d, is %d\n", nextID, LOWORD(defidres));
421 
422     /* set the focus to the second edit box on this page */
423     SetFocus(GetNextDlgTabItem(hdlg, control, FALSE));
424 
425     /* press next again */
426     SendMessageA(hdlg, PSM_PRESSBUTTON, PSBTN_NEXT, 0);
427     if (hwndtoindex_supported)
428         ok(active_page == 2, "Active page should be 2 after pressing Next. Is: %d\n", active_page);
429 
430     control = GetFocus();
431     controlID = GetWindowLongPtrA(control, GWLP_ID);
432     ok(controlID == IDC_PS_RADIO1, "Focus should have been set to item on third page. Expected: %d, Found %ld\n", IDC_PS_RADIO1, controlID);
433 
434     /* back button */
435     SendMessageA(hdlg, PSM_PRESSBUTTON, PSBTN_BACK, 0);
436     if (hwndtoindex_supported)
437         ok(active_page == 1, "Active page should be 1 after pressing Back. Is: %d\n", active_page);
438 
439     control = GetFocus();
440     controlID = GetWindowLongPtrA(control, GWLP_ID);
441     ok(controlID == IDC_PS_EDIT1, "Focus should have been set to the first item on second page. Expected: %d, Found %ld\n", IDC_PS_EDIT1, controlID);
442 
443     defidres = SendMessageA(hdlg, DM_GETDEFID, 0, 0);
444     ok(defidres == MAKELRESULT(backID, DC_HASDEFID), "Expected default button ID to be %d, is %d\n", backID, LOWORD(defidres));
445 
446     /* press next twice */
447     SendMessageA(hdlg, PSM_PRESSBUTTON, PSBTN_NEXT, 0);
448     if (hwndtoindex_supported)
449         ok(active_page == 2, "Active page should be 2 after pressing Next. Is: %d\n", active_page);
450     SendMessageA(hdlg, PSM_PRESSBUTTON, PSBTN_NEXT, 0);
451     if (hwndtoindex_supported)
452         ok(active_page == 3, "Active page should be 3 after pressing Next. Is: %d\n", active_page);
453     else
454         active_page = 3;
455 
456     control = GetFocus();
457     controlID = GetWindowLongPtrA(control, GWLP_ID);
458     ok(controlID == nextID, "Focus should have been set to the Next button. Expected: %d, Found: %ld\n", nextID, controlID);
459 
460     /* try to navigate away, but shouldn't be able to */
461     SendMessageA(hdlg, PSM_PRESSBUTTON, PSBTN_BACK, 0);
462     ok(active_page == 3, "Active page should still be 3 after pressing Back. Is: %d\n", active_page);
463 
464     defidres = SendMessageA(hdlg, DM_GETDEFID, 0, 0);
465     ok(defidres == MAKELRESULT(nextID, DC_HASDEFID), "Expected default button ID to be %d, is %d\n", nextID, LOWORD(defidres));
466 
467     DestroyWindow(hdlg);
468     UnhookWindowsHookEx( hook );
469 }
470 
471 static void test_buttons(void)
472 {
473     HPROPSHEETPAGE hpsp[1];
474     PROPSHEETPAGEA psp;
475     PROPSHEETHEADERA psh;
476     HWND hdlg;
477     HWND button;
478     RECT rc;
479     int prevRight, top;
480 
481     memset(&psp, 0, sizeof(psp));
482     psp.dwSize = sizeof(psp);
483     psp.dwFlags = 0;
484     psp.hInstance = GetModuleHandleA(NULL);
485     U(psp).pszTemplate = "prop_page1";
486     U2(psp).pszIcon = NULL;
487     psp.pfnDlgProc = page_dlg_proc;
488     psp.lParam = 0;
489 
490     hpsp[0] = pCreatePropertySheetPageA(&psp);
491 
492     memset(&psh, 0, sizeof(psh));
493     psh.dwSize = PROPSHEETHEADERA_V1_SIZE;
494     psh.dwFlags = PSH_MODELESS | PSH_USECALLBACK;
495     psh.pszCaption = "test caption";
496     psh.nPages = 1;
497     psh.hwndParent = GetDesktopWindow();
498     U3(psh).phpage = hpsp;
499     psh.pfnCallback = sheet_callback;
500 
501     hdlg = (HWND)pPropertySheetA(&psh);
502     ok(hdlg != INVALID_HANDLE_VALUE, "got null handle\n");
503 
504     /* OK button */
505     button = GetDlgItem(hdlg, IDOK);
506     GetWindowRect(button, &rc);
507     prevRight = rc.right;
508     top = rc.top;
509 
510     /* Cancel button */
511     button = GetDlgItem(hdlg, IDCANCEL);
512     GetWindowRect(button, &rc);
513     ok(rc.top == top, "Cancel button should have same top as OK button\n");
514     if (rtl)
515         ok(rc.left < prevRight, "Cancel button should be to the left of OK button\n");
516     else
517         ok(rc.left > prevRight, "Cancel button should be to the right of OK button\n");
518     prevRight = rc.right;
519 
520     button = GetDlgItem(hdlg, IDC_APPLY_BUTTON);
521     GetWindowRect(button, &rc);
522     ok(rc.top == top, "Apply button should have same top as OK button\n");
523     if (rtl)
524         ok(rc.left < prevRight, "Apply button should be to the left of Cancel button\n");
525     else
526         ok(rc.left > prevRight, "Apply button should be to the right of Cancel button\n");
527     prevRight = rc.right;
528 
529     button = GetDlgItem(hdlg, IDHELP);
530     GetWindowRect(button, &rc);
531     ok(rc.top == top, "Help button should have same top as OK button\n");
532     if (rtl)
533         ok(rc.left < prevRight, "Help button should be to the left of Apply button\n");
534     else
535         ok(rc.left > prevRight, "Help button should be to the right of Apply button\n");
536 
537     DestroyWindow(hdlg);
538 }
539 
540 static BOOL add_button_has_been_pressed;
541 
542 static INT_PTR CALLBACK
543 page_with_custom_default_button_dlg_proc(HWND hdlg, UINT msg, WPARAM wparam, LPARAM lparam)
544 {
545     switch (msg)
546     {
547     case WM_COMMAND:
548         switch(LOWORD(wparam))
549         {
550         case IDC_PS_PUSHBUTTON1:
551             switch(HIWORD(wparam))
552             {
553             case BN_CLICKED:
554                 add_button_has_been_pressed = TRUE;
555                 return TRUE;
556             }
557             break;
558         }
559         break;
560     }
561     return FALSE;
562 }
563 
564 static void test_custom_default_button(void)
565 {
566     HWND hdlg, page;
567     PROPSHEETPAGEA psp[1];
568     PROPSHEETHEADERA psh;
569     MSG msg;
570     LRESULT result;
571 
572     psp[0].dwSize = sizeof (PROPSHEETPAGEA);
573     psp[0].dwFlags = PSP_USETITLE;
574     psp[0].hInstance = GetModuleHandleA(NULL);
575     U(psp[0]).pszTemplate = (LPCSTR)MAKEINTRESOURCE(IDD_PROP_PAGE_WITH_CUSTOM_DEFAULT_BUTTON);
576     U2(psp[0]).pszIcon = NULL;
577     psp[0].pfnDlgProc = page_with_custom_default_button_dlg_proc;
578     psp[0].pszTitle = "Page1";
579     psp[0].lParam = 0;
580 
581     psh.dwSize = PROPSHEETHEADERA_V1_SIZE;
582     psh.dwFlags = PSH_PROPSHEETPAGE | PSH_MODELESS;
583     psh.hwndParent = GetDesktopWindow();
584     psh.hInstance = GetModuleHandleA(NULL);
585     U(psh).pszIcon = NULL;
586     psh.pszCaption =  "PropertySheet1";
587     psh.nPages = 1;
588     U3(psh).ppsp = psp;
589     U2(psh).nStartPage = 0;
590 
591     /* The goal of the test is to make sure that the Add button is pressed
592      * when the ENTER key is pressed and a different control, a combobox,
593      * has the keyboard focus. */
594     add_button_has_been_pressed = FALSE;
595 
596     /* Create the modeless property sheet. */
597     hdlg = (HWND)pPropertySheetA(&psh);
598     ok(hdlg != INVALID_HANDLE_VALUE, "Cannot create the property sheet\n");
599 
600     /* Set the Add button as the default button. */
601     SendMessageA(hdlg, DM_SETDEFID, (WPARAM)IDC_PS_PUSHBUTTON1, 0);
602 
603     /* Make sure the default button is the Add button. */
604     result = SendMessageA(hdlg, DM_GETDEFID, 0, 0);
605     ok(DC_HASDEFID == HIWORD(result), "The property sheet does not have a default button\n");
606     ok(IDC_PS_PUSHBUTTON1 == LOWORD(result), "The default button is not the Add button\n");
607 
608     /* At this point, the combobox should have keyboard focus, so we press ENTER.
609      * Pull the lever, Kronk! */
610     page = (HWND)SendMessageW(hdlg, PSM_GETCURRENTPAGEHWND, 0, 0);
611     PostMessageW(GetDlgItem(page, IDC_PS_COMBO1), WM_KEYDOWN, VK_RETURN, 0);
612 
613     /* Process all the messages in the queue for this thread. */
614     while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE))
615     {
616         /* (!PropSheet_IsDialogMessage(hdlg, &msg)) */
617         if (!((BOOL)SendMessageA(hdlg, PSM_ISDIALOGMESSAGE, 0, (LPARAM)&msg)))
618         {
619             TranslateMessage(&msg);
620             DispatchMessageA(&msg);
621         }
622     }
623 
624     ok(add_button_has_been_pressed, "The Add button has not been pressed!\n");
625 
626     DestroyWindow(hdlg);
627 }
628 
629 #define RECEIVER_SHEET_CALLBACK 0
630 #define RECEIVER_SHEET_WINPROC  1
631 #define RECEIVER_PAGE           2
632 
633 #define NUM_MSG_SEQUENCES   1
634 #define PROPSHEET_SEQ_INDEX 0
635 
636 static struct msg_sequence *sequences[NUM_MSG_SEQUENCES];
637 static WNDPROC oldWndProc;
638 
639 static const struct message property_sheet_seq[] = {
640     { PSCB_PRECREATE, sent|id, 0, 0, RECEIVER_SHEET_CALLBACK },
641     { PSCB_INITIALIZED, sent|id, 0, 0, RECEIVER_SHEET_CALLBACK },
642     { WM_WINDOWPOSCHANGING, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
643     /*{ WM_NCCALCSIZE, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },*/
644     { WM_WINDOWPOSCHANGED, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
645     { WM_MOVE, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
646     { WM_SIZE, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
647     /*{ WM_GETTEXT, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
648     { WM_GETICON, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
649     { WM_GETICON, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
650     { WM_GETICON, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
651     { WM_NCCALCSIZE, sent|id|optional, 0, 0, RECEIVER_SHEET_WINPROC },
652     { DM_REPOSITION, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },*/
653     { WM_WINDOWPOSCHANGING, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
654     { WM_WINDOWPOSCHANGING, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
655     { WM_ACTIVATEAPP, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
656     /*{ WM_NCACTIVATE, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
657     { WM_GETTEXT, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
658     { WM_GETICON, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
659     { WM_GETICON, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
660     { WM_GETICON, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
661     { WM_GETTEXT, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },*/
662     { WM_ACTIVATE, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
663     /*{ WM_IME_SETCONTEXT, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
664     { WM_IME_NOTIFY, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },*/
665     { WM_SETFOCUS, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
666     { WM_KILLFOCUS, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
667     /*{ WM_IME_SETCONTEXT, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },*/
668     { WM_PARENTNOTIFY, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
669     { WM_INITDIALOG, sent|id, 0, 0, RECEIVER_PAGE },
670     { WM_WINDOWPOSCHANGING, sent|id, 0, 0, RECEIVER_PAGE },
671     /*{ WM_NCCALCSIZE, sent|id, 0, 0, RECEIVER_PAGE },*/
672     { WM_CHILDACTIVATE, sent|id, 0, 0, RECEIVER_PAGE },
673     { WM_WINDOWPOSCHANGED, sent|id, 0, 0, RECEIVER_PAGE },
674     { WM_MOVE, sent|id, 0, 0, RECEIVER_PAGE },
675     { WM_SIZE, sent|id, 0, 0, RECEIVER_PAGE },
676     { WM_NOTIFY, sent|id, 0, 0, RECEIVER_PAGE },
677     { WM_STYLECHANGING, sent|id|optional, 0, 0, RECEIVER_SHEET_WINPROC },
678     { WM_STYLECHANGED, sent|id|optional, 0, 0, RECEIVER_SHEET_WINPROC },
679     /*{ WM_GETTEXT, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
680     { WM_GETICON, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
681     { WM_GETICON, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
682     { WM_GETICON, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },*/
683     { WM_SETTEXT, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
684     { WM_SHOWWINDOW, sent|id, 0, 0, RECEIVER_PAGE },
685     /*{ 0x00000401, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
686     { 0x00000400, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },*/
687     { WM_CHANGEUISTATE, sent|id|optional, 0, 0, RECEIVER_SHEET_WINPROC },
688     { WM_UPDATEUISTATE, sent|id|optional, 0, 0, RECEIVER_SHEET_WINPROC },
689     { WM_UPDATEUISTATE, sent|id|optional, 0, 0, RECEIVER_PAGE },
690     { WM_SHOWWINDOW, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
691     { WM_WINDOWPOSCHANGING, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
692     /*{ WM_NCPAINT, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
693     { WM_GETICON, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
694     { WM_GETICON, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
695     { WM_GETICON, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
696     { WM_ERASEBKGND, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },*/
697     { WM_CTLCOLORDLG, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
698     { WM_WINDOWPOSCHANGED, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
699     /*{ WM_GETICON, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
700     { WM_GETICON, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
701     { WM_GETICON, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
702     { WM_PAINT, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
703     { WM_PAINT, sent|id, 0, 0, RECEIVER_PAGE },
704     { WM_NCPAINT, sent|id, 0, 0, RECEIVER_PAGE },
705     { WM_ERASEBKGND, sent|id, 0, 0, RECEIVER_PAGE },*/
706     { WM_CTLCOLORDLG, sent|id, 0, 0, RECEIVER_PAGE },
707     { WM_CTLCOLORSTATIC, sent|id, 0, 0, RECEIVER_PAGE },
708     { WM_CTLCOLORSTATIC, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
709     { WM_CTLCOLORBTN, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
710     { WM_CTLCOLORBTN, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
711     { WM_CTLCOLORBTN, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
712     /*{ WM_GETICON, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
713     { WM_GETICON, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
714     { WM_GETICON, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },*/
715     { WM_COMMAND, sent|id|optional, 0, 0, RECEIVER_SHEET_WINPROC },
716     { WM_NOTIFY, sent|id|optional, 0, 0, RECEIVER_PAGE },
717     { WM_NOTIFY, sent|id|optional, 0, 0, RECEIVER_PAGE },
718     { WM_WINDOWPOSCHANGING, sent|id|optional, 0, 0, RECEIVER_SHEET_WINPROC },
719     { WM_WINDOWPOSCHANGED, sent|id|optional, 0, 0, RECEIVER_SHEET_WINPROC },
720     /*{ WM_NCACTIVATE, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
721     { WM_GETICON, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
722     { WM_GETICON, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
723     { WM_GETICON, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },*/
724     /*{ WM_ACTIVATE, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
725     { WM_ACTIVATE, sent|id, 0, 0, RECEIVER_PAGE },
726     { WM_ACTIVATEAPP, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
727     { WM_ACTIVATEAPP, sent|id, 0, 0, RECEIVER_PAGE },
728     { WM_DESTROY, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },
729     { WM_DESTROY, sent|id, 0, 0, RECEIVER_PAGE },*/
730     /*{ WM_NCDESTROY, sent|id, 0, 0, RECEIVER_PAGE },
731     { WM_NCDESTROY, sent|id, 0, 0, RECEIVER_SHEET_WINPROC },*/
732     { 0 }
733 };
734 
735 static void save_message(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, INT receiver)
736 {
737     struct message msg = { 0 };
738 
739     if (message < WM_USER &&
740         message != WM_GETICON &&
741         message != WM_GETTEXT &&
742         message != WM_IME_SETCONTEXT &&
743         message != WM_IME_NOTIFY &&
744         message != WM_PAINT &&
745         message != WM_ERASEBKGND &&
746         message != WM_SETCURSOR &&
747         (message < WM_NCCREATE || message > WM_NCMBUTTONDBLCLK) &&
748         (message < WM_MOUSEFIRST || message > WM_MOUSEHWHEEL) &&
749         message != 0x90)
750     {
751         msg.message = message;
752         msg.flags = sent|wparam|lparam|id;
753         msg.wParam = wParam;
754         msg.lParam = lParam;
755         msg.id = receiver;
756         add_message(sequences, PROPSHEET_SEQ_INDEX, &msg);
757     }
758 }
759 
760 static LRESULT CALLBACK sheet_callback_messages_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
761 {
762     save_message(hwnd, msg, wParam, lParam, RECEIVER_SHEET_WINPROC);
763 
764     return CallWindowProcA(oldWndProc, hwnd, msg, wParam, lParam);
765 }
766 
767 static int CALLBACK sheet_callback_messages(HWND hwnd, UINT msg, LPARAM lParam)
768 {
769     save_message(hwnd, msg, 0, lParam, RECEIVER_SHEET_CALLBACK);
770 
771     switch (msg)
772     {
773     case PSCB_INITIALIZED:
774         oldWndProc = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_WNDPROC);
775         SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (LONG_PTR)&sheet_callback_messages_proc);
776         return TRUE;
777     }
778 
779     return TRUE;
780 }
781 
782 static INT_PTR CALLBACK page_dlg_proc_messages(HWND hwnd, UINT msg, WPARAM wParam,
783                                                LPARAM lParam)
784 {
785     save_message(hwnd, msg, wParam, lParam, RECEIVER_PAGE);
786 
787     return FALSE;
788 }
789 
790 static void test_messages(void)
791 {
792     HPROPSHEETPAGE hpsp[1];
793     PROPSHEETPAGEA psp;
794     PROPSHEETHEADERA psh;
795     HWND hdlg;
796 
797     init_msg_sequences(sequences, NUM_MSG_SEQUENCES);
798 
799     memset(&psp, 0, sizeof(psp));
800     psp.dwSize = sizeof(psp);
801     psp.dwFlags = 0;
802     psp.hInstance = GetModuleHandleA(NULL);
803     U(psp).pszTemplate = (LPCSTR)MAKEINTRESOURCE(IDD_PROP_PAGE_MESSAGE_TEST);
804     U2(psp).pszIcon = NULL;
805     psp.pfnDlgProc = page_dlg_proc_messages;
806     psp.lParam = 0;
807 
808     hpsp[0] = pCreatePropertySheetPageA(&psp);
809 
810     memset(&psh, 0, sizeof(psh));
811     psh.dwSize = PROPSHEETHEADERA_V1_SIZE;
812     psh.dwFlags = PSH_NOAPPLYNOW | PSH_WIZARD | PSH_USECALLBACK
813                   | PSH_MODELESS | PSH_USEICONID;
814     psh.pszCaption = "test caption";
815     psh.nPages = 1;
816     psh.hwndParent = GetDesktopWindow();
817     U3(psh).phpage = hpsp;
818     psh.pfnCallback = sheet_callback_messages;
819 
820     hdlg = (HWND)pPropertySheetA(&psh);
821     ok(hdlg != INVALID_HANDLE_VALUE, "got invalid handle %p\n", hdlg);
822 
823     ShowWindow(hdlg,SW_NORMAL);
824 
825     ok_sequence(sequences, PROPSHEET_SEQ_INDEX, property_sheet_seq, "property sheet with custom window proc", TRUE);
826 
827     DestroyWindow(hdlg);
828 }
829 
830 static void test_PSM_ADDPAGE(void)
831 {
832     HPROPSHEETPAGE hpsp[5];
833     PROPSHEETPAGEA psp;
834     PROPSHEETHEADERA psh;
835     HWND hdlg, tab;
836     BOOL ret;
837     DWORD r;
838 
839     memset(&psp, 0, sizeof(psp));
840     psp.dwSize = sizeof(psp);
841     psp.dwFlags = 0;
842     psp.hInstance = GetModuleHandleA(NULL);
843     U(psp).pszTemplate = (LPCSTR)MAKEINTRESOURCE(IDD_PROP_PAGE_MESSAGE_TEST);
844     U2(psp).pszIcon = NULL;
845     psp.pfnDlgProc = page_dlg_proc_messages;
846     psp.lParam = 0;
847 
848     /* multiple pages with the same data */
849     hpsp[0] = pCreatePropertySheetPageA(&psp);
850     hpsp[1] = pCreatePropertySheetPageA(&psp);
851     hpsp[2] = pCreatePropertySheetPageA(&psp);
852 
853     U(psp).pszTemplate = (LPCSTR)MAKEINTRESOURCE(IDD_PROP_PAGE_ERROR);
854     hpsp[3] = pCreatePropertySheetPageA(&psp);
855 
856     psp.dwFlags = PSP_PREMATURE;
857     hpsp[4] = pCreatePropertySheetPageA(&psp);
858 
859     memset(&psh, 0, sizeof(psh));
860     psh.dwSize = PROPSHEETHEADERA_V1_SIZE;
861     psh.dwFlags = PSH_MODELESS;
862     psh.pszCaption = "test caption";
863     psh.nPages = 1;
864     psh.hwndParent = GetDesktopWindow();
865     U3(psh).phpage = hpsp;
866 
867     hdlg = (HWND)pPropertySheetA(&psh);
868     ok(hdlg != INVALID_HANDLE_VALUE, "got invalid handle %p\n", hdlg);
869 
870     /* add pages one by one */
871     ret = SendMessageA(hdlg, PSM_ADDPAGE, 0, (LPARAM)hpsp[1]);
872     ok(ret == TRUE, "got %d\n", ret);
873 
874     /* try with null and invalid value */
875     ret = SendMessageA(hdlg, PSM_ADDPAGE, 0, 0);
876     ok(ret == FALSE, "got %d\n", ret);
877 
878 if (0)
879 {
880     /* crashes on native */
881     ret = SendMessageA(hdlg, PSM_ADDPAGE, 0, (LPARAM)INVALID_HANDLE_VALUE);
882 }
883     /* check item count */
884     tab = (HWND)SendMessageA(hdlg, PSM_GETTABCONTROL, 0, 0);
885 
886     r = SendMessageA(tab, TCM_GETITEMCOUNT, 0, 0);
887     ok(r == 2, "got %d\n", r);
888 
889     ret = SendMessageA(hdlg, PSM_ADDPAGE, 0, (LPARAM)hpsp[2]);
890     ok(ret == TRUE, "got %d\n", ret);
891 
892     r = SendMessageA(tab, TCM_GETITEMCOUNT, 0, 0);
893     ok(r == 3, "got %d\n", r);
894 
895     /* add property sheet page that can't be created */
896     ret = SendMessageA(hdlg, PSM_ADDPAGE, 0, (LPARAM)hpsp[3]);
897     ok(ret == TRUE, "got %d\n", ret);
898 
899     r = SendMessageA(tab, TCM_GETITEMCOUNT, 0, 0);
900     ok(r == 4, "got %d\n", r);
901 
902     /* select page that can't be created */
903     ret = SendMessageA(hdlg, PSM_SETCURSEL, 3, 1);
904     ok(ret == TRUE, "got %d\n", ret);
905 
906     r = SendMessageA(tab, TCM_GETITEMCOUNT, 0, 0);
907     ok(r == 3, "got %d\n", r);
908 
909     /* test PSP_PREMATURE flag with incorrect property sheet page */
910     ret = SendMessageA(hdlg, PSM_ADDPAGE, 0, (LPARAM)hpsp[4]);
911     ok(ret == FALSE, "got %d\n", ret);
912 
913     r = SendMessageA(tab, TCM_GETITEMCOUNT, 0, 0);
914     ok(r == 3, "got %d\n", r);
915 
916     pDestroyPropertySheetPage(hpsp[4]);
917     DestroyWindow(hdlg);
918 }
919 
920 static void test_PSM_INSERTPAGE(void)
921 {
922     HPROPSHEETPAGE hpsp[5];
923     PROPSHEETPAGEA psp;
924     PROPSHEETHEADERA psh;
925     HWND hdlg, tab;
926     BOOL ret;
927     DWORD r;
928 
929     memset(&psp, 0, sizeof(psp));
930     psp.dwSize = sizeof(psp);
931     psp.dwFlags = 0;
932     psp.hInstance = GetModuleHandleA(NULL);
933     U(psp).pszTemplate = (LPCSTR)MAKEINTRESOURCE(IDD_PROP_PAGE_MESSAGE_TEST);
934     U2(psp).pszIcon = NULL;
935     psp.pfnDlgProc = page_dlg_proc_messages;
936     psp.lParam = 0;
937 
938     /* multiple pages with the same data */
939     hpsp[0] = pCreatePropertySheetPageA(&psp);
940     hpsp[1] = pCreatePropertySheetPageA(&psp);
941     hpsp[2] = pCreatePropertySheetPageA(&psp);
942 
943     U(psp).pszTemplate = (LPCSTR)MAKEINTRESOURCE(IDD_PROP_PAGE_ERROR);
944     hpsp[3] = pCreatePropertySheetPageA(&psp);
945 
946     psp.dwFlags = PSP_PREMATURE;
947     hpsp[4] = pCreatePropertySheetPageA(&psp);
948 
949     memset(&psh, 0, sizeof(psh));
950     psh.dwSize = PROPSHEETHEADERA_V1_SIZE;
951     psh.dwFlags = PSH_MODELESS;
952     psh.pszCaption = "test caption";
953     psh.nPages = 1;
954     psh.hwndParent = GetDesktopWindow();
955     U3(psh).phpage = hpsp;
956 
957     hdlg = (HWND)pPropertySheetA(&psh);
958     ok(hdlg != INVALID_HANDLE_VALUE, "got invalid handle %p\n", hdlg);
959 
960     /* add pages one by one */
961     ret = SendMessageA(hdlg, PSM_INSERTPAGE, 5, (LPARAM)hpsp[1]);
962     ok(ret == TRUE, "got %d\n", ret);
963 
964     /* try with invalid values */
965     ret = SendMessageA(hdlg, PSM_INSERTPAGE, 0, 0);
966     ok(ret == FALSE, "got %d\n", ret);
967 
968 if (0)
969 {
970     /* crashes on native */
971     ret = SendMessageA(hdlg, PSM_INSERTPAGE, 0, (LPARAM)INVALID_HANDLE_VALUE);
972 }
973 
974     ret = SendMessageA(hdlg, PSM_INSERTPAGE, (WPARAM)INVALID_HANDLE_VALUE, (LPARAM)hpsp[2]);
975     ok(ret == FALSE, "got %d\n", ret);
976 
977     /* check item count */
978     tab = (HWND)SendMessageA(hdlg, PSM_GETTABCONTROL, 0, 0);
979 
980     r = SendMessageA(tab, TCM_GETITEMCOUNT, 0, 0);
981     ok(r == 2, "got %d\n", r);
982 
983     ret = SendMessageA(hdlg, PSM_INSERTPAGE, (WPARAM)hpsp[1], (LPARAM)hpsp[2]);
984     ok(ret == TRUE, "got %d\n", ret);
985 
986     r = SendMessageA(tab, TCM_GETITEMCOUNT, 0, 0);
987     ok(r == 3, "got %d\n", r);
988 
989     /* add property sheet page that can't be created */
990     ret = SendMessageA(hdlg, PSM_INSERTPAGE, 1, (LPARAM)hpsp[3]);
991     ok(ret == TRUE, "got %d\n", ret);
992 
993     r = SendMessageA(tab, TCM_GETITEMCOUNT, 0, 0);
994     ok(r == 4, "got %d\n", r);
995 
996     /* select page that can't be created */
997     ret = SendMessageA(hdlg, PSM_SETCURSEL, 1, 0);
998     ok(ret == TRUE, "got %d\n", ret);
999 
1000     r = SendMessageA(tab, TCM_GETITEMCOUNT, 0, 0);
1001     ok(r == 3, "got %d\n", r);
1002 
1003     /* test PSP_PREMATURE flag with incorrect property sheet page */
1004     ret = SendMessageA(hdlg, PSM_INSERTPAGE, 0, (LPARAM)hpsp[4]);
1005     ok(ret == FALSE, "got %d\n", ret);
1006 
1007     r = SendMessageA(tab, TCM_GETITEMCOUNT, 0, 0);
1008     ok(r == 3, "got %d\n", r);
1009 
1010     pDestroyPropertySheetPage(hpsp[4]);
1011     DestroyWindow(hdlg);
1012 }
1013 
1014 struct custom_proppage
1015 {
1016     union
1017     {
1018         PROPSHEETPAGEA pageA;
1019         PROPSHEETPAGEW pageW;
1020     } u;
1021     unsigned int addref_called;
1022     unsigned int release_called;
1023 };
1024 
1025 static UINT CALLBACK proppage_callback_a(HWND hwnd, UINT msg, PROPSHEETPAGEA *psp)
1026 {
1027     struct custom_proppage *cpage = (struct custom_proppage *)psp->lParam;
1028     PROPSHEETPAGEA *psp_orig = &cpage->u.pageA;
1029 
1030     ok(hwnd == NULL, "Expected NULL hwnd, got %p\n", hwnd);
1031 
1032     ok(psp->lParam && psp->lParam != (LPARAM)psp, "Expected newly allocated page description, got %lx, %p\n",
1033             psp->lParam, psp);
1034     ok(psp_orig->pszTitle == psp->pszTitle, "Expected same page title pointer\n");
1035     ok(!lstrcmpA(psp_orig->pszTitle, psp->pszTitle), "Expected same page title string\n");
1036 
1037     switch (msg)
1038     {
1039     case PSPCB_ADDREF:
1040         ok(psp->dwSize > PROPSHEETPAGEA_V1_SIZE, "Expected ADDREF for V2+ only, got size %u\n", psp->dwSize);
1041         cpage->addref_called++;
1042         break;
1043     case PSPCB_RELEASE:
1044         ok(psp->dwSize >= PROPSHEETPAGEA_V1_SIZE, "Unexpected RELEASE, got size %u\n", psp->dwSize);
1045         cpage->release_called++;
1046         break;
1047     default:
1048         ok(0, "Unexpected message %u\n", msg);
1049     }
1050 
1051     return 1;
1052 }
1053 
1054 static UINT CALLBACK proppage_callback_w(HWND hwnd, UINT msg, PROPSHEETPAGEW *psp)
1055 {
1056     struct custom_proppage *cpage = (struct custom_proppage *)psp->lParam;
1057     PROPSHEETPAGEW *psp_orig = &cpage->u.pageW;
1058 
1059     ok(hwnd == NULL, "Expected NULL hwnd, got %p\n", hwnd);
1060     ok(psp->lParam && psp->lParam != (LPARAM)psp, "Expected newly allocated page description, got %lx, %p\n",
1061             psp->lParam, psp);
1062     ok(psp_orig->pszTitle == psp->pszTitle, "Expected same page title pointer\n");
1063     ok(!lstrcmpW(psp_orig->pszTitle, psp->pszTitle), "Expected same page title string\n");
1064 
1065     switch (msg)
1066     {
1067     case PSPCB_ADDREF:
1068         ok(psp->dwSize > PROPSHEETPAGEW_V1_SIZE, "Expected ADDREF for V2+ only, got size %u\n", psp->dwSize);
1069         cpage->addref_called++;
1070         break;
1071     case PSPCB_RELEASE:
1072         ok(psp->dwSize >= PROPSHEETPAGEW_V1_SIZE, "Unexpected RELEASE, got size %u\n", psp->dwSize);
1073         cpage->release_called++;
1074         break;
1075     default:
1076         ok(0, "Unexpected message %u\n", msg);
1077     }
1078 
1079     return 1;
1080 }
1081 
1082 static void test_CreatePropertySheetPage(void)
1083 {
1084     static const WCHAR titleW[] = {'T','i','t','l','e',0};
1085     struct custom_proppage page;
1086     HPROPSHEETPAGE hpsp;
1087     BOOL ret;
1088 
1089     memset(&page.u.pageA, 0, sizeof(page.u.pageA));
1090     page.u.pageA.dwFlags = PSP_USECALLBACK;
1091     page.u.pageA.pfnDlgProc = page_dlg_proc_messages;
1092     page.u.pageA.pfnCallback = proppage_callback_a;
1093     page.u.pageA.lParam = (LPARAM)&page;
1094     page.u.pageA.pszTitle = "Title";
1095 
1096     /* Only minimal size validation is performed */
1097     for (page.u.pageA.dwSize = PROPSHEETPAGEA_V1_SIZE - 1; page.u.pageA.dwSize <= PROPSHEETPAGEA_V4_SIZE + 1; page.u.pageA.dwSize++)
1098     {
1099         page.addref_called = 0;
1100         hpsp = pCreatePropertySheetPageA(&page.u.pageA);
1101 
1102         if (page.u.pageA.dwSize < PROPSHEETPAGEA_V1_SIZE)
1103             ok(hpsp == NULL, "Expected failure, size %u\n", page.u.pageA.dwSize);
1104         else
1105         {
1106             ok(hpsp != NULL, "Failed to create a page, size %u\n", page.u.pageA.dwSize);
1107             ok(page.addref_called == (page.u.pageA.dwSize > PROPSHEETPAGEA_V1_SIZE) ? 1 : 0, "Expected ADDREF callback message\n");
1108         }
1109 
1110         if (hpsp)
1111         {
1112             page.release_called = 0;
1113             ret = pDestroyPropertySheetPage(hpsp);
1114             ok(ret, "Failed to destroy a page\n");
1115             ok(page.release_called == 1, "Expected RELEASE callback message\n");
1116         }
1117     }
1118 
1119     memset(&page.u.pageW, 0, sizeof(page.u.pageW));
1120     page.u.pageW.dwFlags = PSP_USECALLBACK;
1121     page.u.pageW.pfnDlgProc = page_dlg_proc_messages;
1122     page.u.pageW.pfnCallback = proppage_callback_w;
1123     page.u.pageW.lParam = (LPARAM)&page;
1124     page.u.pageW.pszTitle = titleW;
1125 
1126     for (page.u.pageW.dwSize = PROPSHEETPAGEW_V1_SIZE - 1; page.u.pageW.dwSize <= PROPSHEETPAGEW_V4_SIZE + 1; page.u.pageW.dwSize++)
1127     {
1128         page.addref_called = 0;
1129         hpsp = pCreatePropertySheetPageW(&page.u.pageW);
1130 
1131         if (page.u.pageW.dwSize < PROPSHEETPAGEW_V1_SIZE)
1132             ok(hpsp == NULL, "Expected failure, size %u\n", page.u.pageW.dwSize);
1133         else
1134         {
1135             ok(hpsp != NULL, "Failed to create a page, size %u\n", page.u.pageW.dwSize);
1136             ok(page.addref_called == (page.u.pageW.dwSize > PROPSHEETPAGEW_V1_SIZE) ? 1 : 0, "Expected ADDREF callback message\n");
1137         }
1138 
1139         if (hpsp)
1140         {
1141             page.release_called = 0;
1142             ret = pDestroyPropertySheetPage(hpsp);
1143             ok(ret, "Failed to destroy a page\n");
1144             ok(page.release_called == 1, "Expected RELEASE callback message\n");
1145         }
1146     }
1147 }
1148 
1149 static void test_bad_control_class(void)
1150 {
1151     PROPSHEETPAGEA psp;
1152     PROPSHEETHEADERA psh;
1153     HPROPSHEETPAGE hpsp;
1154     INT_PTR ret;
1155 
1156     memset(&psp, 0, sizeof(psp));
1157     psp.dwSize = sizeof(psp);
1158     psp.hInstance = GetModuleHandleA(NULL);
1159     U(psp).pszTemplate = (LPCSTR)MAKEINTRESOURCE(IDD_PROP_PAGE_BAD_CONTROL);
1160     psp.pfnDlgProc = page_dlg_proc;
1161 
1162     hpsp = pCreatePropertySheetPageA(&psp);
1163     ok(hpsp != 0, "CreatePropertySheetPage failed\n");
1164 
1165     memset(&psh, 0, sizeof(psh));
1166     psh.dwSize = PROPSHEETHEADERA_V1_SIZE;
1167     psh.nPages = 1;
1168     psh.hwndParent = GetDesktopWindow();
1169     U3(psh).phpage = &hpsp;
1170 
1171 #ifndef __REACTOS__ /* FIXME: Inspect why this causes a hang */
1172     ret = pPropertySheetA(&psh);
1173     ok(ret == 0, "got %ld\n", ret);
1174 #endif
1175 
1176     /* Need to recreate hpsp otherwise the test fails under Windows */
1177     hpsp = pCreatePropertySheetPageA(&psp);
1178     ok(hpsp != 0, "CreatePropertySheetPage failed\n");
1179     U3(psh).phpage = &hpsp;
1180 
1181     psh.dwFlags = PSH_MODELESS;
1182     ret = pPropertySheetA(&psh);
1183     ok(ret != 0, "got %ld\n", ret);
1184 
1185     ok(IsWindow((HWND)ret), "bad window handle %#lx\n", ret);
1186     DestroyWindow((HWND)ret);
1187 }
1188 
1189 static void init_functions(void)
1190 {
1191     HMODULE hComCtl32 = LoadLibraryA("comctl32.dll");
1192 
1193 #define X(f) p##f = (void*)GetProcAddress(hComCtl32, #f);
1194     X(CreatePropertySheetPageA);
1195     X(CreatePropertySheetPageW);
1196     X(DestroyPropertySheetPage);
1197     X(PropertySheetA);
1198 #undef X
1199 }
1200 
1201 START_TEST(propsheet)
1202 {
1203     detect_locale();
1204     if (rtl)
1205     {
1206         /* use locale-specific RTL resources when on an RTL locale */
1207         /* without this, propsheets on RTL locales use English LTR resources */
1208         trace("RTL locale detected\n");
1209         SetProcessDefaultLayout(LAYOUT_RTL);
1210     }
1211 
1212     init_functions();
1213 
1214     test_bad_control_class();
1215     test_title();
1216     test_nopage();
1217     test_disableowner();
1218     test_wiznavigation();
1219     test_buttons();
1220     test_custom_default_button();
1221     test_messages();
1222     test_PSM_ADDPAGE();
1223     test_PSM_INSERTPAGE();
1224     test_CreatePropertySheetPage();
1225 }
1226