shell32/tests: Fix uninitialized memory access (Valgrind).
[wine/multimedia.git] / dlls / shell32 / tests / brsfolder.c
blobf8e8e16a55482e81f69de84f4ef93218e0d90e00
1 /*
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
21 #include <windows.h>
22 #include <shlobj.h>
23 #include <shobjidl.h>
24 #include <string.h>
26 #include "wine/test.h"
27 #define IDD_MAKENEWFOLDER 0x3746 /* From "../shresdef.h" */
28 #define TIMER_WAIT_MS 50 /* Should be long enough for slow systems */
30 static const char new_folder_name[] = "foo";
31 static LPITEMIDLIST selected_folder_pidl;
34 * Returns the number of folders in a folder.
36 static int get_number_of_folders(LPCSTR path)
38 int number_of_folders = 0;
39 char path_search_string[MAX_PATH];
40 WIN32_FIND_DATA find_data;
41 HANDLE find_handle;
43 strncpy(path_search_string, path, MAX_PATH);
44 strncat(path_search_string, "*", 1);
46 find_handle = FindFirstFile(path_search_string, &find_data);
47 if (find_handle == INVALID_HANDLE_VALUE)
48 return -1;
52 if ((find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
53 strcmp(find_data.cFileName, ".") != 0 &&
54 strcmp(find_data.cFileName, "..") != 0)
56 number_of_folders++;
59 while (FindNextFile(find_handle, &find_data) != 0);
61 FindClose(find_handle);
62 return number_of_folders;
65 static BOOL does_folder_or_file_exist(LPCSTR folder_path)
67 DWORD file_attributes = GetFileAttributesA(folder_path);
68 return !(file_attributes == INVALID_FILE_ATTRIBUTES);
72 * Timer callback used by test_click_make_new_folder_button. It simulates a user
73 * making a new folder and calling it "foo".
75 static void CALLBACK make_new_folder_timer_callback(HWND hwnd, UINT uMsg,
76 UINT_PTR idEvent, DWORD dwTime)
78 static int step = 0;
80 switch (step++)
82 case 0:
83 /* Click "Make New Folder" button */
84 PostMessage(hwnd, WM_COMMAND, IDD_MAKENEWFOLDER, 0);
85 break;
86 case 1:
87 /* Set the new folder name to foo by replacing text in edit control */
88 SendMessage(GetFocus(), EM_REPLACESEL, 0, (LPARAM) new_folder_name);
89 SetFocus(hwnd);
90 break;
91 case 2:
93 * The test does not trigger the correct state on Windows. This results
94 * in the new folder pidl not being returned. The result is as
95 * expected if the same steps are done manually.
96 * Sending the down key selects the new folder again which sets the
97 * correct state. This ensures that the correct pidl is returned.
99 keybd_event(VK_DOWN, 0, 0, 0);
100 break;
101 case 3:
102 keybd_event(VK_DOWN, 0, KEYEVENTF_KEYUP, 0);
103 break;
104 case 4:
105 KillTimer(hwnd, idEvent);
106 /* Close dialog box */
107 SendMessage(hwnd, WM_COMMAND, IDOK, 0);
108 break;
109 default:
110 break;
115 * Callback used by test_click_make_new_folder_button. It sets up a timer to
116 * simulate user input.
118 static int CALLBACK create_new_folder_callback(HWND hwnd, UINT uMsg,
119 LPARAM lParam, LPARAM lpData)
121 switch (uMsg)
123 case BFFM_INITIALIZED:
124 /* User input is simulated in timer callback */
125 SetTimer(hwnd, 0, TIMER_WAIT_MS, make_new_folder_timer_callback);
126 return TRUE;
127 default:
128 return FALSE;
133 * Tests if clicking the "Make New Folder" button in a SHBrowseForFolder
134 * dialog box creates a new folder. (Bug 17986).
136 * Here follows a description of what happens on W2K,Vista, W2K8, W7:
137 * When the "Make New Folder" button is clicked a new folder is created and
138 * inserted into the tree. The folder is given a default name that depends on
139 * the locale (e.g. "New Folder"). The folder name is selected and the dialog
140 * waits for the user to type in a new name. The folder is renamed when the user
141 * types in a name and presses enter.
143 * Note that XP and W2K3 do not select the folder name or wait for the user
144 * to type in a new folder name. This behavior is considered broken as most
145 * users would like to give the folder a name after creating it. The fact that
146 * it originally waited for the user to type in a new folder name(W2K), and then
147 * again was changed back wait for the new folder name(Vista, W2K8, W7),
148 * indicates that MS also believes that it was broken in XP and W2K3.
150 static void test_click_make_new_folder_button(void)
152 HRESULT resCoInit, hr;
153 BROWSEINFO bi;
154 LPITEMIDLIST pidl = NULL;
155 LPITEMIDLIST test_folder_pidl;
156 IShellFolder *test_folder_object;
157 char test_folder_path[MAX_PATH];
158 WCHAR test_folder_pathW[MAX_PATH];
159 CHAR new_folder_path[MAX_PATH];
160 CHAR new_folder_pidl_path[MAX_PATH];
161 char selected_folder[MAX_PATH];
162 const CHAR title[] = "test_click_make_new_folder_button";
163 int number_of_folders = -1;
164 SHFILEOPSTRUCT shfileop;
166 if (does_folder_or_file_exist(title))
168 skip("The test folder already exists.\n");
169 return;
172 /* Must initialize COM if using the NEWDIAlOGSTYLE according to MSDN. */
173 resCoInit = CoInitialize(NULL);
174 if(!(resCoInit == S_OK || resCoInit == S_FALSE))
176 skip("COM could not be initialized %u\n", GetLastError());
177 return;
180 /* Leave room for concatenating title, two backslashes, and an extra NULL. */
181 if (!GetCurrentDirectoryA(MAX_PATH-strlen(title)-3, test_folder_path))
183 skip("GetCurrentDirectoryA failed %u\n", GetLastError());
185 strncat(test_folder_path, "\\", 1);
186 strncat(test_folder_path, title, MAX_PATH-1);
187 strncat(test_folder_path, "\\", 1);
189 /* Avoid conflicts by creating a test folder. */
190 if (!CreateDirectoryA(title, NULL))
192 skip("CreateDirectoryA failed %u\n", GetLastError());
193 return;
196 /* Initialize browse info struct for SHBrowseForFolder */
197 bi.hwndOwner = NULL;
198 bi.pszDisplayName = (LPTSTR) &selected_folder;
199 bi.lpszTitle = (LPTSTR) title;
200 bi.ulFlags = BIF_NEWDIALOGSTYLE;
201 bi.lpfn = create_new_folder_callback;
202 /* Use test folder as the root folder for dialog box */
203 MultiByteToWideChar(CP_UTF8, 0, test_folder_path, -1,
204 test_folder_pathW, MAX_PATH);
205 hr = SHGetDesktopFolder(&test_folder_object);
206 ok (SUCCEEDED(hr), "SHGetDesktopFolder failed with hr 0x%08x\n", hr);
207 if (FAILED(hr)) {
208 skip("SHGetDesktopFolder failed - skipping\n");
209 return;
211 test_folder_object->lpVtbl->ParseDisplayName(test_folder_object, NULL, NULL,
212 test_folder_pathW, 0UL, &test_folder_pidl, 0UL);
213 bi.pidlRoot = test_folder_pidl;
215 /* Display dialog box and let callback click the buttons */
216 pidl = SHBrowseForFolder(&bi);
218 number_of_folders = get_number_of_folders(test_folder_path);
219 ok(number_of_folders == 1 || broken(number_of_folders == 0) /* W95, W98 */,
220 "Clicking \"Make New Folder\" button did not result in a new folder.\n");
222 /* There should be a new folder foo inside the test folder */
223 strcpy(new_folder_path, test_folder_path);
224 strcat(new_folder_path, new_folder_name);
225 ok(does_folder_or_file_exist(new_folder_path)
226 || broken(!does_folder_or_file_exist(new_folder_path)) /* W95, W98, XP, W2K3 */,
227 "The new folder did not get the name %s\n", new_folder_name);
229 /* Dialog should return a pidl pointing to the new folder */
230 ok(SHGetPathFromIDListA(pidl, new_folder_pidl_path),
231 "SHGetPathFromIDList failed for new folder.\n");
232 ok(strcmp(new_folder_path, new_folder_pidl_path) == 0
233 || broken(strcmp(new_folder_path, new_folder_pidl_path) != 0) /* earlier than Vista */,
234 "SHBrowseForFolder did not return the pidl for the new folder. "
235 "Expected '%s' got '%s'\n", new_folder_path, new_folder_pidl_path);
237 /* Remove test folder and any subfolders created in this test */
238 shfileop.hwnd = NULL;
239 shfileop.wFunc = FO_DELETE;
240 /* Path must be double NULL terminated */
241 test_folder_path[strlen(test_folder_path)+1] = '\0';
242 shfileop.pFrom = test_folder_path;
243 shfileop.pTo = NULL;
244 shfileop.fFlags = FOF_NOCONFIRMATION|FOF_NOERRORUI|FOF_SILENT;
245 SHFileOperation(&shfileop);
247 if (pidl)
248 CoTaskMemFree(pidl);
249 if (test_folder_pidl)
250 CoTaskMemFree(test_folder_pidl);
251 test_folder_object->lpVtbl->Release(test_folder_object);
253 CoUninitialize();
258 * Callback used by test_selection.
260 static int CALLBACK selection_callback(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
262 DWORD ret;
264 switch (uMsg)
266 case BFFM_INITIALIZED:
267 /* test with zero values */
268 ret = SendMessage(hwnd, BFFM_SETSELECTIONA, 0, 0);
269 ok(!ret, "SendMessage returned: %u\n", ret);
270 ret = SendMessage(hwnd, BFFM_SETSELECTIONW, 0, 0);
271 ok(!ret, "SendMessage returned: %u\n", ret);
273 ret = SendMessage(hwnd, BFFM_SETSELECTIONA, 1, 0);
274 ok(!ret, "SendMessage returned: %u\n", ret);
276 if(0)
278 /* Crashes on NT4 */
279 ret = SendMessage(hwnd, BFFM_SETSELECTIONW, 1, 0);
280 ok(!ret, "SendMessage returned: %u\n", ret);
283 ret = SendMessage(hwnd, BFFM_SETSELECTIONA, 0, (LPARAM)selected_folder_pidl);
284 ok(!ret, "SendMessage returned: %u\n", ret);
285 ret = SendMessage(hwnd, BFFM_SETSELECTIONW, 0, (LPARAM)selected_folder_pidl);
286 ok(!ret, "SendMessage returned: %u\n", ret);
288 ret = SendMessage(hwnd, BFFM_SETSELECTIONA, 1, (LPARAM)selected_folder_pidl);
289 ok(!ret, "SendMessage returned: %u\n", ret);
290 ret = SendMessage(hwnd, BFFM_SETSELECTIONW, 1, (LPARAM)selected_folder_pidl);
291 ok(!ret, "SendMessage returned: %u\n", ret);
293 ret = SendMessage(hwnd, BFFM_SETSELECTIONA, 1, (LPARAM)new_folder_name);
294 ok(!ret, "SendMessage returned: %u\n", ret);
295 ret = SendMessage(hwnd, BFFM_SETSELECTIONW, 1, (LPARAM)new_folder_name);
296 ok(!ret, "SendMessage returned: %u\n", ret);
298 SendMessage(hwnd, WM_COMMAND, IDOK, 0);
299 return TRUE;
300 default:
301 return FALSE;
305 static void test_selection(void)
307 HRESULT resCoInit, hr;
308 BROWSEINFO bi;
309 LPITEMIDLIST pidl = NULL;
310 IShellFolder *desktop_object;
311 WCHAR selected_folderW[MAX_PATH];
312 const CHAR title[] = "test_selection";
314 resCoInit = CoInitialize(NULL);
315 if(!(resCoInit == S_OK || resCoInit == S_FALSE))
317 skip("COM could not be initialized %u\n", GetLastError());
318 return;
321 if (!GetCurrentDirectoryW(MAX_PATH, selected_folderW))
323 skip("GetCurrentDirectoryW failed %u\n", GetLastError());
326 /* Initialize browse info struct for SHBrowseForFolder */
327 bi.hwndOwner = NULL;
328 bi.pszDisplayName = NULL;
329 bi.lpszTitle = (LPTSTR) title;
330 bi.lpfn = selection_callback;
332 hr = SHGetDesktopFolder(&desktop_object);
333 ok (SUCCEEDED(hr), "SHGetDesktopFolder failed with hr 0x%08x\n", hr);
334 if (FAILED(hr)) {
335 skip("SHGetDesktopFolder failed - skipping\n");
336 return;
338 desktop_object->lpVtbl->ParseDisplayName(desktop_object, NULL, NULL,
339 selected_folderW, 0UL, &selected_folder_pidl, 0UL);
340 bi.pidlRoot = selected_folder_pidl;
342 /* test without flags */
343 bi.ulFlags = 0;
344 pidl = SHBrowseForFolder(&bi);
346 if (pidl)
347 CoTaskMemFree(pidl);
349 /* test with flag */
350 bi.ulFlags = BIF_NEWDIALOGSTYLE;
351 pidl = SHBrowseForFolder(&bi);
353 if (pidl)
354 CoTaskMemFree(pidl);
356 desktop_object->lpVtbl->Release(desktop_object);
358 CoUninitialize();
361 START_TEST(brsfolder)
363 test_click_make_new_folder_button();
364 test_selection();