[SHELL32_WINETEST] Sync with Wine Staging 4.18. CORE-16441
[reactos.git] / modules / rostests / winetests / shell32 / shlfolder.c
1 /*
2 * Unit test of the IShellFolder functions.
3 *
4 * Copyright 2004 Vitaliy Margolen
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21 #include <stdarg.h>
22 #include <stdio.h>
23
24 #define COBJMACROS
25 #define CONST_VTABLE
26
27 #include "windef.h"
28 #include "winbase.h"
29 #include "wtypes.h"
30 #include "shellapi.h"
31
32
33 #include "shlguid.h"
34 #include "shlobj.h"
35 #include "shobjidl.h"
36 #include "shlwapi.h"
37 #include "ocidl.h"
38 #include "oleauto.h"
39
40 #include "wine/heap.h"
41 #include "wine/test.h"
42
43 #include <initguid.h>
44 DEFINE_GUID(IID_IParentAndItem, 0xB3A4B685, 0xB685, 0x4805, 0x99,0xD9, 0x5D,0xEA,0xD2,0x87,0x32,0x36);
45 DEFINE_GUID(CLSID_ShellDocObjView, 0xe7e4bc40, 0xe76a, 0x11ce, 0xa9,0xbb, 0x00,0xaa,0x00,0x4a,0xe8,0x37);
46
47 static IMalloc *ppM;
48
49 static HRESULT (WINAPI *pSHCreateItemFromIDList)(PCIDLIST_ABSOLUTE pidl, REFIID riid, void **ppv);
50 static HRESULT (WINAPI *pSHCreateItemFromParsingName)(PCWSTR,IBindCtx*,REFIID,void**);
51 static HRESULT (WINAPI *pSHCreateItemFromRelativeName)(IShellItem*,PCWSTR,IBindCtx*,REFIID,void**);
52 static HRESULT (WINAPI *pSHCreateItemInKnownFolder)(REFKNOWNFOLDERID,DWORD,PCWSTR,REFIID,void **);
53 static HRESULT (WINAPI *pSHCreateShellItem)(LPCITEMIDLIST,IShellFolder*,LPCITEMIDLIST,IShellItem**);
54 static HRESULT (WINAPI *pSHCreateShellItemArray)(LPCITEMIDLIST,IShellFolder*,UINT,LPCITEMIDLIST*,IShellItemArray**);
55 static HRESULT (WINAPI *pSHCreateShellItemArrayFromIDLists)(UINT, PCIDLIST_ABSOLUTE*, IShellItemArray**);
56 static HRESULT (WINAPI *pSHCreateShellItemArrayFromDataObject)(IDataObject*, REFIID, void **);
57 static HRESULT (WINAPI *pSHCreateShellItemArrayFromShellItem)(IShellItem*, REFIID, void **);
58 static HRESULT (WINAPI *pSHGetKnownFolderPath)(REFKNOWNFOLDERID,DWORD,HANDLE,PWSTR*);
59 static HRESULT (WINAPI *pSHGetNameFromIDList)(PCIDLIST_ABSOLUTE,SIGDN,PWSTR*);
60 static HRESULT (WINAPI *pSHGetItemFromDataObject)(IDataObject*,DATAOBJ_GET_ITEM_FLAGS,REFIID,void**);
61 static HRESULT (WINAPI *pSHGetIDListFromObject)(IUnknown*, PIDLIST_ABSOLUTE*);
62 static HRESULT (WINAPI *pSHGetItemFromObject)(IUnknown*,REFIID,void**);
63 static BOOL (WINAPI *pIsWow64Process)(HANDLE, PBOOL);
64 static HRESULT (WINAPI *pSHCreateDefaultContextMenu)(const DEFCONTEXTMENU*,REFIID,void**);
65 static BOOL (WINAPI *pSHGetPathFromIDListEx)(PCIDLIST_ABSOLUTE,WCHAR*,DWORD,GPFIDL_FLAGS);
66 #ifdef __REACTOS__
67 typedef SHFOLDERCUSTOMSETTINGSW SHFOLDERCUSTOMSETTINGS, *LPSHFOLDERCUSTOMSETTINGS;
68 #endif
69 static HRESULT (WINAPI *pSHGetSetFolderCustomSettings)(LPSHFOLDERCUSTOMSETTINGS,PCWSTR,DWORD);
70
71 static WCHAR *make_wstr(const char *str)
72 {
73 WCHAR *ret;
74 int len;
75
76 if (!str || !str[0])
77 return NULL;
78
79 len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
80 if(!len || len < 0)
81 return NULL;
82
83 ret = heap_alloc(len * sizeof(WCHAR));
84 if(!ret)
85 return NULL;
86
87 MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
88 return ret;
89 }
90
91 static int strcmp_wa(LPCWSTR strw, const char *stra)
92 {
93 CHAR buf[512];
94 WideCharToMultiByte(CP_ACP, 0, strw, -1, buf, sizeof(buf), NULL, NULL);
95 return lstrcmpA(stra, buf);
96 }
97
98 static void init_function_pointers(void)
99 {
100 HMODULE hmod;
101 HRESULT hr;
102 void *ptr;
103
104 hmod = GetModuleHandleA("shell32.dll");
105
106 #define MAKEFUNC(f) (p##f = (void*)GetProcAddress(hmod, #f))
107 MAKEFUNC(SHCreateItemFromIDList);
108 MAKEFUNC(SHCreateItemFromParsingName);
109 MAKEFUNC(SHCreateItemFromRelativeName);
110 MAKEFUNC(SHCreateItemInKnownFolder);
111 MAKEFUNC(SHCreateShellItem);
112 MAKEFUNC(SHCreateShellItemArray);
113 MAKEFUNC(SHCreateShellItemArrayFromIDLists);
114 MAKEFUNC(SHCreateShellItemArrayFromDataObject);
115 MAKEFUNC(SHCreateShellItemArrayFromShellItem);
116 MAKEFUNC(SHGetKnownFolderPath);
117 MAKEFUNC(SHGetNameFromIDList);
118 MAKEFUNC(SHGetItemFromDataObject);
119 MAKEFUNC(SHGetIDListFromObject);
120 MAKEFUNC(SHGetItemFromObject);
121 MAKEFUNC(SHCreateDefaultContextMenu);
122 MAKEFUNC(SHGetPathFromIDListEx);
123 MAKEFUNC(SHGetSetFolderCustomSettings);
124 #undef MAKEFUNC
125
126 /* test named exports */
127 ptr = GetProcAddress(hmod, "ILFree");
128 ok(broken(ptr == 0) || ptr != 0, "expected named export for ILFree\n");
129 if (ptr)
130 {
131 #define TESTNAMED(f) \
132 ptr = (void*)GetProcAddress(hmod, #f); \
133 ok(ptr != 0, "expected named export for " #f "\n");
134
135 TESTNAMED(ILAppendID);
136 TESTNAMED(ILClone);
137 TESTNAMED(ILCloneFirst);
138 TESTNAMED(ILCombine);
139 TESTNAMED(ILCreateFromPath);
140 TESTNAMED(ILCreateFromPathA);
141 TESTNAMED(ILCreateFromPathW);
142 TESTNAMED(ILFindChild);
143 TESTNAMED(ILFindLastID);
144 TESTNAMED(ILGetNext);
145 TESTNAMED(ILGetSize);
146 TESTNAMED(ILIsEqual);
147 TESTNAMED(ILIsParent);
148 TESTNAMED(ILRemoveLastID);
149 TESTNAMED(ILSaveToStream);
150 #undef TESTNAMED
151 }
152
153 hmod = GetModuleHandleA("kernel32.dll");
154 pIsWow64Process = (void*)GetProcAddress(hmod, "IsWow64Process");
155
156 hr = SHGetMalloc(&ppM);
157 ok(hr == S_OK, "SHGetMalloc failed %08x\n", hr);
158 }
159
160 /* Based on PathAddBackslashW from dlls/shlwapi/path.c */
161 static LPWSTR myPathAddBackslashW( LPWSTR lpszPath )
162 {
163 size_t iLen;
164
165 if (!lpszPath || (iLen = lstrlenW(lpszPath)) >= MAX_PATH)
166 return NULL;
167
168 if (iLen)
169 {
170 lpszPath += iLen;
171 if (lpszPath[-1] != '\\')
172 {
173 *lpszPath++ = '\\';
174 *lpszPath = '\0';
175 }
176 }
177 return lpszPath;
178 }
179
180 static struct
181 {
182 WCHAR path[MAX_PATH];
183 HRESULT hr;
184 int todo;
185 } parse_tests[] = {
186 {{'c',':','\\',0}, S_OK},
187 {{'c',':','\\','\\',0}, E_INVALIDARG, 1},
188 {{'c',':','\\','f','a','k','e',0}, 0x80070002}, /* ERROR_FILE_NOT_FOUND */
189 {{'c',':','f','a','k','e',0}, E_INVALIDARG, 1},
190 {{'c',':','/',0}, E_INVALIDARG, 1},
191 {{'c',':','\\','w','i','n','d','o','w','s',0}, S_OK},
192 {{'c',':','\\','w','i','n','d','o','w','s','\\',0}, S_OK},
193 {{'c',':','\\','w','i','n','d','o','w','s','\\','.',0}, E_INVALIDARG, 1},
194 {{'c',':','\\','w','i','n','d','o','w','s','\\','.','.',0}, E_INVALIDARG, 1},
195 {{'.',0}, E_INVALIDARG, 1},
196 {{'.','.',0}, E_INVALIDARG, 1},
197 {{'t','e','s','t',0}, 0x80070002},
198 {{'t','e','s','t','\\',0}, 0x80070002},
199 {{'s','u','b','\\','d','i','r',0}, 0x80070002},
200 {{'s','u','b','/','d','i','r',0}, E_INVALIDARG, 1},
201 {{'h','t','t','p',':',0}, S_OK, 1},
202 {{'h','t','t','p',':','t','e','s','t',0}, S_OK, 1},
203 {{'h','t','t','p',':','\\','t','e','s','t',0}, S_OK, 1},
204 {{'x','x',':',0}, S_OK, 1},
205 };
206
207 static void test_ParseDisplayName(void)
208 {
209 static WCHAR testdirW[] = {'p','a','r','s','e','t','e','s','t',0};
210 static WCHAR backslashW[] = {'\\',0};
211 WCHAR buffer[MAX_PATH], buffer2[MAX_PATH];
212 IShellFolder *desktop;
213 ITEMIDLIST *pidl;
214 HRESULT hr;
215 BOOL bRes;
216 int i;
217
218 hr = SHGetDesktopFolder(&desktop);
219 ok(hr == S_OK, "Expected SHGetDesktopFolder to return S_OK, got 0x%08x\n", hr);
220
221 hr = IShellFolder_ParseDisplayName(desktop, NULL, NULL, NULL, NULL, &pidl, NULL);
222 ok(hr == E_INVALIDARG, "got %#x\n", hr);
223
224 for (i = 0; i < ARRAY_SIZE(parse_tests); i++)
225 {
226 hr = IShellFolder_ParseDisplayName(desktop, NULL, NULL, parse_tests[i].path, NULL, &pidl, NULL);
227 todo_wine_if(parse_tests[i].todo)
228 ok(hr == parse_tests[i].hr, "%s: expected %#x, got %#x\n",
229 wine_dbgstr_w(parse_tests[i].path), parse_tests[i].hr, hr);
230 if (SUCCEEDED(hr))
231 CoTaskMemFree(pidl);
232 }
233
234 /* I thought that perhaps the DesktopFolder's ParseDisplayName would recognize the
235 * path corresponding to CSIDL_PERSONAL and return a CLSID_MyDocuments PIDL. Turns
236 * out it doesn't. The magic seems to happen in the file dialogs, then. */
237
238 bRes = SHGetSpecialFolderPathW(NULL, buffer, CSIDL_PERSONAL, FALSE);
239 ok(bRes, "SHGetSpecialFolderPath(CSIDL_PERSONAL) failed! %u\n", GetLastError());
240
241 hr = IShellFolder_ParseDisplayName(desktop, NULL, NULL, buffer, NULL, &pidl, 0);
242 ok(hr == S_OK, "DesktopFolder->ParseDisplayName failed. hr = %08x.\n", hr);
243
244 ok(ILFindLastID(pidl)->mkid.abID[0] == 0x31,
245 "Last pidl should be of type PT_FOLDER, but is: %02x\n",
246 ILFindLastID(pidl)->mkid.abID[0]);
247 CoTaskMemFree(pidl);
248
249 /* Relative paths are interpreted relative to the desktop. */
250 GetTempPathW(ARRAY_SIZE(buffer), buffer);
251 GetLongPathNameW(buffer, buffer, ARRAY_SIZE(buffer));
252 SetCurrentDirectoryW(buffer);
253 CreateDirectoryW(testdirW, NULL);
254
255 hr = IShellFolder_ParseDisplayName(desktop, NULL, NULL, testdirW, NULL, &pidl, NULL);
256 ok(hr == 0x80070002, "got %#x\n", hr);
257
258 RemoveDirectoryW(testdirW);
259
260 hr = SHGetSpecialFolderPathW(NULL, buffer, CSIDL_DESKTOP, FALSE);
261 ok(hr == S_FALSE, "got %#x\n", hr);
262 SetCurrentDirectoryW(buffer);
263 CreateDirectoryW(testdirW, NULL);
264
265 hr = IShellFolder_ParseDisplayName(desktop, NULL, NULL, testdirW, NULL, &pidl, NULL);
266 ok(hr == S_OK, "got %#x\n", hr);
267
268 ok(SHGetPathFromIDListW(pidl, buffer2), "SHGetPathFromIDList failed\n");
269 lstrcatW(buffer, backslashW);
270 lstrcatW(buffer, testdirW);
271 ok(!lstrcmpW(buffer, buffer2), "expected %s, got %s\n", wine_dbgstr_w(buffer), wine_dbgstr_w(buffer2));
272
273 RemoveDirectoryW(testdirW);
274 CoTaskMemFree(pidl);
275
276 IShellFolder_Release(desktop);
277 }
278
279 /* creates a file with the specified name for tests */
280 static void CreateTestFile(const CHAR *name)
281 {
282 HANDLE file;
283 DWORD written;
284
285 file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
286 if (file != INVALID_HANDLE_VALUE)
287 {
288 WriteFile(file, name, strlen(name), &written, NULL);
289 WriteFile(file, "\n", strlen("\n"), &written, NULL);
290 CloseHandle(file);
291 }
292 }
293
294
295 /* initializes the tests */
296 static void CreateFilesFolders(void)
297 {
298 CreateDirectoryA(".\\testdir", NULL);
299 CreateDirectoryA(".\\testdir\\test.txt", NULL);
300 CreateTestFile (".\\testdir\\test1.txt ");
301 CreateTestFile (".\\testdir\\test2.txt ");
302 CreateTestFile (".\\testdir\\test3.txt ");
303 CreateDirectoryA(".\\testdir\\testdir2 ", NULL);
304 CreateDirectoryA(".\\testdir\\testdir2\\subdir", NULL);
305 }
306
307 /* cleans after tests */
308 static void Cleanup(void)
309 {
310 DeleteFileA(".\\testdir\\test1.txt");
311 DeleteFileA(".\\testdir\\test2.txt");
312 DeleteFileA(".\\testdir\\test3.txt");
313 RemoveDirectoryA(".\\testdir\\test.txt");
314 RemoveDirectoryA(".\\testdir\\testdir2\\subdir");
315 RemoveDirectoryA(".\\testdir\\testdir2");
316 RemoveDirectoryA(".\\testdir");
317 }
318
319
320 /* perform test */
321 static void test_EnumObjects(IShellFolder *iFolder)
322 {
323 IEnumIDList *iEnumList;
324 LPITEMIDLIST newPIDL, idlArr[10];
325 ULONG NumPIDLs;
326 int i=0, j;
327 HRESULT hr;
328
329 static const WORD iResults [5][5] =
330 {
331 { 0,-1,-1,-1,-1},
332 { 1, 0,-1,-1,-1},
333 { 1, 1, 0,-1,-1},
334 { 1, 1, 1, 0,-1},
335 { 1, 1, 1, 1, 0}
336 };
337
338 #define SFGAO_testfor SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR | SFGAO_CAPABILITYMASK
339 /* Don't test for SFGAO_HASSUBFOLDER since we return real state and native cached */
340 static const ULONG attrs[5] =
341 {
342 SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR,
343 SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR,
344 SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM,
345 SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM,
346 SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM,
347 };
348 static const ULONG full_attrs[5] =
349 {
350 SFGAO_CAPABILITYMASK | SFGAO_STORAGE | SFGAO_STORAGEANCESTOR | SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR,
351 SFGAO_CAPABILITYMASK | SFGAO_STORAGE | SFGAO_STORAGEANCESTOR | SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR,
352 SFGAO_CAPABILITYMASK | SFGAO_STREAM | SFGAO_FILESYSTEM,
353 SFGAO_CAPABILITYMASK | SFGAO_STREAM | SFGAO_FILESYSTEM,
354 SFGAO_CAPABILITYMASK | SFGAO_STREAM | SFGAO_FILESYSTEM,
355 };
356
357 hr = IShellFolder_EnumObjects(iFolder, NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN, &iEnumList);
358 ok(hr == S_OK, "EnumObjects failed %08x\n", hr);
359
360 /* This is to show that, contrary to what is said on MSDN, on IEnumIDList::Next,
361 * the filesystem shellfolders return S_OK even if less than 'celt' items are
362 * returned (in contrast to S_FALSE). We have to do it in a loop since WinXP
363 * only ever returns a single entry per call. */
364 while (IEnumIDList_Next(iEnumList, 10-i, &idlArr[i], &NumPIDLs) == S_OK)
365 i += NumPIDLs;
366 ok (i == 5, "i: %d\n", i);
367
368 hr = IEnumIDList_Release(iEnumList);
369 ok(hr == S_OK, "IEnumIDList_Release failed %08x\n", hr);
370
371 /* Sort them first in case of wrong order from system */
372 for (i=0;i<5;i++) for (j=0;j<5;j++)
373 if ((SHORT)IShellFolder_CompareIDs(iFolder, 0, idlArr[i], idlArr[j]) < 0)
374 {
375 newPIDL = idlArr[i];
376 idlArr[i] = idlArr[j];
377 idlArr[j] = newPIDL;
378 }
379
380 for (i=0;i<5;i++) for (j=0;j<5;j++)
381 {
382 hr = IShellFolder_CompareIDs(iFolder, 0, idlArr[i], idlArr[j]);
383 ok(hr == iResults[i][j], "Got %x expected [%d]-[%d]=%x\n", hr, i, j, iResults[i][j]);
384 }
385
386
387 for (i = 0; i < 5; i++)
388 {
389 SFGAOF flags;
390 #define SFGAO_VISTA SFGAO_DROPTARGET | SFGAO_CANLINK | SFGAO_CANCOPY
391 /* Native returns all flags no matter what we ask for */
392 flags = SFGAO_CANCOPY;
393 hr = IShellFolder_GetAttributesOf(iFolder, 1, (LPCITEMIDLIST*)(idlArr + i), &flags);
394 flags &= SFGAO_testfor;
395 ok(hr == S_OK, "GetAttributesOf returns %08x\n", hr);
396 ok(flags == (attrs[i]) ||
397 flags == ((attrs[i] & ~SFGAO_CAPABILITYMASK) | SFGAO_VISTA), /* Vista and higher */
398 "GetAttributesOf[%i] got %08x, expected %08x\n", i, flags, attrs[i]);
399
400 flags = SFGAO_testfor;
401 hr = IShellFolder_GetAttributesOf(iFolder, 1, (LPCITEMIDLIST*)(idlArr + i), &flags);
402 flags &= SFGAO_testfor;
403 ok(hr == S_OK, "GetAttributesOf returns %08x\n", hr);
404 ok(flags == attrs[i], "GetAttributesOf[%i] got %08x, expected %08x\n", i, flags, attrs[i]);
405
406 flags = ~0u;
407 hr = IShellFolder_GetAttributesOf(iFolder, 1, (LPCITEMIDLIST*)(idlArr + i), &flags);
408 ok(hr == S_OK, "GetAttributesOf returns %08x\n", hr);
409 ok((flags & ~(SFGAO_HASSUBFOLDER|SFGAO_COMPRESSED)) == full_attrs[i], "%d: got %08x expected %08x\n", i, flags, full_attrs[i]);
410 }
411
412 for (i=0;i<5;i++)
413 IMalloc_Free(ppM, idlArr[i]);
414 }
415
416 static void test_BindToObject(void)
417 {
418 HRESULT hr;
419 UINT cChars;
420 IShellFolder *psfDesktop, *psfChild, *psfMyComputer, *psfSystemDir;
421 SHITEMID emptyitem = { 0, { 0 } };
422 LPITEMIDLIST pidlMyComputer, pidlSystemDir, pidl, pidlEmpty = (LPITEMIDLIST)&emptyitem;
423 WCHAR wszSystemDir[MAX_PATH];
424 char szSystemDir[MAX_PATH];
425 char buf[MAX_PATH];
426 WCHAR path[MAX_PATH];
427 CHAR pathA[MAX_PATH];
428 HANDLE hfile;
429 WCHAR wszMyComputer[] = {
430 ':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
431 'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };
432 static const CHAR filename_html[] = "winetest.html";
433 static const CHAR filename_txt[] = "winetest.txt";
434 static const CHAR filename_foo[] = "winetest.foo";
435
436 /* The following tests shows that BindToObject should fail with E_INVALIDARG if called
437 * with an empty pidl. This is tested for Desktop, MyComputer and the FS ShellFolder
438 */
439 hr = SHGetDesktopFolder(&psfDesktop);
440 ok (hr == S_OK, "SHGetDesktopFolder failed! hr = %08x\n", hr);
441 if (hr != S_OK) return;
442
443 hr = IShellFolder_BindToObject(psfDesktop, pidlEmpty, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
444 ok (hr == E_INVALIDARG, "Desktop's BindToObject should fail, when called with empty pidl! hr = %08x\n", hr);
445
446 hr = IShellFolder_BindToObject(psfDesktop, NULL, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
447 ok (hr == E_INVALIDARG, "Desktop's BindToObject should fail, when called with NULL pidl! hr = %08x\n", hr);
448
449 hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyComputer, NULL, &pidlMyComputer, NULL);
450 ok (hr == S_OK, "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08x\n", hr);
451 if (hr != S_OK) {
452 IShellFolder_Release(psfDesktop);
453 return;
454 }
455
456 hr = IShellFolder_BindToObject(psfDesktop, pidlMyComputer, NULL, &IID_IShellFolder, (LPVOID*)&psfMyComputer);
457 ok (hr == S_OK, "Desktop failed to bind to MyComputer object! hr = %08x\n", hr);
458 IShellFolder_Release(psfDesktop);
459 IMalloc_Free(ppM, pidlMyComputer);
460 if (hr != S_OK) return;
461
462 hr = IShellFolder_BindToObject(psfMyComputer, pidlEmpty, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
463 ok (hr == E_INVALIDARG, "MyComputers's BindToObject should fail, when called with empty pidl! hr = %08x\n", hr);
464
465 hr = IShellFolder_BindToObject(psfMyComputer, NULL, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
466 ok (hr == E_INVALIDARG, "MyComputers's BindToObject should fail, when called with NULL pidl! hr = %08x\n", hr);
467
468 cChars = GetSystemDirectoryA(szSystemDir, MAX_PATH);
469 ok (cChars > 0 && cChars < MAX_PATH, "GetSystemDirectoryA failed! LastError: %u\n", GetLastError());
470 if (cChars == 0 || cChars >= MAX_PATH) {
471 IShellFolder_Release(psfMyComputer);
472 return;
473 }
474 MultiByteToWideChar(CP_ACP, 0, szSystemDir, -1, wszSystemDir, MAX_PATH);
475
476 hr = IShellFolder_ParseDisplayName(psfMyComputer, NULL, NULL, wszSystemDir, NULL, &pidlSystemDir, NULL);
477 ok (hr == S_OK, "MyComputers's ParseDisplayName failed to parse the SystemDirectory! hr = %08x\n", hr);
478 if (hr != S_OK) {
479 IShellFolder_Release(psfMyComputer);
480 return;
481 }
482
483 hr = IShellFolder_BindToObject(psfMyComputer, pidlSystemDir, NULL, &IID_IShellFolder, (LPVOID*)&psfSystemDir);
484 ok (hr == S_OK, "MyComputer failed to bind to a FileSystem ShellFolder! hr = %08x\n", hr);
485 IShellFolder_Release(psfMyComputer);
486 IMalloc_Free(ppM, pidlSystemDir);
487 if (hr != S_OK) return;
488
489 hr = IShellFolder_BindToObject(psfSystemDir, pidlEmpty, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
490 ok (hr == E_INVALIDARG,
491 "FileSystem ShellFolder's BindToObject should fail, when called with empty pidl! hr = %08x\n", hr);
492
493 hr = IShellFolder_BindToObject(psfSystemDir, NULL, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
494 ok (hr == E_INVALIDARG,
495 "FileSystem ShellFolder's BindToObject should fail, when called with NULL pidl! hr = %08x\n", hr);
496
497 IShellFolder_Release(psfSystemDir);
498
499 cChars = GetCurrentDirectoryA(MAX_PATH, buf);
500 if(!cChars)
501 {
502 skip("Failed to get current directory, skipping tests.\n");
503 return;
504 }
505 if(buf[cChars-1] != '\\') lstrcatA(buf, "\\");
506
507 SHGetDesktopFolder(&psfDesktop);
508
509 /* Attempt BindToObject on files. */
510
511 /* .html */
512 lstrcpyA(pathA, buf);
513 lstrcatA(pathA, filename_html);
514 hfile = CreateFileA(pathA, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
515 if(hfile != INVALID_HANDLE_VALUE)
516 {
517 CloseHandle(hfile);
518 MultiByteToWideChar(CP_ACP, 0, pathA, -1, path, MAX_PATH);
519 hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, path, NULL, &pidl, NULL);
520 ok(hr == S_OK, "Got 0x%08x\n", hr);
521 if(SUCCEEDED(hr))
522 {
523 hr = IShellFolder_BindToObject(psfDesktop, pidl, NULL, &IID_IShellFolder, (void**)&psfChild);
524 ok(hr == S_OK ||
525 hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), /* XP, W2K3 */
526 "Got 0x%08x\n", hr);
527 if(SUCCEEDED(hr))
528 {
529 IPersist *pp;
530 hr = IShellFolder_QueryInterface(psfChild, &IID_IPersist, (void**)&pp);
531 ok(hr == S_OK, "Got 0x%08x\n", hr);
532 if(SUCCEEDED(hr))
533 {
534 CLSID id;
535 hr = IPersist_GetClassID(pp, &id);
536 ok(hr == S_OK, "Got 0x%08x\n", hr);
537 ok(IsEqualIID(&id, &CLSID_ShellDocObjView), "Unexpected classid %s\n", wine_dbgstr_guid(&id));
538 IPersist_Release(pp);
539 }
540
541 IShellFolder_Release(psfChild);
542 }
543 ILFree(pidl);
544 }
545 DeleteFileA(pathA);
546 }
547 else
548 win_skip("Failed to create .html testfile.\n");
549
550 /* .txt */
551 lstrcpyA(pathA, buf);
552 lstrcatA(pathA, filename_txt);
553 hfile = CreateFileA(pathA, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
554 if(hfile != INVALID_HANDLE_VALUE)
555 {
556 CloseHandle(hfile);
557 MultiByteToWideChar(CP_ACP, 0, pathA, -1, path, MAX_PATH);
558 hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, path, NULL, &pidl, NULL);
559 ok(hr == S_OK, "Got 0x%08x\n", hr);
560 if(SUCCEEDED(hr))
561 {
562 hr = IShellFolder_BindToObject(psfDesktop, pidl, NULL, &IID_IShellFolder, (void**)&psfChild);
563 ok(hr == E_FAIL || /* Vista+ */
564 hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), /* XP, W2K3 */
565 "Got 0x%08x\n", hr);
566 if(SUCCEEDED(hr)) IShellFolder_Release(psfChild);
567 ILFree(pidl);
568 }
569 DeleteFileA(pathA);
570 }
571 else
572 win_skip("Failed to create .txt testfile.\n");
573
574 /* .foo */
575 lstrcpyA(pathA, buf);
576 lstrcatA(pathA, filename_foo);
577 hfile = CreateFileA(pathA, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
578 if(hfile != INVALID_HANDLE_VALUE)
579 {
580 CloseHandle(hfile);
581 MultiByteToWideChar(CP_ACP, 0, pathA, -1, path, MAX_PATH);
582 hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, path, NULL, &pidl, NULL);
583 ok(hr == S_OK, "Got 0x%08x\n", hr);
584 if(SUCCEEDED(hr))
585 {
586 hr = IShellFolder_BindToObject(psfDesktop, pidl, NULL, &IID_IShellFolder, (void**)&psfChild);
587 ok(hr == E_FAIL || /* Vista+ */
588 hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), /* XP, W2K3 */
589 "Got 0x%08x\n", hr);
590 if(SUCCEEDED(hr)) IShellFolder_Release(psfChild);
591 ILFree(pidl);
592 }
593 DeleteFileA(pathA);
594 }
595 else
596 win_skip("Failed to create .foo testfile.\n");
597
598 /* And on the desktop */
599 SHGetSpecialFolderPathA(NULL, pathA, CSIDL_DESKTOP, FALSE);
600 lstrcatA(pathA, "\\");
601 lstrcatA(pathA, filename_html);
602 hfile = CreateFileA(pathA, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
603
604 CloseHandle(hfile);
605 MultiByteToWideChar(CP_ACP, 0, pathA, -1, path, MAX_PATH);
606 hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, path, NULL, &pidl, NULL);
607 ok(hr == S_OK, "Got 0x%08x\n", hr);
608
609 hr = IShellFolder_BindToObject(psfDesktop, pidl, NULL, &IID_IShellFolder, (void **)&psfChild);
610 ok(hr == S_OK ||
611 hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), /* XP, W2K3 */
612 "Got 0x%08x\n", hr);
613 if(SUCCEEDED(hr)) IShellFolder_Release(psfChild);
614 ILFree(pidl);
615 if(!DeleteFileA(pathA))
616 trace("Failed to delete: %d\n", GetLastError());
617
618 SHGetSpecialFolderPathA(NULL, pathA, CSIDL_DESKTOP, FALSE);
619 lstrcatA(pathA, "\\");
620 lstrcatA(pathA, filename_foo);
621 hfile = CreateFileA(pathA, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
622
623 CloseHandle(hfile);
624 MultiByteToWideChar(CP_ACP, 0, pathA, -1, path, MAX_PATH);
625 hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, path, NULL, &pidl, NULL);
626 ok(hr == S_OK, "Got 0x%08x\n", hr);
627
628 hr = IShellFolder_BindToObject(psfDesktop, pidl, NULL, &IID_IShellFolder, (void **)&psfChild);
629 ok(hr == E_FAIL || /* Vista+ */
630 hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), /* XP, W2K3 */
631 "Got 0x%08x\n", hr);
632 if(SUCCEEDED(hr)) IShellFolder_Release(psfChild);
633 ILFree(pidl);
634 DeleteFileA(pathA);
635
636 IShellFolder_Release(psfDesktop);
637 }
638
639 static void test_GetDisplayName(void)
640 {
641 BOOL result;
642 HRESULT hr;
643 HANDLE hTestFile;
644 WCHAR wszTestFile[MAX_PATH], wszTestFile2[MAX_PATH];
645 char szTestFile[MAX_PATH], szTestDir[MAX_PATH];
646 DWORD attr;
647 STRRET strret;
648 LPSHELLFOLDER psfDesktop, psfPersonal;
649 IUnknown *psfFile;
650 SHITEMID emptyitem = { 0, { 0 } };
651 LPITEMIDLIST pidlTestFile, pidlEmpty = (LPITEMIDLIST)&emptyitem;
652 LPCITEMIDLIST pidlLast;
653 static const CHAR szFileName[] = "winetest.foo";
654 static const WCHAR wszFileName[] = { 'w','i','n','e','t','e','s','t','.','f','o','o',0 };
655 static const WCHAR wszDirName[] = { 'w','i','n','e','t','e','s','t',0 };
656
657 /* It's ok to use this fixed path. Call will fail anyway. */
658 WCHAR wszAbsoluteFilename[] = { 'C',':','\\','w','i','n','e','t','e','s','t', 0 };
659 LPITEMIDLIST pidlNew;
660
661 /* I'm trying to figure if there is a functional difference between calling
662 * SHGetPathFromIDListW and calling GetDisplayNameOf(SHGDN_FORPARSING) after
663 * binding to the shellfolder. One thing I thought of was that perhaps
664 * SHGetPathFromIDListW would be able to get the path to a file, which does
665 * not exist anymore, while the other method wouldn't. It turns out there's
666 * no functional difference in this respect.
667 */
668
669 /* First creating a directory in MyDocuments and a file in this directory. */
670 result = SHGetSpecialFolderPathA(NULL, szTestDir, CSIDL_PERSONAL, FALSE);
671 ok(result, "SHGetSpecialFolderPathA failed! Last error: %u\n", GetLastError());
672 if (!result) return;
673
674 /* Use ANSI file functions so this works on Windows 9x */
675 lstrcatA(szTestDir, "\\winetest");
676 CreateDirectoryA(szTestDir, NULL);
677 attr=GetFileAttributesA(szTestDir);
678 if (attr == INVALID_FILE_ATTRIBUTES || !(attr & FILE_ATTRIBUTE_DIRECTORY))
679 {
680 ok(0, "unable to create the '%s' directory\n", szTestDir);
681 return;
682 }
683
684 lstrcpyA(szTestFile, szTestDir);
685 lstrcatA(szTestFile, "\\");
686 lstrcatA(szTestFile, szFileName);
687 hTestFile = CreateFileA(szTestFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
688 ok((hTestFile != INVALID_HANDLE_VALUE), "CreateFileA failed! Last error: %u\n", GetLastError());
689 if (hTestFile == INVALID_HANDLE_VALUE) return;
690 CloseHandle(hTestFile);
691
692 /* Getting an itemidlist for the file. */
693 hr = SHGetDesktopFolder(&psfDesktop);
694 ok(hr == S_OK, "SHGetDesktopFolder failed! hr = %08x\n", hr);
695 if (hr != S_OK) return;
696
697 MultiByteToWideChar(CP_ACP, 0, szTestFile, -1, wszTestFile, MAX_PATH);
698
699 hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszTestFile, NULL, &pidlTestFile, NULL);
700 ok(hr == S_OK, "Desktop->ParseDisplayName failed! hr = %08x\n", hr);
701 if (hr != S_OK) {
702 IShellFolder_Release(psfDesktop);
703 return;
704 }
705
706 pidlLast = ILFindLastID(pidlTestFile);
707 ok(pidlLast->mkid.cb >= 76, "Expected pidl length of at least 76, got %d.\n", pidlLast->mkid.cb);
708 if (pidlLast->mkid.cb >= 28) {
709 ok(!lstrcmpA((CHAR*)&pidlLast->mkid.abID[12], szFileName),
710 "Filename should be stored as ansi-string at this position!\n");
711 }
712 /* WinXP and up store the filenames as both ANSI and UNICODE in the pidls */
713 if (pidlLast->mkid.cb >= 76) {
714 ok(!lstrcmpW((WCHAR*)&pidlLast->mkid.abID[46], wszFileName) ||
715 (pidlLast->mkid.cb >= 94 && !lstrcmpW((WCHAR*)&pidlLast->mkid.abID[64], wszFileName)) || /* Vista */
716 (pidlLast->mkid.cb >= 98 && !lstrcmpW((WCHAR*)&pidlLast->mkid.abID[68], wszFileName)) || /* Win7 */
717 (pidlLast->mkid.cb >= 102 && !lstrcmpW((WCHAR*)&pidlLast->mkid.abID[72], wszFileName)), /* Win8 */
718 "Filename should be stored as wchar-string at this position!\n");
719 }
720
721 /* It seems as if we cannot bind to regular files on windows, but only directories.
722 */
723 hr = IShellFolder_BindToObject(psfDesktop, pidlTestFile, NULL, &IID_IUnknown, (VOID**)&psfFile);
724 ok (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) ||
725 hr == E_NOTIMPL, /* Vista */
726 "hr = %08x\n", hr);
727 if (hr == S_OK) {
728 IUnknown_Release(psfFile);
729 }
730
731 /* Some tests for IShellFolder::SetNameOf */
732 hr = SHBindToParent(pidlTestFile, &IID_IShellFolder, (void **)&psfPersonal, &pidlLast);
733 ok(hr == S_OK, "SHBindToParent failed! hr = %08x\n", hr);
734
735 /* The pidl returned through the last parameter of SetNameOf is a simple one. */
736 hr = IShellFolder_SetNameOf(psfPersonal, NULL, pidlLast, wszDirName, SHGDN_NORMAL, &pidlNew);
737 ok (hr == S_OK, "SetNameOf failed! hr = %08x\n", hr);
738
739 ok (((ITEMIDLIST *)((BYTE *)pidlNew + pidlNew->mkid.cb))->mkid.cb == 0,
740 "pidl returned from SetNameOf should be simple!\n");
741
742 /* Passing an absolute path to SetNameOf fails. The HRESULT code indicates that SetNameOf
743 * is implemented on top of SHFileOperation in WinXP. */
744 hr = IShellFolder_SetNameOf(psfPersonal, NULL, pidlNew, wszAbsoluteFilename, SHGDN_FORPARSING, NULL);
745 ok (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED), "SetNameOf succeeded! hr = %08x\n", hr);
746
747 /* Rename the file back to its original name. SetNameOf ignores the fact, that the
748 * SHGDN flags specify an absolute path. */
749 hr = IShellFolder_SetNameOf(psfPersonal, NULL, pidlNew, wszFileName, SHGDN_FORPARSING, NULL);
750 ok (hr == S_OK, "SetNameOf failed! hr = %08x\n", hr);
751
752 ILFree(pidlNew);
753 IShellFolder_Release(psfPersonal);
754
755 /* Deleting the file and the directory */
756 DeleteFileA(szTestFile);
757 RemoveDirectoryA(szTestDir);
758
759 /* SHGetPathFromIDListW still works, although the file is not present anymore. */
760 result = SHGetPathFromIDListW(pidlTestFile, wszTestFile2);
761 ok (result, "SHGetPathFromIDListW failed! Last error: %u\n", GetLastError());
762 ok (!lstrcmpiW(wszTestFile, wszTestFile2), "SHGetPathFromIDListW returns incorrect path!\n");
763
764 /* SHBindToParent fails, if called with a NULL PIDL. */
765 hr = SHBindToParent(NULL, &IID_IShellFolder, (void **)&psfPersonal, &pidlLast);
766 ok (hr == E_INVALIDARG || broken(hr == E_OUTOFMEMORY) /* XP */,
767 "SHBindToParent(NULL) should fail! hr = %08x\n", hr);
768
769 /* But it succeeds with an empty PIDL. */
770 hr = SHBindToParent(pidlEmpty, &IID_IShellFolder, (void **)&psfPersonal, &pidlLast);
771 ok (hr == S_OK, "SHBindToParent(empty PIDL) should succeed! hr = %08x\n", hr);
772 ok (pidlLast == pidlEmpty, "The last element of an empty PIDL should be the PIDL itself!\n");
773 if (hr == S_OK)
774 IShellFolder_Release(psfPersonal);
775
776 /* Binding to the folder and querying the display name of the file also works. */
777 hr = SHBindToParent(pidlTestFile, &IID_IShellFolder, (void **)&psfPersonal, &pidlLast);
778 ok (hr == S_OK, "SHBindToParent failed! hr = %08x\n", hr);
779 if (hr != S_OK) {
780 IShellFolder_Release(psfDesktop);
781 return;
782 }
783
784 /* This test shows that Windows doesn't allocate a new pidlLast, but returns a pointer into
785 * pidlTestFile (In accordance with MSDN). */
786 ok (ILFindLastID(pidlTestFile) == pidlLast,
787 "SHBindToParent doesn't return the last id of the pidl param!\n");
788
789 hr = IShellFolder_GetDisplayNameOf(psfPersonal, pidlLast, SHGDN_FORPARSING, &strret);
790 ok (hr == S_OK, "Personal->GetDisplayNameOf failed! hr = %08x\n", hr);
791 if (hr != S_OK) {
792 IShellFolder_Release(psfDesktop);
793 IShellFolder_Release(psfPersonal);
794 return;
795 }
796
797 hr = StrRetToBufW(&strret, pidlLast, wszTestFile2, MAX_PATH);
798 ok (hr == S_OK, "StrRetToBufW failed! hr = %08x\n", hr);
799 ok (!lstrcmpiW(wszTestFile, wszTestFile2), "GetDisplayNameOf returns incorrect path!\n");
800
801 ILFree(pidlTestFile);
802 IShellFolder_Release(psfDesktop);
803 IShellFolder_Release(psfPersonal);
804 }
805
806 static void test_CallForAttributes(void)
807 {
808 HKEY hKey;
809 LONG lResult;
810 HRESULT hr;
811 DWORD dwSize;
812 LPSHELLFOLDER psfDesktop;
813 LPITEMIDLIST pidlMyDocuments;
814 DWORD dwAttributes, dwCallForAttributes, dwOrigAttributes, dwOrigCallForAttributes;
815 static const WCHAR wszAttributes[] = { 'A','t','t','r','i','b','u','t','e','s',0 };
816 static const WCHAR wszCallForAttributes[] = {
817 'C','a','l','l','F','o','r','A','t','t','r','i','b','u','t','e','s',0 };
818 static const WCHAR wszMyDocumentsKey[] = {
819 'C','L','S','I','D','\\','{','4','5','0','D','8','F','B','A','-','A','D','2','5','-',
820 '1','1','D','0','-','9','8','A','8','-','0','8','0','0','3','6','1','B','1','1','0','3','}',
821 '\\','S','h','e','l','l','F','o','l','d','e','r',0 };
822 WCHAR wszMyDocuments[] = {
823 ':',':','{','4','5','0','D','8','F','B','A','-','A','D','2','5','-','1','1','D','0','-',
824 '9','8','A','8','-','0','8','0','0','3','6','1','B','1','1','0','3','}',0 };
825
826 /* For the root of a namespace extension, the attributes are not queried by binding
827 * to the object and calling GetAttributesOf. Instead, the attributes are read from
828 * the registry value HKCR/CLSID/{...}/ShellFolder/Attributes. This is documented on MSDN.
829 *
830 * The MyDocuments shellfolder on WinXP has a HKCR/CLSID/{...}/ShellFolder/CallForAttributes
831 * value. It seems that if the folder is queried for one of the flags set in CallForAttributes,
832 * the shell does bind to the folder object and calls GetAttributesOf. This is not documented
833 * on MSDN. This test is meant to document the observed behaviour on WinXP SP2.
834 */
835 hr = SHGetDesktopFolder(&psfDesktop);
836 ok (hr == S_OK, "SHGetDesktopFolder failed! hr = %08x\n", hr);
837 if (hr != S_OK) return;
838
839 hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyDocuments, NULL,
840 &pidlMyDocuments, NULL);
841 ok (hr == S_OK,
842 "Desktop's ParseDisplayName failed to parse MyDocuments's CLSID! hr = %08x\n", hr);
843 if (hr != S_OK) {
844 IShellFolder_Release(psfDesktop);
845 return;
846 }
847
848 dwAttributes = 0xffffffff;
849 hr = IShellFolder_GetAttributesOf(psfDesktop, 1,
850 (LPCITEMIDLIST*)&pidlMyDocuments, &dwAttributes);
851 ok (hr == S_OK, "Desktop->GetAttributesOf(MyDocuments) failed! hr = %08x\n", hr);
852
853 /* We need the following setup (as observed on WinXP SP2), for the tests to make sense. */
854 ok (dwAttributes & SFGAO_FILESYSTEM, "SFGAO_FILESYSTEM attribute is not set for MyDocuments!\n");
855 ok (!(dwAttributes & SFGAO_ISSLOW), "SFGAO_ISSLOW attribute is set for MyDocuments!\n");
856 ok (!(dwAttributes & SFGAO_GHOSTED), "SFGAO_GHOSTED attribute is set for MyDocuments!\n");
857
858 /* We don't have the MyDocuments shellfolder in wine yet, and thus we don't have the registry
859 * key. So the test will return at this point, if run on wine.
860 */
861 lResult = RegOpenKeyExW(HKEY_CLASSES_ROOT, wszMyDocumentsKey, 0, KEY_WRITE|KEY_READ, &hKey);
862 ok (lResult == ERROR_SUCCESS ||
863 lResult == ERROR_ACCESS_DENIED,
864 "RegOpenKeyEx failed! result: %08x\n", lResult);
865 if (lResult != ERROR_SUCCESS) {
866 if (lResult == ERROR_ACCESS_DENIED)
867 skip("Not enough rights to open the registry key\n");
868 IMalloc_Free(ppM, pidlMyDocuments);
869 IShellFolder_Release(psfDesktop);
870 return;
871 }
872
873 /* Query MyDocuments' Attributes value, to be able to restore it later. */
874 dwSize = sizeof(DWORD);
875 lResult = RegQueryValueExW(hKey, wszAttributes, NULL, NULL, (LPBYTE)&dwOrigAttributes, &dwSize);
876 ok (lResult == ERROR_SUCCESS, "RegQueryValueEx failed! result: %08x\n", lResult);
877 if (lResult != ERROR_SUCCESS) {
878 RegCloseKey(hKey);
879 IMalloc_Free(ppM, pidlMyDocuments);
880 IShellFolder_Release(psfDesktop);
881 return;
882 }
883
884 /* Query MyDocuments' CallForAttributes value, to be able to restore it later. */
885 dwSize = sizeof(DWORD);
886 lResult = RegQueryValueExW(hKey, wszCallForAttributes, NULL, NULL,
887 (LPBYTE)&dwOrigCallForAttributes, &dwSize);
888 ok (lResult == ERROR_SUCCESS, "RegQueryValueEx failed! result: %08x\n", lResult);
889 if (lResult != ERROR_SUCCESS) {
890 RegCloseKey(hKey);
891 IMalloc_Free(ppM, pidlMyDocuments);
892 IShellFolder_Release(psfDesktop);
893 return;
894 }
895
896 /* Define via the Attributes value that MyDocuments attributes are SFGAO_ISSLOW and
897 * SFGAO_GHOSTED and that MyDocuments should be called for the SFGAO_ISSLOW and
898 * SFGAO_FILESYSTEM attributes. */
899 dwAttributes = SFGAO_ISSLOW|SFGAO_GHOSTED;
900 RegSetValueExW(hKey, wszAttributes, 0, REG_DWORD, (LPBYTE)&dwAttributes, sizeof(DWORD));
901 dwCallForAttributes = SFGAO_ISSLOW|SFGAO_FILESYSTEM;
902 RegSetValueExW(hKey, wszCallForAttributes, 0, REG_DWORD,
903 (LPBYTE)&dwCallForAttributes, sizeof(DWORD));
904
905 /* Although it is not set in CallForAttributes, the SFGAO_GHOSTED flag is reset by
906 * GetAttributesOf. It seems that once there is a single attribute queried, for which
907 * CallForAttributes is set, all flags are taken from the GetAttributesOf call and
908 * the flags in Attributes are ignored.
909 */
910 dwAttributes = SFGAO_ISSLOW|SFGAO_GHOSTED|SFGAO_FILESYSTEM;
911 hr = IShellFolder_GetAttributesOf(psfDesktop, 1,
912 (LPCITEMIDLIST*)&pidlMyDocuments, &dwAttributes);
913 ok (hr == S_OK, "Desktop->GetAttributesOf(MyDocuments) failed! hr = %08x\n", hr);
914 if (hr == S_OK)
915 ok (dwAttributes == SFGAO_FILESYSTEM,
916 "Desktop->GetAttributes(MyDocuments) returned unexpected attributes: %08x\n",
917 dwAttributes);
918
919 /* Restore MyDocuments' original Attributes and CallForAttributes registry values */
920 RegSetValueExW(hKey, wszAttributes, 0, REG_DWORD, (LPBYTE)&dwOrigAttributes, sizeof(DWORD));
921 RegSetValueExW(hKey, wszCallForAttributes, 0, REG_DWORD,
922 (LPBYTE)&dwOrigCallForAttributes, sizeof(DWORD));
923 RegCloseKey(hKey);
924 IMalloc_Free(ppM, pidlMyDocuments);
925 IShellFolder_Release(psfDesktop);
926 }
927
928 static void test_GetAttributesOf(void)
929 {
930 HRESULT hr;
931 LPSHELLFOLDER psfDesktop, psfMyComputer;
932 SHITEMID emptyitem = { 0, { 0 } };
933 LPCITEMIDLIST pidlEmpty = (LPCITEMIDLIST)&emptyitem;
934 LPITEMIDLIST pidlMyComputer;
935 DWORD dwFlags;
936 static const DWORD desktopFlags = SFGAO_STORAGE | SFGAO_HASPROPSHEET | SFGAO_STORAGEANCESTOR |
937 SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_HASSUBFOLDER;
938 static const DWORD myComputerFlags = SFGAO_CANRENAME | SFGAO_CANDELETE | SFGAO_HASPROPSHEET |
939 SFGAO_DROPTARGET | SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER;
940 WCHAR wszMyComputer[] = {
941 ':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
942 'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };
943 char cCurrDirA [MAX_PATH] = {0};
944 WCHAR cCurrDirW [MAX_PATH];
945 static WCHAR cTestDirW[] = {'t','e','s','t','d','i','r',0};
946 IShellFolder *IDesktopFolder, *testIShellFolder;
947 ITEMIDLIST *newPIDL;
948 int len;
949
950 hr = SHGetDesktopFolder(&psfDesktop);
951 ok (hr == S_OK, "SHGetDesktopFolder failed! hr = %08x\n", hr);
952 if (hr != S_OK) return;
953
954 /* The Desktop attributes can be queried with a single empty itemidlist, .. */
955 dwFlags = 0xffffffff;
956 hr = IShellFolder_GetAttributesOf(psfDesktop, 1, &pidlEmpty, &dwFlags);
957 ok (hr == S_OK, "Desktop->GetAttributesOf(empty pidl) failed! hr = %08x\n", hr);
958 ok (dwFlags == desktopFlags, "Wrong Desktop attributes: %08x\n", dwFlags);
959
960 /* .. or with no itemidlist at all. */
961 dwFlags = 0xffffffff;
962 hr = IShellFolder_GetAttributesOf(psfDesktop, 0, NULL, &dwFlags);
963 ok (hr == S_OK, "Desktop->GetAttributesOf(NULL) failed! hr = %08x\n", hr);
964 ok (dwFlags == desktopFlags, "Wrong Desktop attributes: %08x\n", dwFlags);
965
966 /* Testing the attributes of the MyComputer shellfolder */
967 hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyComputer, NULL, &pidlMyComputer, NULL);
968 ok (hr == S_OK, "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08x\n", hr);
969 if (hr != S_OK) {
970 IShellFolder_Release(psfDesktop);
971 return;
972 }
973
974 /* Windows sets the SFGAO_CANLINK flag, when MyComputer is queried via the Desktop
975 * folder object. It doesn't do this, if MyComputer is queried directly (see below).
976 */
977 dwFlags = 0xffffffff;
978 hr = IShellFolder_GetAttributesOf(psfDesktop, 1, (LPCITEMIDLIST*)&pidlMyComputer, &dwFlags);
979 ok (hr == S_OK, "Desktop->GetAttributesOf(MyComputer) failed! hr = %08x\n", hr);
980 todo_wine
981 ok (dwFlags == (myComputerFlags | SFGAO_CANLINK), "Wrong MyComputer attributes: %08x\n", dwFlags);
982
983 hr = IShellFolder_BindToObject(psfDesktop, pidlMyComputer, NULL, &IID_IShellFolder, (LPVOID*)&psfMyComputer);
984 ok (hr == S_OK, "Desktop failed to bind to MyComputer object! hr = %08x\n", hr);
985 IShellFolder_Release(psfDesktop);
986 IMalloc_Free(ppM, pidlMyComputer);
987 if (hr != S_OK) return;
988
989 hr = IShellFolder_GetAttributesOf(psfMyComputer, 1, &pidlEmpty, &dwFlags);
990 todo_wine
991 ok (hr == E_INVALIDARG, "MyComputer->GetAttributesOf(empty pidl) should fail! hr = %08x\n", hr);
992
993 dwFlags = 0xffffffff;
994 hr = IShellFolder_GetAttributesOf(psfMyComputer, 0, NULL, &dwFlags);
995 ok (hr == S_OK, "MyComputer->GetAttributesOf(NULL) failed! hr = %08x\n", hr);
996 todo_wine
997 ok (dwFlags == myComputerFlags, "Wrong MyComputer attributes: %08x\n", dwFlags);
998
999 IShellFolder_Release(psfMyComputer);
1000
1001 GetCurrentDirectoryA(MAX_PATH, cCurrDirA);
1002 len = lstrlenA(cCurrDirA);
1003
1004 if (len == 0) {
1005 win_skip("GetCurrentDirectoryA returned empty string. Skipping test_GetAttributesOf\n");
1006 return;
1007 }
1008 if (len > 3 && cCurrDirA[len-1] == '\\')
1009 cCurrDirA[len-1] = 0;
1010
1011 /* create test directory */
1012 CreateFilesFolders();
1013
1014 MultiByteToWideChar(CP_ACP, 0, cCurrDirA, -1, cCurrDirW, MAX_PATH);
1015
1016 hr = SHGetDesktopFolder(&IDesktopFolder);
1017 ok(hr == S_OK, "SHGetDesktopfolder failed %08x\n", hr);
1018
1019 hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cCurrDirW, NULL, &newPIDL, 0);
1020 ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
1021
1022 hr = IShellFolder_BindToObject(IDesktopFolder, newPIDL, NULL, (REFIID)&IID_IShellFolder, (LPVOID *)&testIShellFolder);
1023 ok(hr == S_OK, "BindToObject failed %08x\n", hr);
1024
1025 IMalloc_Free(ppM, newPIDL);
1026
1027 /* get relative PIDL */
1028 hr = IShellFolder_ParseDisplayName(testIShellFolder, NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
1029 ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
1030
1031 /* test the shell attributes of the test directory using the relative PIDL */
1032 dwFlags = SFGAO_FOLDER;
1033 hr = IShellFolder_GetAttributesOf(testIShellFolder, 1, (LPCITEMIDLIST*)&newPIDL, &dwFlags);
1034 ok (hr == S_OK, "Desktop->GetAttributesOf() failed! hr = %08x\n", hr);
1035 ok ((dwFlags&SFGAO_FOLDER), "Wrong directory attribute for relative PIDL: %08x\n", dwFlags);
1036
1037 /* free memory */
1038 IMalloc_Free(ppM, newPIDL);
1039
1040 /* append testdirectory name to path */
1041 if (cCurrDirA[len-1] == '\\')
1042 cCurrDirA[len-1] = 0;
1043 lstrcatA(cCurrDirA, "\\testdir");
1044 MultiByteToWideChar(CP_ACP, 0, cCurrDirA, -1, cCurrDirW, MAX_PATH);
1045
1046 hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cCurrDirW, NULL, &newPIDL, 0);
1047 ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
1048
1049 /* test the shell attributes of the test directory using the absolute PIDL */
1050 dwFlags = SFGAO_FOLDER;
1051 hr = IShellFolder_GetAttributesOf(IDesktopFolder, 1, (LPCITEMIDLIST*)&newPIDL, &dwFlags);
1052 ok (hr == S_OK, "Desktop->GetAttributesOf() failed! hr = %08x\n", hr);
1053 ok ((dwFlags&SFGAO_FOLDER), "Wrong directory attribute for absolute PIDL: %08x\n", dwFlags);
1054
1055 /* free memory */
1056 IMalloc_Free(ppM, newPIDL);
1057
1058 IShellFolder_Release(testIShellFolder);
1059
1060 Cleanup();
1061
1062 IShellFolder_Release(IDesktopFolder);
1063 }
1064
1065 static void test_SHGetPathFromIDList(void)
1066 {
1067 SHITEMID emptyitem = { 0, { 0 } };
1068 LPCITEMIDLIST pidlEmpty = (LPCITEMIDLIST)&emptyitem;
1069 LPITEMIDLIST pidlMyComputer;
1070 WCHAR wszPath[MAX_PATH], wszDesktop[MAX_PATH];
1071 BOOL result;
1072 HRESULT hr;
1073 LPSHELLFOLDER psfDesktop;
1074 WCHAR wszMyComputer[] = {
1075 ':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
1076 'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };
1077 WCHAR wszFileName[MAX_PATH];
1078 LPITEMIDLIST pidlTestFile;
1079 HANDLE hTestFile;
1080 STRRET strret;
1081 static WCHAR wszTestFile[] = {
1082 'w','i','n','e','t','e','s','t','.','f','o','o',0 };
1083 LPITEMIDLIST pidlPrograms;
1084
1085 /* Calling SHGetPathFromIDListW with no pidl should return the empty string */
1086 wszPath[0] = 'a';
1087 wszPath[1] = '\0';
1088 result = SHGetPathFromIDListW(NULL, wszPath);
1089 ok(!result, "Expected failure\n");
1090 ok(!wszPath[0], "Expected empty string\n");
1091
1092 /* Calling SHGetPathFromIDListW with an empty pidl should return the desktop folder's path. */
1093 result = SHGetSpecialFolderPathW(NULL, wszDesktop, CSIDL_DESKTOP, FALSE);
1094 ok(result, "SHGetSpecialFolderPathW(CSIDL_DESKTOP) failed! Last error: %u\n", GetLastError());
1095 if (!result) return;
1096
1097 result = SHGetPathFromIDListW(pidlEmpty, wszPath);
1098 ok(result, "SHGetPathFromIDListW failed! Last error: %u\n", GetLastError());
1099 if (!result) return;
1100 ok(!lstrcmpiW(wszDesktop, wszPath), "SHGetPathFromIDListW didn't return desktop path for empty pidl!\n");
1101
1102 /* MyComputer does not map to a filesystem path. SHGetPathFromIDListW should fail. */
1103 hr = SHGetDesktopFolder(&psfDesktop);
1104 ok (hr == S_OK, "SHGetDesktopFolder failed! hr = %08x\n", hr);
1105 if (hr != S_OK) return;
1106
1107 hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyComputer, NULL, &pidlMyComputer, NULL);
1108 ok (hr == S_OK, "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08x\n", hr);
1109 if (hr != S_OK) {
1110 IShellFolder_Release(psfDesktop);
1111 return;
1112 }
1113
1114 SetLastError(0xdeadbeef);
1115 wszPath[0] = 'a';
1116 wszPath[1] = '\0';
1117 result = SHGetPathFromIDListW(pidlMyComputer, wszPath);
1118 ok (!result, "SHGetPathFromIDListW succeeded where it shouldn't!\n");
1119 ok (GetLastError()==0xdeadbeef ||
1120 GetLastError()==ERROR_SUCCESS, /* Vista and higher */
1121 "Unexpected last error from SHGetPathFromIDListW: %u\n", GetLastError());
1122 ok (!wszPath[0], "Expected empty path\n");
1123 if (result) {
1124 IShellFolder_Release(psfDesktop);
1125 return;
1126 }
1127
1128 IMalloc_Free(ppM, pidlMyComputer);
1129
1130 result = SHGetSpecialFolderPathW(NULL, wszFileName, CSIDL_DESKTOPDIRECTORY, FALSE);
1131 ok(result, "SHGetSpecialFolderPathW failed! Last error: %u\n", GetLastError());
1132 if (!result) {
1133 IShellFolder_Release(psfDesktop);
1134 return;
1135 }
1136 myPathAddBackslashW(wszFileName);
1137 lstrcatW(wszFileName, wszTestFile);
1138 hTestFile = CreateFileW(wszFileName, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL);
1139 ok(hTestFile != INVALID_HANDLE_VALUE, "CreateFileW failed! Last error: %u\n", GetLastError());
1140 if (hTestFile == INVALID_HANDLE_VALUE) {
1141 IShellFolder_Release(psfDesktop);
1142 return;
1143 }
1144 CloseHandle(hTestFile);
1145
1146 hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszTestFile, NULL, &pidlTestFile, NULL);
1147 ok (hr == S_OK, "Desktop's ParseDisplayName failed to parse filename hr = %08x\n", hr);
1148 if (hr != S_OK) {
1149 IShellFolder_Release(psfDesktop);
1150 DeleteFileW(wszFileName);
1151 IMalloc_Free(ppM, pidlTestFile);
1152 return;
1153 }
1154
1155 /* This test is to show that the Desktop shellfolder prepends the CSIDL_DESKTOPDIRECTORY
1156 * path for files placed on the desktop, if called with SHGDN_FORPARSING. */
1157 hr = IShellFolder_GetDisplayNameOf(psfDesktop, pidlTestFile, SHGDN_FORPARSING, &strret);
1158 ok (hr == S_OK, "Desktop's GetDisplayNamfOf failed! hr = %08x\n", hr);
1159 IShellFolder_Release(psfDesktop);
1160 DeleteFileW(wszFileName);
1161 if (hr != S_OK) {
1162 IMalloc_Free(ppM, pidlTestFile);
1163 return;
1164 }
1165 StrRetToBufW(&strret, pidlTestFile, wszPath, MAX_PATH);
1166 ok(0 == lstrcmpW(wszFileName, wszPath),
1167 "Desktop->GetDisplayNameOf(pidlTestFile, SHGDN_FORPARSING) "
1168 "returned incorrect path for file placed on desktop\n");
1169
1170 result = SHGetPathFromIDListW(pidlTestFile, wszPath);
1171 ok(result, "SHGetPathFromIDListW failed! Last error: %u\n", GetLastError());
1172 ok(0 == lstrcmpW(wszFileName, wszPath), "SHGetPathFromIDListW returned incorrect path for file placed on desktop\n");
1173
1174 if (pSHGetPathFromIDListEx)
1175 {
1176 result = pSHGetPathFromIDListEx(pidlEmpty, wszPath, MAX_PATH, SFGAO_FILESYSTEM);
1177 ok(result, "SHGetPathFromIDListEx failed: %u\n", GetLastError());
1178 ok(!lstrcmpiW(wszDesktop, wszPath), "Unexpected SHGetPathFromIDListEx result %s, expected %s\n",
1179 wine_dbgstr_w(wszPath), wine_dbgstr_w(wszDesktop));
1180
1181 result = pSHGetPathFromIDListEx(pidlTestFile, wszPath, MAX_PATH, SFGAO_FILESYSTEM);
1182 ok(result, "SHGetPathFromIDListEx failed: %u\n", GetLastError());
1183 ok(!lstrcmpiW(wszFileName, wszPath), "Unexpected SHGetPathFromIDListEx result %s, expected %s\n",
1184 wine_dbgstr_w(wszPath), wine_dbgstr_w(wszFileName));
1185
1186 SetLastError(0xdeadbeef);
1187 memset(wszPath, 0x55, sizeof(wszPath));
1188 result = pSHGetPathFromIDListEx(pidlTestFile, wszPath, 5, SFGAO_FILESYSTEM);
1189 ok(!result, "SHGetPathFromIDListEx returned: %x(%u)\n", result, GetLastError());
1190
1191 SetLastError(0xdeadbeef);
1192 memset(wszPath, 0x55, sizeof(wszPath));
1193 result = pSHGetPathFromIDListEx(pidlEmpty, wszPath, 5, SFGAO_FILESYSTEM);
1194 ok(!result, "SHGetPathFromIDListEx returned: %x(%u)\n", result, GetLastError());
1195 }
1196 else
1197 win_skip("SHGetPathFromIDListEx not available\n");
1198
1199 IMalloc_Free(ppM, pidlTestFile);
1200
1201 /* Test if we can get the path from the start menu "program files" PIDL. */
1202 hr = SHGetSpecialFolderLocation(NULL, CSIDL_PROGRAM_FILES, &pidlPrograms);
1203 ok(hr == S_OK, "SHGetFolderLocation failed: 0x%08x\n", hr);
1204
1205 SetLastError(0xdeadbeef);
1206 result = SHGetPathFromIDListW(pidlPrograms, wszPath);
1207 IMalloc_Free(ppM, pidlPrograms);
1208 ok(result, "SHGetPathFromIDListW failed\n");
1209 }
1210
1211 static void test_EnumObjects_and_CompareIDs(void)
1212 {
1213 ITEMIDLIST *newPIDL;
1214 IShellFolder *IDesktopFolder, *testIShellFolder;
1215 char cCurrDirA [MAX_PATH] = {0};
1216 static const CHAR cTestDirA[] = "\\testdir";
1217 WCHAR cTestDirW[MAX_PATH];
1218 int len;
1219 HRESULT hr;
1220
1221 GetCurrentDirectoryA(MAX_PATH, cCurrDirA);
1222 len = lstrlenA(cCurrDirA);
1223
1224 if(len == 0) {
1225 win_skip("GetCurrentDirectoryA returned empty string. Skipping test_EnumObjects_and_CompareIDs\n");
1226 return;
1227 }
1228 if(cCurrDirA[len-1] == '\\')
1229 cCurrDirA[len-1] = 0;
1230
1231 lstrcatA(cCurrDirA, cTestDirA);
1232 MultiByteToWideChar(CP_ACP, 0, cCurrDirA, -1, cTestDirW, MAX_PATH);
1233
1234 hr = SHGetDesktopFolder(&IDesktopFolder);
1235 ok(hr == S_OK, "SHGetDesktopfolder failed %08x\n", hr);
1236
1237 CreateFilesFolders();
1238
1239 hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
1240 ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
1241
1242 hr = IShellFolder_BindToObject(IDesktopFolder, newPIDL, NULL, (REFIID)&IID_IShellFolder, (LPVOID *)&testIShellFolder);
1243 ok(hr == S_OK, "BindToObject failed %08x\n", hr);
1244
1245 test_EnumObjects(testIShellFolder);
1246
1247 IShellFolder_Release(testIShellFolder);
1248
1249 Cleanup();
1250
1251 IMalloc_Free(ppM, newPIDL);
1252
1253 IShellFolder_Release(IDesktopFolder);
1254 }
1255
1256 /* A simple implementation of an IPropertyBag, which returns fixed values for
1257 * 'Target' and 'Attributes' properties.
1258 */
1259 static HRESULT WINAPI InitPropertyBag_IPropertyBag_QueryInterface(IPropertyBag *iface, REFIID riid,
1260 void **ppvObject)
1261 {
1262 if (!ppvObject)
1263 return E_INVALIDARG;
1264
1265 if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IPropertyBag, riid)) {
1266 *ppvObject = iface;
1267 } else {
1268 ok (FALSE, "InitPropertyBag asked for unknown interface!\n");
1269 return E_NOINTERFACE;
1270 }
1271
1272 IPropertyBag_AddRef(iface);
1273 return S_OK;
1274 }
1275
1276 static ULONG WINAPI InitPropertyBag_IPropertyBag_AddRef(IPropertyBag *iface) {
1277 return 2;
1278 }
1279
1280 static ULONG WINAPI InitPropertyBag_IPropertyBag_Release(IPropertyBag *iface) {
1281 return 1;
1282 }
1283
1284 static HRESULT WINAPI InitPropertyBag_IPropertyBag_Read(IPropertyBag *iface, LPCOLESTR pszPropName,
1285 VARIANT *pVar, IErrorLog *pErrorLog)
1286 {
1287 static const WCHAR wszTargetSpecialFolder[] = {
1288 'T','a','r','g','e','t','S','p','e','c','i','a','l','F','o','l','d','e','r',0 };
1289 static const WCHAR wszTarget[] = {
1290 'T','a','r','g','e','t',0 };
1291 static const WCHAR wszAttributes[] = {
1292 'A','t','t','r','i','b','u','t','e','s',0 };
1293 static const WCHAR wszResolveLinkFlags[] = {
1294 'R','e','s','o','l','v','e','L','i','n','k','F','l','a','g','s',0 };
1295 static const WCHAR wszTargetKnownFolder[] = {
1296 'T','a','r','g','e','t','K','n','o','w','n','F','o','l','d','e','r',0 };
1297 static const WCHAR wszCLSID[] = {
1298 'C','L','S','I','D',0 };
1299
1300 if (!lstrcmpW(pszPropName, wszTargetSpecialFolder)) {
1301 ok(V_VT(pVar) == VT_I4, "Wrong variant type for 'TargetSpecialFolder' property!\n");
1302 return E_INVALIDARG;
1303 }
1304
1305 if (!lstrcmpW(pszPropName, wszResolveLinkFlags))
1306 {
1307 ok(V_VT(pVar) == VT_UI4, "Wrong variant type for 'ResolveLinkFlags' property!\n");
1308 return E_INVALIDARG;
1309 }
1310
1311 if (!lstrcmpW(pszPropName, wszTarget)) {
1312 WCHAR wszPath[MAX_PATH];
1313 BOOL result;
1314
1315 ok(V_VT(pVar) == VT_BSTR, "Wrong variant type for 'Target' property!\n");
1316 if (V_VT(pVar) != VT_BSTR) return E_INVALIDARG;
1317
1318 result = SHGetSpecialFolderPathW(NULL, wszPath, CSIDL_DESKTOPDIRECTORY, FALSE);
1319 ok(result, "SHGetSpecialFolderPathW(DESKTOPDIRECTORY) failed! %u\n", GetLastError());
1320 if (!result) return E_INVALIDARG;
1321
1322 V_BSTR(pVar) = SysAllocString(wszPath);
1323 return S_OK;
1324 }
1325
1326 if (!lstrcmpW(pszPropName, wszAttributes)) {
1327 ok(V_VT(pVar) == VT_UI4, "Wrong variant type for 'Attributes' property!\n");
1328 if (V_VT(pVar) != VT_UI4) return E_INVALIDARG;
1329 V_UI4(pVar) = SFGAO_FOLDER|SFGAO_HASSUBFOLDER|SFGAO_FILESYSANCESTOR|
1330 SFGAO_CANRENAME|SFGAO_FILESYSTEM;
1331 return S_OK;
1332 }
1333
1334 if (!lstrcmpW(pszPropName, wszTargetKnownFolder)) {
1335 ok(V_VT(pVar) == VT_BSTR, "Wrong variant type for 'TargetKnownFolder' property!\n");
1336 /* TODO */
1337 return E_INVALIDARG;
1338 }
1339
1340 if (!lstrcmpW(pszPropName, wszCLSID)) {
1341 ok(V_VT(pVar) == VT_EMPTY, "Wrong variant type for 'CLSID' property!\n");
1342 /* TODO */
1343 return E_INVALIDARG;
1344 }
1345
1346 ok(FALSE, "PropertyBag was asked for unknown property %s (vt=%d)!\n", wine_dbgstr_w(pszPropName), V_VT(pVar));
1347 return E_INVALIDARG;
1348 }
1349
1350 static HRESULT WINAPI InitPropertyBag_IPropertyBag_Write(IPropertyBag *iface, LPCOLESTR pszPropName,
1351 VARIANT *pVar)
1352 {
1353 ok(FALSE, "Unexpected call to IPropertyBag_Write\n");
1354 return E_NOTIMPL;
1355 }
1356
1357 static const IPropertyBagVtbl InitPropertyBag_IPropertyBagVtbl = {
1358 InitPropertyBag_IPropertyBag_QueryInterface,
1359 InitPropertyBag_IPropertyBag_AddRef,
1360 InitPropertyBag_IPropertyBag_Release,
1361 InitPropertyBag_IPropertyBag_Read,
1362 InitPropertyBag_IPropertyBag_Write
1363 };
1364
1365 static struct IPropertyBag InitPropertyBag = {
1366 &InitPropertyBag_IPropertyBagVtbl
1367 };
1368
1369 static void test_FolderShortcut(void) {
1370 IPersistPropertyBag *pPersistPropertyBag;
1371 IShellFolder *pShellFolder, *pDesktopFolder;
1372 IPersistFolder3 *pPersistFolder3;
1373 HRESULT hr;
1374 STRRET strret;
1375 WCHAR wszDesktopPath[MAX_PATH], wszBuffer[MAX_PATH];
1376 BOOL result;
1377 CLSID clsid;
1378 LPITEMIDLIST pidlCurrentFolder, pidlWineTestFolder, pidlSubFolder;
1379 HKEY hShellExtKey;
1380 WCHAR wszWineTestFolder[] = {
1381 ':',':','{','9','B','3','5','2','E','B','F','-','2','7','6','5','-','4','5','C','1','-',
1382 'B','4','C','6','-','8','5','C','C','7','F','7','A','B','C','6','4','}',0 };
1383 WCHAR wszShellExtKey[] = { 'S','o','f','t','w','a','r','e','\\',
1384 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
1385 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
1386 'E','x','p','l','o','r','e','r','\\','D','e','s','k','t','o','p','\\',
1387 'N','a','m','e','S','p','a','c','e','\\',
1388 '{','9','b','3','5','2','e','b','f','-','2','7','6','5','-','4','5','c','1','-',
1389 'b','4','c','6','-','8','5','c','c','7','f','7','a','b','c','6','4','}',0 };
1390
1391 WCHAR wszSomeSubFolder[] = { 'S','u','b','F','o','l','d','e','r', 0};
1392 static const GUID CLSID_UnixDosFolder =
1393 {0x9d20aae8, 0x0625, 0x44b0, {0x9c, 0xa7, 0x71, 0x88, 0x9c, 0x22, 0x54, 0xd9}};
1394
1395 /* These tests basically show, that CLSID_FolderShortcuts are initialized
1396 * via their IPersistPropertyBag interface. And that the target folder
1397 * is taken from the IPropertyBag's 'Target' property.
1398 */
1399 hr = CoCreateInstance(&CLSID_FolderShortcut, NULL, CLSCTX_INPROC_SERVER,
1400 &IID_IPersistPropertyBag, (LPVOID*)&pPersistPropertyBag);
1401 if (hr == REGDB_E_CLASSNOTREG) {
1402 win_skip("CLSID_FolderShortcut is not implemented\n");
1403 return;
1404 }
1405 ok (hr == S_OK, "CoCreateInstance failed! hr = 0x%08x\n", hr);
1406 if (hr != S_OK) return;
1407
1408 hr = IPersistPropertyBag_Load(pPersistPropertyBag, &InitPropertyBag, NULL);
1409 ok(hr == S_OK, "IPersistPropertyBag_Load failed! hr = %08x\n", hr);
1410 if (hr != S_OK) {
1411 IPersistPropertyBag_Release(pPersistPropertyBag);
1412 return;
1413 }
1414
1415 hr = IPersistPropertyBag_QueryInterface(pPersistPropertyBag, &IID_IShellFolder,
1416 (LPVOID*)&pShellFolder);
1417 IPersistPropertyBag_Release(pPersistPropertyBag);
1418 ok(hr == S_OK, "IPersistPropertyBag_QueryInterface(IShellFolder) failed! hr = %08x\n", hr);
1419 if (hr != S_OK) return;
1420
1421 hr = IShellFolder_GetDisplayNameOf(pShellFolder, NULL, SHGDN_FORPARSING, &strret);
1422 ok(hr == S_OK || broken(hr == E_INVALIDARG) /* win10 */,
1423 "IShellFolder_GetDisplayNameOf(NULL) failed! hr = %08x\n", hr);
1424 if (hr != S_OK) {
1425 IShellFolder_Release(pShellFolder);
1426 return;
1427 }
1428
1429 result = SHGetSpecialFolderPathW(NULL, wszDesktopPath, CSIDL_DESKTOPDIRECTORY, FALSE);
1430 ok(result, "SHGetSpecialFolderPathW(CSIDL_DESKTOPDIRECTORY) failed! %u\n", GetLastError());
1431 if (!result) return;
1432
1433 StrRetToBufW(&strret, NULL, wszBuffer, MAX_PATH);
1434 ok(!lstrcmpiW(wszDesktopPath, wszBuffer), "FolderShortcut returned incorrect folder!\n");
1435
1436 hr = IShellFolder_QueryInterface(pShellFolder, &IID_IPersistFolder3, (LPVOID*)&pPersistFolder3);
1437 IShellFolder_Release(pShellFolder);
1438 ok(hr == S_OK, "IShellFolder_QueryInterface(IID_IPersistFolder3 failed! hr = 0x%08x\n", hr);
1439 if (hr != S_OK) return;
1440
1441 hr = IPersistFolder3_GetClassID(pPersistFolder3, &clsid);
1442 ok(hr == S_OK, "IPersistFolder3_GetClassID failed! hr=0x%08x\n", hr);
1443 ok(IsEqualCLSID(&clsid, &CLSID_FolderShortcut), "Unexpected CLSID!\n");
1444
1445 hr = IPersistFolder3_GetCurFolder(pPersistFolder3, &pidlCurrentFolder);
1446 todo_wine ok(hr == S_FALSE, "IPersistFolder3_GetCurFolder failed! hr=0x%08x\n", hr);
1447 ok(!pidlCurrentFolder, "IPersistFolder3_GetCurFolder should return a NULL pidl!\n");
1448
1449 /* For FolderShortcut objects, the Initialize method initialized the folder's position in the
1450 * shell namespace. The target folder, read from the property bag above, remains untouched.
1451 * The following tests show this: The itemidlist for some imaginary shellfolder object
1452 * is created and the FolderShortcut is initialized with it. GetCurFolder now returns this
1453 * itemidlist, but GetDisplayNameOf still returns the path from above.
1454 */
1455 hr = SHGetDesktopFolder(&pDesktopFolder);
1456 ok (hr == S_OK, "SHGetDesktopFolder failed! hr = %08x\n", hr);
1457 if (hr != S_OK) return;
1458
1459 /* Temporarily register WineTestFolder as a shell namespace extension at the Desktop.
1460 * Otherwise ParseDisplayName fails on WinXP with E_INVALIDARG */
1461 RegCreateKeyW(HKEY_CURRENT_USER, wszShellExtKey, &hShellExtKey);
1462 RegCloseKey(hShellExtKey);
1463 hr = IShellFolder_ParseDisplayName(pDesktopFolder, NULL, NULL, wszWineTestFolder, NULL,
1464 &pidlWineTestFolder, NULL);
1465 RegDeleteKeyW(HKEY_CURRENT_USER, wszShellExtKey);
1466 IShellFolder_Release(pDesktopFolder);
1467 ok (hr == S_OK, "IShellFolder::ParseDisplayName failed! hr = %08x\n", hr);
1468 if (hr != S_OK) return;
1469
1470 hr = IPersistFolder3_Initialize(pPersistFolder3, pidlWineTestFolder);
1471 ok (hr == S_OK, "IPersistFolder3::Initialize failed! hr = %08x\n", hr);
1472 if (hr != S_OK) {
1473 IPersistFolder3_Release(pPersistFolder3);
1474 ILFree(pidlWineTestFolder);
1475 return;
1476 }
1477
1478 hr = IPersistFolder3_GetCurFolder(pPersistFolder3, &pidlCurrentFolder);
1479 ok(hr == S_OK, "IPersistFolder3_GetCurFolder failed! hr=0x%08x\n", hr);
1480 ok(ILIsEqual(pidlCurrentFolder, pidlWineTestFolder),
1481 "IPersistFolder3_GetCurFolder should return pidlWineTestFolder!\n");
1482 ILFree(pidlCurrentFolder);
1483 ILFree(pidlWineTestFolder);
1484
1485 hr = IPersistFolder3_QueryInterface(pPersistFolder3, &IID_IShellFolder, (LPVOID*)&pShellFolder);
1486 IPersistFolder3_Release(pPersistFolder3);
1487 ok(hr == S_OK, "IPersistFolder3_QueryInterface(IShellFolder) failed! hr = %08x\n", hr);
1488 if (hr != S_OK) return;
1489
1490 hr = IShellFolder_GetDisplayNameOf(pShellFolder, NULL, SHGDN_FORPARSING, &strret);
1491 ok(hr == S_OK, "IShellFolder_GetDisplayNameOf(NULL) failed! hr = %08x\n", hr);
1492 if (hr != S_OK) {
1493 IShellFolder_Release(pShellFolder);
1494 return;
1495 }
1496
1497 StrRetToBufW(&strret, NULL, wszBuffer, MAX_PATH);
1498 ok(!lstrcmpiW(wszDesktopPath, wszBuffer), "FolderShortcut returned incorrect folder!\n");
1499
1500 /* Next few lines are meant to show that children of FolderShortcuts are not FolderShortcuts,
1501 * but ShellFSFolders. */
1502 myPathAddBackslashW(wszDesktopPath);
1503 lstrcatW(wszDesktopPath, wszSomeSubFolder);
1504 if (!CreateDirectoryW(wszDesktopPath, NULL)) {
1505 IShellFolder_Release(pShellFolder);
1506 return;
1507 }
1508
1509 hr = IShellFolder_ParseDisplayName(pShellFolder, NULL, NULL, wszSomeSubFolder, NULL,
1510 &pidlSubFolder, NULL);
1511 RemoveDirectoryW(wszDesktopPath);
1512 ok (hr == S_OK, "IShellFolder::ParseDisplayName failed! hr = %08x\n", hr);
1513 if (hr != S_OK) {
1514 IShellFolder_Release(pShellFolder);
1515 return;
1516 }
1517
1518 hr = IShellFolder_BindToObject(pShellFolder, pidlSubFolder, NULL, &IID_IPersistFolder3,
1519 (LPVOID*)&pPersistFolder3);
1520 IShellFolder_Release(pShellFolder);
1521 ILFree(pidlSubFolder);
1522 ok (hr == S_OK, "IShellFolder::BindToObject failed! hr = %08x\n", hr);
1523 if (hr != S_OK)
1524 return;
1525
1526 /* On windows, we expect CLSID_ShellFSFolder. On wine we relax this constraint
1527 * a little bit and also allow CLSID_UnixDosFolder. */
1528 hr = IPersistFolder3_GetClassID(pPersistFolder3, &clsid);
1529 ok(hr == S_OK, "IPersistFolder3_GetClassID failed! hr=0x%08x\n", hr);
1530 ok(IsEqualCLSID(&clsid, &CLSID_ShellFSFolder) || IsEqualCLSID(&clsid, &CLSID_UnixDosFolder),
1531 "IPersistFolder3::GetClassID returned unexpected CLSID!\n");
1532
1533 IPersistFolder3_Release(pPersistFolder3);
1534 }
1535
1536 #include "pshpack1.h"
1537 struct FileStructA {
1538 BYTE type;
1539 BYTE dummy;
1540 DWORD dwFileSize;
1541 WORD uFileDate; /* In our current implementation this is */
1542 WORD uFileTime; /* FileTimeToDosDate(WIN32_FIND_DATA->ftLastWriteTime) */
1543 WORD uFileAttribs;
1544 CHAR szName[1];
1545 };
1546
1547 struct FileStructW {
1548 WORD cbLen; /* Length of this element. */
1549 BYTE abFooBar1[6]; /* Beyond any recognition. */
1550 WORD uDate; /* FileTimeToDosDate(WIN32_FIND_DATA->ftCreationTime)? */
1551 WORD uTime; /* (this is currently speculation) */
1552 WORD uDate2; /* FileTimeToDosDate(WIN32_FIND_DATA->ftLastAccessTime)? */
1553 WORD uTime2; /* (this is currently speculation) */
1554 BYTE abFooBar2[4]; /* Beyond any recognition. */
1555 WCHAR wszName[1]; /* The long filename in unicode. */
1556 /* Just for documentation: Right after the unicode string: */
1557 WORD cbOffset; /* FileStructW's offset from the beginning of the SHITMEID.
1558 * SHITEMID->cb == uOffset + cbLen */
1559 };
1560 #include "poppack.h"
1561
1562 static void test_ITEMIDLIST_format(void) {
1563 WCHAR wszPersonal[MAX_PATH];
1564 LPSHELLFOLDER psfDesktop, psfPersonal;
1565 LPITEMIDLIST pidlPersonal, pidlFile;
1566 HANDLE hFile;
1567 HRESULT hr;
1568 BOOL bResult;
1569 WCHAR wszFile[3][17] = { { 'e','v','e','n','_',0 }, { 'o','d','d','_',0 },
1570 { 'l','o','n','g','e','r','_','t','h','a','n','.','8','_','3',0 } };
1571 int i;
1572
1573 bResult = SHGetSpecialFolderPathW(NULL, wszPersonal, CSIDL_PERSONAL, FALSE);
1574 ok(bResult, "SHGetSpecialFolderPathW failed! Last error: %u\n", GetLastError());
1575 if (!bResult) return;
1576
1577 SetLastError(0xdeadbeef);
1578 bResult = SetCurrentDirectoryW(wszPersonal);
1579 if (!bResult && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) {
1580 win_skip("Most W-calls are not implemented\n");
1581 return;
1582 }
1583 ok(bResult, "SetCurrentDirectory failed! Last error: %u\n", GetLastError());
1584 if (!bResult) return;
1585
1586 hr = SHGetDesktopFolder(&psfDesktop);
1587 ok(hr == S_OK, "SHGetDesktopFolder failed! hr: %08x\n", hr);
1588 if (hr != S_OK) return;
1589
1590 hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszPersonal, NULL, &pidlPersonal, NULL);
1591 ok(hr == S_OK, "psfDesktop->ParseDisplayName failed! hr = %08x\n", hr);
1592 if (hr != S_OK) {
1593 IShellFolder_Release(psfDesktop);
1594 return;
1595 }
1596
1597 hr = IShellFolder_BindToObject(psfDesktop, pidlPersonal, NULL, &IID_IShellFolder,
1598 (LPVOID*)&psfPersonal);
1599 IShellFolder_Release(psfDesktop);
1600 ILFree(pidlPersonal);
1601 ok(hr == S_OK, "psfDesktop->BindToObject failed! hr = %08x\n", hr);
1602 if (hr != S_OK) return;
1603
1604 for (i=0; i<3; i++) {
1605 CHAR szFile[MAX_PATH];
1606 struct FileStructA *pFileStructA;
1607 WORD cbOffset;
1608
1609 WideCharToMultiByte(CP_ACP, 0, wszFile[i], -1, szFile, MAX_PATH, NULL, NULL);
1610
1611 hFile = CreateFileW(wszFile[i], GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_FLAG_WRITE_THROUGH, NULL);
1612 ok(hFile != INVALID_HANDLE_VALUE, "CreateFile failed! (%u)\n", GetLastError());
1613 if (hFile == INVALID_HANDLE_VALUE) {
1614 IShellFolder_Release(psfPersonal);
1615 return;
1616 }
1617 CloseHandle(hFile);
1618
1619 hr = IShellFolder_ParseDisplayName(psfPersonal, NULL, NULL, wszFile[i], NULL, &pidlFile, NULL);
1620 DeleteFileW(wszFile[i]);
1621 ok(hr == S_OK, "psfPersonal->ParseDisplayName failed! hr: %08x\n", hr);
1622 if (hr != S_OK) {
1623 IShellFolder_Release(psfPersonal);
1624 return;
1625 }
1626
1627 pFileStructA = (struct FileStructA *)pidlFile->mkid.abID;
1628 ok(pFileStructA->type == 0x32, "PIDLTYPE should be 0x32!\n");
1629 ok(pFileStructA->dummy == 0x00, "Dummy Byte should be 0x00!\n");
1630 ok(pFileStructA->dwFileSize == 0, "Filesize should be zero!\n");
1631
1632 if (i < 2) /* First two file names are already in valid 8.3 format */
1633 ok(!strcmp(szFile, (CHAR*)&pidlFile->mkid.abID[12]), "Wrong file name!\n");
1634 else
1635 /* WinXP stores a derived 8.3 dos name (LONGER~1.8_3) here. We probably
1636 * can't implement this correctly, since unix filesystems don't support
1637 * this nasty short/long filename stuff. So we'll probably stay with our
1638 * current habit of storing the long filename here, which seems to work
1639 * just fine. */
1640 todo_wine
1641 ok(pidlFile->mkid.abID[18] == '~', "Should be derived 8.3 name!\n");
1642
1643 if (i == 0) /* First file name has an even number of chars. No need for alignment. */
1644 ok(pidlFile->mkid.abID[12 + strlen(szFile) + 1] != '\0',
1645 "Alignment byte, where there shouldn't be!\n");
1646
1647 if (i == 1) /* Second file name has an uneven number of chars => alignment byte */
1648 ok(pidlFile->mkid.abID[12 + strlen(szFile) + 1] == '\0',
1649 "There should be an alignment byte, but isn't!\n");
1650
1651 /* The offset of the FileStructW member is stored as a WORD at the end of the pidl. */
1652 cbOffset = *(WORD*)(((LPBYTE)pidlFile)+pidlFile->mkid.cb-sizeof(WORD));
1653 ok ((cbOffset >= sizeof(struct FileStructA) &&
1654 cbOffset <= pidlFile->mkid.cb - sizeof(struct FileStructW)),
1655 "Wrong offset value (%d) stored at the end of the PIDL\n", cbOffset);
1656
1657 if (cbOffset >= sizeof(struct FileStructA) &&
1658 cbOffset <= pidlFile->mkid.cb - sizeof(struct FileStructW))
1659 {
1660 struct FileStructW *pFileStructW = (struct FileStructW *)(((LPBYTE)pidlFile)+cbOffset);
1661 WCHAR *name = pFileStructW->wszName;
1662
1663 ok(pidlFile->mkid.cb == cbOffset + pFileStructW->cbLen,
1664 "FileStructW's offset and length should add up to the PIDL's length!\n");
1665
1666 if (pidlFile->mkid.cb == cbOffset + pFileStructW->cbLen) {
1667 /* Since we just created the file, time of creation,
1668 * time of last access and time of last write access just be the same.
1669 * These tests seem to fail sometimes (on WinXP), if the test is run again shortly
1670 * after the first run. I do remember something with NTFS keeping the creation time
1671 * if a file is deleted and then created again within a couple of seconds or so.
1672 * Might be the reason. */
1673 ok (pFileStructA->uFileDate == pFileStructW->uDate &&
1674 pFileStructA->uFileTime == pFileStructW->uTime,
1675 "Last write time should match creation time!\n");
1676
1677 /* On FAT filesystems the last access time is midnight
1678 local time, so the values of uDate2 and uTime2 will
1679 depend on the local timezone. If the times are exactly
1680 equal then the dates should be identical for both FAT
1681 and NTFS as no timezone is more than 1 day away from UTC.
1682 */
1683 if (pFileStructA->uFileTime == pFileStructW->uTime2)
1684 {
1685 ok (pFileStructA->uFileDate == pFileStructW->uDate2,
1686 "Last write date and time should match last access date and time!\n");
1687 }
1688 else
1689 {
1690 /* Filesystem may be FAT. Check date within 1 day
1691 and seconds are zero. */
1692 trace ("Filesystem may be FAT. Performing less strict atime test.\n");
1693 ok ((pFileStructW->uTime2 & 0x1F) == 0,
1694 "Last access time on FAT filesystems should have zero seconds.\n");
1695 /* TODO: Perform check for date being within one day.*/
1696 }
1697
1698 ok (!lstrcmpW(wszFile[i], name) ||
1699 !lstrcmpW(wszFile[i], name + 9) || /* Vista */
1700 !lstrcmpW(wszFile[i], name + 11) || /* Win7 */
1701 !lstrcmpW(wszFile[i], name + 13), /* Win8 */
1702 "The filename should be stored in unicode at this position!\n");
1703 }
1704 }
1705
1706 ILFree(pidlFile);
1707 }
1708
1709 IShellFolder_Release(psfPersonal);
1710 }
1711
1712 static void test_SHGetFolderPathA(void)
1713 {
1714 static const BOOL is_win64 = sizeof(void *) > sizeof(int);
1715 BOOL is_wow64;
1716 char path[MAX_PATH];
1717 char path_x86[MAX_PATH];
1718 char path_key[MAX_PATH];
1719 HRESULT hr;
1720 HKEY key;
1721
1722 if (!pIsWow64Process || !pIsWow64Process( GetCurrentProcess(), &is_wow64 )) is_wow64 = FALSE;
1723
1724 hr = SHGetFolderPathA( 0, CSIDL_PROGRAM_FILES, 0, SHGFP_TYPE_CURRENT, path );
1725 ok( hr == S_OK, "SHGetFolderPathA failed %x\n", hr );
1726 hr = SHGetFolderPathA( 0, CSIDL_PROGRAM_FILESX86, 0, SHGFP_TYPE_CURRENT, path_x86 );
1727 if (hr == E_FAIL)
1728 {
1729 win_skip( "Program Files (x86) not supported\n" );
1730 return;
1731 }
1732 ok( hr == S_OK, "SHGetFolderPathA failed %x\n", hr );
1733 if (is_win64)
1734 {
1735 ok( lstrcmpiA( path, path_x86 ), "paths are identical '%s'\n", path );
1736 ok( strstr( path, "x86" ) == NULL, "64-bit path '%s' contains x86\n", path );
1737 ok( strstr( path_x86, "x86" ) != NULL, "32-bit path '%s' doesn't contain x86\n", path_x86 );
1738 }
1739 else
1740 {
1741 ok( !lstrcmpiA( path, path_x86 ), "paths differ '%s' != '%s'\n", path, path_x86 );
1742 if (is_wow64)
1743 ok( strstr( path, "x86" ) != NULL, "32-bit path '%s' doesn't contain x86\n", path );
1744 else
1745 ok( strstr( path, "x86" ) == NULL, "32-bit path '%s' contains x86\n", path );
1746 }
1747 if (!RegOpenKeyA( HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion", &key ))
1748 {
1749 DWORD type, count = sizeof(path_x86);
1750 if (!RegQueryValueExA( key, "ProgramFilesDir (x86)", NULL, &type, (BYTE *)path_key, &count ))
1751 {
1752 ok( is_win64 || is_wow64, "ProgramFilesDir (x86) exists on 32-bit setup\n" );
1753 ok( !lstrcmpiA( path_key, path_x86 ), "paths differ '%s' != '%s'\n", path_key, path_x86 );
1754 }
1755 else ok( !is_win64 && !is_wow64, "ProgramFilesDir (x86) should exist on 64-bit setup\n" );
1756 RegCloseKey( key );
1757 }
1758
1759 hr = SHGetFolderPathA( 0, CSIDL_PROGRAM_FILES_COMMON, 0, SHGFP_TYPE_CURRENT, path );
1760 ok( hr == S_OK, "SHGetFolderPathA failed %x\n", hr );
1761 hr = SHGetFolderPathA( 0, CSIDL_PROGRAM_FILES_COMMONX86, 0, SHGFP_TYPE_CURRENT, path_x86 );
1762 if (hr == E_FAIL)
1763 {
1764 win_skip( "Common Files (x86) not supported\n" );
1765 return;
1766 }
1767 ok( hr == S_OK, "SHGetFolderPathA failed %x\n", hr );
1768 if (is_win64)
1769 {
1770 ok( lstrcmpiA( path, path_x86 ), "paths are identical '%s'\n", path );
1771 ok( strstr( path, "x86" ) == NULL, "64-bit path '%s' contains x86\n", path );
1772 ok( strstr( path_x86, "x86" ) != NULL, "32-bit path '%s' doesn't contain x86\n", path_x86 );
1773 }
1774 else
1775 {
1776 ok( !lstrcmpiA( path, path_x86 ), "paths differ '%s' != '%s'\n", path, path_x86 );
1777 if (is_wow64)
1778 ok( strstr( path, "x86" ) != NULL, "32-bit path '%s' doesn't contain x86\n", path );
1779 else
1780 ok( strstr( path, "x86" ) == NULL, "32-bit path '%s' contains x86\n", path );
1781 }
1782 if (!RegOpenKeyA( HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion", &key ))
1783 {
1784 DWORD type, count = sizeof(path_x86);
1785 if (!RegQueryValueExA( key, "CommonFilesDir (x86)", NULL, &type, (BYTE *)path_key, &count ))
1786 {
1787 ok( is_win64 || is_wow64, "CommonFilesDir (x86) exists on 32-bit setup\n" );
1788 ok( !lstrcmpiA( path_key, path_x86 ), "paths differ '%s' != '%s'\n", path_key, path_x86 );
1789 }
1790 else ok( !is_win64 && !is_wow64, "CommonFilesDir (x86) should exist on 64-bit setup\n" );
1791 }
1792 }
1793
1794 static void test_SHGetFolderPathAndSubDirA(void)
1795 {
1796 HRESULT ret;
1797 BOOL delret;
1798 DWORD dwret;
1799 int i;
1800 static const char wine[] = "wine";
1801 static const char winetemp[] = "wine\\temp";
1802 static char appdata[MAX_PATH];
1803 static char testpath[MAX_PATH];
1804 static char toolongpath[MAX_PATH+1];
1805
1806 if(FAILED(SHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, appdata)))
1807 {
1808 win_skip("SHGetFolderPathA failed for CSIDL_LOCAL_APPDATA!\n");
1809 return;
1810 }
1811
1812 sprintf(testpath, "%s\\%s", appdata, winetemp);
1813 delret = RemoveDirectoryA(testpath);
1814 if(!delret && (ERROR_PATH_NOT_FOUND != GetLastError()) ) {
1815 win_skip("RemoveDirectoryA(%s) failed with error %u\n", testpath, GetLastError());
1816 return;
1817 }
1818
1819 sprintf(testpath, "%s\\%s", appdata, wine);
1820 delret = RemoveDirectoryA(testpath);
1821 if(!delret && (ERROR_PATH_NOT_FOUND != GetLastError()) && (ERROR_FILE_NOT_FOUND != GetLastError())) {
1822 win_skip("RemoveDirectoryA(%s) failed with error %u\n", testpath, GetLastError());
1823 return;
1824 }
1825
1826 /* test invalid second parameter */
1827 ret = SHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | 0xff, NULL, SHGFP_TYPE_CURRENT, wine, testpath);
1828 ok(E_INVALIDARG == ret, "expected E_INVALIDARG, got %x\n", ret);
1829
1830 /* test fourth parameter */
1831 ret = SHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, 2, winetemp, testpath);
1832 switch(ret) {
1833 case S_OK: /* winvista */
1834 ok(!strncmp(appdata, testpath, strlen(appdata)),
1835 "expected %s to start with %s\n", testpath, appdata);
1836 ok(!lstrcmpA(&testpath[1 + strlen(appdata)], winetemp),
1837 "expected %s to end with %s\n", testpath, winetemp);
1838 break;
1839 case E_INVALIDARG: /* winxp, win2k3 */
1840 break;
1841 default:
1842 ok(0, "expected S_OK or E_INVALIDARG, got %x\n", ret);
1843 }
1844
1845 /* test fifth parameter */
1846 testpath[0] = '\0';
1847 ret = SHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, NULL, testpath);
1848 ok(S_OK == ret, "expected S_OK, got %x\n", ret);
1849 ok(!lstrcmpA(appdata, testpath), "expected %s, got %s\n", appdata, testpath);
1850
1851 testpath[0] = '\0';
1852 ret = SHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, "", testpath);
1853 ok(S_OK == ret, "expected S_OK, got %x\n", ret);
1854 ok(!lstrcmpA(appdata, testpath), "expected %s, got %s\n", appdata, testpath);
1855
1856 testpath[0] = '\0';
1857 ret = SHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, "\\", testpath);
1858 ok(S_OK == ret, "expected S_OK, got %x\n", ret);
1859 ok(!lstrcmpA(appdata, testpath), "expected %s, got %s\n", appdata, testpath);
1860
1861 for(i=0; i< MAX_PATH; i++)
1862 toolongpath[i] = '0' + i % 10;
1863 toolongpath[MAX_PATH] = '\0';
1864 ret = SHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, toolongpath, testpath);
1865 ok(HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE) == ret,
1866 "expected %x, got %x\n", HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE), ret);
1867
1868 testpath[0] = '\0';
1869 ret = SHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, wine, NULL);
1870 ok((S_OK == ret) || (E_INVALIDARG == ret), "expected S_OK or E_INVALIDARG, got %x\n", ret);
1871
1872 /* test a not existing path */
1873 testpath[0] = '\0';
1874 ret = SHGetFolderPathAndSubDirA(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, winetemp, testpath);
1875 ok(HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == ret,
1876 "expected %x, got %x\n", HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND), ret);
1877
1878 /* create a directory inside a not existing directory */
1879 testpath[0] = '\0';
1880 ret = SHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_CREATE | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, winetemp, testpath);
1881 ok(S_OK == ret, "expected S_OK, got %x\n", ret);
1882 ok(!strncmp(appdata, testpath, strlen(appdata)),
1883 "expected %s to start with %s\n", testpath, appdata);
1884 ok(!lstrcmpA(&testpath[1 + strlen(appdata)], winetemp),
1885 "expected %s to end with %s\n", testpath, winetemp);
1886 dwret = GetFileAttributesA(testpath);
1887 ok(FILE_ATTRIBUTE_DIRECTORY | dwret, "expected %x to contain FILE_ATTRIBUTE_DIRECTORY\n", dwret);
1888
1889 /* cleanup */
1890 sprintf(testpath, "%s\\%s", appdata, winetemp);
1891 RemoveDirectoryA(testpath);
1892 sprintf(testpath, "%s\\%s", appdata, wine);
1893 RemoveDirectoryA(testpath);
1894 }
1895
1896 static void test_LocalizedNames(void)
1897 {
1898 static char cCurrDirA[MAX_PATH];
1899 WCHAR cCurrDirW[MAX_PATH], tempbufW[25];
1900 IShellFolder *IDesktopFolder, *testIShellFolder;
1901 ITEMIDLIST *newPIDL;
1902 int len;
1903 HRESULT hr;
1904 static char resourcefile[MAX_PATH];
1905 DWORD res;
1906 HANDLE file;
1907 STRRET strret;
1908 BOOL ret;
1909
1910 static const char desktopini_contents1[] =
1911 "[.ShellClassInfo]\r\n"
1912 "LocalizedResourceName=@";
1913 static const char desktopini_contents2[] =
1914 ",-1\r\n";
1915 static WCHAR foldernameW[] = {'t','e','s','t','f','o','l','d','e','r',0};
1916 static const WCHAR folderdisplayW[] = {'F','o','l','d','e','r',' ','N','a','m','e',' ','R','e','s','o','u','r','c','e',0};
1917
1918 /* create folder with desktop.ini and localized name in GetModuleFileNameA(NULL) */
1919 CreateDirectoryA(".\\testfolder", NULL);
1920
1921 SetFileAttributesA(".\\testfolder", GetFileAttributesA(".\\testfolder")|FILE_ATTRIBUTE_SYSTEM);
1922
1923 GetModuleFileNameA(NULL, resourcefile, MAX_PATH);
1924
1925 file = CreateFileA(".\\testfolder\\desktop.ini", GENERIC_WRITE, 0, NULL,
1926 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
1927 ok(file != INVALID_HANDLE_VALUE, "CreateFileA failed %i\n", GetLastError());
1928 ret = WriteFile(file, desktopini_contents1, strlen(desktopini_contents1), &res, NULL) &&
1929 WriteFile(file, resourcefile, strlen(resourcefile), &res, NULL) &&
1930 WriteFile(file, desktopini_contents2, strlen(desktopini_contents2), &res, NULL);
1931 ok(ret, "WriteFile failed %i\n", GetLastError());
1932 CloseHandle(file);
1933
1934 /* get IShellFolder for parent */
1935 GetCurrentDirectoryA(MAX_PATH, cCurrDirA);
1936 len = lstrlenA(cCurrDirA);
1937
1938 if (len == 0) {
1939 win_skip("GetCurrentDirectoryA returned empty string. Skipping test_LocalizedNames\n");
1940 goto cleanup;
1941 }
1942 if(cCurrDirA[len-1] == '\\')
1943 cCurrDirA[len-1] = 0;
1944
1945 MultiByteToWideChar(CP_ACP, 0, cCurrDirA, -1, cCurrDirW, MAX_PATH);
1946
1947 hr = SHGetDesktopFolder(&IDesktopFolder);
1948 ok(hr == S_OK, "SHGetDesktopfolder failed %08x\n", hr);
1949
1950 hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cCurrDirW, NULL, &newPIDL, 0);
1951 ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
1952
1953 hr = IShellFolder_BindToObject(IDesktopFolder, newPIDL, NULL, (REFIID)&IID_IShellFolder, (LPVOID *)&testIShellFolder);
1954 ok(hr == S_OK, "BindToObject failed %08x\n", hr);
1955
1956 IMalloc_Free(ppM, newPIDL);
1957
1958 /* windows reads the display name from the resource */
1959 hr = IShellFolder_ParseDisplayName(testIShellFolder, NULL, NULL, foldernameW, NULL, &newPIDL, 0);
1960 ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
1961
1962 hr = IShellFolder_GetDisplayNameOf(testIShellFolder, newPIDL, SHGDN_INFOLDER, &strret);
1963 ok(hr == S_OK, "GetDisplayNameOf failed %08x\n", hr);
1964
1965 hr = StrRetToBufW(&strret, newPIDL, tempbufW, ARRAY_SIZE(tempbufW));
1966 ok (hr == S_OK, "StrRetToBufW failed! hr = %08x\n", hr);
1967 todo_wine
1968 ok (!lstrcmpiW(tempbufW, folderdisplayW), "GetDisplayNameOf returned %s\n", wine_dbgstr_w(tempbufW));
1969
1970 /* editing name is also read from the resource */
1971 hr = IShellFolder_GetDisplayNameOf(testIShellFolder, newPIDL, SHGDN_INFOLDER|SHGDN_FOREDITING, &strret);
1972 ok(hr == S_OK, "GetDisplayNameOf failed %08x\n", hr);
1973
1974 hr = StrRetToBufW(&strret, newPIDL, tempbufW, ARRAY_SIZE(tempbufW));
1975 ok (hr == S_OK, "StrRetToBufW failed! hr = %08x\n", hr);
1976 todo_wine
1977 ok (!lstrcmpiW(tempbufW, folderdisplayW), "GetDisplayNameOf returned %s\n", wine_dbgstr_w(tempbufW));
1978
1979 /* parsing name is unchanged */
1980 hr = IShellFolder_GetDisplayNameOf(testIShellFolder, newPIDL, SHGDN_INFOLDER|SHGDN_FORPARSING, &strret);
1981 ok(hr == S_OK, "GetDisplayNameOf failed %08x\n", hr);
1982
1983 hr = StrRetToBufW(&strret, newPIDL, tempbufW, ARRAY_SIZE(tempbufW));
1984 ok (hr == S_OK, "StrRetToBufW failed! hr = %08x\n", hr);
1985 ok (!lstrcmpiW(tempbufW, foldernameW), "GetDisplayNameOf returned %s\n", wine_dbgstr_w(tempbufW));
1986
1987 IShellFolder_Release(IDesktopFolder);
1988 IShellFolder_Release(testIShellFolder);
1989
1990 IMalloc_Free(ppM, newPIDL);
1991
1992 cleanup:
1993 DeleteFileA(".\\testfolder\\desktop.ini");
1994 SetFileAttributesA(".\\testfolder", GetFileAttributesA(".\\testfolder")&~FILE_ATTRIBUTE_SYSTEM);
1995 RemoveDirectoryA(".\\testfolder");
1996 }
1997
1998 static void test_SHCreateShellItem(void)
1999 {
2000 IShellItem *shellitem, *shellitem2;
2001 IPersistIDList *persistidl;
2002 LPITEMIDLIST pidl_cwd=NULL, pidl_testfile, pidl_abstestfile, pidl_test, pidl_desktop;
2003 HRESULT ret;
2004 char curdirA[MAX_PATH];
2005 WCHAR curdirW[MAX_PATH];
2006 WCHAR fnbufW[MAX_PATH];
2007 IShellFolder *desktopfolder=NULL, *currentfolder=NULL;
2008 static WCHAR testfileW[] = {'t','e','s','t','f','i','l','e',0};
2009
2010 GetCurrentDirectoryA(MAX_PATH, curdirA);
2011
2012 if (!pSHCreateShellItem)
2013 {
2014 win_skip("SHCreateShellItem isn't available\n");
2015 return;
2016 }
2017
2018 if (!curdirA[0])
2019 {
2020 win_skip("GetCurrentDirectoryA returned empty string, skipping test_SHCreateShellItem\n");
2021 return;
2022 }
2023
2024 ret = SHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, &pidl_desktop);
2025 ok(ret == S_OK, "Got 0x%08x\n", ret);
2026
2027 MultiByteToWideChar(CP_ACP, 0, curdirA, -1, curdirW, MAX_PATH);
2028
2029 ret = SHGetDesktopFolder(&desktopfolder);
2030 ok(SUCCEEDED(ret), "SHGetShellFolder returned %x\n", ret);
2031
2032 ret = IShellFolder_ParseDisplayName(desktopfolder, NULL, NULL, curdirW, NULL, &pidl_cwd, NULL);
2033 ok(SUCCEEDED(ret), "ParseDisplayName returned %x\n", ret);
2034
2035 ret = IShellFolder_BindToObject(desktopfolder, pidl_cwd, NULL, &IID_IShellFolder, (void**)&currentfolder);
2036 ok(SUCCEEDED(ret), "BindToObject returned %x\n", ret);
2037
2038 CreateTestFile(".\\testfile");
2039
2040 ret = IShellFolder_ParseDisplayName(currentfolder, NULL, NULL, testfileW, NULL, &pidl_testfile, NULL);
2041 ok(SUCCEEDED(ret), "ParseDisplayName returned %x\n", ret);
2042
2043 pidl_abstestfile = ILCombine(pidl_cwd, pidl_testfile);
2044
2045 shellitem = (void*)0xdeadbeef;
2046 ret = pSHCreateShellItem(NULL, NULL, NULL, &shellitem);
2047 ok(ret == E_INVALIDARG, "SHCreateShellItem returned %x\n", ret);
2048 ok(shellitem == 0, "Got %p\n", shellitem);
2049
2050 if (0) /* crashes on Windows XP */
2051 {
2052 pSHCreateShellItem(NULL, NULL, pidl_cwd, NULL);
2053 pSHCreateShellItem(pidl_cwd, NULL, NULL, &shellitem);
2054 pSHCreateShellItem(NULL, currentfolder, NULL, &shellitem);
2055 pSHCreateShellItem(pidl_cwd, currentfolder, NULL, &shellitem);
2056 }
2057
2058 ret = pSHCreateShellItem(NULL, NULL, pidl_cwd, &shellitem);
2059 ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret);
2060 if (SUCCEEDED(ret))
2061 {
2062 ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
2063 ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
2064 if (SUCCEEDED(ret))
2065 {
2066 ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
2067 ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
2068 if (SUCCEEDED(ret))
2069 {
2070 ok(ILIsEqual(pidl_cwd, pidl_test), "id lists are not equal\n");
2071 ILFree(pidl_test);
2072 }
2073 IPersistIDList_Release(persistidl);
2074 }
2075 IShellItem_Release(shellitem);
2076 }
2077
2078 ret = pSHCreateShellItem(pidl_cwd, NULL, pidl_testfile, &shellitem);
2079 ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret);
2080 if (SUCCEEDED(ret))
2081 {
2082 ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
2083 ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
2084 if (SUCCEEDED(ret))
2085 {
2086 ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
2087 ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
2088 if (SUCCEEDED(ret))
2089 {
2090 ok(ILIsEqual(pidl_abstestfile, pidl_test), "id lists are not equal\n");
2091 ILFree(pidl_test);
2092 }
2093 IPersistIDList_Release(persistidl);
2094 }
2095
2096 ret = IShellItem_GetParent(shellitem, &shellitem2);
2097 ok(SUCCEEDED(ret), "GetParent returned %x\n", ret);
2098 if (SUCCEEDED(ret))
2099 {
2100 ret = IShellItem_QueryInterface(shellitem2, &IID_IPersistIDList, (void**)&persistidl);
2101 ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
2102 if (SUCCEEDED(ret))
2103 {
2104 ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
2105 ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
2106 if (SUCCEEDED(ret))
2107 {
2108 ok(ILIsEqual(pidl_cwd, pidl_test), "id lists are not equal\n");
2109 ILFree(pidl_test);
2110 }
2111 IPersistIDList_Release(persistidl);
2112 }
2113 IShellItem_Release(shellitem2);
2114 }
2115
2116 IShellItem_Release(shellitem);
2117 }
2118
2119 ret = pSHCreateShellItem(NULL, currentfolder, pidl_testfile, &shellitem);
2120 ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret);
2121 if (SUCCEEDED(ret))
2122 {
2123 ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
2124 ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
2125 if (SUCCEEDED(ret))
2126 {
2127 ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
2128 ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
2129 if (SUCCEEDED(ret))
2130 {
2131 ok(ILIsEqual(pidl_abstestfile, pidl_test), "id lists are not equal\n");
2132 ILFree(pidl_test);
2133 }
2134 IPersistIDList_Release(persistidl);
2135 }
2136 IShellItem_Release(shellitem);
2137 }
2138
2139 /* if a parent pidl and shellfolder are specified, the shellfolder is ignored */
2140 ret = pSHCreateShellItem(pidl_cwd, desktopfolder, pidl_testfile, &shellitem);
2141 ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret);
2142 if (SUCCEEDED(ret))
2143 {
2144 ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
2145 ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
2146 if (SUCCEEDED(ret))
2147 {
2148 ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
2149 ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
2150 if (SUCCEEDED(ret))
2151 {
2152 ok(ILIsEqual(pidl_abstestfile, pidl_test), "id lists are not equal\n");
2153 ILFree(pidl_test);
2154 }
2155 IPersistIDList_Release(persistidl);
2156 }
2157 IShellItem_Release(shellitem);
2158 }
2159
2160 ret = pSHCreateShellItem(NULL, desktopfolder, pidl_testfile, &shellitem);
2161 ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret);
2162 if (SUCCEEDED(ret))
2163 {
2164 ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
2165 ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
2166 if (SUCCEEDED(ret))
2167 {
2168 ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
2169 ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
2170 if (SUCCEEDED(ret))
2171 {
2172 ok(ILIsEqual(pidl_testfile, pidl_test), "id lists are not equal\n");
2173 ILFree(pidl_test);
2174 }
2175 IPersistIDList_Release(persistidl);
2176 }
2177
2178 IShellItem_Release(shellitem);
2179 }
2180
2181 ret = pSHCreateShellItem(NULL, NULL, pidl_desktop, &shellitem);
2182 ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret);
2183 if (SUCCEEDED(ret))
2184 {
2185 ret = IShellItem_GetParent(shellitem, &shellitem2);
2186 ok(FAILED(ret), "Got 0x%08x\n", ret);
2187 if(SUCCEEDED(ret)) IShellItem_Release(shellitem2);
2188 IShellItem_Release(shellitem);
2189 }
2190
2191 /* SHCreateItemFromParsingName */
2192 if(pSHCreateItemFromParsingName)
2193 {
2194 if(0)
2195 {
2196 /* Crashes under windows 7 */
2197 pSHCreateItemFromParsingName(NULL, NULL, &IID_IShellItem, NULL);
2198 }
2199
2200 shellitem = (void*)0xdeadbeef;
2201 ret = pSHCreateItemFromParsingName(NULL, NULL, &IID_IShellItem, (void**)&shellitem);
2202 ok(ret == E_INVALIDARG, "SHCreateItemFromParsingName returned %x\n", ret);
2203 ok(shellitem == NULL, "shellitem was %p.\n", shellitem);
2204
2205 ret = pSHCreateItemFromParsingName(testfileW, NULL, &IID_IShellItem, (void**)&shellitem);
2206 ok(ret == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
2207 "SHCreateItemFromParsingName returned %x\n", ret);
2208 if(SUCCEEDED(ret)) IShellItem_Release(shellitem);
2209
2210 lstrcpyW(fnbufW, curdirW);
2211 myPathAddBackslashW(fnbufW);
2212 lstrcatW(fnbufW, testfileW);
2213
2214 ret = pSHCreateItemFromParsingName(fnbufW, NULL, &IID_IShellItem, (void**)&shellitem);
2215 ok(ret == S_OK, "SHCreateItemFromParsingName returned %x\n", ret);
2216 if(SUCCEEDED(ret))
2217 {
2218 LPWSTR tmp_fname;
2219 ret = IShellItem_GetDisplayName(shellitem, SIGDN_FILESYSPATH, &tmp_fname);
2220 ok(ret == S_OK, "GetDisplayName returned %x\n", ret);
2221 if(SUCCEEDED(ret))
2222 {
2223 ok(!lstrcmpW(fnbufW, tmp_fname), "strings not equal\n");
2224 CoTaskMemFree(tmp_fname);
2225 }
2226 IShellItem_Release(shellitem);
2227 }
2228 }
2229 else
2230 win_skip("No SHCreateItemFromParsingName\n");
2231
2232
2233 /* SHCreateItemFromIDList */
2234 if(pSHCreateItemFromIDList)
2235 {
2236 if(0)
2237 {
2238 /* Crashes under win7 */
2239 pSHCreateItemFromIDList(NULL, &IID_IShellItem, NULL);
2240 }
2241
2242 ret = pSHCreateItemFromIDList(NULL, &IID_IShellItem, (void**)&shellitem);
2243 ok(ret == E_INVALIDARG, "SHCreateItemFromIDList returned %x\n", ret);
2244
2245 ret = pSHCreateItemFromIDList(pidl_cwd, &IID_IShellItem, (void**)&shellitem);
2246 ok(ret == S_OK, "SHCreateItemFromIDList returned %x\n", ret);
2247 if (SUCCEEDED(ret))
2248 {
2249 ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
2250 ok(ret == S_OK, "QueryInterface returned %x\n", ret);
2251 if (SUCCEEDED(ret))
2252 {
2253 ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
2254 ok(ret == S_OK, "GetIDList returned %x\n", ret);
2255 if (SUCCEEDED(ret))
2256 {
2257 ok(ILIsEqual(pidl_cwd, pidl_test), "id lists are not equal\n");
2258 ILFree(pidl_test);
2259 }
2260 IPersistIDList_Release(persistidl);
2261 }
2262 IShellItem_Release(shellitem);
2263 }
2264
2265 ret = pSHCreateItemFromIDList(pidl_testfile, &IID_IShellItem, (void**)&shellitem);
2266 ok(ret == S_OK, "SHCreateItemFromIDList returned %x\n", ret);
2267 if (SUCCEEDED(ret))
2268 {
2269 ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
2270 ok(ret == S_OK, "QueryInterface returned %x\n", ret);
2271 if (SUCCEEDED(ret))
2272 {
2273 ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
2274 ok(ret == S_OK, "GetIDList returned %x\n", ret);
2275 if (SUCCEEDED(ret))
2276 {
2277 ok(ILIsEqual(pidl_testfile, pidl_test), "id lists are not equal\n");
2278 ILFree(pidl_test);
2279 }
2280 IPersistIDList_Release(persistidl);
2281 }
2282 IShellItem_Release(shellitem);
2283 }
2284 }
2285 else
2286 win_skip("No SHCreateItemFromIDList\n");
2287
2288 /* SHCreateItemFromRelativeName */
2289 if(pSHCreateItemFromRelativeName && pSHGetKnownFolderPath)
2290 {
2291 IShellItem *shellitem_desktop = NULL;
2292 WCHAR *desktop_path, *displayname;
2293 WCHAR testfile_path[MAX_PATH] = {0};
2294 HANDLE file;
2295 LPITEMIDLIST pidl_desktop_testfile = NULL;
2296 int order;
2297
2298 ret = pSHCreateShellItem(NULL, NULL, pidl_desktop, &shellitem_desktop);
2299 ok(ret == S_OK, "SHCreateShellItem failed: 0x%08x.\n", ret);
2300
2301 shellitem = (void*)0xdeadbeef;
2302 ret = pSHCreateItemFromRelativeName(shellitem_desktop, NULL, NULL, &IID_IShellItem,
2303 (void**)&shellitem);
2304 ok(ret == E_INVALIDARG, "Expected 0x%08x but SHCreateItemFromRelativeName return: 0x%08x.\n",
2305 E_INVALIDARG, ret);
2306 ok(shellitem == NULL, "shellitem was %p.\n", shellitem);
2307
2308 /* Test with a non-existent file */
2309 shellitem = (void*)0xdeadbeef;
2310 ret = pSHCreateItemFromRelativeName(shellitem_desktop, testfileW, NULL, &IID_IShellItem,
2311 (void**)&shellitem);
2312 ok(ret == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
2313 "Expected 0x%08x but SHCreateItemFromRelativeName return: 0x%08x.\n",
2314 HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), ret);
2315 ok(shellitem == NULL, "shellitem was %p.\n", shellitem);
2316
2317 /* Create a file for testing in desktop folder */
2318 pSHGetKnownFolderPath(&FOLDERID_Desktop, 0, NULL, &desktop_path);
2319 lstrcatW(testfile_path, desktop_path);
2320 myPathAddBackslashW(testfile_path);
2321 lstrcatW(testfile_path, testfileW);
2322 file = CreateFileW(testfile_path, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL);
2323 ok(file != INVALID_HANDLE_VALUE, "CreateFileW failed! Last error: 0x%08x.\n", GetLastError());
2324 CloseHandle(file);
2325
2326 shellitem = (void*)0xdeadbeef;
2327 ret = pSHCreateItemFromRelativeName(shellitem_desktop, testfileW, NULL, &IID_IShellItem,
2328 (void**)&shellitem);
2329 ok(ret == S_OK, "SHCreateItemFromRelativeName failed: 0x%08x.\n", ret);
2330 ok(shellitem != NULL, "shellitem was %p.\n", shellitem);
2331 if(SUCCEEDED(ret))
2332 {
2333 ret = IShellItem_GetDisplayName(shellitem, 0, &displayname);
2334 ok(ret == S_OK, "IShellItem_GetDisplayName failed: 0x%08x.\n", ret);
2335 ok(!lstrcmpW(displayname, testfileW), "got wrong display name: %s.\n", wine_dbgstr_w(displayname));
2336 CoTaskMemFree(displayname);
2337
2338 shellitem2 = (void*)0xdeadbeef;
2339 ret = pSHCreateItemFromRelativeName(shellitem_desktop, testfileW, NULL, &IID_IShellItem,
2340 (void**)&shellitem2);
2341 ok(ret == S_OK, "SHCreateItemFromRelativeName failed: 0x%08x.\n", ret);
2342 ret = IShellItem_Compare(shellitem, shellitem2, 0, &order);
2343 ok(ret == S_OK, "IShellItem_Compare failed: 0x%08x.\n", ret);
2344 ok(!order, "order got wrong value: %d.\n", order);
2345 IShellItem_Release(shellitem2);
2346
2347 shellitem2 = (void*)0xdeadbeef;
2348 ret = IShellFolder_ParseDisplayName(desktopfolder, NULL, NULL, testfileW, NULL,
2349 &pidl_desktop_testfile, NULL);
2350 ok(ret == S_OK, "ParseDisplayName failed 0x%08x.\n", ret);
2351 ret = pSHCreateItemFromIDList(pidl_desktop_testfile, &IID_IShellItem, (void**)&shellitem2);
2352 ret = IShellItem_Compare(shellitem, shellitem2, 0, &order);
2353 ok(ret == S_OK, "IShellItem_Compare fail: 0x%08x.\n", ret);
2354 ok(!order, "order got wrong value: %d.\n", order);
2355 ILFree(pidl_desktop_testfile);
2356 IShellItem_Release(shellitem2);
2357
2358 IShellItem_Release(shellitem);
2359 }
2360
2361 DeleteFileW(testfile_path);
2362 CoTaskMemFree(desktop_path);
2363 IShellItem_Release(shellitem_desktop);
2364 }
2365 else
2366 win_skip("No SHCreateItemFromRelativeName or SHGetKnownFolderPath\n");
2367
2368 /* SHCreateItemInKnownFolder */
2369 if(pSHCreateItemInKnownFolder && pSHGetKnownFolderPath)
2370 {
2371 WCHAR *desktop_path;
2372 WCHAR testfile_path[MAX_PATH] = {0};
2373 HANDLE file;
2374 WCHAR *displayname = NULL;
2375 int order;
2376 LPITEMIDLIST pidl_desktop_testfile = NULL;
2377
2378 shellitem = (void*)0xdeadbeef;
2379 ret = pSHCreateItemInKnownFolder(&FOLDERID_Desktop, 0, NULL, &IID_IShellItem,
2380 (void**)&shellitem);
2381 ok(ret == S_OK, "SHCreateItemInKnownFolder failed: 0x%08x.\n", ret);
2382 ok(shellitem != NULL, "shellitem was %p.\n", shellitem);
2383 if(SUCCEEDED(ret))
2384 {
2385 shellitem2 = (void*)0xdeadbeef;
2386 ret = pSHCreateShellItem(NULL, NULL, pidl_desktop, &shellitem2);
2387 ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret);
2388 if(SUCCEEDED(ret))
2389 {
2390 ret = IShellItem_Compare(shellitem, shellitem2, 0, &order);
2391 ok(ret == S_OK, "IShellItem_Compare failed: 0x%08x.\n", ret);
2392 ok(!order, "order got wrong value: %d.\n", order);
2393 IShellItem_Release(shellitem2);
2394 }
2395 IShellItem_Release(shellitem);
2396 }
2397
2398 /* Test with a non-existent file */
2399 shellitem = (void*)0xdeadbeef;
2400 ret = pSHCreateItemInKnownFolder(&FOLDERID_Desktop, 0, testfileW, &IID_IShellItem,
2401 (void**)&shellitem);
2402 ok(ret == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
2403 "Expected 0x%08x but SHCreateItemInKnownFolder return: 0x%08x.\n",
2404 HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), ret);
2405 ok(shellitem == NULL, "shellitem was %p.\n", shellitem);
2406
2407 pSHGetKnownFolderPath(&FOLDERID_Desktop, 0, NULL, &desktop_path);
2408 lstrcatW(testfile_path, desktop_path);
2409 myPathAddBackslashW(testfile_path);
2410 lstrcatW(testfile_path, testfileW);
2411 file = CreateFileW(testfile_path, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL);
2412 ok(file != INVALID_HANDLE_VALUE, "CreateFileW failed! Last error: 0x%08x.\n", GetLastError());
2413 CloseHandle(file);
2414
2415 shellitem = (void*)0xdeadbeef;
2416 ret = pSHCreateItemInKnownFolder(&FOLDERID_Desktop, 0, testfileW, &IID_IShellItem,
2417 (void**)&shellitem);
2418 ok(ret == S_OK, "SHCreateItemInKnownFolder failed: 0x%08x.\n", ret);
2419 ok(shellitem != NULL, "shellitem was %p.\n", shellitem);
2420 if(SUCCEEDED(ret))
2421 {
2422 ret = IShellItem_GetDisplayName(shellitem, 0, &displayname);
2423 ok(ret == S_OK, "IShellItem_GetDisplayName failed: 0x%08x.\n", ret);
2424 ok(!lstrcmpW(displayname, testfileW), "got wrong display name: %s.\n",
2425 wine_dbgstr_w(displayname));
2426 CoTaskMemFree(displayname);
2427
2428 shellitem2 = (void*)0xdeadbeef;
2429 ret = pSHCreateItemInKnownFolder(&FOLDERID_Desktop, 0, testfileW, &IID_IShellItem,
2430 (void**)&shellitem2);
2431 ok(ret == S_OK, "SHCreateItemInKnownFolder failed: 0x%08x.\n", ret);
2432 ok(shellitem2 != NULL, "shellitem was %p.\n", shellitem);
2433 ret = IShellItem_Compare(shellitem, shellitem2, 0, &order);
2434 ok(ret == S_OK, "IShellItem_Compare failed: 0x%08x.\n", ret);
2435 ok(!order, "order got wrong value: %d.\n", order);
2436 IShellItem_Release(shellitem2);
2437
2438 shellitem2 = (void*)0xdeadbeef;
2439 ret = IShellFolder_ParseDisplayName(desktopfolder, NULL, NULL, testfileW, NULL,
2440 &pidl_desktop_testfile, NULL);
2441 ok(SUCCEEDED(ret), "ParseDisplayName returned %x.\n", ret);
2442 ret = pSHCreateItemFromIDList(pidl_desktop_testfile, &IID_IShellItem, (void**)&shellitem2);
2443 ret = IShellItem_Compare(shellitem, shellitem2, 0, &order);
2444 ok(ret == S_OK, "IShellItem_Compare failed: 0x%08x.\n", ret);
2445 ok(!order, "order got wrong value: %d.\n", order);
2446 ILFree(pidl_desktop_testfile);
2447 IShellItem_Release(shellitem2);
2448
2449 IShellItem_Release(shellitem);
2450 }
2451
2452 shellitem = (void*)0xdeadbeef;
2453 ret = pSHCreateItemInKnownFolder(&FOLDERID_Documents, 0, NULL, &IID_IShellItem,
2454 (void**)&shellitem);
2455 ok(ret == S_OK, "SHCreateItemInKnownFolder failed: 0x%08x.\n", ret);
2456 ok(shellitem != NULL, "shellitem was %p.\n", shellitem);
2457 if(SUCCEEDED(ret))
2458 {
2459 shellitem2 = (void*)0xdeadbeef;
2460 ret = pSHCreateItemInKnownFolder(&FOLDERID_Documents, 0, NULL, &IID_IShellItem,
2461 (void**)&shellitem2);
2462 ok(ret == S_OK, "SHCreateItemInKnownFolder failed: 0x%08x.\n", ret);
2463 ok(shellitem2 != NULL, "shellitem was %p.\n", shellitem);
2464 ret = IShellItem_Compare(shellitem, shellitem2, 0, &order);
2465 ok(ret == S_OK, "IShellItem_Compare failed: 0x%08x.\n", ret);
2466 ok(!order, "order got wrong value: %d.\n", order);
2467 IShellItem_Release(shellitem2);
2468
2469 IShellItem_Release(shellitem);
2470 }
2471 DeleteFileW(testfile_path);
2472 CoTaskMemFree(desktop_path);
2473 }
2474 else
2475 win_skip("No SHCreateItemInKnownFolder or SHGetKnownFolderPath\n");
2476
2477 DeleteFileA(".\\testfile");
2478 ILFree(pidl_abstestfile);
2479 ILFree(pidl_testfile);
2480 ILFree(pidl_desktop);
2481 ILFree(pidl_cwd);
2482 IShellFolder_Release(currentfolder);
2483 IShellFolder_Release(desktopfolder);
2484 }
2485
2486 static void test_SHGetNameFromIDList(void)
2487 {
2488 IShellItem *shellitem;
2489 LPITEMIDLIST pidl;
2490 LPWSTR name_string;
2491 HRESULT hres;
2492 UINT i;
2493 static const DWORD flags[] = {
2494 SIGDN_NORMALDISPLAY, SIGDN_PARENTRELATIVEPARSING,
2495 SIGDN_DESKTOPABSOLUTEPARSING,SIGDN_PARENTRELATIVEEDITING,
2496 SIGDN_DESKTOPABSOLUTEEDITING, /*SIGDN_FILESYSPATH, SIGDN_URL, */
2497 SIGDN_PARENTRELATIVEFORADDRESSBAR,SIGDN_PARENTRELATIVE, -1234};
2498
2499 if(!pSHGetNameFromIDList)
2500 {
2501 win_skip("SHGetNameFromIDList missing.\n");
2502 return;
2503 }
2504
2505 /* This should be available on any platform that passed the above test. */
2506 ok(pSHCreateShellItem != NULL, "SHCreateShellItem missing.\n");
2507
2508 if(0)
2509 {
2510 /* Crashes under win7 */
2511 pSHGetNameFromIDList(NULL, 0, NULL);
2512 }
2513
2514 hres = pSHGetNameFromIDList(NULL, 0, &name_string);
2515 ok(hres == E_INVALIDARG, "Got 0x%08x\n", hres);
2516
2517 /* Test the desktop */
2518 hres = SHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, &pidl);
2519 ok(hres == S_OK, "Got 0x%08x\n", hres);
2520 hres = pSHCreateShellItem(NULL, NULL, pidl, &shellitem);
2521 ok(hres == S_OK, "Got 0x%08x\n", hres);
2522 if(SUCCEEDED(hres))
2523 {
2524 WCHAR *nameSI, *nameSH;
2525 WCHAR buf[MAX_PATH];
2526 HRESULT hrSI, hrSH, hrSF;
2527 STRRET strret;
2528 IShellFolder *psf;
2529 BOOL res;
2530
2531 SHGetDesktopFolder(&psf);
2532 for(i = 0; flags[i] != -1234; i++)
2533 {
2534 hrSI = IShellItem_GetDisplayName(shellitem, flags[i], &nameSI);
2535 ok(hrSI == S_OK, "Got 0x%08x\n", hrSI);
2536 hrSH = pSHGetNameFromIDList(pidl, flags[i], &nameSH);
2537 ok(hrSH == S_OK, "Got 0x%08x\n", hrSH);
2538 hrSF = IShellFolder_GetDisplayNameOf(psf, pidl, flags[i] & 0xffff, &strret);
2539 ok(hrSF == S_OK, "Got 0x%08x\n", hrSF);
2540
2541 if(SUCCEEDED(hrSI) && SUCCEEDED(hrSH))
2542 ok(!lstrcmpW(nameSI, nameSH), "Strings differ.\n");
2543
2544 if(SUCCEEDED(hrSF))
2545 {
2546 StrRetToBufW(&strret, NULL, buf, MAX_PATH);
2547 if(SUCCEEDED(hrSI))
2548 ok(!lstrcmpW(nameSI, buf), "Strings differ.\n");
2549 if(SUCCEEDED(hrSF))