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