1 /*
2  * Unit test suite for comdlg32 API functions: file dialogs
3  *
4  * Copyright 2007 Google (Lei Zhang)
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 
22 #include <windows.h>
23 #include <wine/test.h>
24 
25 #include "shlguid.h"
26 #define COBJMACROS
27 #include "shobjidl.h"
28 #include "commdlg.h"
29 #include "cderr.h"
30 #include "dlgs.h"
31 
32 #ifdef __REACTOS__
33 #include <reactos/undocuser.h>
34 #endif
35 
36 /* ##### */
37 
38 static BOOL resizesupported = TRUE;
39 
40 static void toolbarcheck( HWND hDlg)
41 {
42     /* test toolbar properties */
43     /* bug #10532 */
44     int maxtextrows;
45     HWND ctrl;
46     DWORD ret;
47     char classname[20];
48 
49     for( ctrl = GetWindow( hDlg, GW_CHILD);
50             ctrl ; ctrl = GetWindow( ctrl, GW_HWNDNEXT)) {
51         GetClassNameA( ctrl, classname, 10);
52         classname[7] = '\0';
53         if( !strcmp( classname, "Toolbar")) break;
54     }
55     ok( ctrl != NULL, "could not get the toolbar control\n");
56     ret = SendMessageA( ctrl, TB_ADDSTRINGA, 0, (LPARAM)"winetestwinetest\0\0");
57     ok( ret == 0, "addstring returned %d (expected 0)\n", ret);
58     maxtextrows = SendMessageA( ctrl, TB_GETTEXTROWS, 0, 0);
59     ok( maxtextrows == 0 || broken(maxtextrows == 1),  /* Win2k and below */
60         "Get(Max)TextRows returned %d (expected 0)\n", maxtextrows);
61 }
62 
63 
64 static UINT_PTR CALLBACK OFNHookProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
65 {
66     LPNMHDR nmh;
67 
68     if( msg == WM_NOTIFY)
69     {
70         nmh = (LPNMHDR) lParam;
71         if( nmh->code == CDN_INITDONE)
72         {
73             PostMessageA( GetParent(hDlg), WM_COMMAND, IDCANCEL, FALSE);
74         } else if (nmh->code == CDN_FOLDERCHANGE )
75         {
76             char buf[1024];
77             int ret;
78 
79             memset(buf, 0x66, sizeof(buf));
80             ret = SendMessageA( GetParent(hDlg), CDM_GETFOLDERIDLIST, 5, (LPARAM)buf);
81             ok(ret > 0, "CMD_GETFOLDERIDLIST not implemented\n");
82             if (ret > 5)
83                 ok(buf[0] == 0x66 && buf[1] == 0x66, "CMD_GETFOLDERIDLIST: The buffer was touched on failure\n");
84             toolbarcheck( GetParent(hDlg));
85         }
86     }
87 
88     return 0;
89 }
90 
91 /* bug 6829 */
92 static void test_DialogCancel(void)
93 {
94     OPENFILENAMEA ofn;
95     BOOL result;
96     char szFileName[MAX_PATH] = "";
97     char szInitialDir[MAX_PATH];
98 
99     GetWindowsDirectoryA(szInitialDir, MAX_PATH);
100 
101     ZeroMemory(&ofn, sizeof(ofn));
102 
103     ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400A;
104     ofn.hwndOwner = NULL;
105     ofn.lpstrFilter = "Text Files (*.txt)\0*.txt\0All Files (*.*)\0*.*\0";
106     ofn.lpstrFile = szFileName;
107     ofn.nMaxFile = MAX_PATH;
108     ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_ENABLEHOOK;
109     ofn.lpstrDefExt = "txt";
110     ofn.lpfnHook = OFNHookProc;
111     ofn.lpstrInitialDir = szInitialDir;
112 
113     PrintDlgA(NULL);
114     ok(CDERR_INITIALIZATION == CommDlgExtendedError(),
115        "expected CDERR_INITIALIZATION, got %d\n", CommDlgExtendedError());
116 
117     result = GetOpenFileNameA(&ofn);
118     ok(FALSE == result, "expected FALSE, got %d\n", result);
119     ok(0 == CommDlgExtendedError(), "expected 0, got %d\n",
120        CommDlgExtendedError());
121 
122     PrintDlgA(NULL);
123     ok(CDERR_INITIALIZATION == CommDlgExtendedError(),
124        "expected CDERR_INITIALIZATION, got %d\n", CommDlgExtendedError());
125 
126     result = GetSaveFileNameA(&ofn);
127     ok(FALSE == result, "expected FALSE, got %d\n", result);
128     ok(0 == CommDlgExtendedError(), "expected 0, got %d\n",
129        CommDlgExtendedError());
130 
131     PrintDlgA(NULL);
132     ok(CDERR_INITIALIZATION == CommDlgExtendedError(),
133        "expected CDERR_INITIALIZATION, got %d\n", CommDlgExtendedError());
134 
135     /* Before passing the ofn to Unicode functions, remove the ANSI strings */
136     ofn.lpstrFilter = NULL;
137     ofn.lpstrInitialDir = NULL;
138     ofn.lpstrDefExt = NULL;
139 
140     PrintDlgA(NULL);
141     ok(CDERR_INITIALIZATION == CommDlgExtendedError(),
142        "expected CDERR_INITIALIZATION, got %d\n", CommDlgExtendedError());
143 
144     SetLastError(0xdeadbeef);
145     result = GetOpenFileNameW((LPOPENFILENAMEW) &ofn);
146     if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
147         win_skip("GetOpenFileNameW is not implemented\n");
148     else
149     {
150         ok(FALSE == result, "expected FALSE, got %d\n", result);
151         ok(0 == CommDlgExtendedError(), "expected 0, got %d\n", CommDlgExtendedError());
152     }
153 
154     SetLastError(0xdeadbeef);
155     result = GetSaveFileNameW((LPOPENFILENAMEW) &ofn);
156     if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
157         win_skip("GetSaveFileNameW is not implemented\n");
158     else
159     {
160         ok(FALSE == result, "expected FALSE, got %d\n", result);
161         ok(0 == CommDlgExtendedError(), "expected 0, got %d\n", CommDlgExtendedError());
162     }
163 }
164 
165 static UINT_PTR CALLBACK create_view_window2_hook(HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam)
166 {
167     if (msg == WM_NOTIFY)
168     {
169         if (((LPNMHDR)lParam)->code == CDN_FOLDERCHANGE)
170         {
171             IShellBrowser *shell_browser = (IShellBrowser *)SendMessageA(GetParent(dlg), WM_USER + 7 /* WM_GETISHELLBROWSER */, 0, 0);
172             IShellView *shell_view = NULL;
173             IShellView2 *shell_view2 = NULL;
174             SV2CVW2_PARAMS view_params;
175             FOLDERSETTINGS folder_settings;
176             HRESULT hr;
177             RECT rect = {0, 0, 0, 0};
178 
179             hr = IShellBrowser_QueryActiveShellView(shell_browser, &shell_view);
180             ok(SUCCEEDED(hr), "QueryActiveShellView returned %#x\n", hr);
181             if (FAILED(hr)) goto cleanup;
182 
183             hr = IShellView_QueryInterface(shell_view, &IID_IShellView2, (void **)&shell_view2);
184             if (hr == E_NOINTERFACE)
185             {
186                 win_skip("IShellView2 not supported\n");
187                 goto cleanup;
188             }
189             ok(SUCCEEDED(hr), "QueryInterface returned %#x\n", hr);
190             if (FAILED(hr)) goto cleanup;
191 
192             hr = IShellView2_DestroyViewWindow(shell_view2);
193             ok(SUCCEEDED(hr), "DestroyViewWindow returned %#x\n", hr);
194 
195             folder_settings.ViewMode = FVM_LIST;
196             folder_settings.fFlags = 0;
197 
198             view_params.cbSize = sizeof(view_params);
199             view_params.psvPrev = NULL;
200             view_params.pfs = &folder_settings;
201             view_params.psbOwner = shell_browser;
202             view_params.prcView = &rect;
203             view_params.pvid = NULL;
204             view_params.hwndView = NULL;
205 
206             hr = IShellView2_CreateViewWindow2(shell_view2, &view_params);
207             if (hr == E_FAIL)
208             {
209                 win_skip("CreateViewWindow2 is broken on Vista/W2K8\n");
210                 goto cleanup;
211             }
212             ok(SUCCEEDED(hr), "CreateViewWindow2 returned %#x\n", hr);
213             if (FAILED(hr)) goto cleanup;
214 
215             hr = IShellView2_GetCurrentInfo(shell_view2, &folder_settings);
216             ok(SUCCEEDED(hr), "GetCurrentInfo returned %#x\n", hr);
217             ok(folder_settings.ViewMode == FVM_LIST,
218                "view mode is %d, expected FVM_LIST\n",
219                folder_settings.ViewMode);
220 
221             hr = IShellView2_DestroyViewWindow(shell_view2);
222             ok(SUCCEEDED(hr), "DestroyViewWindow returned %#x\n", hr);
223 
224             /* XP and W2K3 need this. On W2K the call to DestroyWindow() fails and has
225              * no side effects. NT4 doesn't get here. (FIXME: Vista doesn't get here yet).
226              */
227             DestroyWindow(view_params.hwndView);
228 
229             view_params.pvid = &VID_Details;
230             hr = IShellView2_CreateViewWindow2(shell_view2, &view_params);
231             ok(SUCCEEDED(hr), "CreateViewWindow2 returned %#x\n", hr);
232             if (FAILED(hr)) goto cleanup;
233 
234             hr = IShellView2_GetCurrentInfo(shell_view2, &folder_settings);
235             ok(SUCCEEDED(hr), "GetCurrentInfo returned %#x\n", hr);
236             ok(folder_settings.ViewMode == FVM_DETAILS || broken(folder_settings.ViewMode == FVM_LIST), /* nt4 */
237                "view mode is %d, expected FVM_DETAILS\n",
238                folder_settings.ViewMode);
239 
240 cleanup:
241             if (shell_view2) IShellView2_Release(shell_view2);
242             if (shell_view) IShellView_Release(shell_view);
243             PostMessageA(GetParent(dlg), WM_COMMAND, IDCANCEL, 0);
244         }
245     }
246     return 0;
247 }
248 
249 static UINT_PTR WINAPI template_hook(HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam)
250 {
251     if (msg == WM_INITDIALOG)
252     {
253         HWND p,cb;
254         INT sel;
255         p = GetParent(dlg);
256         ok(p!=NULL, "Failed to get parent of template\n");
257         cb = GetDlgItem(p,0x470);
258         ok(cb!=NULL, "Failed to get filter combobox\n");
259         sel = SendMessageA(cb, CB_GETCURSEL, 0, 0);
260         ok (sel != -1, "Failed to get selection from filter listbox\n");
261     }
262     if (msg == WM_NOTIFY)
263     {
264         if (((LPNMHDR)lParam)->code == CDN_FOLDERCHANGE)
265             PostMessageA(GetParent(dlg), WM_COMMAND, IDCANCEL, 0);
266     }
267     return 0;
268 }
269 
270 static void test_create_view_window2(void)
271 {
272     OPENFILENAMEA ofn = {0};
273     char filename[1024] = {0};
274     DWORD ret;
275 
276     ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400A;
277     ofn.lpstrFile = filename;
278     ofn.nMaxFile = 1024;
279     ofn.lpfnHook = create_view_window2_hook;
280     ofn.Flags = OFN_ENABLEHOOK | OFN_EXPLORER;
281     ret = GetOpenFileNameA(&ofn);
282     ok(!ret, "GetOpenFileNameA returned %#x\n", ret);
283     ret = CommDlgExtendedError();
284     ok(!ret, "CommDlgExtendedError returned %#x\n", ret);
285 }
286 
287 static void test_create_view_template(void)
288 {
289     OPENFILENAMEA ofn = {0};
290     char filename[1024] = {0};
291     DWORD ret;
292 
293     ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400A;
294     ofn.lpstrFile = filename;
295     ofn.nMaxFile = 1024;
296     ofn.lpfnHook = template_hook;
297     ofn.Flags = OFN_ENABLEHOOK | OFN_EXPLORER| OFN_ENABLETEMPLATE;
298     ofn.hInstance = GetModuleHandleA(NULL);
299     ofn.lpTemplateName = "template1";
300     ofn.lpstrFilter="text\0*.txt\0All\0*\0\0";
301     ret = GetOpenFileNameA(&ofn);
302     ok(!ret, "GetOpenFileNameA returned %#x\n", ret);
303     ret = CommDlgExtendedError();
304     ok(!ret, "CommDlgExtendedError returned %#x\n", ret);
305 }
306 
307 /* test cases for resizing of the file dialog */
308 static const struct {
309     DWORD flags;
310     int resize_folderchange;/* change in CDN_FOLDERCHANGE handler */
311     int resize_timer1;      /* change in first WM_TIMER handler */
312     int resize_check;       /* expected change (in second  WM_TIMER handler) */
313     BOOL todo;              /* mark that test todo_wine */
314     BOOL testcontrols;      /* test resizing and moving of the controls */
315 } resize_testcases[] = {
316     { 0                , 10, 10, 20,FALSE,FALSE},   /* 0 */
317     { 0                ,-10,-10,-20,FALSE,FALSE},
318     { OFN_ENABLESIZING ,  0,  0,  0,FALSE,FALSE},
319     { OFN_ENABLESIZING ,  0,-10,  0,FALSE,FALSE},
320     { OFN_ENABLESIZING ,  0, 10, 10,FALSE, TRUE},
321     { OFN_ENABLESIZING ,-10,  0, 10,FALSE,FALSE},   /* 5 */
322     { OFN_ENABLESIZING , 10,  0, 10,FALSE,FALSE},
323     { OFN_ENABLESIZING ,  0, 10, 20,FALSE,FALSE},
324     /* mark the end */
325     { 0xffffffff }
326 };
327 
328 static UINT_PTR WINAPI resize_template_hook(HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam)
329 {
330     static RECT initrc, rc;
331     static int index, count;
332     static BOOL gotSWP_bottom, gotShowWindow;
333     HWND parent = GetParent( dlg);
334     int resize;
335 #define MAXNRCTRLS 30
336     static RECT ctrlrcs[MAXNRCTRLS];
337     static int ctrlids[MAXNRCTRLS];
338     static HWND ctrls[MAXNRCTRLS];
339     static int nrctrls;
340 
341     switch( msg)
342     {
343         case WM_INITDIALOG:
344         {
345             DWORD style;
346 
347             index = ((OPENFILENAMEA*)lParam)->lCustData;
348             count = 0;
349             gotSWP_bottom = gotShowWindow = FALSE;
350             /* test style */
351             style = GetWindowLongA( parent, GWL_STYLE);
352             if( resize_testcases[index].flags & OFN_ENABLESIZING)
353                 if( !(style & WS_SIZEBOX)) {
354                     win_skip( "OFN_ENABLESIZING flag not supported.\n");
355                     resizesupported = FALSE;
356                     PostMessageA( parent, WM_COMMAND, IDCANCEL, 0);
357                 } else
358                     ok( style & WS_SIZEBOX,
359                             "testid %d: dialog should have a WS_SIZEBOX style.\n", index);
360             else
361                 ok( !(style & WS_SIZEBOX),
362                         "testid %d: dialog should not have a WS_SIZEBOX style.\n", index);
363             break;
364         }
365         case WM_NOTIFY:
366         {
367             if(( (LPNMHDR)lParam)->code == CDN_FOLDERCHANGE){
368                 GetWindowRect( parent, &initrc);
369                 if( (resize  = resize_testcases[index].resize_folderchange)){
370                     MoveWindow( parent, initrc.left,initrc.top, initrc.right - initrc.left + resize,
371                             initrc.bottom - initrc.top + resize, TRUE);
372                 }
373                 SetTimer( dlg, 0, 100, 0);
374             }
375             break;
376         }
377         case WM_TIMER:
378         {
379             if( count == 0){
380                 /* store the control rectangles */
381                 if( resize_testcases[index].testcontrols) {
382                     HWND ctrl;
383                     int i;
384                     for( i = 0, ctrl = GetWindow( parent, GW_CHILD);
385                             i < MAXNRCTRLS && ctrl;
386                             i++, ctrl = GetWindow( ctrl, GW_HWNDNEXT)) {
387                         ctrlids[i] = GetDlgCtrlID( ctrl);
388                         GetWindowRect( ctrl, &ctrlrcs[i]);
389                         MapWindowPoints( NULL, parent, (LPPOINT) &ctrlrcs[i], 2);
390                         ctrls[i] = ctrl;
391                     }
392                     nrctrls = i;
393                 }
394                 if( (resize  = resize_testcases[index].resize_timer1)){
395                     GetWindowRect( parent, &rc);
396                     MoveWindow( parent, rc.left,rc.top, rc.right - rc.left + resize,
397                             rc.bottom - rc.top + resize, TRUE);
398                 }
399             } else if( count == 1){
400                 resize  = resize_testcases[index].resize_check;
401                 GetWindowRect( parent, &rc);
402                 todo_wine_if( resize_testcases[index].todo){
403                         ok( resize == rc.right - rc.left - initrc.right + initrc.left,
404                             "testid %d size-x change %d expected %d\n", index,
405                             rc.right - rc.left - initrc.right + initrc.left, resize);
406                         ok( resize == rc.bottom - rc.top - initrc.bottom + initrc.top,
407                             "testid %d size-y change %d expected %d\n", index,
408                             rc.bottom - rc.top - initrc.bottom + initrc.top, resize);
409                     }
410                 if( resize_testcases[index].testcontrols) {
411                     int i;
412                     RECT rc;
413                     for( i = 0; i < nrctrls; i++) {
414                         GetWindowRect( ctrls[i], &rc);
415                         MapWindowPoints( NULL, parent, (LPPOINT) &rc, 2);
416                         switch( ctrlids[i]){
417 
418 /* test if RECT R1, moved and sized result in R2 */
419 #define TESTRECTS( R1, R2, Mx, My, Sx, Sy) \
420          ((R1).left + (Mx) ==(R2).left \
421         &&(R1).top + (My) ==(R2).top \
422         &&(R1).right + (Mx) + (Sx) == (R2).right \
423         &&(R1).bottom + (My) + (Sy) ==(R2).bottom)
424 
425                             /* sized horizontal and moved vertical */
426                             case cmb1:
427                             case edt1:
428                                 ok( TESTRECTS( ctrlrcs[i], rc, 0, 10, 10, 0),
429                                     "control id %03x should have sized horizontally and moved vertically, before %s after %s\n",
430                                     ctrlids[i], wine_dbgstr_rect( &ctrlrcs[i] ),
431                                     wine_dbgstr_rect( &rc ));
432                                 break;
433                             /* sized horizontal and vertical */
434                             case lst2:
435                                 ok( TESTRECTS( ctrlrcs[i], rc, 0, 0, 10, 10),
436                                     "control id %03x should have sized horizontally and vertically, before %s after %s\n",
437                                     ctrlids[i], wine_dbgstr_rect( &ctrlrcs[i] ),
438                                     wine_dbgstr_rect( &rc ));
439                                 break;
440                             /* moved horizontal and vertical */
441                             case IDCANCEL:
442                             case pshHelp:
443                                 ok( TESTRECTS( ctrlrcs[i], rc, 10, 10, 0, 0),
444                                     "control id %03x should have moved horizontally and vertically, before %s after %s\n",
445                                     ctrlids[i], wine_dbgstr_rect( &ctrlrcs[i] ),
446                                     wine_dbgstr_rect( &rc ));
447                                 break;
448                             /* moved vertically */
449                             case chx1:
450                             case stc2:
451                             case stc3:
452                                 ok( TESTRECTS( ctrlrcs[i], rc, 0, 10, 0, 0),
453                                     "control id %03x should have moved vertically, before %s after %s\n",
454                                     ctrlids[i], wine_dbgstr_rect( &ctrlrcs[i] ),
455                                     wine_dbgstr_rect( &rc ));
456                                 break;
457                             /* resized horizontal */
458                             case cmb2: /* aka IDC_LOOKIN */
459                                 ok( TESTRECTS( ctrlrcs[i], rc, 0, 0, 10, 0)||
460                                         TESTRECTS( ctrlrcs[i], rc, 0, 0, 0, 0), /* Vista and higher */
461                                     "control id %03x should have resized horizontally, before %s after %s\n",
462                                     ctrlids[i], wine_dbgstr_rect( &ctrlrcs[i] ),
463                                     wine_dbgstr_rect( &rc ));
464                                 break;
465                             /* non moving non sizing controls */
466                             case stc4:
467                                 ok( TESTRECTS( rc, ctrlrcs[i], 0, 0, 0, 0),
468                                     "control id %03x was moved/resized, before %s after %s\n",
469                                     ctrlids[i], wine_dbgstr_rect( &ctrlrcs[i] ),
470                                     wine_dbgstr_rect( &rc ));
471                                 break;
472                             /* todo_wine: non moving non sizing controls */
473                             case lst1:
474 todo_wine
475                                 ok( TESTRECTS( rc, ctrlrcs[i], 0, 0, 0, 0),
476                                     "control id %03x was moved/resized, before %s after %s\n",
477                                     ctrlids[i], wine_dbgstr_rect( &ctrlrcs[i] ),
478                                     wine_dbgstr_rect( &rc ));
479                                 break;
480                             /* don't test: id is not unique */
481                             case IDOK:
482                             case stc1:
483                             case 0:
484                             case  -1:
485                                 break;
486                             default:
487                                 trace("untested control id %03x before %s after %s\n",
488                                     ctrlids[i], wine_dbgstr_rect( &ctrlrcs[i] ),
489                                     wine_dbgstr_rect( &rc ));
490 #undef TESTRECTS
491 #undef MAXNRCTRLS
492                         }
493                     }
494                 }
495                 KillTimer( dlg, 0);
496                 PostMessageA( parent, WM_COMMAND, IDCANCEL, 0);
497             }
498             count++;
499         }
500         break;
501         case WM_WINDOWPOSCHANGING:
502         {
503             WINDOWPOS *pwp = (WINDOWPOS *)lParam;
504             if(  !index && pwp->hwndInsertAfter == HWND_BOTTOM){
505                 gotSWP_bottom = TRUE;
506                 ok(!gotShowWindow, "The WM_WINDOWPOSCHANGING message came after a WM_SHOWWINDOW message\n");
507             }
508         }
509         break;
510         case WM_SHOWWINDOW:
511         {
512             if(  !index){
513                 gotShowWindow = TRUE;
514                 ok(gotSWP_bottom, "No WM_WINDOWPOSCHANGING message came before a WM_SHOWWINDOW message\n");
515             }
516         }
517         break;
518     }
519     return 0;
520 }
521 
522 static void test_resize(void)
523 {
524     OPENFILENAMEA ofn = { OPENFILENAME_SIZE_VERSION_400A };
525     char filename[1024] = {0};
526     DWORD ret;
527     int i;
528 
529     ofn.lpstrFile = filename;
530     ofn.nMaxFile = 1024;
531     ofn.lpfnHook = resize_template_hook;
532     ofn.hInstance = GetModuleHandleA(NULL);
533     ofn.lpTemplateName = "template_sz";
534     for( i = 0; resize_testcases[i].flags != 0xffffffff; i++) {
535         ofn.lCustData = i;
536         ofn.Flags = resize_testcases[i].flags |
537             OFN_ENABLEHOOK | OFN_EXPLORER| OFN_ENABLETEMPLATE | OFN_SHOWHELP ;
538         ret = GetOpenFileNameA(&ofn);
539         ok(!ret, "GetOpenFileName returned %#x\n", ret);
540         ret = CommDlgExtendedError();
541         ok(!ret, "CommDlgExtendedError returned %#x\n", ret);
542     }
543 }
544 
545 /* test cases for control message IDOK */
546 /* Show case for bug #19079 */
547 typedef struct {
548     int  retval;        /* return code of the message handler */
549     BOOL setmsgresult;  /* set the result in the DWLP_MSGRESULT */
550     BOOL usemsgokstr;   /* use the FILEOKSTRING message instead of WM_NOTIFY:CDN_FILEOK */
551     BOOL do_subclass;   /* subclass the dialog hook procedure */
552     BOOL expclose;      /* is the dialog expected to close ? */
553     BOOL actclose;      /* has the dialog actually closed ? */
554 } ok_wndproc_testcase;
555 
556 static ok_wndproc_testcase ok_testcases[] = {
557     { 0,        FALSE,  FALSE,  FALSE,  TRUE},
558     { 0,         TRUE,  FALSE,  FALSE,  TRUE},
559     { 0,        FALSE,  FALSE,   TRUE,  TRUE},
560     { 0,         TRUE,  FALSE,   TRUE,  TRUE},
561     { 1,        FALSE,  FALSE,  FALSE,  TRUE},
562     { 1,         TRUE,  FALSE,  FALSE, FALSE},
563     { 1,        FALSE,  FALSE,   TRUE, FALSE},
564     { 1,         TRUE,  FALSE,   TRUE, FALSE},
565     /* FILEOKSTRING tests */
566     { 1,         TRUE,   TRUE,  FALSE, FALSE},
567     { 1,        FALSE,   TRUE,   TRUE, FALSE},
568     /* mark the end */
569     { -1 }
570 };
571 
572 /* test_ok_wndproc can be used as hook procedure or a subclass
573  * window proc for the file dialog */
574 static UINT_PTR WINAPI test_ok_wndproc(HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam)
575 {
576     HWND parent = GetParent( dlg);
577     static ok_wndproc_testcase *testcase = NULL;
578     static UINT msgFILEOKSTRING;
579     if (msg == WM_INITDIALOG)
580     {
581         testcase = (ok_wndproc_testcase*)((OPENFILENAMEA*)lParam)->lCustData;
582         testcase->actclose = TRUE;
583         msgFILEOKSTRING = RegisterWindowMessageA( FILEOKSTRINGA);
584     }
585     if( msg == WM_NOTIFY) {
586         if(((LPNMHDR)lParam)->code == CDN_FOLDERCHANGE) {
587             SetTimer( dlg, 0, 100, 0);
588             PostMessageA( parent, WM_COMMAND, IDOK, 0);
589             return FALSE;
590         } else if(((LPNMHDR)lParam)->code == CDN_FILEOK) {
591             if( testcase->usemsgokstr)
592                 return FALSE;
593             if( testcase->setmsgresult)
594                 SetWindowLongPtrA( dlg, DWLP_MSGRESULT, testcase->retval);
595             return testcase->retval;
596         }
597     }
598     if( msg == msgFILEOKSTRING) {
599         if( !testcase->usemsgokstr)
600             return FALSE;
601         if( testcase->setmsgresult)
602             SetWindowLongPtrA( dlg, DWLP_MSGRESULT, testcase->retval);
603         return testcase->retval;
604     }
605     if( msg == WM_TIMER) {
606         /* the dialog did not close automatically */
607         testcase->actclose = FALSE;
608         KillTimer( dlg, 0);
609         PostMessageA( parent, WM_COMMAND, IDCANCEL, 0);
610         return FALSE;
611     }
612     if( testcase && testcase->do_subclass)
613         return DefWindowProcA( dlg, msg, wParam, lParam);
614     return FALSE;
615 }
616 
617 static UINT_PTR WINAPI ok_template_hook(HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam)
618 {
619     if (msg == WM_SETFONT)
620         SetWindowLongPtrA( dlg, GWLP_WNDPROC, (LONG_PTR) test_ok_wndproc);
621     return FALSE;
622 }
623 
624 static void test_ok(void)
625 {
626     OPENFILENAMEA ofn = { OPENFILENAME_SIZE_VERSION_400A };
627     char filename[1024] = {0};
628     char tmpfilename[ MAX_PATH];
629     char curdir[MAX_PATH];
630     int i;
631     DWORD ret;
632     BOOL cdret;
633 
634     cdret = GetCurrentDirectoryA(sizeof(curdir), curdir);
635     ok(cdret, "Failed to get current dir err %d\n", GetLastError());
636     if (!GetTempFileNameA(".", "txt", 0, tmpfilename)) {
637         skip("Failed to create a temporary file name\n");
638         return;
639     }
640     ofn.lpstrFile = filename;
641     ofn.nMaxFile = 1024;
642     ofn.hInstance = GetModuleHandleA(NULL);
643     ofn.lpTemplateName = "template1";
644     ofn.Flags =  OFN_ENABLEHOOK | OFN_EXPLORER| OFN_ENABLETEMPLATE ;
645     for( i = 0; ok_testcases[i].retval != -1; i++) {
646         strcpy( filename, tmpfilename);
647         ofn.lCustData = (LPARAM)(ok_testcases + i);
648         ofn.lpfnHook = ok_testcases[i].do_subclass ? ok_template_hook : test_ok_wndproc;
649         ret = GetOpenFileNameA(&ofn);
650         ok( ok_testcases[i].expclose == ok_testcases[i].actclose,
651                 "testid %d: Open File dialog should %shave closed.\n", i,
652                 ok_testcases[i].expclose ? "" : "NOT ");
653         ok(ret == ok_testcases[i].expclose, "testid %d: GetOpenFileName returned %#x\n", i, ret);
654         ret = CommDlgExtendedError();
655         ok(!ret, "CommDlgExtendedError returned %#x\n", ret);
656         cdret = SetCurrentDirectoryA(curdir);
657         ok(cdret, "Failed to restore current dir err %d\n", GetLastError());
658     }
659     ret =  DeleteFileA( tmpfilename);
660     ok( ret, "Failed to delete temporary file %s err %d\n", tmpfilename, GetLastError());
661 }
662 
663 /* test arranging with a custom template */
664 typedef struct {
665     int x, y;  /* left, top coordinates */
666     int cx, cy; /* width and height */
667 } posz;
668 static struct {
669    int nrcontrols;    /* 0: no controls, 1: just the stc32 control 2: with button */
670    posz poszDlg;
671    posz poszStc32;
672    posz poszBtn;
673    DWORD ofnflags;
674 } arrange_tests[] = {
675     /* do not change the first two cases: used to get the uncustomized sizes */
676     { 0, {0},{0},{0},0 },
677     { 0, {0},{0},{0}, OFN_SHOWHELP},
678     /* two tests with just a subdialog, no controls */
679     { 0, {0, 0, 316, 76},{0},{0},0 },
680     { 0, {0, 0, 100, 76},{0},{0}, OFN_SHOWHELP},
681     /* now with a control with id stc32 */
682     { 1, {0, 0, 316, 76} ,{0, 0, 204, 76,},{0},0 }, /* bug #17748*/
683     { 1, {0, 0, 316, 76} ,{0, 0, 204, 76,},{0}, OFN_SHOWHELP}, /* bug #17748*/
684     /* tests with size of the stc32 control higher or wider then the standard dialog */
685     { 1, {0, 0, 316, 170} ,{0, 0, 204, 170,},{0},0 },
686     { 1, {0, 0, 316, 165} ,{0, 0, 411, 165,},{0}, OFN_SHOWHELP },
687     /* move the stc32 control around */
688     { 1, {0, 0, 300, 100} ,{73, 17, 50, 50,},{0},0 },
689     /* add control */
690     { 2, {0, 0, 280, 100} ,{0, 0, 50, 50,},{300,20,30,30},0 },
691     /* enable resizing should make the dialog bigger */
692     { 0, {0},{0},{0}, OFN_SHOWHELP|OFN_ENABLESIZING},
693     /* mark the end */
694     { -1 }
695 };
696 
697 static UINT_PTR WINAPI template_hook_arrange(HWND dlgChild, UINT msg, WPARAM wParam, LPARAM lParam)
698 {
699     static int index, fixhelp;
700     static posz posz0[2];
701     static RECT clrcParent, clrcChild, rcStc32;
702     static HWND hwndStc32;
703     HWND dlgParent;
704 
705     dlgParent = GetParent( dlgChild);
706     if (msg == WM_INITDIALOG) {
707         index = ((OPENFILENAMEA*)lParam)->lCustData;
708         /* get the positions before rearrangement */
709         GetClientRect( dlgParent, &clrcParent);
710         GetClientRect( dlgChild, &clrcChild);
711         hwndStc32 = GetDlgItem( dlgChild, stc32);
712         if( hwndStc32)  GetWindowRect( hwndStc32, &rcStc32);
713     }
714     if (msg == WM_NOTIFY && ((LPNMHDR)lParam)->code == CDN_FOLDERCHANGE) {
715         RECT wrcParent;
716 
717         GetWindowRect( dlgParent, &wrcParent);
718         /* the fist two "tests" just save the dialogs position, with and without
719          * help button */
720         if( index == 0) {
721             posz0[0].x = wrcParent.left;
722             posz0[0].y = wrcParent.top;
723             posz0[0].cx = wrcParent.right - wrcParent.left;
724             posz0[0].cy = wrcParent.bottom - wrcParent.top;
725         } else if( index == 1) {
726             posz0[1].x = wrcParent.left;
727             posz0[1].y = wrcParent.top;
728             posz0[1].cx = wrcParent.right - wrcParent.left;
729             posz0[1].cy = wrcParent.bottom - wrcParent.top;
730             fixhelp = posz0[1].cy - posz0[0].cy;
731         } else {
732             /* the real tests */
733             int withhelp;
734             int expectx, expecty;
735             DWORD style;
736 
737             withhelp = (arrange_tests[index].ofnflags & OFN_SHOWHELP) != 0;
738             GetWindowRect( dlgParent, &wrcParent);
739             if( !hwndStc32) {
740                 /* case with no custom subitem with stc32:
741                  * default to all custom controls below the standard */
742                 expecty = posz0[withhelp].cy + clrcChild.bottom;
743                 expectx =  posz0[withhelp].cx;
744             } else {
745                 /* special case: there is a control with id stc32 */
746                 /* expected height */
747                 expecty = posz0[withhelp].cy;
748                 if( rcStc32.bottom - rcStc32.top + (withhelp ? 0 : fixhelp) > clrcParent.bottom) {
749                     expecty +=  clrcChild.bottom -  clrcParent.bottom;
750                     if( !withhelp) expecty += fixhelp;
751                 }
752                 else
753                     expecty +=  clrcChild.bottom - ( rcStc32.bottom - rcStc32.top) ;
754                 /* expected width */
755                 expectx = posz0[withhelp].cx;
756                 if( rcStc32.right - rcStc32.left > clrcParent.right) {
757                     expectx +=  clrcChild.right -  clrcParent.right;
758                 }
759                 else
760                     expectx +=  clrcChild.right - ( rcStc32.right - rcStc32.left) ;
761             }
762             style = GetWindowLongA( dlgParent, GWL_STYLE);
763             if( !(style & WS_SIZEBOX)) {
764                 /* without the OFN_ENABLESIZING flag */
765                 ok( wrcParent.bottom - wrcParent.top == expecty,
766                         "Wrong height of dialog %d, expected %d\n",
767                         wrcParent.bottom - wrcParent.top, expecty);
768                 ok( wrcParent.right - wrcParent.left == expectx,
769                         "Wrong width of dialog %d, expected %d\n",
770                         wrcParent.right - wrcParent.left, expectx);
771             } else {
772                 /* with the OFN_ENABLESIZING flag */
773                 ok( wrcParent.bottom - wrcParent.top > expecty,
774                         "Wrong height of dialog %d, expected more than %d\n",
775                         wrcParent.bottom - wrcParent.top, expecty);
776                 ok( wrcParent.right - wrcParent.left > expectx,
777                         "Wrong width of dialog %d, expected more than %d\n",
778                         wrcParent.right - wrcParent.left, expectx);
779             }
780 
781         }
782         PostMessageA( dlgParent, WM_COMMAND, IDCANCEL, 0);
783     }
784     return 0;
785 }
786 
787 static void test_arrange(void)
788 {
789     OPENFILENAMEA ofn = {0};
790     char filename[1024] = {0};
791     DWORD ret;
792     HRSRC hRes;
793     HANDLE hDlgTmpl;
794     LPBYTE pv;
795     DLGTEMPLATE *template;
796     DLGITEMTEMPLATE *itemtemplateStc32, *itemtemplateBtn;
797     int i;
798 
799     /* load subdialog template into memory */
800     hRes = FindResourceA( GetModuleHandleA(NULL), "template_stc32", (LPSTR)RT_DIALOG);
801     hDlgTmpl = LoadResource( GetModuleHandleA(NULL), hRes );
802     /* get pointers to the structures for the dialog and the controls */
803     pv = LockResource( hDlgTmpl );
804     template = (DLGTEMPLATE*)pv;
805     if( template->x != 11111) {
806         win_skip("could not find the dialog template\n");
807         return;
808     }
809     /* skip dialog template, menu, class and title */
810     pv +=  sizeof(DLGTEMPLATE);
811     pv += 3 * sizeof(WORD);
812     /* skip font info */
813     while( *(WORD*)pv)
814         pv += sizeof(WORD);
815     pv += sizeof(WORD);
816     /* align on 32 bit boundaries */
817     pv = (LPBYTE)(((UINT_PTR)pv + 3 ) & ~3);
818     itemtemplateStc32 = (DLGITEMTEMPLATE*)pv;
819     if( itemtemplateStc32->x != 22222) {
820         win_skip("could not find the first item template\n");
821         return;
822     }
823     /* skip itemtemplate, class, title and creation data */
824     pv += sizeof(DLGITEMTEMPLATE);
825     pv +=  4 * sizeof(WORD);
826     /* align on 32 bit boundaries */
827     pv = (LPBYTE)(((UINT_PTR)pv + 3 ) & ~3);
828     itemtemplateBtn = (DLGITEMTEMPLATE*)pv;
829     if( itemtemplateBtn->x != 12345) {
830         win_skip("could not find the second item template\n");
831         return;
832     }
833 
834     ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400A;
835     ofn.lpstrFile = filename;
836     ofn.nMaxFile = 1024;
837     ofn.lpfnHook = template_hook_arrange;
838     ofn.hInstance = hDlgTmpl;
839     ofn.lpstrFilter="text\0*.txt\0All\0*\0\0";
840     for( i = 0; arrange_tests[i].nrcontrols != -1; i++) {
841         ofn.lCustData = i;
842         ofn.Flags = OFN_ENABLEHOOK | OFN_EXPLORER| OFN_ENABLETEMPLATEHANDLE | OFN_HIDEREADONLY |
843             arrange_tests[i].ofnflags;
844         template->cdit = arrange_tests[i].nrcontrols;
845         template->x = arrange_tests[i].poszDlg.x;
846         template->y = arrange_tests[i].poszDlg.y;
847         template->cx = arrange_tests[i].poszDlg.cx;
848         template->cy = arrange_tests[i].poszDlg.cy;
849         itemtemplateStc32->x = arrange_tests[i].poszStc32.x;
850         itemtemplateStc32->y = arrange_tests[i].poszStc32.y;
851         itemtemplateStc32->cx = arrange_tests[i].poszStc32.cx;
852         itemtemplateStc32->cy = arrange_tests[i].poszStc32.cy;
853         itemtemplateBtn->x = arrange_tests[i].poszBtn.x;
854         itemtemplateBtn->y = arrange_tests[i].poszBtn.y;
855         itemtemplateBtn->cx = arrange_tests[i].poszBtn.cx;
856         itemtemplateBtn->cy = arrange_tests[i].poszBtn.cy;
857         ret = GetOpenFileNameA(&ofn);
858         ok(!ret, "GetOpenFileNameA returned %#x\n", ret);
859         ret = CommDlgExtendedError();
860         ok(!ret, "CommDlgExtendedError returned %#x\n", ret);
861     }
862 }
863 
864 static CHAR SYSDIR[MAX_PATH];
865 
866 static UINT_PTR CALLBACK path_hook_proc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
867 {
868     LPNMHDR nmh;
869 
870     if( msg == WM_NOTIFY)
871     {
872         nmh = (LPNMHDR) lParam;
873         if( nmh->code == CDN_INITDONE)
874         {
875             PostMessageA( GetParent(hDlg), WM_COMMAND, IDCANCEL, FALSE);
876         }
877         else if ( nmh->code == CDN_FOLDERCHANGE)
878         {
879             char buf[1024];
880             int ret;
881 
882             memset(buf, 0x66, sizeof(buf));
883             ret = SendMessageA( GetParent(hDlg), CDM_GETFOLDERPATH, sizeof(buf), (LPARAM)buf);
884             ok(!lstrcmpiA(SYSDIR, buf), "Expected '%s', got '%s'\n", SYSDIR, buf);
885             ok(lstrlenA(SYSDIR) + 1 == ret, "Expected %d, got %d\n", lstrlenA(SYSDIR) + 1, ret);
886         }
887     }
888 
889     return 0;
890 }
891 
892 static void test_getfolderpath(void)
893 {
894     OPENFILENAMEA ofn;
895     BOOL result;
896     char szFileName[MAX_PATH] = "";
897     char szInitialDir[MAX_PATH];
898 
899     /* We need to pick a different directory as the other tests because of new
900      * Windows 7 behavior.
901      */
902     GetSystemDirectoryA(szInitialDir, MAX_PATH);
903     lstrcpyA(SYSDIR, szInitialDir);
904 
905     ZeroMemory(&ofn, sizeof(ofn));
906 
907     ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400A;
908     ofn.hwndOwner = NULL;
909     ofn.lpstrFilter = "Text Files (*.txt)\0*.txt\0All Files (*.*)\0*.*\0";
910     ofn.lpstrFile = szFileName;
911     ofn.nMaxFile = MAX_PATH;
912     ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_ENABLEHOOK;
913     ofn.lpstrDefExt = "txt";
914     ofn.lpfnHook = path_hook_proc;
915     ofn.lpstrInitialDir = szInitialDir;
916 
917     result = GetOpenFileNameA(&ofn);
918     ok(FALSE == result, "expected FALSE, got %d\n", result);
919     ok(0 == CommDlgExtendedError(), "expected 0, got %d\n",
920        CommDlgExtendedError());
921 
922     result = GetSaveFileNameA(&ofn);
923     ok(FALSE == result, "expected FALSE, got %d\n", result);
924     ok(0 == CommDlgExtendedError(), "expected 0, got %d\n",
925        CommDlgExtendedError());
926 }
927 
928 static void test_resizable2(void)
929 {
930     OPENFILENAMEA ofn = {0};
931     char filename[1024] = "pls press Enter if sizable, Esc otherwise";
932     DWORD ret;
933 
934     /* interactive because there is no hook function */
935     if( !winetest_interactive) {
936         skip( "some interactive resizable dialog tests (set WINETEST_INTERACTIVE=1)\n");
937         return;
938     }
939     ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400A;
940     ofn.lpstrFile = filename;
941     ofn.nMaxFile = 1024;
942     ofn.lpfnHook = NULL;
943     ofn.hInstance = GetModuleHandleA(NULL);
944     ofn.lpTemplateName = "template1";
945     ofn.Flags = OFN_EXPLORER;
946     ret = GetOpenFileNameA(&ofn);
947     ok( ret == TRUE, "File Dialog should have been sizable\n");
948     ret = CommDlgExtendedError();
949     ok(!ret, "CommDlgExtendedError returned %#x\n", ret);
950     ofn.Flags = OFN_EXPLORER | OFN_ENABLETEMPLATE;
951     ret = GetOpenFileNameA(&ofn);
952     ok( !ret, "File Dialog should NOT have been sizable\n");
953     ret = CommDlgExtendedError();
954     ok(!ret, "CommDlgExtendedError returned %#x\n", ret);
955     ofn.Flags = OFN_EXPLORER | OFN_ENABLETEMPLATEHANDLE;
956     ofn.hInstance = LoadResource( GetModuleHandleA(NULL), FindResourceA( GetModuleHandleA(NULL), "template1", (LPSTR)RT_DIALOG));
957     ofn.lpTemplateName = NULL;
958     ret = GetOpenFileNameA(&ofn);
959     ok( !ret, "File Dialog should NOT have been sizable\n");
960     ret = CommDlgExtendedError();
961     ok(!ret, "CommDlgExtendedError returned %#x\n", ret);
962     ofn.Flags = OFN_EXPLORER | OFN_ENABLEHOOK;
963     ret = GetOpenFileNameA(&ofn);
964     ok( !ret, "File Dialog should NOT have been sizable\n");
965     ret = CommDlgExtendedError();
966     ok(!ret, "CommDlgExtendedError returned %#x\n", ret);
967 }
968 
969 static void test_mru(void)
970 {
971     ok_wndproc_testcase testcase = {0};
972     OPENFILENAMEA ofn = { OPENFILENAME_SIZE_VERSION_400A };
973     const char *test_dir_name = "C:\\mru_test";
974     const char *test_file_name = "test.txt";
975     const char *test_full_path = "C:\\mru_test\\test.txt";
976     char filename_buf[MAX_PATH];
977     DWORD ret;
978 
979     ofn.lpstrFile = filename_buf;
980     ofn.nMaxFile = sizeof(filename_buf);
981     ofn.lpTemplateName = "template1";
982     ofn.hInstance = GetModuleHandleA(NULL);
983     ofn.Flags =  OFN_ENABLEHOOK | OFN_EXPLORER | OFN_ENABLETEMPLATE | OFN_NOCHANGEDIR;
984     ofn.lCustData = (LPARAM)&testcase;
985     ofn.lpfnHook = test_ok_wndproc;
986 
987     SetLastError(0xdeadbeef);
988     ret = CreateDirectoryA(test_dir_name, NULL);
989     ok(ret == TRUE, "CreateDirectoryA should have succeeded: %d\n", GetLastError());
990 
991     /* "teach" comdlg32 about this directory */
992     strcpy(filename_buf, test_full_path);
993     SetLastError(0xdeadbeef);
994     ret = GetOpenFileNameA(&ofn);
995     ok(ret, "GetOpenFileNameA should have succeeded: %d\n", GetLastError());
996     ret = CommDlgExtendedError();
997     ok(!ret, "CommDlgExtendedError returned %x\n", ret);
998     ok(testcase.actclose, "Open File dialog should have closed.\n");
999     ok(!strcmp(ofn.lpstrFile, test_full_path), "Expected to get %s, got %s\n", test_full_path, ofn.lpstrFile);
1000 
1001     /* get a filename without a full path. it should return the file in
1002      * test_dir_name, not in the CWD */
1003     strcpy(filename_buf, test_file_name);
1004     SetLastError(0xdeadbeef);
1005     ret = GetOpenFileNameA(&ofn);
1006     ok(ret, "GetOpenFileNameA should have succeeded: %d\n", GetLastError());
1007     ret = CommDlgExtendedError();
1008     ok(!ret, "CommDlgExtendedError returned %x\n", ret);
1009     ok(testcase.actclose, "Open File dialog should have closed.\n");
1010     if(strcmp(ofn.lpstrFile, test_full_path) != 0)
1011         win_skip("Platform doesn't save MRU data\n");
1012 
1013     SetLastError(0xdeadbeef);
1014     ret = RemoveDirectoryA(test_dir_name);
1015     ok(ret == TRUE, "RemoveDirectoryA should have succeeded: %d\n", GetLastError());
1016 }
1017 
1018 static UINT_PTR WINAPI test_extension_wndproc(HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam)
1019 {
1020     HWND parent = GetParent( dlg);
1021     if( msg == WM_NOTIFY) {
1022         SetTimer( dlg, 0, 1000, 0);
1023         PostMessageA( parent, WM_COMMAND, IDOK, 0);
1024     }
1025     if( msg == WM_TIMER) {
1026         /* the dialog did not close automatically */
1027         KillTimer( dlg, 0);
1028         PostMessageA( parent, WM_COMMAND, IDCANCEL, 0);
1029     }
1030     return FALSE;
1031 }
1032 
1033 static void test_extension_helper(OPENFILENAMEA* ofn, const char *filter,
1034                                   const char *expected_filename)
1035 {
1036     char *filename_ptr;
1037     DWORD ret;
1038     BOOL boolret;
1039 
1040     strcpy(ofn->lpstrFile, "deadbeef");
1041     ofn->lpstrFilter = filter;
1042 
1043     boolret = GetSaveFileNameA(ofn);
1044     ok(boolret, "%s: expected TRUE\n", filter);
1045 
1046     ret = CommDlgExtendedError();
1047     ok(!ret, "%s: CommDlgExtendedError returned %#x\n", filter, ret);
1048 
1049     filename_ptr = ofn->lpstrFile + ofn->nFileOffset;
1050     ok(strcmp(filename_ptr, expected_filename) == 0,
1051         "%s: Filename is %s, expected %s\n", filter, filename_ptr, expected_filename);
1052 }
1053 
1054 static void test_extension(void)
1055 {
1056     OPENFILENAMEA ofn = { OPENFILENAME_SIZE_VERSION_400A };
1057     char filename[1024] = {0};
1058     char curdir[MAX_PATH];
1059     unsigned int i;
1060     BOOL boolret;
1061 
1062     const char *defext_concrete_filters[] = {
1063         "TestFilter (*.abc)\0*.abc\0",
1064         "TestFilter (*.abc;)\0*.abc;\0",
1065         "TestFilter (*.abc;*.def)\0*.abc;*.def\0",
1066     };
1067 
1068     const char *defext_wildcard_filters[] = {
1069         "TestFilter (*.pt*)\0*.pt*\0",
1070         "TestFilter (*.pt*;*.abc)\0*.pt*;*.abc\0",
1071         "TestFilter (*.ab?)\0*.ab?\0",
1072         "TestFilter (*.*)\0*.*\0",
1073         "TestFilter (*sav)\0*sav\0",
1074         NULL    /* is a test, not an endmark! */
1075     };
1076 
1077     boolret = GetCurrentDirectoryA(sizeof(curdir), curdir);
1078     ok(boolret, "Failed to get current dir err %d\n", GetLastError());
1079 
1080     ofn.hwndOwner = NULL;
1081     ofn.lpstrFile = filename;
1082     ofn.nMaxFile = MAX_PATH;
1083     ofn.Flags = OFN_EXPLORER | OFN_ENABLEHOOK;
1084     ofn.lpstrInitialDir = curdir;
1085     ofn.lpfnHook = test_extension_wndproc;
1086     ofn.nFileExtension = 0;
1087 
1088     ofn.lpstrDefExt = NULL;
1089 
1090     /* Without lpstrDefExt, append no extension */
1091     test_extension_helper(&ofn, "TestFilter (*.abc) lpstrDefExt=NULL\0*.abc\0", "deadbeef");
1092     test_extension_helper(&ofn, "TestFilter (*.ab?) lpstrDefExt=NULL\0*.ab?\0", "deadbeef");
1093 
1094     ofn.lpstrDefExt = "";
1095 
1096     /* If lpstrDefExt="" and the filter has a concrete extension, append it */
1097     test_extension_helper(&ofn, "TestFilter (*.abc) lpstrDefExt=\"\"\0*.abc\0", "deadbeef.abc");
1098 
1099     /* If lpstrDefExt="" and the filter has a wildcard extension, do nothing */
1100     test_extension_helper(&ofn, "TestFilter (*.ab?) lpstrDefExt=\"\"\0*.ab?\0", "deadbeef");
1101 
1102     ofn.lpstrDefExt = "xyz";
1103 
1104     /* Append concrete extensions from filters */
1105     for (i = 0; i < ARRAY_SIZE(defext_concrete_filters); i++) {
1106         test_extension_helper(&ofn, defext_concrete_filters[i], "deadbeef.abc");
1107     }
1108 
1109     /* Append nothing from this filter */
1110     test_extension_helper(&ofn, "TestFilter (*.)\0*.\0", "deadbeef");
1111 
1112     /* Ignore wildcard extensions in filters */
1113     for (i = 0; i < ARRAY_SIZE(defext_wildcard_filters); i++) {
1114         test_extension_helper(&ofn, defext_wildcard_filters[i], "deadbeef.xyz");
1115     }
1116 
1117     /* Append valid extensions consisting of multiple parts */
1118     test_extension_helper(&ofn, "TestFilter (*.abc.def)\0*.abc.def\0", "deadbeef.abc.def");
1119     test_extension_helper(&ofn, "TestFilter (.abc.def)\0.abc.def\0", "deadbeef.abc.def");
1120     test_extension_helper(&ofn, "TestFilter (*.*.def)\0*.*.def\0", "deadbeef.xyz");
1121 }
1122 
1123 static BOOL WINAPI test_null_enum(HWND hwnd, LPARAM lParam)
1124 {
1125     /* Find the textbox and send a filename so IDOK will work.
1126        If the file textbox is empty IDOK will be ignored */
1127     CHAR className[20];
1128     if(GetClassNameA(hwnd, className, sizeof(className)) > 0 && !strcmp("Edit",className))
1129     {
1130         SetWindowTextA(hwnd, "testfile");
1131         return FALSE; /* break window enumeration */
1132     }
1133     return TRUE;
1134 }
1135 
1136 static UINT_PTR WINAPI test_null_wndproc(HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam)
1137 {
1138     HWND parent = GetParent( dlg);
1139     if( msg == WM_NOTIFY) {
1140         SetTimer( dlg, 0, 100, 0);
1141         SetTimer( dlg, 1, 1000, 0);
1142         EnumChildWindows( parent, test_null_enum, 0);
1143     }
1144     if( msg == WM_TIMER) {
1145         if(!wParam)
1146             PostMessageA( parent, WM_COMMAND, IDOK, 0);
1147         else {
1148             /* the dialog did not close automatically */
1149             KillTimer( dlg, 0);
1150             PostMessageA( parent, WM_COMMAND, IDCANCEL, 0);
1151         }
1152     }
1153     return FALSE;
1154 }
1155 
1156 static void test_null_filename(void)
1157 {
1158     OPENFILENAMEA ofnA = {0};
1159     OPENFILENAMEW ofnW = {0};
1160     WCHAR filterW[] = {'t','e','x','t','\0','*','.','t','x','t','\0',
1161                        'A','l','l','\0','*','\0','\0'};
1162     DWORD ret;
1163 
1164     ofnA.lStructSize = OPENFILENAME_SIZE_VERSION_400A;
1165     ofnA.lpstrFile = NULL;
1166     ofnA.nMaxFile = 0;
1167     ofnA.nFileOffset = 0xdead;
1168     ofnA.nFileExtension = 0xbeef;
1169     ofnA.lpfnHook = test_null_wndproc;
1170     ofnA.Flags = OFN_ENABLEHOOK | OFN_EXPLORER;
1171     ofnA.hInstance = GetModuleHandleA(NULL);
1172     ofnA.lpstrFilter = "text\0*.txt\0All\0*\0\0";
1173     ofnA.lpstrDefExt = NULL;
1174     ret = GetOpenFileNameA(&ofnA);
1175     todo_wine ok(ret, "GetOpenFileNameA returned %#x\n", ret);
1176     ret = CommDlgExtendedError();
1177     todo_wine ok(!ret, "CommDlgExtendedError returned %#x, should be 0\n", ret);
1178 
1179     todo_wine ok(ofnA.nFileOffset != 0xdead, "ofnA.nFileOffset is 0xdead\n");
1180     todo_wine ok(ofnA.nFileExtension != 0xbeef, "ofnA.nFileExtension is 0xbeef\n");
1181 
1182     ofnA.lpstrFile = NULL;
1183     ofnA.nMaxFile = 1024; /* bogus input - lpstrFile = NULL but fake 1024 bytes available */
1184     ofnA.nFileOffset = 0xdead;
1185     ofnA.nFileExtension = 0xbeef;
1186     ret = GetOpenFileNameA(&ofnA);
1187     ok(ret, "GetOpenFileNameA returned %#x\n", ret);
1188     ret = CommDlgExtendedError();
1189     ok(!ret, "CommDlgExtendedError returned %#x\n", ret);
1190 
1191     ok(ofnA.nFileOffset != 0xdead, "ofnA.nFileOffset is 0xdead\n");
1192     ok(ofnA.nFileExtension == 0, "ofnA.nFileExtension is 0x%x, should be 0\n", ofnA.nFileExtension);
1193 
1194     /* unicode tests */
1195     ofnW.lStructSize = OPENFILENAME_SIZE_VERSION_400W;
1196     ofnW.lpstrFile = NULL;
1197     ofnW.nMaxFile = 0;
1198     ofnW.nFileOffset = 0xdead;
1199     ofnW.nFileExtension = 0xbeef;
1200     ofnW.lpfnHook = test_null_wndproc;
1201     ofnW.Flags = OFN_ENABLEHOOK | OFN_EXPLORER;
1202     ofnW.hInstance = GetModuleHandleW(NULL);
1203     ofnW.lpstrFilter = filterW;
1204     ofnW.lpstrDefExt = NULL;
1205     ret = GetOpenFileNameW(&ofnW);
1206     todo_wine ok(ret, "GetOpenFileNameW returned %#x\n", ret);
1207     ret = CommDlgExtendedError();
1208     todo_wine ok(!ret, "CommDlgExtendedError returned %#x\n", ret);
1209 
1210     todo_wine ok(ofnW.nFileOffset != 0xdead, "ofnW.nFileOffset is 0xdead\n");
1211     todo_wine ok(ofnW.nFileExtension != 0xbeef, "ofnW.nFileExtension is 0xbeef\n");
1212 
1213     ofnW.lpstrFile = NULL;
1214     ofnW.nMaxFile = 1024; /* bogus input - lpstrFile = NULL but fake 1024 bytes available */
1215     ofnW.nFileOffset = 0xdead;
1216     ofnW.nFileExtension = 0xbeef;
1217     ret = GetOpenFileNameW(&ofnW);
1218     ok(ret, "GetOpenFileNameA returned %#x\n", ret);
1219     ret = CommDlgExtendedError();
1220     ok(!ret, "CommDlgExtendedError returned %#x\n", ret);
1221 
1222     ok(ofnW.nFileOffset != 0xdead, "ofnW.nFileOffset is 0xdead\n");
1223     ok(ofnW.nFileExtension == 0, "ofnW.nFileExtension is 0x%x, should be 0\n", ofnW.nFileExtension);
1224 }
1225 
1226 static void test_directory_filename(void)
1227 {
1228     OPENFILENAMEA ofnA = {0};
1229     OPENFILENAMEW ofnW = {0};
1230     WCHAR filterW[] = {'t','e','x','t','\0','*','.','t','x','t','\0',
1231                        'A','l','l','\0','*','\0','\0'};
1232     char szInitialDir[MAX_PATH] = {0};
1233     WCHAR szInitialDirW[MAX_PATH] = {0};
1234     DWORD ret;
1235 
1236     GetWindowsDirectoryA(szInitialDir, MAX_PATH);
1237     GetWindowsDirectoryW(szInitialDirW, MAX_PATH);
1238 
1239     szInitialDir[strlen(szInitialDir)] = '\\';
1240     szInitialDirW[lstrlenW(szInitialDirW)] = '\\';
1241 
1242     ofnA.lStructSize = OPENFILENAME_SIZE_VERSION_400A;
1243     ofnA.lpstrFile = szInitialDir;
1244     ofnA.nMaxFile = MAX_PATH;
1245     ofnA.lpfnHook = test_null_wndproc;
1246     ofnA.Flags = OFN_ENABLEHOOK | OFN_EXPLORER;
1247     ofnA.hInstance = GetModuleHandleA(NULL);
1248     ofnA.lpstrFilter = "text\0*.txt\0All\0*\0\0";
1249     ofnA.lpstrDefExt = NULL;
1250     ret = GetOpenFileNameA(&ofnA);
1251     todo_wine ok(!ret, "GetOpenFileNameA returned %#x\n", ret);
1252 
1253     /* unicode tests */
1254     ofnW.lStructSize = OPENFILENAME_SIZE_VERSION_400W;
1255     ofnW.lpstrFile = szInitialDirW;
1256     ofnW.nMaxFile = MAX_PATH;
1257     ofnW.lpfnHook = test_null_wndproc;
1258     ofnW.Flags = OFN_ENABLEHOOK | OFN_EXPLORER;
1259     ofnW.hInstance = GetModuleHandleW(NULL);
1260     ofnW.lpstrFilter = filterW;
1261     ofnW.lpstrDefExt = NULL;
1262     ret = GetOpenFileNameW(&ofnW);
1263     todo_wine ok(!ret, "GetOpenFileNameW returned %#x\n", ret);
1264 }
1265 
1266 static UINT_PTR WINAPI test_ole_init_wndproc(HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam)
1267 {
1268     HRESULT hr;
1269 
1270     hr = OleInitialize(NULL);
1271     ok(hr == S_FALSE, "OleInitialize() returned %#x\n", hr);
1272     OleUninitialize();
1273 
1274     if (msg == WM_NOTIFY)
1275         PostMessageA(GetParent(dlg), WM_COMMAND, IDCANCEL, 0);
1276     return FALSE;
1277 }
1278 
1279 static LRESULT CALLBACK hook_proc(int code, WPARAM wp, LPARAM lp)
1280 {
1281     static BOOL first_dlg = TRUE;
1282     HRESULT hr;
1283 
1284     if (code == HCBT_CREATEWND)
1285     {
1286         CBT_CREATEWNDW *c = (CBT_CREATEWNDW *)lp;
1287 
1288         if (c->lpcs->lpszClass == (LPWSTR)WC_DIALOG)
1289         {
1290             /* OleInitialize() creates a window for the main apartment. Since
1291              * Vista OleInitialize() is called before the file dialog is
1292              * created. SimCity 2000 expects that the first window created
1293              * after GetOpenFileA() is a file dialog window. Mark Vista+
1294              * behavior as broken. */
1295             hr = OleInitialize(NULL);
1296             ok((first_dlg ? hr == S_OK : hr == S_FALSE)
1297                     || broken(first_dlg && hr == S_FALSE),
1298                     "OleInitialize() returned %#x (first dialog %#x)\n", hr, first_dlg);
1299             OleUninitialize();
1300             first_dlg = FALSE;
1301         }
1302     }
1303 
1304     return CallNextHookEx(NULL, code, wp, lp);
1305 }
1306 
1307 static void test_ole_initialization(void)
1308 {
1309     char file[MAX_PATH] = {0};
1310     OPENFILENAMEA ofn = {0};
1311     HRESULT hr;
1312     HHOOK hook;
1313     BOOL ret;
1314 
1315     hook = SetWindowsHookExW(WH_CBT, hook_proc, NULL, GetCurrentThreadId());
1316 
1317     ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400A;
1318     ofn.lpstrFile = file;
1319     ofn.nMaxFile = MAX_PATH;
1320     ofn.lpfnHook = test_ole_init_wndproc;
1321     ofn.Flags = OFN_ENABLEHOOK | OFN_EXPLORER;
1322     ofn.hInstance = GetModuleHandleA(NULL);
1323     ret = GetOpenFileNameA(&ofn);
1324     ok(!ret, "GetOpenFileNameA returned %#x\n", ret);
1325 
1326     hr = OleInitialize(NULL);
1327     ok(hr == S_OK, "OleInitialize() returned %#x\n", hr);
1328     OleUninitialize();
1329 
1330     UnhookWindowsHookEx(hook);
1331 }
1332 
1333 START_TEST(filedlg)
1334 {
1335     test_DialogCancel();
1336     test_create_view_window2();
1337     test_create_view_template();
1338     test_arrange();
1339     test_resize();
1340     test_ok();
1341     test_getfolderpath();
1342     test_mru();
1343     if( resizesupported) test_resizable2();
1344     test_extension();
1345     test_null_filename();
1346     test_directory_filename();
1347     test_ole_initialization();
1348 }
1349