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