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