2 * Unit test of the SHBrowseForFolder function.
4 * Copyright 2009-2010 Michael Mc Donnell
5 * Copyright 2011 André Hentschel
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 #include "wine/test.h"
31 #define IDD_MAKENEWFOLDER 0x3746 /* From "../shresdef.h" */
32 #define TIMER_WAIT_MS 50 /* Should be long enough for slow systems */
34 static const char new_folder_name
[] = "foo";
35 static LPITEMIDLIST selected_folder_pidl
;
38 * Returns the number of folders in a folder.
40 static int get_number_of_folders(LPCSTR path
)
42 int number_of_folders
= 0;
43 char path_search_string
[MAX_PATH
];
44 WIN32_FIND_DATAA find_data
;
47 lstrcpynA(path_search_string
, path
, MAX_PATH
- 1);
48 strcat(path_search_string
, "*");
50 find_handle
= FindFirstFileA(path_search_string
, &find_data
);
51 if (find_handle
== INVALID_HANDLE_VALUE
)
56 if ((find_data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) &&
57 strcmp(find_data
.cFileName
, ".") != 0 &&
58 strcmp(find_data
.cFileName
, "..") != 0)
63 while (FindNextFileA(find_handle
, &find_data
) != 0);
65 FindClose(find_handle
);
66 return number_of_folders
;
69 static BOOL
does_folder_or_file_exist(LPCSTR folder_path
)
71 DWORD file_attributes
= GetFileAttributesA(folder_path
);
72 return !(file_attributes
== INVALID_FILE_ATTRIBUTES
);
76 * Timer callback used by test_click_make_new_folder_button. It simulates a user
77 * making a new folder and calling it "foo".
79 static void CALLBACK
make_new_folder_timer_callback(HWND hwnd
, UINT uMsg
,
80 UINT_PTR idEvent
, DWORD dwTime
)
87 /* Click "Make New Folder" button */
88 PostMessageA(hwnd
, WM_COMMAND
, IDD_MAKENEWFOLDER
, 0);
91 /* Set the new folder name to foo by replacing text in edit control */
92 SendMessageA(GetFocus(), EM_REPLACESEL
, 0, (LPARAM
) new_folder_name
);
97 * The test does not trigger the correct state on Windows. This results
98 * in the new folder pidl not being returned. The result is as
99 * expected if the same steps are done manually.
100 * Sending the down key selects the new folder again which sets the
101 * correct state. This ensures that the correct pidl is returned.
103 keybd_event(VK_DOWN
, 0, 0, 0);
106 keybd_event(VK_DOWN
, 0, KEYEVENTF_KEYUP
, 0);
109 KillTimer(hwnd
, idEvent
);
110 /* Close dialog box */
111 SendMessageA(hwnd
, WM_COMMAND
, IDOK
, 0);
119 * Callback used by test_click_make_new_folder_button. It sets up a timer to
120 * simulate user input.
122 static int CALLBACK
create_new_folder_callback(HWND hwnd
, UINT uMsg
,
123 LPARAM lParam
, LPARAM lpData
)
127 case BFFM_INITIALIZED
:
128 /* User input is simulated in timer callback */
129 SetTimer(hwnd
, 0, TIMER_WAIT_MS
, make_new_folder_timer_callback
);
137 * Tests if clicking the "Make New Folder" button in a SHBrowseForFolder
138 * dialog box creates a new folder. (Bug 17986).
140 * Here follows a description of what happens on W2K,Vista, W2K8, W7:
141 * When the "Make New Folder" button is clicked a new folder is created and
142 * inserted into the tree. The folder is given a default name that depends on
143 * the locale (e.g. "New Folder"). The folder name is selected and the dialog
144 * waits for the user to type in a new name. The folder is renamed when the user
145 * types in a name and presses enter.
147 * Note that XP and W2K3 do not select the folder name or wait for the user
148 * to type in a new folder name. This behavior is considered broken as most
149 * users would like to give the folder a name after creating it. The fact that
150 * it originally waited for the user to type in a new folder name(W2K), and then
151 * again was changed back wait for the new folder name(Vista, W2K8, W7),
152 * indicates that MS also believes that it was broken in XP and W2K3.
154 static void test_click_make_new_folder_button(void)
156 HRESULT resCoInit
, hr
;
158 LPITEMIDLIST pidl
= NULL
;
159 LPITEMIDLIST test_folder_pidl
;
160 IShellFolder
*test_folder_object
;
161 char test_folder_path
[MAX_PATH
];
162 WCHAR test_folder_pathW
[MAX_PATH
];
163 CHAR new_folder_path
[MAX_PATH
];
164 CHAR new_folder_pidl_path
[MAX_PATH
];
165 char selected_folder
[MAX_PATH
];
166 const CHAR title
[] = "test_click_make_new_folder_button";
167 int number_of_folders
= -1;
168 SHFILEOPSTRUCTA shfileop
;
170 if (does_folder_or_file_exist(title
))
172 skip("The test folder already exists.\n");
176 /* Must initialize COM if using the NEWDIAlOGSTYLE according to MSDN. */
177 resCoInit
= CoInitialize(NULL
);
178 if(!(resCoInit
== S_OK
|| resCoInit
== S_FALSE
))
180 skip("COM could not be initialized %lu\n", GetLastError());
184 /* Leave room for concatenating title, two backslashes, and an extra NULL. */
185 if (!GetCurrentDirectoryA(MAX_PATH
-strlen(title
)-3, test_folder_path
))
187 skip("GetCurrentDirectoryA failed %lu\n", GetLastError());
189 strcat(test_folder_path
, "\\");
190 strcat(test_folder_path
, title
);
191 strcat(test_folder_path
, "\\");
193 /* Avoid conflicts by creating a test folder. */
194 if (!CreateDirectoryA(title
, NULL
))
196 skip("CreateDirectoryA failed %lu\n", GetLastError());
200 /* Initialize browse info struct for SHBrowseForFolder */
202 bi
.pszDisplayName
= selected_folder
;
203 bi
.lpszTitle
= title
;
204 bi
.ulFlags
= BIF_NEWDIALOGSTYLE
;
205 bi
.lpfn
= create_new_folder_callback
;
206 /* Use test folder as the root folder for dialog box */
207 MultiByteToWideChar(CP_UTF8
, 0, test_folder_path
, -1,
208 test_folder_pathW
, MAX_PATH
);
209 hr
= SHGetDesktopFolder(&test_folder_object
);
210 ok (SUCCEEDED(hr
), "SHGetDesktopFolder failed with hr 0x%08lx\n", hr
);
212 skip("SHGetDesktopFolder failed - skipping\n");
215 test_folder_object
->lpVtbl
->ParseDisplayName(test_folder_object
, NULL
, NULL
,
216 test_folder_pathW
, 0UL, &test_folder_pidl
, 0UL);
217 bi
.pidlRoot
= test_folder_pidl
;
219 /* Display dialog box and let callback click the buttons */
220 pidl
= SHBrowseForFolderA(&bi
);
222 number_of_folders
= get_number_of_folders(test_folder_path
);
223 ok(number_of_folders
== 1 || broken(number_of_folders
== 0) /* W95, W98 */,
224 "Clicking \"Make New Folder\" button did not result in a new folder.\n");
226 /* There should be a new folder foo inside the test folder */
227 strcpy(new_folder_path
, test_folder_path
);
228 strcat(new_folder_path
, new_folder_name
);
229 ok(does_folder_or_file_exist(new_folder_path
)
230 || broken(!does_folder_or_file_exist(new_folder_path
)) /* W95, W98, XP, W2K3 */,
231 "The new folder did not get the name %s\n", new_folder_name
);
233 /* Dialog should return a pidl pointing to the new folder */
234 ok(SHGetPathFromIDListA(pidl
, new_folder_pidl_path
),
235 "SHGetPathFromIDList failed for new folder.\n");
236 ok(strcmp(new_folder_path
, new_folder_pidl_path
) == 0
237 || broken(strcmp(new_folder_path
, new_folder_pidl_path
) != 0) /* earlier than Vista */,
238 "SHBrowseForFolder did not return the pidl for the new folder. "
239 "Expected '%s' got '%s'\n", new_folder_path
, new_folder_pidl_path
);
241 /* Remove test folder and any subfolders created in this test */
242 shfileop
.hwnd
= NULL
;
243 shfileop
.wFunc
= FO_DELETE
;
244 /* Path must be double NULL terminated */
245 test_folder_path
[strlen(test_folder_path
)+1] = '\0';
246 shfileop
.pFrom
= test_folder_path
;
248 shfileop
.fFlags
= FOF_NOCONFIRMATION
|FOF_NOERRORUI
|FOF_SILENT
;
249 SHFileOperationA(&shfileop
);
252 CoTaskMemFree(test_folder_pidl
);
253 test_folder_object
->lpVtbl
->Release(test_folder_object
);
260 * Callback used by test_selection.
262 static int CALLBACK
selection_callback(HWND hwnd
, UINT uMsg
, LPARAM lParam
, LPARAM lpData
)
268 case BFFM_INITIALIZED
:
269 /* test with zero values */
270 ret
= SendMessageA(hwnd
, BFFM_SETSELECTIONA
, 0, 0);
271 ok(!ret
, "SendMessage returned: %lu\n", ret
);
272 ret
= SendMessageA(hwnd
, BFFM_SETSELECTIONW
, 0, 0);
273 ok(!ret
, "SendMessage returned: %lu\n", ret
);
275 ret
= SendMessageA(hwnd
, BFFM_SETSELECTIONA
, 1, 0);
276 ok(!ret
, "SendMessage returned: %lu\n", ret
);
281 ret
= SendMessageA(hwnd
, BFFM_SETSELECTIONW
, 1, 0);
282 ok(!ret
, "SendMessage returned: %lu\n", ret
);
285 ret
= SendMessageA(hwnd
, BFFM_SETSELECTIONA
, 0, (LPARAM
)selected_folder_pidl
);
286 ok(!ret
, "SendMessage returned: %lu\n", ret
);
287 ret
= SendMessageW(hwnd
, BFFM_SETSELECTIONW
, 0, (LPARAM
)selected_folder_pidl
);
288 ok(!ret
, "SendMessage returned: %lu\n", ret
);
290 ret
= SendMessageA(hwnd
, BFFM_SETSELECTIONA
, 1, (LPARAM
)selected_folder_pidl
);
291 ok(!ret
, "SendMessage returned: %lu\n", ret
);
292 ret
= SendMessageW(hwnd
, BFFM_SETSELECTIONW
, 1, (LPARAM
)selected_folder_pidl
);
293 ok(!ret
, "SendMessage returned: %lu\n", ret
);
295 ret
= SendMessageA(hwnd
, BFFM_SETSELECTIONA
, 1, (LPARAM
)new_folder_name
);
296 ok(!ret
, "SendMessage returned: %lu\n", ret
);
297 ret
= SendMessageW(hwnd
, BFFM_SETSELECTIONW
, 1, (LPARAM
)new_folder_name
);
298 ok(!ret
, "SendMessage returned: %lu\n", ret
);
300 SendMessageA(hwnd
, WM_COMMAND
, IDOK
, 0);
307 static void test_selection(void)
309 HRESULT resCoInit
, hr
;
311 LPITEMIDLIST pidl
= NULL
;
312 IShellFolder
*desktop_object
;
313 WCHAR selected_folderW
[MAX_PATH
];
314 const CHAR title
[] = "test_selection";
316 resCoInit
= CoInitialize(NULL
);
317 if(!(resCoInit
== S_OK
|| resCoInit
== S_FALSE
))
319 skip("COM could not be initialized %lu\n", GetLastError());
323 if (!GetCurrentDirectoryW(MAX_PATH
, selected_folderW
))
325 skip("GetCurrentDirectoryW failed %lu\n", GetLastError());
328 /* Initialize browse info struct for SHBrowseForFolder */
330 bi
.pszDisplayName
= NULL
;
331 bi
.lpszTitle
= title
;
332 bi
.lpfn
= selection_callback
;
334 hr
= SHGetDesktopFolder(&desktop_object
);
335 ok (SUCCEEDED(hr
), "SHGetDesktopFolder failed with hr 0x%08lx\n", hr
);
337 skip("SHGetDesktopFolder failed - skipping\n");
340 desktop_object
->lpVtbl
->ParseDisplayName(desktop_object
, NULL
, NULL
,
341 selected_folderW
, 0UL, &selected_folder_pidl
, 0UL);
342 bi
.pidlRoot
= selected_folder_pidl
;
344 /* test without flags */
346 pidl
= SHBrowseForFolderA(&bi
);
350 bi
.ulFlags
= BIF_NEWDIALOGSTYLE
;
351 pidl
= SHBrowseForFolderA(&bi
);
354 IShellFolder_Release(desktop_object
);
359 START_TEST(brsfolder
)
361 test_click_make_new_folder_button();