2 * Unit test of the IShellFolder functions.
4 * Copyright 2004 Vitaliy Margolen
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.
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.
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
40 #include "wine/test.h"
45 static HRESULT (WINAPI *pSHBindToParent)(LPCITEMIDLIST, REFIID, LPVOID*, LPCITEMIDLIST*);
46 static HRESULT (WINAPI *pSHGetFolderPathA)(HWND, int, HANDLE, DWORD, LPSTR);
47 static HRESULT (WINAPI *pSHGetFolderPathAndSubDirA)(HWND, int, HANDLE, DWORD, LPCSTR, LPSTR);
48 static BOOL (WINAPI *pSHGetPathFromIDListW)(LPCITEMIDLIST,LPWSTR);
49 static BOOL (WINAPI *pSHGetSpecialFolderPathA)(HWND, LPSTR, int, BOOL);
50 static BOOL (WINAPI *pSHGetSpecialFolderPathW)(HWND, LPWSTR, int, BOOL);
51 static HRESULT (WINAPI *pStrRetToBufW)(STRRET*,LPCITEMIDLIST,LPWSTR,UINT);
52 static LPITEMIDLIST (WINAPI *pILFindLastID)(LPCITEMIDLIST);
53 static void (WINAPI *pILFree)(LPITEMIDLIST);
54 static BOOL (WINAPI *pILIsEqual)(LPCITEMIDLIST, LPCITEMIDLIST);
55 static HRESULT (WINAPI *pSHCreateShellItem)(LPCITEMIDLIST,IShellFolder*,LPCITEMIDLIST,IShellItem**);
56 static LPITEMIDLIST (WINAPI *pILCombine)(LPCITEMIDLIST,LPCITEMIDLIST);
57 static HRESULT (WINAPI *pSHParseDisplayName)(LPCWSTR,IBindCtx*,LPITEMIDLIST*,SFGAOF,SFGAOF*);
58 static LPITEMIDLIST (WINAPI *pSHSimpleIDListFromPathAW)(LPCVOID);
60 static void init_function_pointers(void)
66 hmod = GetModuleHandleA("shell32.dll");
68 #define MAKEFUNC(f) (p##f = (void*)GetProcAddress(hmod, #f))
69 MAKEFUNC(SHBindToParent);
70 MAKEFUNC(SHCreateShellItem);
71 MAKEFUNC(SHGetFolderPathA);
72 MAKEFUNC(SHGetFolderPathAndSubDirA);
73 MAKEFUNC(SHGetPathFromIDListW);
74 MAKEFUNC(SHGetSpecialFolderPathA);
75 MAKEFUNC(SHGetSpecialFolderPathW);
76 MAKEFUNC(SHParseDisplayName);
79 #define MAKEFUNC_ORD(f, ord) (p##f = (void*)GetProcAddress(hmod, (LPSTR)(ord)))
80 MAKEFUNC_ORD(ILFindLastID, 16);
81 MAKEFUNC_ORD(ILIsEqual, 21);
82 MAKEFUNC_ORD(ILCombine, 25);
83 MAKEFUNC_ORD(ILFree, 155);
84 MAKEFUNC_ORD(SHSimpleIDListFromPathAW, 162);
87 /* test named exports */
88 ptr = GetProcAddress(hmod, "ILFree");
89 ok(broken(ptr == 0) || ptr != 0, "expected named export for ILFree\n");
92 #define TESTNAMED(f) \
93 ptr = (void*)GetProcAddress(hmod, #f); \
94 ok(ptr != 0, "expected named export for " #f "\n");
96 TESTNAMED(ILAppendID);
98 TESTNAMED(ILCloneFirst);
100 TESTNAMED(ILCreateFromPath);
101 TESTNAMED(ILCreateFromPathA);
102 TESTNAMED(ILCreateFromPathW);
103 TESTNAMED(ILFindChild);
104 TESTNAMED(ILFindLastID);
105 TESTNAMED(ILGetNext);
106 TESTNAMED(ILGetSize);
107 TESTNAMED(ILIsEqual);
108 TESTNAMED(ILIsParent);
109 TESTNAMED(ILRemoveLastID);
110 TESTNAMED(ILSaveToStream);
114 hmod = GetModuleHandleA("shlwapi.dll");
115 pStrRetToBufW = (void*)GetProcAddress(hmod, "StrRetToBufW");
117 hr = SHGetMalloc(&ppM);
118 ok(hr == S_OK, "SHGetMalloc failed %08x\n", hr);
121 static void test_ParseDisplayName(void)
124 IShellFolder *IDesktopFolder;
125 static const char *cNonExistDir1A = "c:\\nonexist_subdir";
126 static const char *cNonExistDir2A = "c:\\\\nonexist_subdir";
127 static const char *cInetTestA = "http:\\yyy";
128 static const char *cInetTest2A = "xx:yyy";
130 WCHAR cTestDirW [MAX_PATH] = {0};
134 hr = SHGetDesktopFolder(&IDesktopFolder);
135 ok(hr == S_OK, "Expected SHGetDesktopFolder to return S_OK, got 0x%08x\n", hr);
136 if(hr != S_OK) return;
138 /* Tests crash on W2K and below (SHCreateShellItem available as of XP) */
139 if (pSHCreateShellItem)
141 /* null name and pidl */
142 hr = IShellFolder_ParseDisplayName(IDesktopFolder,
143 NULL, NULL, NULL, NULL, NULL, 0);
144 ok(hr == E_INVALIDARG, "returned %08x, expected E_INVALIDARG\n", hr);
147 newPIDL = (ITEMIDLIST*)0xdeadbeef;
148 hr = IShellFolder_ParseDisplayName(IDesktopFolder,
149 NULL, NULL, NULL, NULL, &newPIDL, 0);
150 ok(newPIDL == 0, "expected null, got %p\n", newPIDL);
151 ok(hr == E_INVALIDARG, "returned %08x, expected E_INVALIDARG\n", hr);
154 win_skip("Tests would crash on W2K and below\n");
156 MultiByteToWideChar(CP_ACP, 0, cInetTestA, -1, cTestDirW, MAX_PATH);
157 hr = IShellFolder_ParseDisplayName(IDesktopFolder,
158 NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
159 todo_wine ok(hr == S_OK || broken(hr == E_FAIL) /* NT4 */,
160 "ParseDisplayName returned %08x, expected SUCCESS or E_FAIL\n", hr);
163 ok(pILFindLastID(newPIDL)->mkid.abID[0] == 0x61, "Last pidl should be of type "
164 "PT_IESPECIAL1, but is: %02x\n", pILFindLastID(newPIDL)->mkid.abID[0]);
165 IMalloc_Free(ppM, newPIDL);
168 MultiByteToWideChar(CP_ACP, 0, cInetTest2A, -1, cTestDirW, MAX_PATH);
169 hr = IShellFolder_ParseDisplayName(IDesktopFolder,
170 NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
171 todo_wine ok(hr == S_OK || broken(hr == E_FAIL) /* NT4 */,
172 "ParseDisplayName returned %08x, expected SUCCESS or E_FAIL\n", hr);
175 ok(pILFindLastID(newPIDL)->mkid.abID[0] == 0x61, "Last pidl should be of type "
176 "PT_IESPECIAL1, but is: %02x\n", pILFindLastID(newPIDL)->mkid.abID[0]);
177 IMalloc_Free(ppM, newPIDL);
180 res = GetFileAttributesA(cNonExistDir1A);
181 if(res != INVALID_FILE_ATTRIBUTES)
183 skip("Test directory unexpectedly exists\n");
187 MultiByteToWideChar(CP_ACP, 0, cNonExistDir1A, -1, cTestDirW, MAX_PATH);
188 hr = IShellFolder_ParseDisplayName(IDesktopFolder,
189 NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
190 ok((hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) || (hr == E_FAIL),
191 "ParseDisplayName returned %08x, expected 80070002 or E_FAIL\n", hr);
193 res = GetFileAttributesA(cNonExistDir2A);
194 if(res != INVALID_FILE_ATTRIBUTES)
196 skip("Test directory unexpectedly exists\n");
200 MultiByteToWideChar(CP_ACP, 0, cNonExistDir2A, -1, cTestDirW, MAX_PATH);
201 hr = IShellFolder_ParseDisplayName(IDesktopFolder,
202 NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
203 ok((hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) || (hr == E_FAIL) || (hr == E_INVALIDARG),
204 "ParseDisplayName returned %08x, expected 80070002, E_FAIL or E_INVALIDARG\n", hr);
206 /* I thought that perhaps the DesktopFolder's ParseDisplayName would recognize the
207 * path corresponding to CSIDL_PERSONAL and return a CLSID_MyDocuments PIDL. Turns
208 * out it doesn't. The magic seems to happen in the file dialogs, then. */
209 if (!pSHGetSpecialFolderPathW || !pILFindLastID)
211 win_skip("SHGetSpecialFolderPathW and/or ILFindLastID are not available\n");
215 bRes = pSHGetSpecialFolderPathW(NULL, cTestDirW, CSIDL_PERSONAL, FALSE);
216 ok(bRes, "SHGetSpecialFolderPath(CSIDL_PERSONAL) failed! %u\n", GetLastError());
217 if (!bRes) goto finished;
219 hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
220 ok(hr == S_OK, "DesktopFolder->ParseDisplayName failed. hr = %08x.\n", hr);
221 if (hr != S_OK) goto finished;
223 ok(pILFindLastID(newPIDL)->mkid.abID[0] == 0x31 ||
224 pILFindLastID(newPIDL)->mkid.abID[0] == 0xb1, /* Win98 */
225 "Last pidl should be of type PT_FOLDER or PT_IESPECIAL2, but is: %02x\n",
226 pILFindLastID(newPIDL)->mkid.abID[0]);
227 IMalloc_Free(ppM, newPIDL);
230 IShellFolder_Release(IDesktopFolder);
233 /* creates a file with the specified name for tests */
234 static void CreateTestFile(const CHAR *name)
239 file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
240 if (file != INVALID_HANDLE_VALUE)
242 WriteFile(file, name, strlen(name), &written, NULL);
243 WriteFile(file, "\n", strlen("\n"), &written, NULL);
249 /* initializes the tests */
250 static void CreateFilesFolders(void)
252 CreateDirectoryA(".\\testdir", NULL);
253 CreateDirectoryA(".\\testdir\\test.txt", NULL);
254 CreateTestFile (".\\testdir\\test1.txt ");
255 CreateTestFile (".\\testdir\\test2.txt ");
256 CreateTestFile (".\\testdir\\test3.txt ");
257 CreateDirectoryA(".\\testdir\\testdir2 ", NULL);
258 CreateDirectoryA(".\\testdir\\testdir2\\subdir", NULL);
261 /* cleans after tests */
262 static void Cleanup(void)
264 DeleteFileA(".\\testdir\\test1.txt");
265 DeleteFileA(".\\testdir\\test2.txt");
266 DeleteFileA(".\\testdir\\test3.txt");
267 RemoveDirectoryA(".\\testdir\\test.txt");
268 RemoveDirectoryA(".\\testdir\\testdir2\\subdir");
269 RemoveDirectoryA(".\\testdir\\testdir2");
270 RemoveDirectoryA(".\\testdir");
275 static void test_EnumObjects(IShellFolder *iFolder)
277 IEnumIDList *iEnumList;
278 LPITEMIDLIST newPIDL, idlArr[10];
283 static const WORD iResults [5][5] =
292 #define SFGAO_testfor SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR | SFGAO_CAPABILITYMASK
293 /* Don't test for SFGAO_HASSUBFOLDER since we return real state and native cached */
294 static const ULONG attrs[5] =
296 SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR,
297 SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR,
298 SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM,
299 SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM,
300 SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM,
303 hr = IShellFolder_EnumObjects(iFolder, NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN, &iEnumList);
304 ok(hr == S_OK, "EnumObjects failed %08x\n", hr);
306 /* This is to show that, contrary to what is said on MSDN, on IEnumIDList::Next,
307 * the filesystem shellfolders return S_OK even if less than 'celt' items are
308 * returned (in contrast to S_FALSE). We have to do it in a loop since WinXP
309 * only ever returns a single entry per call. */
310 while (IEnumIDList_Next(iEnumList, 10-i, &idlArr[i], &NumPIDLs) == S_OK)
312 ok (i == 5, "i: %d\n", i);
314 hr = IEnumIDList_Release(iEnumList);
315 ok(hr == S_OK, "IEnumIDList_Release failed %08x\n", hr);
317 /* Sort them first in case of wrong order from system */
318 for (i=0;i<5;i++) for (j=0;j<5;j++)
319 if ((SHORT)IShellFolder_CompareIDs(iFolder, 0, idlArr[i], idlArr[j]) < 0)
322 idlArr[i] = idlArr[j];
326 for (i=0;i<5;i++) for (j=0;j<5;j++)
328 hr = IShellFolder_CompareIDs(iFolder, 0, idlArr[i], idlArr[j]);
329 ok(hr == iResults[i][j], "Got %x expected [%d]-[%d]=%x\n", hr, i, j, iResults[i][j]);
333 for (i = 0; i < 5; i++)
336 #define SFGAO_VISTA SFGAO_DROPTARGET | SFGAO_CANLINK | SFGAO_CANCOPY
337 /* Native returns all flags no matter what we ask for */
338 flags = SFGAO_CANCOPY;
339 hr = IShellFolder_GetAttributesOf(iFolder, 1, (LPCITEMIDLIST*)(idlArr + i), &flags);
340 flags &= SFGAO_testfor;
341 ok(hr == S_OK, "GetAttributesOf returns %08x\n", hr);
342 ok(flags == (attrs[i]) ||
343 flags == (attrs[i] & ~SFGAO_FILESYSANCESTOR) || /* Win9x, NT4 */
344 flags == ((attrs[i] & ~SFGAO_CAPABILITYMASK) | SFGAO_VISTA), /* Vista and higher */
345 "GetAttributesOf[%i] got %08x, expected %08x\n", i, flags, attrs[i]);
347 flags = SFGAO_testfor;
348 hr = IShellFolder_GetAttributesOf(iFolder, 1, (LPCITEMIDLIST*)(idlArr + i), &flags);
349 flags &= SFGAO_testfor;
350 ok(hr == S_OK, "GetAttributesOf returns %08x\n", hr);
351 ok(flags == attrs[i] ||
352 flags == (attrs[i] & ~SFGAO_FILESYSANCESTOR), /* Win9x, NT4 */
353 "GetAttributesOf[%i] got %08x, expected %08x\n", i, flags, attrs[i]);
357 IMalloc_Free(ppM, idlArr[i]);
360 static void test_BindToObject(void)
364 IShellFolder *psfDesktop, *psfChild, *psfMyComputer, *psfSystemDir;
365 SHITEMID emptyitem = { 0, { 0 } };
366 LPITEMIDLIST pidlMyComputer, pidlSystemDir, pidlEmpty = (LPITEMIDLIST)&emptyitem;
367 WCHAR wszSystemDir[MAX_PATH];
368 char szSystemDir[MAX_PATH];
369 WCHAR wszMyComputer[] = {
370 ':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
371 'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };
373 /* The following tests shows that BindToObject should fail with E_INVALIDARG if called
374 * with an empty pidl. This is tested for Desktop, MyComputer and the FS ShellFolder
376 hr = SHGetDesktopFolder(&psfDesktop);
377 ok (hr == S_OK, "SHGetDesktopFolder failed! hr = %08x\n", hr);
378 if (hr != S_OK) return;
380 hr = IShellFolder_BindToObject(psfDesktop, pidlEmpty, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
381 ok (hr == E_INVALIDARG, "Desktop's BindToObject should fail, when called with empty pidl! hr = %08x\n", hr);
383 hr = IShellFolder_BindToObject(psfDesktop, NULL, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
384 ok (hr == E_INVALIDARG, "Desktop's BindToObject should fail, when called with NULL pidl! hr = %08x\n", hr);
386 hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyComputer, NULL, &pidlMyComputer, NULL);
387 ok (hr == S_OK, "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08x\n", hr);
389 IShellFolder_Release(psfDesktop);
393 hr = IShellFolder_BindToObject(psfDesktop, pidlMyComputer, NULL, &IID_IShellFolder, (LPVOID*)&psfMyComputer);
394 ok (hr == S_OK, "Desktop failed to bind to MyComputer object! hr = %08x\n", hr);
395 IShellFolder_Release(psfDesktop);
396 IMalloc_Free(ppM, pidlMyComputer);
397 if (hr != S_OK) return;
399 hr = IShellFolder_BindToObject(psfMyComputer, pidlEmpty, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
400 ok (hr == E_INVALIDARG, "MyComputers's BindToObject should fail, when called with empty pidl! hr = %08x\n", hr);
404 /* this call segfaults on 98SE */
405 hr = IShellFolder_BindToObject(psfMyComputer, NULL, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
406 ok (hr == E_INVALIDARG, "MyComputers's BindToObject should fail, when called with NULL pidl! hr = %08x\n", hr);
409 cChars = GetSystemDirectoryA(szSystemDir, MAX_PATH);
410 ok (cChars > 0 && cChars < MAX_PATH, "GetSystemDirectoryA failed! LastError: %u\n", GetLastError());
411 if (cChars == 0 || cChars >= MAX_PATH) {
412 IShellFolder_Release(psfMyComputer);
415 MultiByteToWideChar(CP_ACP, 0, szSystemDir, -1, wszSystemDir, MAX_PATH);
417 hr = IShellFolder_ParseDisplayName(psfMyComputer, NULL, NULL, wszSystemDir, NULL, &pidlSystemDir, NULL);
418 ok (hr == S_OK, "MyComputers's ParseDisplayName failed to parse the SystemDirectory! hr = %08x\n", hr);
420 IShellFolder_Release(psfMyComputer);
424 hr = IShellFolder_BindToObject(psfMyComputer, pidlSystemDir, NULL, &IID_IShellFolder, (LPVOID*)&psfSystemDir);
425 ok (hr == S_OK, "MyComputer failed to bind to a FileSystem ShellFolder! hr = %08x\n", hr);
426 IShellFolder_Release(psfMyComputer);
427 IMalloc_Free(ppM, pidlSystemDir);
428 if (hr != S_OK) return;
430 hr = IShellFolder_BindToObject(psfSystemDir, pidlEmpty, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
431 ok (hr == E_INVALIDARG,
432 "FileSystem ShellFolder's BindToObject should fail, when called with empty pidl! hr = %08x\n", hr);
436 /* this call segfaults on 98SE */
437 hr = IShellFolder_BindToObject(psfSystemDir, NULL, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
438 ok (hr == E_INVALIDARG,
439 "FileSystem ShellFolder's BindToObject should fail, when called with NULL pidl! hr = %08x\n", hr);
442 IShellFolder_Release(psfSystemDir);
445 /* Based on PathAddBackslashW from dlls/shlwapi/path.c */
446 static LPWSTR myPathAddBackslashW( LPWSTR lpszPath )
450 if (!lpszPath || (iLen = lstrlenW(lpszPath)) >= MAX_PATH)
456 if (lpszPath[-1] != '\\')
465 static void test_GetDisplayName(void)
470 WCHAR wszTestFile[MAX_PATH], wszTestFile2[MAX_PATH];
471 char szTestFile[MAX_PATH], szTestDir[MAX_PATH];
474 LPSHELLFOLDER psfDesktop, psfPersonal;
476 SHITEMID emptyitem = { 0, { 0 } };
477 LPITEMIDLIST pidlTestFile, pidlEmpty = (LPITEMIDLIST)&emptyitem;
478 LPCITEMIDLIST pidlLast;
479 static const CHAR szFileName[] = "winetest.foo";
480 static const WCHAR wszFileName[] = { 'w','i','n','e','t','e','s','t','.','f','o','o',0 };
481 static const WCHAR wszDirName[] = { 'w','i','n','e','t','e','s','t',0 };
483 /* I'm trying to figure if there is a functional difference between calling
484 * SHGetPathFromIDListW and calling GetDisplayNameOf(SHGDN_FORPARSING) after
485 * binding to the shellfolder. One thing I thought of was that perhaps
486 * SHGetPathFromIDListW would be able to get the path to a file, which does
487 * not exist anymore, while the other method wouldn't. It turns out there's
488 * no functional difference in this respect.
491 if(!pSHGetSpecialFolderPathA) {
492 win_skip("SHGetSpecialFolderPathA is not available\n");
496 /* First creating a directory in MyDocuments and a file in this directory. */
497 result = pSHGetSpecialFolderPathA(NULL, szTestDir, CSIDL_PERSONAL, FALSE);
498 ok(result, "SHGetSpecialFolderPathA failed! Last error: %u\n", GetLastError());
501 /* Use ANSI file functions so this works on Windows 9x */
502 lstrcatA(szTestDir, "\\winetest");
503 CreateDirectoryA(szTestDir, NULL);
504 attr=GetFileAttributesA(szTestDir);
505 if (attr == INVALID_FILE_ATTRIBUTES || !(attr & FILE_ATTRIBUTE_DIRECTORY))
507 ok(0, "unable to create the '%s' directory\n", szTestDir);
511 lstrcpyA(szTestFile, szTestDir);
512 lstrcatA(szTestFile, "\\");
513 lstrcatA(szTestFile, szFileName);
514 hTestFile = CreateFileA(szTestFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
515 ok((hTestFile != INVALID_HANDLE_VALUE), "CreateFileA failed! Last error: %u\n", GetLastError());
516 if (hTestFile == INVALID_HANDLE_VALUE) return;
517 CloseHandle(hTestFile);
519 /* Getting an itemidlist for the file. */
520 hr = SHGetDesktopFolder(&psfDesktop);
521 ok(hr == S_OK, "SHGetDesktopFolder failed! hr = %08x\n", hr);
522 if (hr != S_OK) return;
524 MultiByteToWideChar(CP_ACP, 0, szTestFile, -1, wszTestFile, MAX_PATH);
526 hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszTestFile, NULL, &pidlTestFile, NULL);
527 ok(hr == S_OK, "Desktop->ParseDisplayName failed! hr = %08x\n", hr);
529 IShellFolder_Release(psfDesktop);
533 pidlLast = pILFindLastID(pidlTestFile);
534 ok(pidlLast->mkid.cb >=76 ||
535 broken(pidlLast->mkid.cb == 28) || /* W2K */
536 broken(pidlLast->mkid.cb == 40), /* Win9x, WinME */
537 "Expected pidl length of at least 76, got %d.\n", pidlLast->mkid.cb);
538 if (pidlLast->mkid.cb >= 28) {
539 ok(!lstrcmpA((CHAR*)&pidlLast->mkid.abID[12], szFileName),
540 "Filename should be stored as ansi-string at this position!\n");
542 /* WinXP and up store the filenames as both ANSI and UNICODE in the pidls */
543 if (pidlLast->mkid.cb >= 76) {
544 ok(!lstrcmpW((WCHAR*)&pidlLast->mkid.abID[46], wszFileName) ||
545 (pidlLast->mkid.cb >= 94 && !lstrcmpW((WCHAR*)&pidlLast->mkid.abID[64], wszFileName)) || /* Vista */
546 (pidlLast->mkid.cb >= 98 && !lstrcmpW((WCHAR*)&pidlLast->mkid.abID[68], wszFileName)), /* Win7 */
547 "Filename should be stored as wchar-string at this position!\n");
550 /* It seems as if we cannot bind to regular files on windows, but only directories.
552 hr = IShellFolder_BindToObject(psfDesktop, pidlTestFile, NULL, &IID_IUnknown, (VOID**)&psfFile);
554 ok (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) ||
555 hr == E_NOTIMPL || /* Vista */
556 broken(hr == S_OK), /* Win9x, W2K */
559 IShellFolder_Release(psfFile);
562 if (!pSHBindToParent)
564 win_skip("SHBindToParent is missing\n");
565 DeleteFileA(szTestFile);
566 RemoveDirectoryA(szTestDir);
570 /* Some tests for IShellFolder::SetNameOf */
571 if (pSHGetFolderPathAndSubDirA)
573 hr = pSHBindToParent(pidlTestFile, &IID_IShellFolder, (VOID**)&psfPersonal, &pidlLast);
574 ok(hr == S_OK, "SHBindToParent failed! hr = %08x\n", hr);
576 /* It's ok to use this fixed path. Call will fail anyway. */
577 WCHAR wszAbsoluteFilename[] = { 'C',':','\\','w','i','n','e','t','e','s','t', 0 };
578 LPITEMIDLIST pidlNew;
580 /* The pidl returned through the last parameter of SetNameOf is a simple one. */
581 hr = IShellFolder_SetNameOf(psfPersonal, NULL, pidlLast, wszDirName, SHGDN_NORMAL, &pidlNew);
582 ok (hr == S_OK, "SetNameOf failed! hr = %08x\n", hr);
585 ok (((LPITEMIDLIST)((LPBYTE)pidlNew+pidlNew->mkid.cb))->mkid.cb == 0,
586 "pidl returned from SetNameOf should be simple!\n");
588 /* Passing an absolute path to SetNameOf fails. The HRESULT code indicates that SetNameOf
589 * is implemented on top of SHFileOperation in WinXP. */
590 hr = IShellFolder_SetNameOf(psfPersonal, NULL, pidlNew, wszAbsoluteFilename,
591 SHGDN_FORPARSING, NULL);
592 ok (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED), "SetNameOf succeeded! hr = %08x\n", hr);
594 /* Rename the file back to its original name. SetNameOf ignores the fact, that the
595 * SHGDN flags specify an absolute path. */
596 hr = IShellFolder_SetNameOf(psfPersonal, NULL, pidlNew, wszFileName, SHGDN_FORPARSING, NULL);
597 ok (hr == S_OK, "SetNameOf failed! hr = %08x\n", hr);
602 IShellFolder_Release(psfPersonal);
606 win_skip("Avoid needs of interaction on Win2k\n");
608 /* Deleting the file and the directory */
609 DeleteFileA(szTestFile);
610 RemoveDirectoryA(szTestDir);
612 /* SHGetPathFromIDListW still works, although the file is not present anymore. */
613 if (pSHGetPathFromIDListW)
615 result = pSHGetPathFromIDListW(pidlTestFile, wszTestFile2);
616 ok (result, "SHGetPathFromIDListW failed! Last error: %u\n", GetLastError());
617 ok (!lstrcmpiW(wszTestFile, wszTestFile2), "SHGetPathFromIDListW returns incorrect path!\n");
620 /* SHBindToParent fails, if called with a NULL PIDL. */
621 hr = pSHBindToParent(NULL, &IID_IShellFolder, (VOID**)&psfPersonal, &pidlLast);
622 ok (hr != S_OK, "SHBindToParent(NULL) should fail!\n");
624 /* But it succeeds with an empty PIDL. */
625 hr = pSHBindToParent(pidlEmpty, &IID_IShellFolder, (VOID**)&psfPersonal, &pidlLast);
626 ok (hr == S_OK, "SHBindToParent(empty PIDL) should succeed! hr = %08x\n", hr);
627 ok (pidlLast == pidlEmpty, "The last element of an empty PIDL should be the PIDL itself!\n");
629 IShellFolder_Release(psfPersonal);
631 /* Binding to the folder and querying the display name of the file also works. */
632 hr = pSHBindToParent(pidlTestFile, &IID_IShellFolder, (VOID**)&psfPersonal, &pidlLast);
633 ok (hr == S_OK, "SHBindToParent failed! hr = %08x\n", hr);
635 IShellFolder_Release(psfDesktop);
639 /* This test shows that Windows doesn't allocate a new pidlLast, but returns a pointer into
640 * pidlTestFile (In accordance with MSDN). */
641 ok (pILFindLastID(pidlTestFile) == pidlLast,
642 "SHBindToParent doesn't return the last id of the pidl param!\n");
644 hr = IShellFolder_GetDisplayNameOf(psfPersonal, pidlLast, SHGDN_FORPARSING, &strret);
645 ok (hr == S_OK, "Personal->GetDisplayNameOf failed! hr = %08x\n", hr);
647 IShellFolder_Release(psfDesktop);
648 IShellFolder_Release(psfPersonal);
654 hr = pStrRetToBufW(&strret, pidlLast, wszTestFile2, MAX_PATH);
655 ok (hr == S_OK, "StrRetToBufW failed! hr = %08x\n", hr);
656 ok (!lstrcmpiW(wszTestFile, wszTestFile2), "GetDisplayNameOf returns incorrect path!\n");
659 ILFree(pidlTestFile);
660 IShellFolder_Release(psfDesktop);
661 IShellFolder_Release(psfPersonal);
664 static void test_CallForAttributes(void)
670 LPSHELLFOLDER psfDesktop;
671 LPITEMIDLIST pidlMyDocuments;
672 DWORD dwAttributes, dwCallForAttributes, dwOrigAttributes, dwOrigCallForAttributes;
673 static const WCHAR wszAttributes[] = { 'A','t','t','r','i','b','u','t','e','s',0 };
674 static const WCHAR wszCallForAttributes[] = {
675 'C','a','l','l','F','o','r','A','t','t','r','i','b','u','t','e','s',0 };
676 static const WCHAR wszMyDocumentsKey[] = {
677 'C','L','S','I','D','\\','{','4','5','0','D','8','F','B','A','-','A','D','2','5','-',
678 '1','1','D','0','-','9','8','A','8','-','0','8','0','0','3','6','1','B','1','1','0','3','}',
679 '\\','S','h','e','l','l','F','o','l','d','e','r',0 };
680 WCHAR wszMyDocuments[] = {
681 ':',':','{','4','5','0','D','8','F','B','A','-','A','D','2','5','-','1','1','D','0','-',
682 '9','8','A','8','-','0','8','0','0','3','6','1','B','1','1','0','3','}',0 };
684 /* For the root of a namespace extension, the attributes are not queried by binding
685 * to the object and calling GetAttributesOf. Instead, the attributes are read from
686 * the registry value HKCR/CLSID/{...}/ShellFolder/Attributes. This is documented on MSDN.
688 * The MyDocuments shellfolder on WinXP has a HKCR/CLSID/{...}/ShellFolder/CallForAttributes
689 * value. It seems that if the folder is queried for one of the flags set in CallForAttributes,
690 * the shell does bind to the folder object and calls GetAttributesOf. This is not documented
691 * on MSDN. This test is meant to document the observed behaviour on WinXP SP2.
693 hr = SHGetDesktopFolder(&psfDesktop);
694 ok (hr == S_OK, "SHGetDesktopFolder failed! hr = %08x\n", hr);
695 if (hr != S_OK) return;
697 hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyDocuments, NULL,
698 &pidlMyDocuments, NULL);
700 broken(hr == E_INVALIDARG), /* Win95, NT4 */
701 "Desktop's ParseDisplayName failed to parse MyDocuments's CLSID! hr = %08x\n", hr);
703 IShellFolder_Release(psfDesktop);
707 dwAttributes = 0xffffffff;
708 hr = IShellFolder_GetAttributesOf(psfDesktop, 1,
709 (LPCITEMIDLIST*)&pidlMyDocuments, &dwAttributes);
710 ok (hr == S_OK, "Desktop->GetAttributesOf(MyDocuments) failed! hr = %08x\n", hr);
712 /* We need the following setup (as observed on WinXP SP2), for the tests to make sense. */
713 ok (dwAttributes & SFGAO_FILESYSTEM, "SFGAO_FILESYSTEM attribute is not set for MyDocuments!\n");
714 ok (!(dwAttributes & SFGAO_ISSLOW), "SFGAO_ISSLOW attribute is set for MyDocuments!\n");
715 ok (!(dwAttributes & SFGAO_GHOSTED), "SFGAO_GHOSTED attribute is set for MyDocuments!\n");
717 /* We don't have the MyDocuments shellfolder in wine yet, and thus we don't have the registry
718 * key. So the test will return at this point, if run on wine.
720 lResult = RegOpenKeyExW(HKEY_CLASSES_ROOT, wszMyDocumentsKey, 0, KEY_WRITE|KEY_READ, &hKey);
721 ok (lResult == ERROR_SUCCESS ||
722 lResult == ERROR_ACCESS_DENIED,
723 "RegOpenKeyEx failed! result: %08x\n", lResult);
724 if (lResult != ERROR_SUCCESS) {
725 if (lResult == ERROR_ACCESS_DENIED)
726 skip("Not enough rights to open the registry key\n");
727 IMalloc_Free(ppM, pidlMyDocuments);
728 IShellFolder_Release(psfDesktop);
732 /* Query MyDocuments' Attributes value, to be able to restore it later. */
733 dwSize = sizeof(DWORD);
734 lResult = RegQueryValueExW(hKey, wszAttributes, NULL, NULL, (LPBYTE)&dwOrigAttributes, &dwSize);
735 ok (lResult == ERROR_SUCCESS, "RegQueryValueEx failed! result: %08x\n", lResult);
736 if (lResult != ERROR_SUCCESS) {
738 IMalloc_Free(ppM, pidlMyDocuments);
739 IShellFolder_Release(psfDesktop);
743 /* Query MyDocuments' CallForAttributes value, to be able to restore it later. */
744 dwSize = sizeof(DWORD);
745 lResult = RegQueryValueExW(hKey, wszCallForAttributes, NULL, NULL,
746 (LPBYTE)&dwOrigCallForAttributes, &dwSize);
747 ok (lResult == ERROR_SUCCESS, "RegQueryValueEx failed! result: %08x\n", lResult);
748 if (lResult != ERROR_SUCCESS) {
750 IMalloc_Free(ppM, pidlMyDocuments);
751 IShellFolder_Release(psfDesktop);
755 /* Define via the Attributes value that MyDocuments attributes are SFGAO_ISSLOW and
756 * SFGAO_GHOSTED and that MyDocuments should be called for the SFGAO_ISSLOW and
757 * SFGAO_FILESYSTEM attributes. */
758 dwAttributes = SFGAO_ISSLOW|SFGAO_GHOSTED;
759 RegSetValueExW(hKey, wszAttributes, 0, REG_DWORD, (LPBYTE)&dwAttributes, sizeof(DWORD));
760 dwCallForAttributes = SFGAO_ISSLOW|SFGAO_FILESYSTEM;
761 RegSetValueExW(hKey, wszCallForAttributes, 0, REG_DWORD,
762 (LPBYTE)&dwCallForAttributes, sizeof(DWORD));
764 /* Although it is not set in CallForAttributes, the SFGAO_GHOSTED flag is reset by
765 * GetAttributesOf. It seems that once there is a single attribute queried, for which
766 * CallForAttributes is set, all flags are taken from the GetAttributesOf call and
767 * the flags in Attributes are ignored.
769 dwAttributes = SFGAO_ISSLOW|SFGAO_GHOSTED|SFGAO_FILESYSTEM;
770 hr = IShellFolder_GetAttributesOf(psfDesktop, 1,
771 (LPCITEMIDLIST*)&pidlMyDocuments, &dwAttributes);
772 ok (hr == S_OK, "Desktop->GetAttributesOf(MyDocuments) failed! hr = %08x\n", hr);
774 ok (dwAttributes == SFGAO_FILESYSTEM,
775 "Desktop->GetAttributes(MyDocuments) returned unexpected attributes: %08x\n",
778 /* Restore MyDocuments' original Attributes and CallForAttributes registry values */
779 RegSetValueExW(hKey, wszAttributes, 0, REG_DWORD, (LPBYTE)&dwOrigAttributes, sizeof(DWORD));
780 RegSetValueExW(hKey, wszCallForAttributes, 0, REG_DWORD,
781 (LPBYTE)&dwOrigCallForAttributes, sizeof(DWORD));
783 IMalloc_Free(ppM, pidlMyDocuments);
784 IShellFolder_Release(psfDesktop);
787 static void test_GetAttributesOf(void)
790 LPSHELLFOLDER psfDesktop, psfMyComputer;
791 SHITEMID emptyitem = { 0, { 0 } };
792 LPCITEMIDLIST pidlEmpty = (LPCITEMIDLIST)&emptyitem;
793 LPITEMIDLIST pidlMyComputer;
795 static const DWORD desktopFlags[] = {
797 SFGAO_STORAGE | SFGAO_HASPROPSHEET | SFGAO_STORAGEANCESTOR | SFGAO_FILESYSANCESTOR |
798 SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_HASSUBFOLDER,
800 SFGAO_CANRENAME | SFGAO_HASPROPSHEET | SFGAO_STREAM | SFGAO_FILESYSANCESTOR |
801 SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_HASSUBFOLDER,
802 /* WinMe, Win9x, WinNT*/
803 SFGAO_CANRENAME | SFGAO_HASPROPSHEET | SFGAO_FILESYSANCESTOR |
804 SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_HASSUBFOLDER
806 static const DWORD myComputerFlags[] = {
808 SFGAO_CANRENAME | SFGAO_CANDELETE | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET |
809 SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER,
811 SFGAO_CANRENAME | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET | SFGAO_STREAM |
812 SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER,
813 /* WinMe, Win9x, WinNT */
814 SFGAO_CANRENAME | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET | SFGAO_FILESYSANCESTOR |
815 SFGAO_FOLDER | SFGAO_HASSUBFOLDER,
816 /* Win95, WinNT when queried directly */
817 SFGAO_CANLINK | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET | SFGAO_FILESYSANCESTOR |
818 SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_HASSUBFOLDER
820 WCHAR wszMyComputer[] = {
821 ':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
822 'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };
823 char cCurrDirA [MAX_PATH] = {0};
824 WCHAR cCurrDirW [MAX_PATH];
825 static WCHAR cTestDirW[] = {'t','e','s','t','d','i','r',0};
826 IShellFolder *IDesktopFolder, *testIShellFolder;
829 BOOL foundFlagsMatch;
831 hr = SHGetDesktopFolder(&psfDesktop);
832 ok (hr == S_OK, "SHGetDesktopFolder failed! hr = %08x\n", hr);
833 if (hr != S_OK) return;
835 /* The Desktop attributes can be queried with a single empty itemidlist, .. */
836 dwFlags = 0xffffffff;
837 hr = IShellFolder_GetAttributesOf(psfDesktop, 1, &pidlEmpty, &dwFlags);
838 ok (hr == S_OK, "Desktop->GetAttributesOf(empty pidl) failed! hr = %08x\n", hr);
839 for (i = 0, foundFlagsMatch = FALSE; !foundFlagsMatch &&
840 i < sizeof(desktopFlags) / sizeof(desktopFlags[0]); i++)
842 if (desktopFlags[i] == dwFlags)
843 foundFlagsMatch = TRUE;
845 ok (foundFlagsMatch, "Wrong Desktop attributes: %08x\n", dwFlags);
847 /* .. or with no itemidlist at all. */
848 dwFlags = 0xffffffff;
849 hr = IShellFolder_GetAttributesOf(psfDesktop, 0, NULL, &dwFlags);
850 ok (hr == S_OK, "Desktop->GetAttributesOf(NULL) failed! hr = %08x\n", hr);
851 for (i = 0, foundFlagsMatch = FALSE; !foundFlagsMatch &&
852 i < sizeof(desktopFlags) / sizeof(desktopFlags[0]); i++)
854 if (desktopFlags[i] == dwFlags)
855 foundFlagsMatch = TRUE;
857 ok (foundFlagsMatch, "Wrong Desktop attributes: %08x\n", dwFlags);
859 /* Testing the attributes of the MyComputer shellfolder */
860 hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyComputer, NULL, &pidlMyComputer, NULL);
861 ok (hr == S_OK, "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08x\n", hr);
863 IShellFolder_Release(psfDesktop);
867 /* Windows sets the SFGAO_CANLINK flag, when MyComputer is queried via the Desktop
868 * folder object. It doesn't do this, if MyComputer is queried directly (see below).
870 dwFlags = 0xffffffff;
871 hr = IShellFolder_GetAttributesOf(psfDesktop, 1, (LPCITEMIDLIST*)&pidlMyComputer, &dwFlags);
872 ok (hr == S_OK, "Desktop->GetAttributesOf(MyComputer) failed! hr = %08x\n", hr);
873 for (i = 0, foundFlagsMatch = FALSE; !foundFlagsMatch &&
874 i < sizeof(myComputerFlags) / sizeof(myComputerFlags[0]); i++)
876 if ((myComputerFlags[i] | SFGAO_CANLINK) == dwFlags)
877 foundFlagsMatch = TRUE;
880 ok (foundFlagsMatch, "Wrong MyComputer attributes: %08x\n", dwFlags);
882 hr = IShellFolder_BindToObject(psfDesktop, pidlMyComputer, NULL, &IID_IShellFolder, (LPVOID*)&psfMyComputer);
883 ok (hr == S_OK, "Desktop failed to bind to MyComputer object! hr = %08x\n", hr);
884 IShellFolder_Release(psfDesktop);
885 IMalloc_Free(ppM, pidlMyComputer);
886 if (hr != S_OK) return;
888 hr = IShellFolder_GetAttributesOf(psfMyComputer, 1, &pidlEmpty, &dwFlags);
890 ok (hr == E_INVALIDARG ||
891 broken(hr == S_OK), /* W2K and earlier */
892 "MyComputer->GetAttributesOf(emtpy pidl) should fail! hr = %08x\n", hr);
894 dwFlags = 0xffffffff;
895 hr = IShellFolder_GetAttributesOf(psfMyComputer, 0, NULL, &dwFlags);
896 ok (hr == S_OK, "MyComputer->GetAttributesOf(NULL) failed! hr = %08x\n", hr);
897 for (i = 0, foundFlagsMatch = FALSE; !foundFlagsMatch &&
898 i < sizeof(myComputerFlags) / sizeof(myComputerFlags[0]); i++)
900 if (myComputerFlags[i] == dwFlags)
901 foundFlagsMatch = TRUE;
904 ok (foundFlagsMatch, "Wrong MyComputer attributes: %08x\n", dwFlags);
906 IShellFolder_Release(psfMyComputer);
908 GetCurrentDirectoryA(MAX_PATH, cCurrDirA);
909 len = lstrlenA(cCurrDirA);
912 win_skip("GetCurrentDirectoryA returned empty string. Skipping test_GetAttributesOf\n");
915 if (len > 3 && cCurrDirA[len-1] == '\\')
916 cCurrDirA[len-1] = 0;
918 /* create test directory */
919 CreateFilesFolders();
921 MultiByteToWideChar(CP_ACP, 0, cCurrDirA, -1, cCurrDirW, MAX_PATH);
923 hr = SHGetDesktopFolder(&IDesktopFolder);
924 ok(hr == S_OK, "SHGetDesktopfolder failed %08x\n", hr);
926 hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cCurrDirW, NULL, &newPIDL, 0);
927 ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
929 hr = IShellFolder_BindToObject(IDesktopFolder, newPIDL, NULL, (REFIID)&IID_IShellFolder, (LPVOID *)&testIShellFolder);
930 ok(hr == S_OK, "BindToObject failed %08x\n", hr);
932 IMalloc_Free(ppM, newPIDL);
934 /* get relative PIDL */
935 hr = IShellFolder_ParseDisplayName(testIShellFolder, NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
936 ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
938 /* test the shell attributes of the test directory using the relative PIDL */
939 dwFlags = SFGAO_FOLDER;
940 hr = IShellFolder_GetAttributesOf(testIShellFolder, 1, (LPCITEMIDLIST*)&newPIDL, &dwFlags);
941 ok (hr == S_OK, "Desktop->GetAttributesOf() failed! hr = %08x\n", hr);
942 ok ((dwFlags&SFGAO_FOLDER), "Wrong directory attribute for relative PIDL: %08x\n", dwFlags);
945 IMalloc_Free(ppM, newPIDL);
947 /* append testdirectory name to path */
948 if (cCurrDirA[len-1] == '\\')
949 cCurrDirA[len-1] = 0;
950 lstrcatA(cCurrDirA, "\\testdir");
951 MultiByteToWideChar(CP_ACP, 0, cCurrDirA, -1, cCurrDirW, MAX_PATH);
953 hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cCurrDirW, NULL, &newPIDL, 0);
954 ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
956 /* test the shell attributes of the test directory using the absolute PIDL */
957 dwFlags = SFGAO_FOLDER;
958 hr = IShellFolder_GetAttributesOf(IDesktopFolder, 1, (LPCITEMIDLIST*)&newPIDL, &dwFlags);
959 ok (hr == S_OK, "Desktop->GetAttributesOf() failed! hr = %08x\n", hr);
960 ok ((dwFlags&SFGAO_FOLDER), "Wrong directory attribute for absolute PIDL: %08x\n", dwFlags);
963 IMalloc_Free(ppM, newPIDL);
965 IShellFolder_Release(testIShellFolder);
969 IShellFolder_Release(IDesktopFolder);
972 static void test_SHGetPathFromIDList(void)
974 SHITEMID emptyitem = { 0, { 0 } };
975 LPCITEMIDLIST pidlEmpty = (LPCITEMIDLIST)&emptyitem;
976 LPITEMIDLIST pidlMyComputer;
977 WCHAR wszPath[MAX_PATH], wszDesktop[MAX_PATH];
980 LPSHELLFOLDER psfDesktop;
981 WCHAR wszMyComputer[] = {
982 ':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
983 'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };
984 WCHAR wszFileName[MAX_PATH];
985 LPITEMIDLIST pidlTestFile;
988 static WCHAR wszTestFile[] = {
989 'w','i','n','e','t','e','s','t','.','f','o','o',0 };
990 HRESULT (WINAPI *pSHGetSpecialFolderLocation)(HWND, int, LPITEMIDLIST *);
992 LPITEMIDLIST pidlPrograms;
994 if(!pSHGetPathFromIDListW || !pSHGetSpecialFolderPathW)
996 win_skip("SHGetPathFromIDListW() or SHGetSpecialFolderPathW() is missing\n");
1000 /* Calling SHGetPathFromIDListW with no pidl should return the empty string */
1003 result = pSHGetPathFromIDListW(NULL, wszPath);
1004 ok(!result, "Expected failure\n");
1005 ok(!wszPath[0], "Expected empty string\n");
1007 /* Calling SHGetPathFromIDListW with an empty pidl should return the desktop folder's path. */
1008 result = pSHGetSpecialFolderPathW(NULL, wszDesktop, CSIDL_DESKTOP, FALSE);
1009 ok(result, "SHGetSpecialFolderPathW(CSIDL_DESKTOP) failed! Last error: %u\n", GetLastError());
1010 if (!result) return;
1012 /* Check if we are on Win9x */
1013 SetLastError(0xdeadbeef);
1014 lstrcmpiW(wszDesktop, wszDesktop);
1015 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
1017 win_skip("Most W-calls are not implemented\n");
1021 result = pSHGetPathFromIDListW(pidlEmpty, wszPath);
1022 ok(result, "SHGetPathFromIDListW failed! Last error: %u\n", GetLastError());
1023 if (!result) return;
1024 ok(!lstrcmpiW(wszDesktop, wszPath), "SHGetPathFromIDListW didn't return desktop path for empty pidl!\n");
1026 /* MyComputer does not map to a filesystem path. SHGetPathFromIDListW should fail. */
1027 hr = SHGetDesktopFolder(&psfDesktop);
1028 ok (hr == S_OK, "SHGetDesktopFolder failed! hr = %08x\n", hr);
1029 if (hr != S_OK) return;
1031 hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyComputer, NULL, &pidlMyComputer, NULL);
1032 ok (hr == S_OK, "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08x\n", hr);
1034 IShellFolder_Release(psfDesktop);
1038 SetLastError(0xdeadbeef);
1041 result = pSHGetPathFromIDListW(pidlMyComputer, wszPath);
1042 ok (!result, "SHGetPathFromIDListW succeeded where it shouldn't!\n");
1043 ok (GetLastError()==0xdeadbeef ||
1044 GetLastError()==ERROR_SUCCESS, /* Vista and higher */
1045 "Unexpected last error from SHGetPathFromIDListW: %u\n", GetLastError());
1046 ok (!wszPath[0], "Expected empty path\n");
1048 IShellFolder_Release(psfDesktop);
1052 IMalloc_Free(ppM, pidlMyComputer);
1054 result = pSHGetSpecialFolderPathW(NULL, wszFileName, CSIDL_DESKTOPDIRECTORY, FALSE);
1055 ok(result, "SHGetSpecialFolderPathW failed! Last error: %u\n", GetLastError());
1057 IShellFolder_Release(psfDesktop);
1060 myPathAddBackslashW(wszFileName);
1061 lstrcatW(wszFileName, wszTestFile);
1062 hTestFile = CreateFileW(wszFileName, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL);
1063 ok(hTestFile != INVALID_HANDLE_VALUE, "CreateFileW failed! Last error: %u\n", GetLastError());
1064 if (hTestFile == INVALID_HANDLE_VALUE) {
1065 IShellFolder_Release(psfDesktop);
1068 CloseHandle(hTestFile);
1070 hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszTestFile, NULL, &pidlTestFile, NULL);
1071 ok (hr == S_OK, "Desktop's ParseDisplayName failed to parse filename hr = %08x\n", hr);
1073 IShellFolder_Release(psfDesktop);
1074 DeleteFileW(wszFileName);
1075 IMalloc_Free(ppM, pidlTestFile);
1079 /* This test is to show that the Desktop shellfolder prepends the CSIDL_DESKTOPDIRECTORY
1080 * path for files placed on the desktop, if called with SHGDN_FORPARSING. */
1081 hr = IShellFolder_GetDisplayNameOf(psfDesktop, pidlTestFile, SHGDN_FORPARSING, &strret);
1082 ok (hr == S_OK, "Desktop's GetDisplayNamfOf failed! hr = %08x\n", hr);
1083 IShellFolder_Release(psfDesktop);
1084 DeleteFileW(wszFileName);
1086 IMalloc_Free(ppM, pidlTestFile);
1091 pStrRetToBufW(&strret, pidlTestFile, wszPath, MAX_PATH);
1092 ok(0 == lstrcmpW(wszFileName, wszPath),
1093 "Desktop->GetDisplayNameOf(pidlTestFile, SHGDN_FORPARSING) "
1094 "returned incorrect path for file placed on desktop\n");
1097 result = pSHGetPathFromIDListW(pidlTestFile, wszPath);
1098 ok(result, "SHGetPathFromIDListW failed! Last error: %u\n", GetLastError());
1099 IMalloc_Free(ppM, pidlTestFile);
1100 if (!result) return;
1101 ok(0 == lstrcmpW(wszFileName, wszPath), "SHGetPathFromIDListW returned incorrect path for file placed on desktop\n");
1104 /* Test if we can get the path from the start menu "program files" PIDL. */
1105 hShell32 = GetModuleHandleA("shell32");
1106 pSHGetSpecialFolderLocation = (void *)GetProcAddress(hShell32, "SHGetSpecialFolderLocation");
1108 hr = pSHGetSpecialFolderLocation(NULL, CSIDL_PROGRAM_FILES, &pidlPrograms);
1109 ok(hr == S_OK, "SHGetFolderLocation failed: 0x%08x\n", hr);
1111 SetLastError(0xdeadbeef);
1112 result = pSHGetPathFromIDListW(pidlPrograms, wszPath);
1113 IMalloc_Free(ppM, pidlPrograms);
1114 ok(result, "SHGetPathFromIDListW failed\n");
1117 static void test_EnumObjects_and_CompareIDs(void)
1119 ITEMIDLIST *newPIDL;
1120 IShellFolder *IDesktopFolder, *testIShellFolder;
1121 char cCurrDirA [MAX_PATH] = {0};
1122 static const CHAR cTestDirA[] = "\\testdir";
1123 WCHAR cTestDirW[MAX_PATH];
1127 GetCurrentDirectoryA(MAX_PATH, cCurrDirA);
1128 len = lstrlenA(cCurrDirA);
1131 win_skip("GetCurrentDirectoryA returned empty string. Skipping test_EnumObjects_and_CompareIDs\n");
1134 if(cCurrDirA[len-1] == '\\')
1135 cCurrDirA[len-1] = 0;
1137 lstrcatA(cCurrDirA, cTestDirA);
1138 MultiByteToWideChar(CP_ACP, 0, cCurrDirA, -1, cTestDirW, MAX_PATH);
1140 hr = SHGetDesktopFolder(&IDesktopFolder);
1141 ok(hr == S_OK, "SHGetDesktopfolder failed %08x\n", hr);
1143 CreateFilesFolders();
1145 hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
1146 ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
1148 hr = IShellFolder_BindToObject(IDesktopFolder, newPIDL, NULL, (REFIID)&IID_IShellFolder, (LPVOID *)&testIShellFolder);
1149 ok(hr == S_OK, "BindToObject failed %08x\n", hr);
1151 test_EnumObjects(testIShellFolder);
1153 IShellFolder_Release(testIShellFolder);
1157 IMalloc_Free(ppM, newPIDL);
1159 IShellFolder_Release(IDesktopFolder);
1162 /* A simple implementation of an IPropertyBag, which returns fixed values for
1163 * 'Target' and 'Attributes' properties.
1165 static HRESULT WINAPI InitPropertyBag_IPropertyBag_QueryInterface(IPropertyBag *iface, REFIID riid,
1169 return E_INVALIDARG;
1171 if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IPropertyBag, riid)) {
1174 ok (FALSE, "InitPropertyBag asked for unknown interface!\n");
1175 return E_NOINTERFACE;
1178 IPropertyBag_AddRef(iface);
1182 static ULONG WINAPI InitPropertyBag_IPropertyBag_AddRef(IPropertyBag *iface) {
1186 static ULONG WINAPI InitPropertyBag_IPropertyBag_Release(IPropertyBag *iface) {
1190 static HRESULT WINAPI InitPropertyBag_IPropertyBag_Read(IPropertyBag *iface, LPCOLESTR pszPropName,
1191 VARIANT *pVar, IErrorLog *pErrorLog)
1193 static const WCHAR wszTargetSpecialFolder[] = {
1194 'T','a','r','g','e','t','S','p','e','c','i','a','l','F','o','l','d','e','r',0 };
1195 static const WCHAR wszTarget[] = {
1196 'T','a','r','g','e','t',0 };
1197 static const WCHAR wszAttributes[] = {
1198 'A','t','t','r','i','b','u','t','e','s',0 };
1199 static const WCHAR wszResolveLinkFlags[] = {
1200 'R','e','s','o','l','v','e','L','i','n','k','F','l','a','g','s',0 };
1201 static const WCHAR wszTargetKnownFolder[] = {
1202 'T','a','r','g','e','t','K','n','o','w','n','F','o','l','d','e','r',0 };
1203 static const WCHAR wszCLSID[] = {
1204 'C','L','S','I','D',0 };
1206 if (!lstrcmpW(pszPropName, wszTargetSpecialFolder)) {
1207 ok(V_VT(pVar) == VT_I4 ||
1208 broken(V_VT(pVar) == VT_BSTR), /* Win2k */
1209 "Wrong variant type for 'TargetSpecialFolder' property!\n");
1210 return E_INVALIDARG;
1213 if (!lstrcmpW(pszPropName, wszResolveLinkFlags))
1215 ok(V_VT(pVar) == VT_UI4, "Wrong variant type for 'ResolveLinkFlags' property!\n");
1216 return E_INVALIDARG;
1219 if (!lstrcmpW(pszPropName, wszTarget)) {
1220 WCHAR wszPath[MAX_PATH];
1223 ok(V_VT(pVar) == VT_BSTR ||
1224 broken(V_VT(pVar) == VT_EMPTY), /* Win2k */
1225 "Wrong variant type for 'Target' property!\n");
1226 if (V_VT(pVar) != VT_BSTR) return E_INVALIDARG;
1228 result = pSHGetSpecialFolderPathW(NULL, wszPath, CSIDL_DESKTOPDIRECTORY, FALSE);
1229 ok(result, "SHGetSpecialFolderPathW(DESKTOPDIRECTORY) failed! %u\n", GetLastError());
1230 if (!result) return E_INVALIDARG;
1232 V_BSTR(pVar) = SysAllocString(wszPath);
1236 if (!lstrcmpW(pszPropName, wszAttributes)) {
1237 ok(V_VT(pVar) == VT_UI4, "Wrong variant type for 'Attributes' property!\n");
1238 if (V_VT(pVar) != VT_UI4) return E_INVALIDARG;
1239 V_UI4(pVar) = SFGAO_FOLDER|SFGAO_HASSUBFOLDER|SFGAO_FILESYSANCESTOR|
1240 SFGAO_CANRENAME|SFGAO_FILESYSTEM;
1244 if (!lstrcmpW(pszPropName, wszTargetKnownFolder)) {
1245 ok(V_VT(pVar) == VT_BSTR, "Wrong variant type for 'TargetKnownFolder' property!\n");
1247 return E_INVALIDARG;
1250 if (!lstrcmpW(pszPropName, wszCLSID)) {
1251 ok(V_VT(pVar) == VT_EMPTY, "Wrong variant type for 'CLSID' property!\n");
1253 return E_INVALIDARG;
1256 ok(FALSE, "PropertyBag was asked for unknown property %s (vt=%d)!\n", wine_dbgstr_w(pszPropName), V_VT(pVar));
1257 return E_INVALIDARG;
1260 static HRESULT WINAPI InitPropertyBag_IPropertyBag_Write(IPropertyBag *iface, LPCOLESTR pszPropName,
1263 ok(FALSE, "Unexpected call to IPropertyBag_Write\n");
1267 static const IPropertyBagVtbl InitPropertyBag_IPropertyBagVtbl = {
1268 InitPropertyBag_IPropertyBag_QueryInterface,
1269 InitPropertyBag_IPropertyBag_AddRef,
1270 InitPropertyBag_IPropertyBag_Release,
1271 InitPropertyBag_IPropertyBag_Read,
1272 InitPropertyBag_IPropertyBag_Write
1275 static struct IPropertyBag InitPropertyBag = {
1276 &InitPropertyBag_IPropertyBagVtbl
1279 static void test_FolderShortcut(void) {
1280 IPersistPropertyBag *pPersistPropertyBag;
1281 IShellFolder *pShellFolder, *pDesktopFolder;
1282 IPersistFolder3 *pPersistFolder3;
1285 WCHAR wszDesktopPath[MAX_PATH], wszBuffer[MAX_PATH];
1288 LPITEMIDLIST pidlCurrentFolder, pidlWineTestFolder, pidlSubFolder;
1290 WCHAR wszWineTestFolder[] = {
1291 ':',':','{','9','B','3','5','2','E','B','F','-','2','7','6','5','-','4','5','C','1','-',
1292 'B','4','C','6','-','8','5','C','C','7','F','7','A','B','C','6','4','}',0 };
1293 WCHAR wszShellExtKey[] = { 'S','o','f','t','w','a','r','e','\\',
1294 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
1295 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
1296 'E','x','p','l','o','r','e','r','\\','D','e','s','k','t','o','p','\\',
1297 'N','a','m','e','S','p','a','c','e','\\',
1298 '{','9','b','3','5','2','e','b','f','-','2','7','6','5','-','4','5','c','1','-',
1299 'b','4','c','6','-','8','5','c','c','7','f','7','a','b','c','6','4','}',0 };
1301 WCHAR wszSomeSubFolder[] = { 'S','u','b','F','o','l','d','e','r', 0};
1302 static const GUID CLSID_UnixDosFolder =
1303 {0x9d20aae8, 0x0625, 0x44b0, {0x9c, 0xa7, 0x71, 0x88, 0x9c, 0x22, 0x54, 0xd9}};
1305 if (!pSHGetSpecialFolderPathW || !pStrRetToBufW) {
1306 win_skip("SHGetSpecialFolderPathW and/or StrRetToBufW are not available\n");
1310 if (!pSHGetFolderPathAndSubDirA)
1312 win_skip("FolderShortcut test doesn't work on Win2k\n");
1316 /* These tests basically show, that CLSID_FolderShortcuts are initialized
1317 * via their IPersistPropertyBag interface. And that the target folder
1318 * is taken from the IPropertyBag's 'Target' property.
1320 hr = CoCreateInstance(&CLSID_FolderShortcut, NULL, CLSCTX_INPROC_SERVER,
1321 &IID_IPersistPropertyBag, (LPVOID*)&pPersistPropertyBag);
1322 if (hr == REGDB_E_CLASSNOTREG) {
1323 win_skip("CLSID_FolderShortcut is not implemented\n");
1326 ok (hr == S_OK, "CoCreateInstance failed! hr = 0x%08x\n", hr);
1327 if (hr != S_OK) return;
1329 hr = IPersistPropertyBag_Load(pPersistPropertyBag, &InitPropertyBag, NULL);
1330 ok(hr == S_OK, "IPersistPropertyBag_Load failed! hr = %08x\n", hr);
1332 IPersistPropertyBag_Release(pPersistPropertyBag);
1336 hr = IPersistPropertyBag_QueryInterface(pPersistPropertyBag, &IID_IShellFolder,
1337 (LPVOID*)&pShellFolder);
1338 IPersistPropertyBag_Release(pPersistPropertyBag);
1339 ok(hr == S_OK, "IPersistPropertyBag_QueryInterface(IShellFolder) failed! hr = %08x\n", hr);
1340 if (hr != S_OK) return;
1342 hr = IShellFolder_GetDisplayNameOf(pShellFolder, NULL, SHGDN_FORPARSING, &strret);
1343 ok(hr == S_OK, "IShellFolder_GetDisplayNameOf(NULL) failed! hr = %08x\n", hr);
1345 IShellFolder_Release(pShellFolder);
1349 result = pSHGetSpecialFolderPathW(NULL, wszDesktopPath, CSIDL_DESKTOPDIRECTORY, FALSE);
1350 ok(result, "SHGetSpecialFolderPathW(CSIDL_DESKTOPDIRECTORY) failed! %u\n", GetLastError());
1351 if (!result) return;
1353 pStrRetToBufW(&strret, NULL, wszBuffer, MAX_PATH);
1354 ok(!lstrcmpiW(wszDesktopPath, wszBuffer), "FolderShortcut returned incorrect folder!\n");
1356 hr = IShellFolder_QueryInterface(pShellFolder, &IID_IPersistFolder3, (LPVOID*)&pPersistFolder3);
1357 IShellFolder_Release(pShellFolder);
1358 ok(hr == S_OK, "IShellFolder_QueryInterface(IID_IPersistFolder3 failed! hr = 0x%08x\n", hr);
1359 if (hr != S_OK) return;
1361 hr = IPersistFolder3_GetClassID(pPersistFolder3, &clsid);
1362 ok(hr == S_OK, "IPersistFolder3_GetClassID failed! hr=0x%08x\n", hr);
1363 ok(IsEqualCLSID(&clsid, &CLSID_FolderShortcut), "Unexpected CLSID!\n");
1365 hr = IPersistFolder3_GetCurFolder(pPersistFolder3, &pidlCurrentFolder);
1366 todo_wine ok(hr == S_FALSE, "IPersistFolder3_GetCurFolder failed! hr=0x%08x\n", hr);
1367 ok(!pidlCurrentFolder, "IPersistFolder3_GetCurFolder should return a NULL pidl!\n");
1369 /* For FolderShortcut objects, the Initialize method initialized the folder's position in the
1370 * shell namespace. The target folder, read from the property bag above, remains untouched.
1371 * The following tests show this: The itemidlist for some imaginary shellfolder object
1372 * is created and the FolderShortcut is initialized with it. GetCurFolder now returns this
1373 * itemidlist, but GetDisplayNameOf still returns the path from above.
1375 hr = SHGetDesktopFolder(&pDesktopFolder);
1376 ok (hr == S_OK, "SHGetDesktopFolder failed! hr = %08x\n", hr);
1377 if (hr != S_OK) return;
1379 /* Temporarily register WineTestFolder as a shell namespace extension at the Desktop.
1380 * Otherwise ParseDisplayName fails on WinXP with E_INVALIDARG */
1381 RegCreateKeyW(HKEY_CURRENT_USER, wszShellExtKey, &hShellExtKey);
1382 RegCloseKey(hShellExtKey);
1383 hr = IShellFolder_ParseDisplayName(pDesktopFolder, NULL, NULL, wszWineTestFolder, NULL,
1384 &pidlWineTestFolder, NULL);
1385 RegDeleteKeyW(HKEY_CURRENT_USER, wszShellExtKey);
1386 IShellFolder_Release(pDesktopFolder);
1387 ok (hr == S_OK, "IShellFolder::ParseDisplayName failed! hr = %08x\n", hr);
1388 if (hr != S_OK) return;
1390 hr = IPersistFolder3_Initialize(pPersistFolder3, pidlWineTestFolder);
1391 ok (hr == S_OK, "IPersistFolder3::Initialize failed! hr = %08x\n", hr);
1393 IPersistFolder3_Release(pPersistFolder3);
1394 pILFree(pidlWineTestFolder);
1398 hr = IPersistFolder3_GetCurFolder(pPersistFolder3, &pidlCurrentFolder);
1399 ok(hr == S_OK, "IPersistFolder3_GetCurFolder failed! hr=0x%08x\n", hr);
1400 ok(pILIsEqual(pidlCurrentFolder, pidlWineTestFolder),
1401 "IPersistFolder3_GetCurFolder should return pidlWineTestFolder!\n");
1402 pILFree(pidlCurrentFolder);
1403 pILFree(pidlWineTestFolder);
1405 hr = IPersistFolder3_QueryInterface(pPersistFolder3, &IID_IShellFolder, (LPVOID*)&pShellFolder);
1406 IPersistFolder3_Release(pPersistFolder3);
1407 ok(hr == S_OK, "IPersistFolder3_QueryInterface(IShellFolder) failed! hr = %08x\n", hr);
1408 if (hr != S_OK) return;
1410 hr = IShellFolder_GetDisplayNameOf(pShellFolder, NULL, SHGDN_FORPARSING, &strret);
1411 ok(hr == S_OK, "IShellFolder_GetDisplayNameOf(NULL) failed! hr = %08x\n", hr);
1413 IShellFolder_Release(pShellFolder);
1417 pStrRetToBufW(&strret, NULL, wszBuffer, MAX_PATH);
1418 ok(!lstrcmpiW(wszDesktopPath, wszBuffer), "FolderShortcut returned incorrect folder!\n");
1420 /* Next few lines are meant to show that children of FolderShortcuts are not FolderShortcuts,
1421 * but ShellFSFolders. */
1422 myPathAddBackslashW(wszDesktopPath);
1423 lstrcatW(wszDesktopPath, wszSomeSubFolder);
1424 if (!CreateDirectoryW(wszDesktopPath, NULL)) {
1425 IShellFolder_Release(pShellFolder);
1429 hr = IShellFolder_ParseDisplayName(pShellFolder, NULL, NULL, wszSomeSubFolder, NULL,
1430 &pidlSubFolder, NULL);
1431 RemoveDirectoryW(wszDesktopPath);
1432 ok (hr == S_OK, "IShellFolder::ParseDisplayName failed! hr = %08x\n", hr);
1434 IShellFolder_Release(pShellFolder);
1438 hr = IShellFolder_BindToObject(pShellFolder, pidlSubFolder, NULL, &IID_IPersistFolder3,
1439 (LPVOID*)&pPersistFolder3);
1440 IShellFolder_Release(pShellFolder);
1441 pILFree(pidlSubFolder);
1442 ok (hr == S_OK, "IShellFolder::BindToObject failed! hr = %08x\n", hr);
1446 /* On windows, we expect CLSID_ShellFSFolder. On wine we relax this constraint
1447 * a little bit and also allow CLSID_UnixDosFolder. */
1448 hr = IPersistFolder3_GetClassID(pPersistFolder3, &clsid);
1449 ok(hr == S_OK, "IPersistFolder3_GetClassID failed! hr=0x%08x\n", hr);
1450 ok(IsEqualCLSID(&clsid, &CLSID_ShellFSFolder) || IsEqualCLSID(&clsid, &CLSID_UnixDosFolder),
1451 "IPersistFolder3::GetClassID returned unexpected CLSID!\n");
1453 IPersistFolder3_Release(pPersistFolder3);
1456 #include "pshpack1.h"
1457 struct FileStructA {
1461 WORD uFileDate; /* In our current implementation this is */
1462 WORD uFileTime; /* FileTimeToDosDate(WIN32_FIND_DATA->ftLastWriteTime) */
1467 struct FileStructW {
1468 WORD cbLen; /* Length of this element. */
1469 BYTE abFooBar1[6]; /* Beyond any recognition. */
1470 WORD uDate; /* FileTimeToDosDate(WIN32_FIND_DATA->ftCreationTime)? */
1471 WORD uTime; /* (this is currently speculation) */
1472 WORD uDate2; /* FileTimeToDosDate(WIN32_FIND_DATA->ftLastAccessTime)? */
1473 WORD uTime2; /* (this is currently speculation) */
1474 BYTE abFooBar2[4]; /* Beyond any recognition. */
1475 WCHAR wszName[1]; /* The long filename in unicode. */
1476 /* Just for documentation: Right after the unicode string: */
1477 WORD cbOffset; /* FileStructW's offset from the beginning of the SHITMEID.
1478 * SHITEMID->cb == uOffset + cbLen */
1480 #include "poppack.h"
1482 static void test_ITEMIDLIST_format(void) {
1483 WCHAR wszPersonal[MAX_PATH];
1484 LPSHELLFOLDER psfDesktop, psfPersonal;
1485 LPITEMIDLIST pidlPersonal, pidlFile;
1489 WCHAR wszFile[3][17] = { { 'e','v','e','n','_',0 }, { 'o','d','d','_',0 },
1490 { 'l','o','n','g','e','r','_','t','h','a','n','.','8','_','3',0 } };
1493 if (!pSHGetSpecialFolderPathW) return;
1495 bResult = pSHGetSpecialFolderPathW(NULL, wszPersonal, CSIDL_PERSONAL, FALSE);
1496 ok(bResult, "SHGetSpecialFolderPathW failed! Last error: %u\n", GetLastError());
1497 if (!bResult) return;
1499 SetLastError(0xdeadbeef);
1500 bResult = SetCurrentDirectoryW(wszPersonal);
1501 if (!bResult && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) {
1502 win_skip("Most W-calls are not implemented\n");
1505 ok(bResult, "SetCurrentDirectory failed! Last error: %u\n", GetLastError());
1506 if (!bResult) return;
1508 hr = SHGetDesktopFolder(&psfDesktop);
1509 ok(hr == S_OK, "SHGetDesktopFolder failed! hr: %08x\n", hr);
1510 if (hr != S_OK) return;
1512 hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszPersonal, NULL, &pidlPersonal, NULL);
1513 ok(hr == S_OK, "psfDesktop->ParseDisplayName failed! hr = %08x\n", hr);
1515 IShellFolder_Release(psfDesktop);
1519 hr = IShellFolder_BindToObject(psfDesktop, pidlPersonal, NULL, &IID_IShellFolder,
1520 (LPVOID*)&psfPersonal);
1521 IShellFolder_Release(psfDesktop);
1522 pILFree(pidlPersonal);
1523 ok(hr == S_OK, "psfDesktop->BindToObject failed! hr = %08x\n", hr);
1524 if (hr != S_OK) return;
1526 for (i=0; i<3; i++) {
1527 CHAR szFile[MAX_PATH];
1528 struct FileStructA *pFileStructA;
1531 WideCharToMultiByte(CP_ACP, 0, wszFile[i], -1, szFile, MAX_PATH, NULL, NULL);
1533 hFile = CreateFileW(wszFile[i], GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_FLAG_WRITE_THROUGH, NULL);
1534 ok(hFile != INVALID_HANDLE_VALUE, "CreateFile failed! (%u)\n", GetLastError());
1535 if (hFile == INVALID_HANDLE_VALUE) {
1536 IShellFolder_Release(psfPersonal);
1541 hr = IShellFolder_ParseDisplayName(psfPersonal, NULL, NULL, wszFile[i], NULL, &pidlFile, NULL);
1542 DeleteFileW(wszFile[i]);
1543 ok(hr == S_OK, "psfPersonal->ParseDisplayName failed! hr: %08x\n", hr);
1545 IShellFolder_Release(psfPersonal);
1549 pFileStructA = (struct FileStructA *)pidlFile->mkid.abID;
1550 ok(pFileStructA->type == 0x32, "PIDLTYPE should be 0x32!\n");
1551 ok(pFileStructA->dummy == 0x00, "Dummy Byte should be 0x00!\n");
1552 ok(pFileStructA->dwFileSize == 0, "Filesize should be zero!\n");
1554 if (i < 2) /* First two file names are already in valid 8.3 format */
1555 ok(!strcmp(szFile, (CHAR*)&pidlFile->mkid.abID[12]), "Wrong file name!\n");
1557 /* WinXP stores a derived 8.3 dos name (LONGER~1.8_3) here. We probably
1558 * can't implement this correctly, since unix filesystems don't support
1559 * this nasty short/long filename stuff. So we'll probably stay with our
1560 * current habbit of storing the long filename here, which seems to work
1563 ok(pidlFile->mkid.abID[18] == '~' ||
1564 broken(pidlFile->mkid.abID[34] == '~'), /* Win2k */
1565 "Should be derived 8.3 name!\n");
1567 if (i == 0) /* First file name has an even number of chars. No need for alignment. */
1568 ok(pidlFile->mkid.abID[12 + strlen(szFile) + 1] != '\0' ||
1569 broken(pidlFile->mkid.cb == 2 + 12 + strlen(szFile) + 1 + 1), /* Win2k */
1570 "Alignment byte, where there shouldn't be!\n");
1572 if (i == 1) /* Second file name has an uneven number of chars => alignment byte */
1573 ok(pidlFile->mkid.abID[12 + strlen(szFile) + 1] == '\0',
1574 "There should be an alignment byte, but isn't!\n");
1576 /* The offset of the FileStructW member is stored as a WORD at the end of the pidl. */
1577 cbOffset = *(WORD*)(((LPBYTE)pidlFile)+pidlFile->mkid.cb-sizeof(WORD));
1578 ok ((cbOffset >= sizeof(struct FileStructA) &&
1579 cbOffset <= pidlFile->mkid.cb - sizeof(struct FileStructW)) ||
1580 broken(pidlFile->mkid.cb == 2 + 12 + strlen(szFile) + 1 + 1) || /* Win2k on short names */
1581 broken(pidlFile->mkid.cb == 2 + 12 + strlen(szFile) + 1 + 12 + 1), /* Win2k on long names */
1582 "Wrong offset value (%d) stored at the end of the PIDL\n", cbOffset);
1584 if (cbOffset >= sizeof(struct FileStructA) &&
1585 cbOffset <= pidlFile->mkid.cb - sizeof(struct FileStructW))
1587 struct FileStructW *pFileStructW = (struct FileStructW *)(((LPBYTE)pidlFile)+cbOffset);
1589 ok(pidlFile->mkid.cb == cbOffset + pFileStructW->cbLen,
1590 "FileStructW's offset and length should add up to the PIDL's length!\n");
1592 if (pidlFile->mkid.cb == cbOffset + pFileStructW->cbLen) {
1593 /* Since we just created the file, time of creation,
1594 * time of last access and time of last write access just be the same.
1595 * These tests seem to fail sometimes (on WinXP), if the test is run again shortly
1596 * after the first run. I do remember something with NTFS keeping the creation time
1597 * if a file is deleted and then created again within a couple of seconds or so.
1598 * Might be the reason. */
1599 ok (pFileStructA->uFileDate == pFileStructW->uDate &&
1600 pFileStructA->uFileTime == pFileStructW->uTime,
1601 "Last write time should match creation time!\n");
1603 /* On FAT filesystems the last access time is midnight
1604 local time, so the values of uDate2 and uTime2 will
1605 depend on the local timezone. If the times are exactly
1606 equal then the dates should be identical for both FAT
1607 and NTFS as no timezone is more than 1 day away from UTC.
1609 if (pFileStructA->uFileTime == pFileStructW->uTime2)
1611 ok (pFileStructA->uFileDate == pFileStructW->uDate2,
1612 "Last write date and time should match last access date and time!\n");
1616 /* Filesystem may be FAT. Check date within 1 day
1617 and seconds are zero. */
1618 trace ("Filesystem may be FAT. Performing less strict atime test.\n");
1619 ok ((pFileStructW->uTime2 & 0x1F) == 0,
1620 "Last access time on FAT filesystems should have zero seconds.\n");
1621 /* TODO: Perform check for date being within one day.*/
1624 ok (!lstrcmpW(wszFile[i], pFileStructW->wszName) ||
1625 !lstrcmpW(wszFile[i], (WCHAR *)(pFileStructW->abFooBar2 + 22)) || /* Vista */
1626 !lstrcmpW(wszFile[i], (WCHAR *)(pFileStructW->abFooBar2 + 26)), /* Win7 */
1627 "The filename should be stored in unicode at this position!\n");
1634 IShellFolder_Release(psfPersonal);
1637 static void test_SHGetFolderPathAndSubDirA(void)
1643 static char wine[] = "wine";
1644 static char winetemp[] = "wine\\temp";
1645 static char appdata[MAX_PATH];
1646 static char testpath[MAX_PATH];
1647 static char toolongpath[MAX_PATH+1];
1649 if(!pSHGetFolderPathAndSubDirA)
1651 win_skip("SHGetFolderPathAndSubDirA not present!\n");
1655 if(!pSHGetFolderPathA) {
1656 win_skip("SHGetFolderPathA not present!\n");
1659 if(FAILED(pSHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, appdata)))
1661 win_skip("SHGetFolderPathA failed for CSIDL_LOCAL_APPDATA!\n");
1665 sprintf(testpath, "%s\\%s", appdata, winetemp);
1666 delret = RemoveDirectoryA(testpath);
1667 if(!delret && (ERROR_PATH_NOT_FOUND != GetLastError()) ) {
1668 win_skip("RemoveDirectoryA(%s) failed with error %u\n", testpath, GetLastError());
1672 sprintf(testpath, "%s\\%s", appdata, wine);
1673 delret = RemoveDirectoryA(testpath);
1674 if(!delret && (ERROR_PATH_NOT_FOUND != GetLastError()) && (ERROR_FILE_NOT_FOUND != GetLastError())) {
1675 win_skip("RemoveDirectoryA(%s) failed with error %u\n", testpath, GetLastError());
1679 /* test invalid second parameter */
1680 ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | 0xff, NULL, SHGFP_TYPE_CURRENT, wine, testpath);
1681 ok(E_INVALIDARG == ret, "expected E_INVALIDARG, got %x\n", ret);
1683 /* test fourth parameter */
1684 ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, 2, winetemp, testpath);
1686 case S_OK: /* winvista */
1687 ok(!strncmp(appdata, testpath, strlen(appdata)),
1688 "expected %s to start with %s\n", testpath, appdata);
1689 ok(!lstrcmpA(&testpath[1 + strlen(appdata)], winetemp),
1690 "expected %s to end with %s\n", testpath, winetemp);
1692 case E_INVALIDARG: /* winxp, win2k3 */
1695 ok(0, "expected S_OK or E_INVALIDARG, got %x\n", ret);
1698 /* test fifth parameter */
1700 ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, NULL, testpath);
1701 ok(S_OK == ret, "expected S_OK, got %x\n", ret);
1702 ok(!lstrcmpA(appdata, testpath), "expected %s, got %s\n", appdata, testpath);
1705 ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, "", testpath);
1706 ok(S_OK == ret, "expected S_OK, got %x\n", ret);
1707 ok(!lstrcmpA(appdata, testpath), "expected %s, got %s\n", appdata, testpath);
1710 ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, "\\", testpath);
1711 ok(S_OK == ret, "expected S_OK, got %x\n", ret);
1712 ok(!lstrcmpA(appdata, testpath), "expected %s, got %s\n", appdata, testpath);
1714 for(i=0; i< MAX_PATH; i++)
1715 toolongpath[i] = '0' + i % 10;
1716 toolongpath[MAX_PATH] = '\0';
1717 ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, toolongpath, testpath);
1718 ok(HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE) == ret,
1719 "expected %x, got %x\n", HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE), ret);
1722 ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, wine, NULL);
1723 ok((S_OK == ret) || (E_INVALIDARG == ret), "expected S_OK or E_INVALIDARG, got %x\n", ret);
1725 /* test a not existing path */
1727 ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, winetemp, testpath);
1728 ok(HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == ret,
1729 "expected %x, got %x\n", HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND), ret);
1731 /* create a directory inside a not existing directory */
1733 ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_CREATE | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, winetemp, testpath);
1734 ok(S_OK == ret, "expected S_OK, got %x\n", ret);
1735 ok(!strncmp(appdata, testpath, strlen(appdata)),
1736 "expected %s to start with %s\n", testpath, appdata);
1737 ok(!lstrcmpA(&testpath[1 + strlen(appdata)], winetemp),
1738 "expected %s to end with %s\n", testpath, winetemp);
1739 dwret = GetFileAttributes(testpath);
1740 ok(FILE_ATTRIBUTE_DIRECTORY | dwret, "expected %x to contain FILE_ATTRIBUTE_DIRECTORY\n", dwret);
1743 sprintf(testpath, "%s\\%s", appdata, winetemp);
1744 RemoveDirectoryA(testpath);
1745 sprintf(testpath, "%s\\%s", appdata, wine);
1746 RemoveDirectoryA(testpath);
1749 static void test_LocalizedNames(void)
1751 static char cCurrDirA[MAX_PATH];
1752 WCHAR cCurrDirW[MAX_PATH], tempbufW[25];
1753 IShellFolder *IDesktopFolder, *testIShellFolder;
1754 ITEMIDLIST *newPIDL;
1757 static char resourcefile[MAX_PATH];
1762 static const char desktopini_contents1[] =
1763 "[.ShellClassInfo]\r\n"
1764 "LocalizedResourceName=@";
1765 static const char desktopini_contents2[] =
1767 static WCHAR foldernameW[] = {'t','e','s','t','f','o','l','d','e','r',0};
1768 static const WCHAR folderdisplayW[] = {'F','o','l','d','e','r',' ','N','a','m','e',' ','R','e','s','o','u','r','c','e',0};
1770 /* create folder with desktop.ini and localized name in GetModuleFileNameA(NULL) */
1771 CreateDirectoryA(".\\testfolder", NULL);
1773 SetFileAttributesA(".\\testfolder", GetFileAttributesA(".\\testfolder")|FILE_ATTRIBUTE_SYSTEM);
1775 GetModuleFileNameA(NULL, resourcefile, MAX_PATH);
1777 file = CreateFileA(".\\testfolder\\desktop.ini", GENERIC_WRITE, 0, NULL,
1778 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
1779 ok(file != INVALID_HANDLE_VALUE, "CreateFileA failed %i\n", GetLastError());
1780 ok(WriteFile(file, desktopini_contents1, strlen(desktopini_contents1), &res, NULL) &&
1781 WriteFile(file, resourcefile, strlen(resourcefile), &res, NULL) &&
1782 WriteFile(file, desktopini_contents2, strlen(desktopini_contents2), &res, NULL),
1783 "WriteFile failed %i\n", GetLastError());
1786 /* get IShellFolder for parent */
1787 GetCurrentDirectoryA(MAX_PATH, cCurrDirA);
1788 len = lstrlenA(cCurrDirA);
1791 win_skip("GetCurrentDirectoryA returned empty string. Skipping test_LocalizedNames\n");
1794 if(cCurrDirA[len-1] == '\\')
1795 cCurrDirA[len-1] = 0;
1797 MultiByteToWideChar(CP_ACP, 0, cCurrDirA, -1, cCurrDirW, MAX_PATH);
1799 hr = SHGetDesktopFolder(&IDesktopFolder);
1800 ok(hr == S_OK, "SHGetDesktopfolder failed %08x\n", hr);
1802 hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cCurrDirW, NULL, &newPIDL, 0);
1803 ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
1805 hr = IShellFolder_BindToObject(IDesktopFolder, newPIDL, NULL, (REFIID)&IID_IShellFolder, (LPVOID *)&testIShellFolder);
1806 ok(hr == S_OK, "BindToObject failed %08x\n", hr);
1808 IMalloc_Free(ppM, newPIDL);
1810 /* windows reads the display name from the resource */
1811 hr = IShellFolder_ParseDisplayName(testIShellFolder, NULL, NULL, foldernameW, NULL, &newPIDL, 0);
1812 ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
1814 hr = IShellFolder_GetDisplayNameOf(testIShellFolder, newPIDL, SHGDN_INFOLDER, &strret);
1815 ok(hr == S_OK, "GetDisplayNameOf failed %08x\n", hr);
1817 if (hr == S_OK && pStrRetToBufW)
1819 hr = pStrRetToBufW(&strret, newPIDL, tempbufW, sizeof(tempbufW)/sizeof(WCHAR));
1820 ok (hr == S_OK, "StrRetToBufW failed! hr = %08x\n", hr);
1822 ok (!lstrcmpiW(tempbufW, folderdisplayW) ||
1823 broken(!lstrcmpiW(tempbufW, foldernameW)), /* W2K */
1824 "GetDisplayNameOf returned %s\n", wine_dbgstr_w(tempbufW));
1827 /* editing name is also read from the resource */
1828 hr = IShellFolder_GetDisplayNameOf(testIShellFolder, newPIDL, SHGDN_INFOLDER|SHGDN_FOREDITING, &strret);
1829 ok(hr == S_OK, "GetDisplayNameOf failed %08x\n", hr);
1831 if (hr == S_OK && pStrRetToBufW)
1833 hr = pStrRetToBufW(&strret, newPIDL, tempbufW, sizeof(tempbufW)/sizeof(WCHAR));
1834 ok (hr == S_OK, "StrRetToBufW failed! hr = %08x\n", hr);
1836 ok (!lstrcmpiW(tempbufW, folderdisplayW) ||
1837 broken(!lstrcmpiW(tempbufW, foldernameW)), /* W2K */
1838 "GetDisplayNameOf returned %s\n", wine_dbgstr_w(tempbufW));
1841 /* parsing name is unchanged */
1842 hr = IShellFolder_GetDisplayNameOf(testIShellFolder, newPIDL, SHGDN_INFOLDER|SHGDN_FORPARSING, &strret);
1843 ok(hr == S_OK, "GetDisplayNameOf failed %08x\n", hr);
1845 if (hr == S_OK && pStrRetToBufW)
1847 hr = pStrRetToBufW(&strret, newPIDL, tempbufW, sizeof(tempbufW)/sizeof(WCHAR));
1848 ok (hr == S_OK, "StrRetToBufW failed! hr = %08x\n", hr);
1849 ok (!lstrcmpiW(tempbufW, foldernameW), "GetDisplayNameOf returned %s\n", wine_dbgstr_w(tempbufW));
1852 IShellFolder_Release(IDesktopFolder);
1853 IShellFolder_Release(testIShellFolder);
1855 IMalloc_Free(ppM, newPIDL);
1858 DeleteFileA(".\\testfolder\\desktop.ini");
1859 SetFileAttributesA(".\\testfolder", GetFileAttributesA(".\\testfolder")&~FILE_ATTRIBUTE_SYSTEM);
1860 RemoveDirectoryA(".\\testfolder");
1863 static void test_SHCreateShellItem(void)
1865 IShellItem *shellitem, *shellitem2;
1866 IPersistIDList *persistidl;
1867 LPITEMIDLIST pidl_cwd=NULL, pidl_testfile, pidl_abstestfile, pidl_test;
1869 char curdirA[MAX_PATH];
1870 WCHAR curdirW[MAX_PATH];
1871 IShellFolder *desktopfolder=NULL, *currentfolder=NULL;
1872 static WCHAR testfileW[] = {'t','e','s','t','f','i','l','e',0};
1874 GetCurrentDirectoryA(MAX_PATH, curdirA);
1876 if (!pSHCreateShellItem)
1878 win_skip("SHCreateShellItem isn't available\n");
1882 if (!lstrlenA(curdirA))
1884 win_skip("GetCurrentDirectoryA returned empty string, skipping test_SHCreateShellItem\n");
1888 MultiByteToWideChar(CP_ACP, 0, curdirA, -1, curdirW, MAX_PATH);
1890 ret = SHGetDesktopFolder(&desktopfolder);
1891 ok(SUCCEEDED(ret), "SHGetShellFolder returned %x\n", ret);
1893 ret = IShellFolder_ParseDisplayName(desktopfolder, NULL, NULL, curdirW, NULL, &pidl_cwd, NULL);
1894 ok(SUCCEEDED(ret), "ParseDisplayName returned %x\n", ret);
1896 ret = IShellFolder_BindToObject(desktopfolder, pidl_cwd, NULL, &IID_IShellFolder, (void**)¤tfolder);
1897 ok(SUCCEEDED(ret), "BindToObject returned %x\n", ret);
1899 CreateTestFile(".\\testfile");
1901 ret = IShellFolder_ParseDisplayName(currentfolder, NULL, NULL, testfileW, NULL, &pidl_testfile, NULL);
1902 ok(SUCCEEDED(ret), "ParseDisplayName returned %x\n", ret);
1904 pidl_abstestfile = pILCombine(pidl_cwd, pidl_testfile);
1906 ret = pSHCreateShellItem(NULL, NULL, NULL, &shellitem);
1907 ok(ret == E_INVALIDARG, "SHCreateShellItem returned %x\n", ret);
1909 if (0) /* crashes on Windows XP */
1911 pSHCreateShellItem(NULL, NULL, pidl_cwd, NULL);
1912 pSHCreateShellItem(pidl_cwd, NULL, NULL, &shellitem);
1913 pSHCreateShellItem(NULL, currentfolder, NULL, &shellitem);
1914 pSHCreateShellItem(pidl_cwd, currentfolder, NULL, &shellitem);
1917 ret = pSHCreateShellItem(NULL, NULL, pidl_cwd, &shellitem);
1918 ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret);
1921 ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
1922 ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
1925 ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
1926 ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
1929 ok(ILIsEqual(pidl_cwd, pidl_test), "id lists are not equal\n");
1932 IPersistIDList_Release(persistidl);
1934 IShellItem_Release(shellitem);
1937 ret = pSHCreateShellItem(pidl_cwd, NULL, pidl_testfile, &shellitem);
1938 ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret);
1941 ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
1942 ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
1945 ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
1946 ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
1949 ok(ILIsEqual(pidl_abstestfile, pidl_test), "id lists are not equal\n");
1952 IPersistIDList_Release(persistidl);
1955 ret = IShellItem_GetParent(shellitem, &shellitem2);
1956 ok(SUCCEEDED(ret), "GetParent returned %x\n", ret);
1959 ret = IShellItem_QueryInterface(shellitem2, &IID_IPersistIDList, (void**)&persistidl);
1960 ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
1963 ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
1964 ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
1967 ok(ILIsEqual(pidl_cwd, pidl_test), "id lists are not equal\n");
1970 IPersistIDList_Release(persistidl);
1972 IShellItem_Release(shellitem2);
1975 IShellItem_Release(shellitem);
1978 ret = pSHCreateShellItem(NULL, currentfolder, pidl_testfile, &shellitem);
1979 ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret);
1982 ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
1983 ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
1986 ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
1987 ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
1990 ok(ILIsEqual(pidl_abstestfile, pidl_test), "id lists are not equal\n");
1993 IPersistIDList_Release(persistidl);
1995 IShellItem_Release(shellitem);
1998 /* if a parent pidl and shellfolder are specified, the shellfolder is ignored */
1999 ret = pSHCreateShellItem(pidl_cwd, desktopfolder, pidl_testfile, &shellitem);
2000 ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret);
2003 ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
2004 ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
2007 ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
2008 ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
2011 ok(ILIsEqual(pidl_abstestfile, pidl_test), "id lists are not equal\n");
2014 IPersistIDList_Release(persistidl);
2016 IShellItem_Release(shellitem);
2019 ret = pSHCreateShellItem(NULL, desktopfolder, pidl_testfile, &shellitem);
2020 ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret);
2023 ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
2024 ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
2027 ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
2028 ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
2031 ok(ILIsEqual(pidl_testfile, pidl_test), "id lists are not equal\n");
2034 IPersistIDList_Release(persistidl);
2036 IShellItem_Release(shellitem);
2039 DeleteFileA(".\\testfile");
2040 pILFree(pidl_abstestfile);
2041 pILFree(pidl_testfile);
2043 IShellFolder_Release(currentfolder);
2044 IShellFolder_Release(desktopfolder);
2047 static void test_SHParseDisplayName(void)
2049 LPITEMIDLIST pidl1, pidl2;
2050 IShellFolder *desktop;
2051 WCHAR dirW[MAX_PATH];
2056 if (!pSHParseDisplayName)
2058 win_skip("SHParseDisplayName isn't available\n");
2064 /* crashes on native */
2065 hr = pSHParseDisplayName(NULL, NULL, NULL, 0, NULL);
2067 hr = pSHParseDisplayName(nameW, NULL, NULL, 0, NULL);
2070 pidl1 = (LPITEMIDLIST)0xdeadbeef;
2071 hr = pSHParseDisplayName(NULL, NULL, &pidl1, 0, NULL);
2072 ok(broken(hr == E_OUTOFMEMORY) /* < Vista */ ||
2073 hr == E_INVALIDARG, "failed %08x\n", hr);
2074 ok(pidl1 == 0, "expected null ptr, got %p\n", pidl1);
2078 hr = pSHParseDisplayName(nameW, NULL, &pidl1, 0, NULL);
2079 ok(hr == S_OK, "failed %08x\n", hr);
2080 hr = SHGetDesktopFolder(&desktop);
2081 ok(hr == S_OK, "failed %08x\n", hr);
2082 hr = IShellFolder_ParseDisplayName(desktop, NULL, NULL, nameW, NULL, &pidl2, NULL);
2083 ok(hr == S_OK, "failed %08x\n", hr);
2084 ret = pILIsEqual(pidl1, pidl2);
2085 ok(ret == TRUE, "expected equal idls\n");
2090 GetWindowsDirectoryW( dirW, MAX_PATH );
2092 hr = pSHParseDisplayName(dirW, NULL, &pidl1, 0, NULL);
2093 ok(hr == S_OK, "failed %08x\n", hr);
2094 hr = IShellFolder_ParseDisplayName(desktop, NULL, NULL, dirW, NULL, &pidl2, NULL);
2095 ok(hr == S_OK, "failed %08x\n", hr);
2097 ret = pILIsEqual(pidl1, pidl2);
2098 ok(ret == TRUE, "expected equal idls\n");
2102 IShellFolder_Release(desktop);
2105 static void test_desktop_IPersist(void)
2107 IShellFolder *desktop;
2109 IPersistFolder2 *ppf2;
2113 hr = SHGetDesktopFolder(&desktop);
2114 ok(hr == S_OK, "failed %08x\n", hr);
2116 hr = IShellFolder_QueryInterface(desktop, &IID_IPersist, (void**)&persist);
2117 ok(hr == S_OK || broken(hr == E_NOINTERFACE) /* NT4, W9X */, "failed %08x\n", hr);
2123 /* crashes on native */
2124 hr = IPersist_GetClassID(persist, NULL);
2126 memset(&clsid, 0, sizeof(clsid));
2127 hr = IPersist_GetClassID(persist, &clsid);
2128 ok(hr == S_OK, "failed %08x\n", hr);
2129 ok(IsEqualIID(&CLSID_ShellDesktop, &clsid), "Expected CLSID_ShellDesktop\n");
2130 IPersist_Release(persist);
2133 hr = IShellFolder_QueryInterface(desktop, &IID_IPersistFolder2, (void**)&ppf2);
2134 ok(hr == S_OK || broken(hr == E_NOINTERFACE) /* pre-Vista */, "failed %08x\n", hr);
2137 IPersistFolder *ppf;
2139 hr = IShellFolder_QueryInterface(desktop, &IID_IPersistFolder, (void**)&ppf);
2140 ok(hr == S_OK, "IID_IPersistFolder2 without IID_IPersistFolder.\n");
2142 IPersistFolder_Release(ppf);
2145 hr = IPersistFolder2_Initialize(ppf2, NULL);
2146 ok(hr == S_OK, "got %08x\n", hr);
2150 hr = IPersistFolder2_GetCurFolder(ppf2, &pidl);
2151 ok(hr == S_OK, "got %08x\n", hr);
2152 ok(pidl != NULL, "pidl was NULL.\n");
2153 if(SUCCEEDED(hr)) pILFree(pidl);
2155 IPersistFolder2_Release(ppf2);
2158 IShellFolder_Release(desktop);
2161 static void test_GetUIObject(void)
2163 IShellFolder *psf_desktop;
2167 WCHAR path[MAX_PATH];
2168 const WCHAR filename[] =
2169 {'\\','t','e','s','t','d','i','r','\\','t','e','s','t','1','.','t','x','t',0};
2171 if(!pSHBindToParent)
2173 win_skip("SHBindToParent missing.\n");
2177 GetCurrentDirectoryW(MAX_PATH, path);
2180 skip("GetCurrentDirectoryW returned an empty string.\n");
2183 lstrcatW(path, filename);
2184 SHGetDesktopFolder(&psf_desktop);
2186 CreateFilesFolders();
2188 hr = IShellFolder_ParseDisplayName(psf_desktop, NULL, NULL, path, NULL, &pidl, 0);
2189 ok(hr == S_OK, "Got 0x%08x\n", hr);
2193 LPCITEMIDLIST pidl_child;
2194 hr = pSHBindToParent(pidl, &IID_IShellFolder, (void**)&psf, &pidl_child);
2195 ok(hr == S_OK, "Got 0x%08x\n", hr);
2198 hr = IShellFolder_GetUIObjectOf(psf, NULL, 1, (LPCITEMIDLIST*)&pidl_child,
2199 &IID_IContextMenu, NULL, (void**)&pcm);
2200 ok(hr == S_OK, "Got 0x%08x\n", hr);
2203 HMENU hmenu = CreatePopupMenu();
2204 INT max_id, max_id_check;
2206 const int id_upper_limit = 32767;
2207 hr = IContextMenu_QueryContextMenu(pcm, hmenu, 0, 0, id_upper_limit, CMF_NORMAL);
2208 ok(SUCCEEDED(hr), "Got 0x%08x\n", hr);
2209 max_id = HRESULT_CODE(hr) - 1; /* returns max_id + 1 */
2210 ok(max_id <= id_upper_limit, "Got %d\n", max_id);
2211 count = GetMenuItemCount(hmenu);
2212 ok(count, "Got %d\n", count);
2215 for(i = 0; i < count; i++)
2219 ZeroMemory(&mii, sizeof(MENUITEMINFOA));
2220 mii.cbSize = sizeof(MENUITEMINFOA);
2221 mii.fMask = MIIM_ID | MIIM_FTYPE;
2224 res = GetMenuItemInfoA(hmenu, i, TRUE, &mii);
2225 ok(res, "Failed (last error: %d).\n", GetLastError());
2227 ok( (mii.wID <= id_upper_limit) || (mii.fType & MFT_SEPARATOR),
2228 "Got non-separator ID out of range: %d (type: %x) \n", mii.wID, mii.fType);
2229 if(!(mii.fType & MFT_SEPARATOR))
2230 max_id_check = (mii.wID>max_id_check)?mii.wID:max_id_check;
2232 ok((max_id_check == max_id) ||
2233 (max_id_check == max_id-1 /* Win 7 */),
2234 "Not equal (or near equal), got %d and %d\n", max_id_check, max_id);
2236 #define is_win2k() (pSHGetFolderPathA && !pSHGetFolderPathAndSubDirA)
2238 if(count && !is_win2k()) /* Test is interactive on w2k, so skip */
2240 CMINVOKECOMMANDINFO cmi;
2241 ZeroMemory(&cmi, sizeof(CMINVOKECOMMANDINFO));
2242 cmi.cbSize = sizeof(CMINVOKECOMMANDINFO);
2244 /* Attempt to execute non-existing command */
2245 cmi.lpVerb = MAKEINTRESOURCEA(9999);
2246 hr = IContextMenu_InvokeCommand(pcm, &cmi);
2247 ok(hr == E_INVALIDARG, "Got 0x%08x\n", hr);
2249 cmi.lpVerb = "foobar_wine_test";
2250 hr = IContextMenu_InvokeCommand(pcm, &cmi);
2251 ok( (hr == E_INVALIDARG) || (hr == E_FAIL /* Win7 */) ||
2252 (hr == HRESULT_FROM_WIN32(ERROR_NO_ASSOCIATION) /* Vista */),
2253 "Got 0x%08x\n", hr);
2258 IContextMenu_Release(pcm);
2260 IShellFolder_Release(psf);
2262 if(pILFree) pILFree(pidl);
2265 IShellFolder_Release(psf_desktop);
2269 #define verify_pidl(i,p) r_verify_pidl(__LINE__, i, p)
2270 static void r_verify_pidl(unsigned l, LPCITEMIDLIST pidl, const WCHAR *path)
2272 LPCITEMIDLIST child;
2273 IShellFolder *parent;
2277 if(!pSHBindToParent){
2278 win_skip("SHBindToParent is not available, not performing full PIDL verification\n");
2280 ok_(__FILE__,l)(pidl != NULL, "Expected PIDL to be non-NULL\n");
2282 ok_(__FILE__,l)(pidl == NULL, "Expected PIDL to be NULL\n");
2288 ok_(__FILE__,l)(0, "didn't get expected path (%s), instead: NULL\n", wine_dbgstr_w(path));
2292 hr = pSHBindToParent(pidl, &IID_IShellFolder, (LPVOID*)&parent, &child);
2293 ok_(__FILE__,l)(hr == S_OK, "SHBindToParent failed: 0x%08x\n", hr);
2297 hr = IShellFolder_GetDisplayNameOf(parent, child, SHGDN_FORPARSING, &filename);
2298 ok_(__FILE__,l)(hr == S_OK, "GetDisplayNameOf failed: 0x%08x\n", hr);
2300 IShellFolder_Release(parent);
2304 ok_(__FILE__,l)(filename.uType == STRRET_WSTR, "Got unexpected string type: %d\n", filename.uType);
2305 ok_(__FILE__,l)(lstrcmpW(path, filename.pOleStr) == 0,
2306 "didn't get expected path (%s), instead: %s\n",
2307 wine_dbgstr_w(path), wine_dbgstr_w(filename.pOleStr));
2309 IShellFolder_Release(parent);
2311 ok_(__FILE__,l)(pidl == NULL, "Expected PIDL to be NULL\n");
2314 static void test_SHSimpleIDListFromPath(void)
2316 const WCHAR adirW[] = {'C',':','\\','s','i','d','l','f','p','d','i','r',0};
2317 const CHAR adirA[] = "C:\\sidlfpdir";
2318 BOOL br, is_unicode = !(GetVersion() & 0x80000000);
2320 LPITEMIDLIST pidl = NULL;
2322 if(!pSHSimpleIDListFromPathAW){
2323 win_skip("SHSimpleIDListFromPathAW not available\n");
2327 br = CreateDirectoryA(adirA, NULL);
2328 ok(br == TRUE, "CreateDirectory failed: %d\n", GetLastError());
2331 pidl = pSHSimpleIDListFromPathAW(adirW);
2333 pidl = pSHSimpleIDListFromPathAW(adirA);
2334 verify_pidl(pidl, adirW);
2337 br = RemoveDirectoryA(adirA);
2338 ok(br == TRUE, "RemoveDirectory failed: %d\n", GetLastError());
2341 pidl = pSHSimpleIDListFromPathAW(adirW);
2343 pidl = pSHSimpleIDListFromPathAW(adirA);
2344 verify_pidl(pidl, adirW);
2348 /* IFileSystemBindData impl */
2349 static HRESULT WINAPI fsbd_QueryInterface(IFileSystemBindData *fsbd,
2350 REFIID riid, void **ppv)
2352 if(IsEqualIID(riid, &IID_IFileSystemBindData) ||
2353 IsEqualIID(riid, &IID_IUnknown)){
2357 return E_NOINTERFACE;
2360 static ULONG WINAPI fsbd_AddRef(IFileSystemBindData *fsbd)
2365 static ULONG WINAPI fsbd_Release(IFileSystemBindData *fsbd)
2370 static HRESULT WINAPI fsbd_SetFindData(IFileSystemBindData *fsbd,
2371 const WIN32_FIND_DATAW *pfd)
2373 ok(0, "SetFindData called\n");
2377 static HRESULT WINAPI fsbd_GetFindData_nul(IFileSystemBindData *fsbd,
2378 WIN32_FIND_DATAW *pfd)
2380 memset(pfd, 0, sizeof(WIN32_FIND_DATAW));
2384 static HRESULT WINAPI fsbd_GetFindData_junk(IFileSystemBindData *fsbd,
2385 WIN32_FIND_DATAW *pfd)
2387 memset(pfd, 0xdeadbeef, sizeof(WIN32_FIND_DATAW));
2391 static HRESULT WINAPI fsbd_GetFindData_invalid(IFileSystemBindData *fsbd,
2392 WIN32_FIND_DATAW *pfd)
2394 memset(pfd, 0, sizeof(WIN32_FIND_DATAW));
2395 *pfd->cFileName = 'a';
2396 *pfd->cAlternateFileName = 'a';
2400 static HRESULT WINAPI fsbd_GetFindData_valid(IFileSystemBindData *fsbd,
2401 WIN32_FIND_DATAW *pfd)
2403 static const WCHAR adirW[] = {'C',':','\\','f','s','b','d','d','i','r',0};
2404 HANDLE handle = FindFirstFileW(adirW, pfd);
2409 static HRESULT WINAPI fsbd_GetFindData_fail(IFileSystemBindData *fsbd,
2410 WIN32_FIND_DATAW *pfd)
2415 static IFileSystemBindDataVtbl fsbdVtbl = {
2416 fsbd_QueryInterface,
2423 static IFileSystemBindData fsbd = { &fsbdVtbl };
2425 static void test_ParseDisplayNamePBC(void)
2427 WCHAR wFileSystemBindData[] =
2428 {'F','i','l','e',' ','S','y','s','t','e','m',' ','B','i','n','d',' ','D','a','t','a',0};
2429 WCHAR adirW[] = {'C',':','\\','f','s','b','d','d','i','r',0};
2430 const HRESULT exp_err = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
2437 /* Check if we support WCHAR functions */
2438 SetLastError(0xdeadbeef);
2439 lstrcmpiW(adirW, adirW);
2440 if(GetLastError() == ERROR_CALL_NOT_IMPLEMENTED){
2441 win_skip("Most W-calls are not implemented\n");
2445 hres = SHGetDesktopFolder(&psf);
2446 ok(hres == S_OK, "SHGetDesktopFolder failed: 0x%08x\n", hres);
2448 win_skip("Failed to get IShellFolder, can't run tests\n");
2452 /* fails on unknown dir with no IBindCtx */
2453 hres = IShellFolder_ParseDisplayName(psf, NULL, NULL, adirW, NULL, &pidl, NULL);
2454 ok(hres == exp_err || broken(hres == E_FAIL) /* NT4 */,
2455 "ParseDisplayName failed with wrong error: 0x%08x\n", hres);
2457 /* fails on unknown dir with IBindCtx with no IFileSystemBindData */
2458 hres = CreateBindCtx(0, &pbc);
2459 ok(hres == S_OK, "CreateBindCtx failed: 0x%08x\n", hres);
2461 hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, adirW, NULL, &pidl, NULL);
2462 ok(hres == exp_err || broken(hres == E_FAIL) /* NT4 */,
2463 "ParseDisplayName failed with wrong error: 0x%08x\n", hres);
2465 /* unknown dir with IBindCtx with IFileSystemBindData */
2466 hres = IBindCtx_RegisterObjectParam(pbc, wFileSystemBindData, (IUnknown*)&fsbd);
2467 ok(hres == S_OK, "RegisterObjectParam failed: 0x%08x\n", hres);
2469 /* return E_FAIL from GetFindData */
2470 pidl = (ITEMIDLIST*)0xdeadbeef;
2471 fsbdVtbl.GetFindData = fsbd_GetFindData_fail;
2472 hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, adirW, NULL, &pidl, NULL);
2473 ok(hres == S_OK || broken(hres == E_FAIL) /* NT4 */,
2474 "ParseDisplayName failed: 0x%08x\n", hres);
2475 if(SUCCEEDED(hres)){
2476 verify_pidl(pidl, adirW);
2480 /* set FIND_DATA struct to NULLs */
2481 pidl = (ITEMIDLIST*)0xdeadbeef;
2482 fsbdVtbl.GetFindData = fsbd_GetFindData_nul;
2483 hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, adirW, NULL, &pidl, NULL);
2484 ok(hres == S_OK || broken(hres == E_FAIL) /* NT4 */,
2485 "ParseDisplayName failed: 0x%08x\n", hres);
2486 if(SUCCEEDED(hres)){
2487 verify_pidl(pidl, adirW);
2491 /* set FIND_DATA struct to junk */
2492 pidl = (ITEMIDLIST*)0xdeadbeef;
2493 fsbdVtbl.GetFindData = fsbd_GetFindData_junk;
2494 hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, adirW, NULL, &pidl, NULL);
2495 ok(hres == S_OK || broken(hres == E_FAIL) /* NT4 */,
2496 "ParseDisplayName failed: 0x%08x\n", hres);
2497 if(SUCCEEDED(hres)){
2498 verify_pidl(pidl, adirW);
2502 /* set FIND_DATA struct to invalid data */
2503 pidl = (ITEMIDLIST*)0xdeadbeef;
2504 fsbdVtbl.GetFindData = fsbd_GetFindData_invalid;
2505 hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, adirW, NULL, &pidl, NULL);
2506 ok(hres == S_OK || broken(hres == E_FAIL) /* NT4 */,
2507 "ParseDisplayName failed: 0x%08x\n", hres);
2508 if(SUCCEEDED(hres)){
2509 verify_pidl(pidl, adirW);
2513 /* set FIND_DATA struct to valid data */
2514 pidl = (ITEMIDLIST*)0xdeadbeef;
2515 fsbdVtbl.GetFindData = fsbd_GetFindData_valid;
2516 hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, adirW, NULL, &pidl, NULL);
2517 ok(hres == S_OK || broken(hres == E_FAIL) /* NT4 */,
2518 "ParseDisplayName failed: 0x%08x\n", hres);
2519 if(SUCCEEDED(hres)){
2520 verify_pidl(pidl, adirW);
2524 IBindCtx_Release(pbc);
2525 IShellFolder_Release(psf);
2528 START_TEST(shlfolder)
2530 init_function_pointers();
2531 /* if OleInitialize doesn't get called, ParseDisplayName returns
2532 CO_E_NOTINITIALIZED for malformed directory names on win2k. */
2533 OleInitialize(NULL);
2535 test_ParseDisplayName();
2536 test_SHParseDisplayName();
2537 test_BindToObject();
2538 test_EnumObjects_and_CompareIDs();
2539 test_GetDisplayName();
2540 test_GetAttributesOf();
2541 test_SHGetPathFromIDList();
2542 test_CallForAttributes();
2543 test_FolderShortcut();
2544 test_ITEMIDLIST_format();
2545 test_SHGetFolderPathAndSubDirA();
2546 test_LocalizedNames();
2547 test_SHCreateShellItem();
2548 test_desktop_IPersist();
2550 test_SHSimpleIDListFromPath();
2551 test_ParseDisplayNamePBC();