push 149f0a5527ac85057a8ef03858d34d91c36f97e8
[wine/hacks.git] / dlls / shell32 / tests / shlfolder.c
blobcaae4254f0684fb304195e5d9932df937ce800b3
1 /*
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
21 #include <stdarg.h>
22 #include <stdio.h>
24 #define COBJMACROS
25 #define CONST_VTABLE
27 #include "windef.h"
28 #include "winbase.h"
29 #include "wtypes.h"
30 #include "shellapi.h"
33 #include "shlguid.h"
34 #include "shlobj.h"
35 #include "shobjidl.h"
36 #include "shlwapi.h"
37 #include "ocidl.h"
38 #include "oleauto.h"
40 #include "wine/test.h"
43 static IMalloc *ppM;
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);
59 static void init_function_pointers(void)
61 HMODULE hmod;
62 HRESULT hr;
64 hmod = GetModuleHandleA("shell32.dll");
65 pSHBindToParent = (void*)GetProcAddress(hmod, "SHBindToParent");
66 pSHGetFolderPathA = (void*)GetProcAddress(hmod, "SHGetFolderPathA");
67 pSHGetFolderPathAndSubDirA = (void*)GetProcAddress(hmod, "SHGetFolderPathAndSubDirA");
68 pSHGetPathFromIDListW = (void*)GetProcAddress(hmod, "SHGetPathFromIDListW");
69 pSHGetSpecialFolderPathA = (void*)GetProcAddress(hmod, "SHGetSpecialFolderPathA");
70 pSHGetSpecialFolderPathW = (void*)GetProcAddress(hmod, "SHGetSpecialFolderPathW");
71 pILFindLastID = (void *)GetProcAddress(hmod, (LPCSTR)16);
72 pILFree = (void*)GetProcAddress(hmod, (LPSTR)155);
73 pILIsEqual = (void*)GetProcAddress(hmod, (LPSTR)21);
74 pSHCreateShellItem = (void*)GetProcAddress(hmod, "SHCreateShellItem");
75 pILCombine = (void*)GetProcAddress(hmod, (LPSTR)25);
77 hmod = GetModuleHandleA("shlwapi.dll");
78 pStrRetToBufW = (void*)GetProcAddress(hmod, "StrRetToBufW");
80 hr = SHGetMalloc(&ppM);
81 ok(hr == S_OK, "SHGetMalloc failed %08x\n", hr);
84 static void test_ParseDisplayName(void)
86 HRESULT hr;
87 IShellFolder *IDesktopFolder;
88 static const char *cNonExistDir1A = "c:\\nonexist_subdir";
89 static const char *cNonExistDir2A = "c:\\\\nonexist_subdir";
90 static const char *cInetTestA = "http:\\yyy";
91 static const char *cInetTest2A = "xx:yyy";
92 DWORD res;
93 WCHAR cTestDirW [MAX_PATH] = {0};
94 ITEMIDLIST *newPIDL;
95 BOOL bRes;
97 hr = SHGetDesktopFolder(&IDesktopFolder);
98 if(hr != S_OK) return;
100 MultiByteToWideChar(CP_ACP, 0, cInetTestA, -1, cTestDirW, MAX_PATH);
101 hr = IShellFolder_ParseDisplayName(IDesktopFolder,
102 NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
103 todo_wine ok((SUCCEEDED(hr) || broken(hr == E_FAIL) /* NT4 */),
104 "ParseDisplayName returned %08x, expected SUCCESS or E_FAIL\n", hr);
105 if (SUCCEEDED(hr))
107 ok(pILFindLastID(newPIDL)->mkid.abID[0] == 0x61, "Last pidl should be of type "
108 "PT_IESPECIAL1, but is: %02x\n", pILFindLastID(newPIDL)->mkid.abID[0]);
109 IMalloc_Free(ppM, newPIDL);
112 MultiByteToWideChar(CP_ACP, 0, cInetTest2A, -1, cTestDirW, MAX_PATH);
113 hr = IShellFolder_ParseDisplayName(IDesktopFolder,
114 NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
115 todo_wine ok((SUCCEEDED(hr) || broken(hr == E_FAIL) /* NT4 */),
116 "ParseDisplayName returned %08x, expected SUCCESS or E_FAIL\n", hr);
117 if (SUCCEEDED(hr))
119 ok(pILFindLastID(newPIDL)->mkid.abID[0] == 0x61, "Last pidl should be of type "
120 "PT_IESPECIAL1, but is: %02x\n", pILFindLastID(newPIDL)->mkid.abID[0]);
121 IMalloc_Free(ppM, newPIDL);
124 res = GetFileAttributesA(cNonExistDir1A);
125 if(res != INVALID_FILE_ATTRIBUTES) return;
127 MultiByteToWideChar(CP_ACP, 0, cNonExistDir1A, -1, cTestDirW, MAX_PATH);
128 hr = IShellFolder_ParseDisplayName(IDesktopFolder,
129 NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
130 ok((hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) || (hr == E_FAIL),
131 "ParseDisplayName returned %08x, expected 80070002 or E_FAIL\n", hr);
133 res = GetFileAttributesA(cNonExistDir2A);
134 if(res != INVALID_FILE_ATTRIBUTES) return;
136 MultiByteToWideChar(CP_ACP, 0, cNonExistDir2A, -1, cTestDirW, MAX_PATH);
137 hr = IShellFolder_ParseDisplayName(IDesktopFolder,
138 NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
139 ok((hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) || (hr == E_FAIL) || (hr == E_INVALIDARG),
140 "ParseDisplayName returned %08x, expected 80070002, E_FAIL or E_INVALIDARG\n", hr);
142 /* I thought that perhaps the DesktopFolder's ParseDisplayName would recognize the
143 * path corresponding to CSIDL_PERSONAL and return a CLSID_MyDocuments PIDL. Turns
144 * out it doesn't. The magic seems to happen in the file dialogs, then. */
145 if (!pSHGetSpecialFolderPathW || !pILFindLastID) goto finished;
147 bRes = pSHGetSpecialFolderPathW(NULL, cTestDirW, CSIDL_PERSONAL, FALSE);
148 ok(bRes, "SHGetSpecialFolderPath(CSIDL_PERSONAL) failed! %u\n", GetLastError());
149 if (!bRes) goto finished;
151 hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
152 ok(SUCCEEDED(hr), "DesktopFolder->ParseDisplayName failed. hr = %08x.\n", hr);
153 if (FAILED(hr)) goto finished;
155 ok(pILFindLastID(newPIDL)->mkid.abID[0] == 0x31 ||
156 pILFindLastID(newPIDL)->mkid.abID[0] == 0xb1, /* Win98 */
157 "Last pidl should be of type PT_FOLDER or PT_IESPECIAL2, but is: %02x\n",
158 pILFindLastID(newPIDL)->mkid.abID[0]);
159 IMalloc_Free(ppM, newPIDL);
161 finished:
162 IShellFolder_Release(IDesktopFolder);
165 /* creates a file with the specified name for tests */
166 static void CreateTestFile(const CHAR *name)
168 HANDLE file;
169 DWORD written;
171 file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
172 if (file != INVALID_HANDLE_VALUE)
174 WriteFile(file, name, strlen(name), &written, NULL);
175 WriteFile(file, "\n", strlen("\n"), &written, NULL);
176 CloseHandle(file);
181 /* initializes the tests */
182 static void CreateFilesFolders(void)
184 CreateDirectoryA(".\\testdir", NULL);
185 CreateDirectoryA(".\\testdir\\test.txt", NULL);
186 CreateTestFile (".\\testdir\\test1.txt ");
187 CreateTestFile (".\\testdir\\test2.txt ");
188 CreateTestFile (".\\testdir\\test3.txt ");
189 CreateDirectoryA(".\\testdir\\testdir2 ", NULL);
190 CreateDirectoryA(".\\testdir\\testdir2\\subdir", NULL);
193 /* cleans after tests */
194 static void Cleanup(void)
196 DeleteFileA(".\\testdir\\test1.txt");
197 DeleteFileA(".\\testdir\\test2.txt");
198 DeleteFileA(".\\testdir\\test3.txt");
199 RemoveDirectoryA(".\\testdir\\test.txt");
200 RemoveDirectoryA(".\\testdir\\testdir2\\subdir");
201 RemoveDirectoryA(".\\testdir\\testdir2");
202 RemoveDirectoryA(".\\testdir");
206 /* perform test */
207 static void test_EnumObjects(IShellFolder *iFolder)
209 IEnumIDList *iEnumList;
210 LPITEMIDLIST newPIDL, idlArr[10];
211 ULONG NumPIDLs;
212 int i=0, j;
213 HRESULT hr;
215 static const WORD iResults [5][5] =
217 { 0,-1,-1,-1,-1},
218 { 1, 0,-1,-1,-1},
219 { 1, 1, 0,-1,-1},
220 { 1, 1, 1, 0,-1},
221 { 1, 1, 1, 1, 0}
224 #define SFGAO_testfor SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR | SFGAO_CAPABILITYMASK
225 /* Don't test for SFGAO_HASSUBFOLDER since we return real state and native cached */
226 static const ULONG attrs[5] =
228 SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR,
229 SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR,
230 SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM,
231 SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM,
232 SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM,
235 hr = IShellFolder_EnumObjects(iFolder, NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN, &iEnumList);
236 ok(hr == S_OK, "EnumObjects failed %08x\n", hr);
238 /* This is to show that, contrary to what is said on MSDN, on IEnumIDList::Next,
239 * the filesystem shellfolders return S_OK even if less than 'celt' items are
240 * returned (in contrast to S_FALSE). We have to do it in a loop since WinXP
241 * only ever returns a single entry per call. */
242 while (IEnumIDList_Next(iEnumList, 10-i, &idlArr[i], &NumPIDLs) == S_OK)
243 i += NumPIDLs;
244 ok (i == 5, "i: %d\n", i);
246 hr = IEnumIDList_Release(iEnumList);
247 ok(hr == S_OK, "IEnumIDList_Release failed %08x\n", hr);
249 /* Sort them first in case of wrong order from system */
250 for (i=0;i<5;i++) for (j=0;j<5;j++)
251 if ((SHORT)IShellFolder_CompareIDs(iFolder, 0, idlArr[i], idlArr[j]) < 0)
253 newPIDL = idlArr[i];
254 idlArr[i] = idlArr[j];
255 idlArr[j] = newPIDL;
258 for (i=0;i<5;i++) for (j=0;j<5;j++)
260 hr = IShellFolder_CompareIDs(iFolder, 0, idlArr[i], idlArr[j]);
261 ok(hr == iResults[i][j], "Got %x expected [%d]-[%d]=%x\n", hr, i, j, iResults[i][j]);
265 for (i = 0; i < 5; i++)
267 SFGAOF flags;
268 #define SFGAO_VISTA SFGAO_DROPTARGET | SFGAO_CANLINK | SFGAO_CANCOPY
269 /* Native returns all flags no matter what we ask for */
270 flags = SFGAO_CANCOPY;
271 hr = IShellFolder_GetAttributesOf(iFolder, 1, (LPCITEMIDLIST*)(idlArr + i), &flags);
272 flags &= SFGAO_testfor;
273 ok(hr == S_OK, "GetAttributesOf returns %08x\n", hr);
274 ok(flags == (attrs[i]) ||
275 flags == (attrs[i] & ~SFGAO_FILESYSANCESTOR) || /* Win9x, NT4 */
276 flags == ((attrs[i] & ~SFGAO_CAPABILITYMASK) | SFGAO_VISTA), /* Vista and higher */
277 "GetAttributesOf[%i] got %08x, expected %08x\n", i, flags, attrs[i]);
279 flags = SFGAO_testfor;
280 hr = IShellFolder_GetAttributesOf(iFolder, 1, (LPCITEMIDLIST*)(idlArr + i), &flags);
281 flags &= SFGAO_testfor;
282 ok(hr == S_OK, "GetAttributesOf returns %08x\n", hr);
283 ok(flags == attrs[i] ||
284 flags == (attrs[i] & ~SFGAO_FILESYSANCESTOR), /* Win9x, NT4 */
285 "GetAttributesOf[%i] got %08x, expected %08x\n", i, flags, attrs[i]);
288 for (i=0;i<5;i++)
289 IMalloc_Free(ppM, idlArr[i]);
292 static void test_BindToObject(void)
294 HRESULT hr;
295 UINT cChars;
296 IShellFolder *psfDesktop, *psfChild, *psfMyComputer, *psfSystemDir;
297 SHITEMID emptyitem = { 0, { 0 } };
298 LPITEMIDLIST pidlMyComputer, pidlSystemDir, pidlEmpty = (LPITEMIDLIST)&emptyitem;
299 WCHAR wszSystemDir[MAX_PATH];
300 char szSystemDir[MAX_PATH];
301 WCHAR wszMyComputer[] = {
302 ':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
303 'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };
305 /* The following tests shows that BindToObject should fail with E_INVALIDARG if called
306 * with an empty pidl. This is tested for Desktop, MyComputer and the FS ShellFolder
308 hr = SHGetDesktopFolder(&psfDesktop);
309 ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08x\n", hr);
310 if (FAILED(hr)) return;
312 hr = IShellFolder_BindToObject(psfDesktop, pidlEmpty, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
313 ok (hr == E_INVALIDARG, "Desktop's BindToObject should fail, when called with empty pidl! hr = %08x\n", hr);
315 hr = IShellFolder_BindToObject(psfDesktop, NULL, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
316 ok (hr == E_INVALIDARG, "Desktop's BindToObject should fail, when called with NULL pidl! hr = %08x\n", hr);
318 hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyComputer, NULL, &pidlMyComputer, NULL);
319 ok (SUCCEEDED(hr), "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08x\n", hr);
320 if (FAILED(hr)) {
321 IShellFolder_Release(psfDesktop);
322 return;
325 hr = IShellFolder_BindToObject(psfDesktop, pidlMyComputer, NULL, &IID_IShellFolder, (LPVOID*)&psfMyComputer);
326 ok (SUCCEEDED(hr), "Desktop failed to bind to MyComputer object! hr = %08x\n", hr);
327 IShellFolder_Release(psfDesktop);
328 IMalloc_Free(ppM, pidlMyComputer);
329 if (FAILED(hr)) return;
331 hr = IShellFolder_BindToObject(psfMyComputer, pidlEmpty, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
332 ok (hr == E_INVALIDARG, "MyComputers's BindToObject should fail, when called with empty pidl! hr = %08x\n", hr);
334 #if 0
335 /* this call segfaults on 98SE */
336 hr = IShellFolder_BindToObject(psfMyComputer, NULL, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
337 ok (hr == E_INVALIDARG, "MyComputers's BindToObject should fail, when called with NULL pidl! hr = %08x\n", hr);
338 #endif
340 cChars = GetSystemDirectoryA(szSystemDir, MAX_PATH);
341 ok (cChars > 0 && cChars < MAX_PATH, "GetSystemDirectoryA failed! LastError: %u\n", GetLastError());
342 if (cChars == 0 || cChars >= MAX_PATH) {
343 IShellFolder_Release(psfMyComputer);
344 return;
346 MultiByteToWideChar(CP_ACP, 0, szSystemDir, -1, wszSystemDir, MAX_PATH);
348 hr = IShellFolder_ParseDisplayName(psfMyComputer, NULL, NULL, wszSystemDir, NULL, &pidlSystemDir, NULL);
349 ok (SUCCEEDED(hr), "MyComputers's ParseDisplayName failed to parse the SystemDirectory! hr = %08x\n", hr);
350 if (FAILED(hr)) {
351 IShellFolder_Release(psfMyComputer);
352 return;
355 hr = IShellFolder_BindToObject(psfMyComputer, pidlSystemDir, NULL, &IID_IShellFolder, (LPVOID*)&psfSystemDir);
356 ok (SUCCEEDED(hr), "MyComputer failed to bind to a FileSystem ShellFolder! hr = %08x\n", hr);
357 IShellFolder_Release(psfMyComputer);
358 IMalloc_Free(ppM, pidlSystemDir);
359 if (FAILED(hr)) return;
361 hr = IShellFolder_BindToObject(psfSystemDir, pidlEmpty, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
362 ok (hr == E_INVALIDARG,
363 "FileSystem ShellFolder's BindToObject should fail, when called with empty pidl! hr = %08x\n", hr);
365 #if 0
366 /* this call segfaults on 98SE */
367 hr = IShellFolder_BindToObject(psfSystemDir, NULL, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
368 ok (hr == E_INVALIDARG,
369 "FileSystem ShellFolder's BindToObject should fail, when called with NULL pidl! hr = %08x\n", hr);
370 #endif
372 IShellFolder_Release(psfSystemDir);
375 /* Based on PathAddBackslashW from dlls/shlwapi/path.c */
376 static LPWSTR myPathAddBackslashW( LPWSTR lpszPath )
378 size_t iLen;
380 if (!lpszPath || (iLen = lstrlenW(lpszPath)) >= MAX_PATH)
381 return NULL;
383 if (iLen)
385 lpszPath += iLen;
386 if (lpszPath[-1] != '\\')
388 *lpszPath++ = '\\';
389 *lpszPath = '\0';
392 return lpszPath;
395 static void test_GetDisplayName(void)
397 BOOL result;
398 HRESULT hr;
399 HANDLE hTestFile;
400 WCHAR wszTestFile[MAX_PATH], wszTestFile2[MAX_PATH];
401 char szTestFile[MAX_PATH], szTestDir[MAX_PATH];
402 DWORD attr;
403 STRRET strret;
404 LPSHELLFOLDER psfDesktop, psfPersonal;
405 IUnknown *psfFile;
406 SHITEMID emptyitem = { 0, { 0 } };
407 LPITEMIDLIST pidlTestFile, pidlEmpty = (LPITEMIDLIST)&emptyitem;
408 LPCITEMIDLIST pidlLast;
409 static const CHAR szFileName[] = "winetest.foo";
410 static const WCHAR wszFileName[] = { 'w','i','n','e','t','e','s','t','.','f','o','o',0 };
411 static const WCHAR wszDirName[] = { 'w','i','n','e','t','e','s','t',0 };
413 /* I'm trying to figure if there is a functional difference between calling
414 * SHGetPathFromIDListW and calling GetDisplayNameOf(SHGDN_FORPARSING) after
415 * binding to the shellfolder. One thing I thought of was that perhaps
416 * SHGetPathFromIDListW would be able to get the path to a file, which does
417 * not exist anymore, while the other method wouldn't. It turns out there's
418 * no functional difference in this respect.
421 if(!pSHGetSpecialFolderPathA) {
422 win_skip("SHGetSpecialFolderPathA is not available\n");
423 return;
426 /* First creating a directory in MyDocuments and a file in this directory. */
427 result = pSHGetSpecialFolderPathA(NULL, szTestDir, CSIDL_PERSONAL, FALSE);
428 ok(result, "SHGetSpecialFolderPathA failed! Last error: %u\n", GetLastError());
429 if (!result) return;
431 /* Use ANSI file functions so this works on Windows 9x */
432 lstrcatA(szTestDir, "\\winetest");
433 CreateDirectoryA(szTestDir, NULL);
434 attr=GetFileAttributesA(szTestDir);
435 if (attr == INVALID_FILE_ATTRIBUTES || !(attr & FILE_ATTRIBUTE_DIRECTORY))
437 ok(0, "unable to create the '%s' directory\n", szTestDir);
438 return;
441 lstrcpyA(szTestFile, szTestDir);
442 lstrcatA(szTestFile, "\\");
443 lstrcatA(szTestFile, szFileName);
444 hTestFile = CreateFileA(szTestFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
445 ok((hTestFile != INVALID_HANDLE_VALUE), "CreateFileA failed! Last error: %u\n", GetLastError());
446 if (hTestFile == INVALID_HANDLE_VALUE) return;
447 CloseHandle(hTestFile);
449 /* Getting an itemidlist for the file. */
450 hr = SHGetDesktopFolder(&psfDesktop);
451 ok(SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08x\n", hr);
452 if (FAILED(hr)) return;
454 MultiByteToWideChar(CP_ACP, 0, szTestFile, -1, wszTestFile, MAX_PATH);
456 hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszTestFile, NULL, &pidlTestFile, NULL);
457 ok(SUCCEEDED(hr), "Desktop->ParseDisplayName failed! hr = %08x\n", hr);
458 if (FAILED(hr)) {
459 IShellFolder_Release(psfDesktop);
460 return;
463 pidlLast = pILFindLastID(pidlTestFile);
464 ok(pidlLast->mkid.cb >=76 ||
465 broken(pidlLast->mkid.cb == 28) || /* W2K */
466 broken(pidlLast->mkid.cb == 40), /* Win9x, WinME */
467 "Expected pidl length of at least 76, got %d.\n", pidlLast->mkid.cb);
468 if (pidlLast->mkid.cb >= 28) {
469 ok(!lstrcmpA((CHAR*)&pidlLast->mkid.abID[12], szFileName),
470 "Filename should be stored as ansi-string at this position!\n");
472 /* WinXP and up store the filenames as both ANSI and UNICODE in the pidls */
473 if (pidlLast->mkid.cb >= 76) {
474 ok(!lstrcmpW((WCHAR*)&pidlLast->mkid.abID[46], wszFileName) ||
475 (pidlLast->mkid.cb >= 94 && !lstrcmpW((WCHAR*)&pidlLast->mkid.abID[64], wszFileName)) || /* Vista */
476 (pidlLast->mkid.cb >= 98 && !lstrcmpW((WCHAR*)&pidlLast->mkid.abID[68], wszFileName)), /* Win7 */
477 "Filename should be stored as wchar-string at this position!\n");
480 /* It seems as if we cannot bind to regular files on windows, but only directories.
482 hr = IShellFolder_BindToObject(psfDesktop, pidlTestFile, NULL, &IID_IUnknown, (VOID**)&psfFile);
483 todo_wine
484 ok (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) ||
485 hr == E_NOTIMPL || /* Vista */
486 broken(SUCCEEDED(hr)), /* Win9x, W2K */
487 "hr = %08x\n", hr);
488 if (SUCCEEDED(hr)) {
489 IShellFolder_Release(psfFile);
492 if (!pSHBindToParent)
494 win_skip("SHBindToParent is missing\n");
495 DeleteFileA(szTestFile);
496 RemoveDirectoryA(szTestDir);
497 return;
500 /* Some tests for IShellFolder::SetNameOf */
501 if (pSHGetFolderPathAndSubDirA)
503 hr = pSHBindToParent(pidlTestFile, &IID_IShellFolder, (VOID**)&psfPersonal, &pidlLast);
504 ok(SUCCEEDED(hr), "SHBindToParent failed! hr = %08x\n", hr);
505 if (SUCCEEDED(hr)) {
506 /* It's ok to use this fixed path. Call will fail anyway. */
507 WCHAR wszAbsoluteFilename[] = { 'C',':','\\','w','i','n','e','t','e','s','t', 0 };
508 LPITEMIDLIST pidlNew;
510 /* The pidl returned through the last parameter of SetNameOf is a simple one. */
511 hr = IShellFolder_SetNameOf(psfPersonal, NULL, pidlLast, wszDirName, SHGDN_NORMAL, &pidlNew);
512 ok (SUCCEEDED(hr), "SetNameOf failed! hr = %08x\n", hr);
513 if (hr == S_OK)
515 ok (((LPITEMIDLIST)((LPBYTE)pidlNew+pidlNew->mkid.cb))->mkid.cb == 0,
516 "pidl returned from SetNameOf should be simple!\n");
518 /* Passing an absolute path to SetNameOf fails. The HRESULT code indicates that SetNameOf
519 * is implemented on top of SHFileOperation in WinXP. */
520 hr = IShellFolder_SetNameOf(psfPersonal, NULL, pidlNew, wszAbsoluteFilename,
521 SHGDN_FORPARSING, NULL);
522 ok (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED), "SetNameOf succeeded! hr = %08x\n", hr);
524 /* Rename the file back to its original name. SetNameOf ignores the fact, that the
525 * SHGDN flags specify an absolute path. */
526 hr = IShellFolder_SetNameOf(psfPersonal, NULL, pidlNew, wszFileName, SHGDN_FORPARSING, NULL);
527 ok (SUCCEEDED(hr), "SetNameOf failed! hr = %08x\n", hr);
529 pILFree(pidlNew);
532 IShellFolder_Release(psfPersonal);
535 else
536 win_skip("Avoid needs of interaction on Win2k\n");
538 /* Deleting the file and the directory */
539 DeleteFileA(szTestFile);
540 RemoveDirectoryA(szTestDir);
542 /* SHGetPathFromIDListW still works, although the file is not present anymore. */
543 if (pSHGetPathFromIDListW)
545 result = pSHGetPathFromIDListW(pidlTestFile, wszTestFile2);
546 ok (result, "SHGetPathFromIDListW failed! Last error: %u\n", GetLastError());
547 ok (!lstrcmpiW(wszTestFile, wszTestFile2), "SHGetPathFromIDListW returns incorrect path!\n");
550 /* SHBindToParent fails, if called with a NULL PIDL. */
551 hr = pSHBindToParent(NULL, &IID_IShellFolder, (VOID**)&psfPersonal, &pidlLast);
552 ok (FAILED(hr), "SHBindToParent(NULL) should fail!\n");
554 /* But it succeeds with an empty PIDL. */
555 hr = pSHBindToParent(pidlEmpty, &IID_IShellFolder, (VOID**)&psfPersonal, &pidlLast);
556 ok (SUCCEEDED(hr), "SHBindToParent(empty PIDL) should succeed! hr = %08x\n", hr);
557 ok (pidlLast == pidlEmpty, "The last element of an empty PIDL should be the PIDL itself!\n");
558 if (SUCCEEDED(hr))
559 IShellFolder_Release(psfPersonal);
561 /* Binding to the folder and querying the display name of the file also works. */
562 hr = pSHBindToParent(pidlTestFile, &IID_IShellFolder, (VOID**)&psfPersonal, &pidlLast);
563 ok (SUCCEEDED(hr), "SHBindToParent failed! hr = %08x\n", hr);
564 if (FAILED(hr)) {
565 IShellFolder_Release(psfDesktop);
566 return;
569 /* This test shows that Windows doesn't allocate a new pidlLast, but returns a pointer into
570 * pidlTestFile (In accordance with MSDN). */
571 ok (pILFindLastID(pidlTestFile) == pidlLast,
572 "SHBindToParent doesn't return the last id of the pidl param!\n");
574 hr = IShellFolder_GetDisplayNameOf(psfPersonal, pidlLast, SHGDN_FORPARSING, &strret);
575 ok (SUCCEEDED(hr), "Personal->GetDisplayNameOf failed! hr = %08x\n", hr);
576 if (FAILED(hr)) {
577 IShellFolder_Release(psfDesktop);
578 IShellFolder_Release(psfPersonal);
579 return;
582 if (pStrRetToBufW)
584 hr = pStrRetToBufW(&strret, pidlLast, wszTestFile2, MAX_PATH);
585 ok (SUCCEEDED(hr), "StrRetToBufW failed! hr = %08x\n", hr);
586 ok (!lstrcmpiW(wszTestFile, wszTestFile2), "GetDisplayNameOf returns incorrect path!\n");
589 IShellFolder_Release(psfDesktop);
590 IShellFolder_Release(psfPersonal);
593 static void test_CallForAttributes(void)
595 HKEY hKey;
596 LONG lResult;
597 HRESULT hr;
598 DWORD dwSize;
599 LPSHELLFOLDER psfDesktop;
600 LPITEMIDLIST pidlMyDocuments;
601 DWORD dwAttributes, dwCallForAttributes, dwOrigAttributes, dwOrigCallForAttributes;
602 static const WCHAR wszAttributes[] = { 'A','t','t','r','i','b','u','t','e','s',0 };
603 static const WCHAR wszCallForAttributes[] = {
604 'C','a','l','l','F','o','r','A','t','t','r','i','b','u','t','e','s',0 };
605 static const WCHAR wszMyDocumentsKey[] = {
606 'C','L','S','I','D','\\','{','4','5','0','D','8','F','B','A','-','A','D','2','5','-',
607 '1','1','D','0','-','9','8','A','8','-','0','8','0','0','3','6','1','B','1','1','0','3','}',
608 '\\','S','h','e','l','l','F','o','l','d','e','r',0 };
609 WCHAR wszMyDocuments[] = {
610 ':',':','{','4','5','0','D','8','F','B','A','-','A','D','2','5','-','1','1','D','0','-',
611 '9','8','A','8','-','0','8','0','0','3','6','1','B','1','1','0','3','}',0 };
613 /* For the root of a namespace extension, the attributes are not queried by binding
614 * to the object and calling GetAttributesOf. Instead, the attributes are read from
615 * the registry value HKCR/CLSID/{...}/ShellFolder/Attributes. This is documented on MSDN.
617 * The MyDocuments shellfolder on WinXP has a HKCR/CLSID/{...}/ShellFolder/CallForAttributes
618 * value. It seems that if the folder is queried for one of the flags set in CallForAttributes,
619 * the shell does bind to the folder object and calls GetAttributesOf. This is not documented
620 * on MSDN. This test is meant to document the observed behaviour on WinXP SP2.
622 hr = SHGetDesktopFolder(&psfDesktop);
623 ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08x\n", hr);
624 if (FAILED(hr)) return;
626 hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyDocuments, NULL,
627 &pidlMyDocuments, NULL);
628 ok (SUCCEEDED(hr) ||
629 broken(hr == E_INVALIDARG), /* Win95, NT4 */
630 "Desktop's ParseDisplayName failed to parse MyDocuments's CLSID! hr = %08x\n", hr);
631 if (FAILED(hr)) {
632 IShellFolder_Release(psfDesktop);
633 return;
636 dwAttributes = 0xffffffff;
637 hr = IShellFolder_GetAttributesOf(psfDesktop, 1,
638 (LPCITEMIDLIST*)&pidlMyDocuments, &dwAttributes);
639 ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(MyDocuments) failed! hr = %08x\n", hr);
641 /* We need the following setup (as observed on WinXP SP2), for the tests to make sense. */
642 ok (dwAttributes & SFGAO_FILESYSTEM, "SFGAO_FILESYSTEM attribute is not set for MyDocuments!\n");
643 ok (!(dwAttributes & SFGAO_ISSLOW), "SFGAO_ISSLOW attribute is set for MyDocuments!\n");
644 ok (!(dwAttributes & SFGAO_GHOSTED), "SFGAO_GHOSTED attribute is set for MyDocuments!\n");
646 /* We don't have the MyDocuments shellfolder in wine yet, and thus we don't have the registry
647 * key. So the test will return at this point, if run on wine.
649 lResult = RegOpenKeyExW(HKEY_CLASSES_ROOT, wszMyDocumentsKey, 0, KEY_WRITE|KEY_READ, &hKey);
650 ok (lResult == ERROR_SUCCESS, "RegOpenKeyEx failed! result: %08x\n", lResult);
651 if (lResult != ERROR_SUCCESS) {
652 IMalloc_Free(ppM, pidlMyDocuments);
653 IShellFolder_Release(psfDesktop);
654 return;
657 /* Query MyDocuments' Attributes value, to be able to restore it later. */
658 dwSize = sizeof(DWORD);
659 lResult = RegQueryValueExW(hKey, wszAttributes, NULL, NULL, (LPBYTE)&dwOrigAttributes, &dwSize);
660 ok (lResult == ERROR_SUCCESS, "RegQueryValueEx failed! result: %08x\n", lResult);
661 if (lResult != ERROR_SUCCESS) {
662 RegCloseKey(hKey);
663 IMalloc_Free(ppM, pidlMyDocuments);
664 IShellFolder_Release(psfDesktop);
665 return;
668 /* Query MyDocuments' CallForAttributes value, to be able to restore it later. */
669 dwSize = sizeof(DWORD);
670 lResult = RegQueryValueExW(hKey, wszCallForAttributes, NULL, NULL,
671 (LPBYTE)&dwOrigCallForAttributes, &dwSize);
672 ok (lResult == ERROR_SUCCESS, "RegQueryValueEx failed! result: %08x\n", lResult);
673 if (lResult != ERROR_SUCCESS) {
674 RegCloseKey(hKey);
675 IMalloc_Free(ppM, pidlMyDocuments);
676 IShellFolder_Release(psfDesktop);
677 return;
680 /* Define via the Attributes value that MyDocuments attributes are SFGAO_ISSLOW and
681 * SFGAO_GHOSTED and that MyDocuments should be called for the SFGAO_ISSLOW and
682 * SFGAO_FILESYSTEM attributes. */
683 dwAttributes = SFGAO_ISSLOW|SFGAO_GHOSTED;
684 RegSetValueExW(hKey, wszAttributes, 0, REG_DWORD, (LPBYTE)&dwAttributes, sizeof(DWORD));
685 dwCallForAttributes = SFGAO_ISSLOW|SFGAO_FILESYSTEM;
686 RegSetValueExW(hKey, wszCallForAttributes, 0, REG_DWORD,
687 (LPBYTE)&dwCallForAttributes, sizeof(DWORD));
689 /* Although it is not set in CallForAttributes, the SFGAO_GHOSTED flag is reset by
690 * GetAttributesOf. It seems that once there is a single attribute queried, for which
691 * CallForAttributes is set, all flags are taken from the GetAttributesOf call and
692 * the flags in Attributes are ignored.
694 dwAttributes = SFGAO_ISSLOW|SFGAO_GHOSTED|SFGAO_FILESYSTEM;
695 hr = IShellFolder_GetAttributesOf(psfDesktop, 1,
696 (LPCITEMIDLIST*)&pidlMyDocuments, &dwAttributes);
697 ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(MyDocuments) failed! hr = %08x\n", hr);
698 if (SUCCEEDED(hr))
699 ok (dwAttributes == SFGAO_FILESYSTEM,
700 "Desktop->GetAttributes(MyDocuments) returned unexpected attributes: %08x\n",
701 dwAttributes);
703 /* Restore MyDocuments' original Attributes and CallForAttributes registry values */
704 RegSetValueExW(hKey, wszAttributes, 0, REG_DWORD, (LPBYTE)&dwOrigAttributes, sizeof(DWORD));
705 RegSetValueExW(hKey, wszCallForAttributes, 0, REG_DWORD,
706 (LPBYTE)&dwOrigCallForAttributes, sizeof(DWORD));
707 RegCloseKey(hKey);
708 IMalloc_Free(ppM, pidlMyDocuments);
709 IShellFolder_Release(psfDesktop);
712 static void test_GetAttributesOf(void)
714 HRESULT hr;
715 LPSHELLFOLDER psfDesktop, psfMyComputer;
716 SHITEMID emptyitem = { 0, { 0 } };
717 LPCITEMIDLIST pidlEmpty = (LPCITEMIDLIST)&emptyitem;
718 LPITEMIDLIST pidlMyComputer;
719 DWORD dwFlags;
720 static const DWORD desktopFlags[] = {
721 /* WinXP */
722 SFGAO_STORAGE | SFGAO_HASPROPSHEET | SFGAO_STORAGEANCESTOR | SFGAO_FILESYSANCESTOR |
723 SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_HASSUBFOLDER,
724 /* Win2k */
725 SFGAO_CANRENAME | SFGAO_HASPROPSHEET | SFGAO_STREAM | SFGAO_FILESYSANCESTOR |
726 SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_HASSUBFOLDER,
727 /* WinMe, Win9x, WinNT*/
728 SFGAO_CANRENAME | SFGAO_HASPROPSHEET | SFGAO_FILESYSANCESTOR |
729 SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_HASSUBFOLDER
731 static const DWORD myComputerFlags[] = {
732 /* WinXP */
733 SFGAO_CANRENAME | SFGAO_CANDELETE | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET |
734 SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER,
735 /* Win2k */
736 SFGAO_CANRENAME | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET | SFGAO_STREAM |
737 SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER,
738 /* WinMe, Win9x, WinNT */
739 SFGAO_CANRENAME | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET | SFGAO_FILESYSANCESTOR |
740 SFGAO_FOLDER | SFGAO_HASSUBFOLDER,
741 /* Win95, WinNT when queried directly */
742 SFGAO_CANLINK | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET | SFGAO_FILESYSANCESTOR |
743 SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_HASSUBFOLDER
745 WCHAR wszMyComputer[] = {
746 ':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
747 'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };
748 char cCurrDirA [MAX_PATH] = {0};
749 WCHAR cCurrDirW [MAX_PATH];
750 static WCHAR cTestDirW[] = {'t','e','s','t','d','i','r',0};
751 IShellFolder *IDesktopFolder, *testIShellFolder;
752 ITEMIDLIST *newPIDL;
753 int len, i;
754 BOOL foundFlagsMatch;
756 hr = SHGetDesktopFolder(&psfDesktop);
757 ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08x\n", hr);
758 if (FAILED(hr)) return;
760 /* The Desktop attributes can be queried with a single empty itemidlist, .. */
761 dwFlags = 0xffffffff;
762 hr = IShellFolder_GetAttributesOf(psfDesktop, 1, &pidlEmpty, &dwFlags);
763 ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(empty pidl) failed! hr = %08x\n", hr);
764 for (i = 0, foundFlagsMatch = FALSE; !foundFlagsMatch &&
765 i < sizeof(desktopFlags) / sizeof(desktopFlags[0]); i++)
767 if (desktopFlags[i] == dwFlags)
768 foundFlagsMatch = TRUE;
770 ok (foundFlagsMatch, "Wrong Desktop attributes: %08x\n", dwFlags);
772 /* .. or with no itemidlist at all. */
773 dwFlags = 0xffffffff;
774 hr = IShellFolder_GetAttributesOf(psfDesktop, 0, NULL, &dwFlags);
775 ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(NULL) failed! hr = %08x\n", hr);
776 for (i = 0, foundFlagsMatch = FALSE; !foundFlagsMatch &&
777 i < sizeof(desktopFlags) / sizeof(desktopFlags[0]); i++)
779 if (desktopFlags[i] == dwFlags)
780 foundFlagsMatch = TRUE;
782 ok (foundFlagsMatch, "Wrong Desktop attributes: %08x\n", dwFlags);
784 /* Testing the attributes of the MyComputer shellfolder */
785 hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyComputer, NULL, &pidlMyComputer, NULL);
786 ok (SUCCEEDED(hr), "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08x\n", hr);
787 if (FAILED(hr)) {
788 IShellFolder_Release(psfDesktop);
789 return;
792 /* Windows sets the SFGAO_CANLINK flag, when MyComputer is queried via the Desktop
793 * folder object. It doesn't do this, if MyComputer is queried directly (see below).
795 dwFlags = 0xffffffff;
796 hr = IShellFolder_GetAttributesOf(psfDesktop, 1, (LPCITEMIDLIST*)&pidlMyComputer, &dwFlags);
797 ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(MyComputer) failed! hr = %08x\n", hr);
798 for (i = 0, foundFlagsMatch = FALSE; !foundFlagsMatch &&
799 i < sizeof(myComputerFlags) / sizeof(myComputerFlags[0]); i++)
801 if ((myComputerFlags[i] | SFGAO_CANLINK) == dwFlags)
802 foundFlagsMatch = TRUE;
804 todo_wine
805 ok (foundFlagsMatch, "Wrong MyComputer attributes: %08x\n", dwFlags);
807 hr = IShellFolder_BindToObject(psfDesktop, pidlMyComputer, NULL, &IID_IShellFolder, (LPVOID*)&psfMyComputer);
808 ok (SUCCEEDED(hr), "Desktop failed to bind to MyComputer object! hr = %08x\n", hr);
809 IShellFolder_Release(psfDesktop);
810 IMalloc_Free(ppM, pidlMyComputer);
811 if (FAILED(hr)) return;
813 hr = IShellFolder_GetAttributesOf(psfMyComputer, 1, &pidlEmpty, &dwFlags);
814 todo_wine
815 ok (hr == E_INVALIDARG ||
816 broken(SUCCEEDED(hr)), /* W2K and earlier */
817 "MyComputer->GetAttributesOf(emtpy pidl) should fail! hr = %08x\n", hr);
819 dwFlags = 0xffffffff;
820 hr = IShellFolder_GetAttributesOf(psfMyComputer, 0, NULL, &dwFlags);
821 ok (SUCCEEDED(hr), "MyComputer->GetAttributesOf(NULL) failed! hr = %08x\n", hr);
822 for (i = 0, foundFlagsMatch = FALSE; !foundFlagsMatch &&
823 i < sizeof(myComputerFlags) / sizeof(myComputerFlags[0]); i++)
825 if (myComputerFlags[i] == dwFlags)
826 foundFlagsMatch = TRUE;
828 todo_wine
829 ok (foundFlagsMatch, "Wrong MyComputer attributes: %08x\n", dwFlags);
831 IShellFolder_Release(psfMyComputer);
833 GetCurrentDirectoryA(MAX_PATH, cCurrDirA);
834 len = lstrlenA(cCurrDirA);
836 if (len == 0) {
837 win_skip("GetCurrentDirectoryA returned empty string. Skipping test_GetAttributesOf\n");
838 return;
840 if (len > 3 && cCurrDirA[len-1] == '\\')
841 cCurrDirA[len-1] = 0;
843 /* create test directory */
844 CreateFilesFolders();
846 MultiByteToWideChar(CP_ACP, 0, cCurrDirA, -1, cCurrDirW, MAX_PATH);
848 hr = SHGetDesktopFolder(&IDesktopFolder);
849 ok(hr == S_OK, "SHGetDesktopfolder failed %08x\n", hr);
851 hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cCurrDirW, NULL, &newPIDL, 0);
852 ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
854 hr = IShellFolder_BindToObject(IDesktopFolder, newPIDL, NULL, (REFIID)&IID_IShellFolder, (LPVOID *)&testIShellFolder);
855 ok(hr == S_OK, "BindToObject failed %08x\n", hr);
857 IMalloc_Free(ppM, newPIDL);
859 /* get relative PIDL */
860 hr = IShellFolder_ParseDisplayName(testIShellFolder, NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
861 ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
863 /* test the shell attributes of the test directory using the relative PIDL */
864 dwFlags = SFGAO_FOLDER;
865 hr = IShellFolder_GetAttributesOf(testIShellFolder, 1, (LPCITEMIDLIST*)&newPIDL, &dwFlags);
866 ok (SUCCEEDED(hr), "Desktop->GetAttributesOf() failed! hr = %08x\n", hr);
867 ok ((dwFlags&SFGAO_FOLDER), "Wrong directory attribute for relative PIDL: %08x\n", dwFlags);
869 /* free memory */
870 IMalloc_Free(ppM, newPIDL);
872 /* append testdirectory name to path */
873 if (cCurrDirA[len-1] == '\\')
874 cCurrDirA[len-1] = 0;
875 lstrcatA(cCurrDirA, "\\testdir");
876 MultiByteToWideChar(CP_ACP, 0, cCurrDirA, -1, cCurrDirW, MAX_PATH);
878 hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cCurrDirW, NULL, &newPIDL, 0);
879 ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
881 /* test the shell attributes of the test directory using the absolute PIDL */
882 dwFlags = SFGAO_FOLDER;
883 hr = IShellFolder_GetAttributesOf(IDesktopFolder, 1, (LPCITEMIDLIST*)&newPIDL, &dwFlags);
884 ok (SUCCEEDED(hr), "Desktop->GetAttributesOf() failed! hr = %08x\n", hr);
885 ok ((dwFlags&SFGAO_FOLDER), "Wrong directory attribute for absolute PIDL: %08x\n", dwFlags);
887 /* free memory */
888 IMalloc_Free(ppM, newPIDL);
890 IShellFolder_Release(testIShellFolder);
892 Cleanup();
894 IShellFolder_Release(IDesktopFolder);
897 static void test_SHGetPathFromIDList(void)
899 SHITEMID emptyitem = { 0, { 0 } };
900 LPCITEMIDLIST pidlEmpty = (LPCITEMIDLIST)&emptyitem;
901 LPITEMIDLIST pidlMyComputer;
902 WCHAR wszPath[MAX_PATH], wszDesktop[MAX_PATH];
903 BOOL result;
904 HRESULT hr;
905 LPSHELLFOLDER psfDesktop;
906 WCHAR wszMyComputer[] = {
907 ':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
908 'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };
909 WCHAR wszFileName[MAX_PATH];
910 LPITEMIDLIST pidlTestFile;
911 HANDLE hTestFile;
912 STRRET strret;
913 static WCHAR wszTestFile[] = {
914 'w','i','n','e','t','e','s','t','.','f','o','o',0 };
915 HRESULT (WINAPI *pSHGetSpecialFolderLocation)(HWND, int, LPITEMIDLIST *);
916 HMODULE hShell32;
917 LPITEMIDLIST pidlPrograms;
919 if(!pSHGetPathFromIDListW || !pSHGetSpecialFolderPathW)
921 win_skip("SHGetPathFromIDListW() or SHGetSpecialFolderPathW() is missing\n");
922 return;
925 /* Calling SHGetPathFromIDListW with no pidl should return the empty string */
926 wszPath[0] = 'a';
927 wszPath[1] = '\0';
928 result = pSHGetPathFromIDListW(NULL, wszPath);
929 ok(!result, "Expected failure\n");
930 ok(!wszPath[0], "Expected empty string\n");
932 /* Calling SHGetPathFromIDListW with an empty pidl should return the desktop folder's path. */
933 result = pSHGetSpecialFolderPathW(NULL, wszDesktop, CSIDL_DESKTOP, FALSE);
934 ok(result, "SHGetSpecialFolderPathW(CSIDL_DESKTOP) failed! Last error: %u\n", GetLastError());
935 if (!result) return;
937 /* Check if we are on Win9x */
938 SetLastError(0xdeadbeef);
939 lstrcmpiW(wszDesktop, wszDesktop);
940 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
942 win_skip("Most W-calls are not implemented\n");
943 return;
946 result = pSHGetPathFromIDListW(pidlEmpty, wszPath);
947 ok(result, "SHGetPathFromIDListW failed! Last error: %u\n", GetLastError());
948 if (!result) return;
949 ok(!lstrcmpiW(wszDesktop, wszPath), "SHGetPathFromIDListW didn't return desktop path for empty pidl!\n");
951 /* MyComputer does not map to a filesystem path. SHGetPathFromIDListW should fail. */
952 hr = SHGetDesktopFolder(&psfDesktop);
953 ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08x\n", hr);
954 if (FAILED(hr)) return;
956 hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyComputer, NULL, &pidlMyComputer, NULL);
957 ok (SUCCEEDED(hr), "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08x\n", hr);
958 if (FAILED(hr)) {
959 IShellFolder_Release(psfDesktop);
960 return;
963 SetLastError(0xdeadbeef);
964 wszPath[0] = 'a';
965 wszPath[1] = '\0';
966 result = pSHGetPathFromIDListW(pidlMyComputer, wszPath);
967 ok (!result, "SHGetPathFromIDListW succeeded where it shouldn't!\n");
968 ok (GetLastError()==0xdeadbeef ||
969 GetLastError()==ERROR_SUCCESS, /* Vista and higher */
970 "Unexpected last error from SHGetPathFromIDListW: %u\n", GetLastError());
971 ok (!wszPath[0], "Expected empty path\n");
972 if (result) {
973 IShellFolder_Release(psfDesktop);
974 return;
977 IMalloc_Free(ppM, pidlMyComputer);
979 result = pSHGetSpecialFolderPathW(NULL, wszFileName, CSIDL_DESKTOPDIRECTORY, FALSE);
980 ok(result, "SHGetSpecialFolderPathW failed! Last error: %u\n", GetLastError());
981 if (!result) {
982 IShellFolder_Release(psfDesktop);
983 return;
985 myPathAddBackslashW(wszFileName);
986 lstrcatW(wszFileName, wszTestFile);
987 hTestFile = CreateFileW(wszFileName, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL);
988 ok(hTestFile != INVALID_HANDLE_VALUE, "CreateFileW failed! Last error: %u\n", GetLastError());
989 if (hTestFile == INVALID_HANDLE_VALUE) {
990 IShellFolder_Release(psfDesktop);
991 return;
993 CloseHandle(hTestFile);
995 hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszTestFile, NULL, &pidlTestFile, NULL);
996 ok (SUCCEEDED(hr), "Desktop's ParseDisplayName failed to parse filename hr = %08x\n", hr);
997 if (FAILED(hr)) {
998 IShellFolder_Release(psfDesktop);
999 DeleteFileW(wszFileName);
1000 IMalloc_Free(ppM, pidlTestFile);
1001 return;
1004 /* This test is to show that the Desktop shellfolder prepends the CSIDL_DESKTOPDIRECTORY
1005 * path for files placed on the desktop, if called with SHGDN_FORPARSING. */
1006 hr = IShellFolder_GetDisplayNameOf(psfDesktop, pidlTestFile, SHGDN_FORPARSING, &strret);
1007 ok (SUCCEEDED(hr), "Desktop's GetDisplayNamfOf failed! hr = %08x\n", hr);
1008 IShellFolder_Release(psfDesktop);
1009 DeleteFileW(wszFileName);
1010 if (FAILED(hr)) {
1011 IMalloc_Free(ppM, pidlTestFile);
1012 return;
1014 if (pStrRetToBufW)
1016 pStrRetToBufW(&strret, pidlTestFile, wszPath, MAX_PATH);
1017 ok(0 == lstrcmpW(wszFileName, wszPath),
1018 "Desktop->GetDisplayNameOf(pidlTestFile, SHGDN_FORPARSING) "
1019 "returned incorrect path for file placed on desktop\n");
1022 result = pSHGetPathFromIDListW(pidlTestFile, wszPath);
1023 ok(result, "SHGetPathFromIDListW failed! Last error: %u\n", GetLastError());
1024 IMalloc_Free(ppM, pidlTestFile);
1025 if (!result) return;
1026 ok(0 == lstrcmpW(wszFileName, wszPath), "SHGetPathFromIDListW returned incorrect path for file placed on desktop\n");
1029 /* Test if we can get the path from the start menu "program files" PIDL. */
1030 hShell32 = GetModuleHandleA("shell32");
1031 pSHGetSpecialFolderLocation = (void *)GetProcAddress(hShell32, "SHGetSpecialFolderLocation");
1033 hr = pSHGetSpecialFolderLocation(NULL, CSIDL_PROGRAM_FILES, &pidlPrograms);
1034 ok(SUCCEEDED(hr), "SHGetFolderLocation failed: 0x%08x\n", hr);
1036 SetLastError(0xdeadbeef);
1037 result = pSHGetPathFromIDListW(pidlPrograms, wszPath);
1038 IMalloc_Free(ppM, pidlPrograms);
1039 ok(result, "SHGetPathFromIDListW failed\n");
1042 static void test_EnumObjects_and_CompareIDs(void)
1044 ITEMIDLIST *newPIDL;
1045 IShellFolder *IDesktopFolder, *testIShellFolder;
1046 char cCurrDirA [MAX_PATH] = {0};
1047 static const CHAR cTestDirA[] = "\\testdir";
1048 WCHAR cTestDirW[MAX_PATH];
1049 int len;
1050 HRESULT hr;
1052 GetCurrentDirectoryA(MAX_PATH, cCurrDirA);
1053 len = lstrlenA(cCurrDirA);
1055 if(len == 0) {
1056 win_skip("GetCurrentDirectoryA returned empty string. Skipping test_EnumObjects_and_CompareIDs\n");
1057 return;
1059 if(cCurrDirA[len-1] == '\\')
1060 cCurrDirA[len-1] = 0;
1062 lstrcatA(cCurrDirA, cTestDirA);
1063 MultiByteToWideChar(CP_ACP, 0, cCurrDirA, -1, cTestDirW, MAX_PATH);
1065 hr = SHGetDesktopFolder(&IDesktopFolder);
1066 ok(hr == S_OK, "SHGetDesktopfolder failed %08x\n", hr);
1068 CreateFilesFolders();
1070 hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
1071 ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
1073 hr = IShellFolder_BindToObject(IDesktopFolder, newPIDL, NULL, (REFIID)&IID_IShellFolder, (LPVOID *)&testIShellFolder);
1074 ok(hr == S_OK, "BindToObject failed %08x\n", hr);
1076 test_EnumObjects(testIShellFolder);
1078 IShellFolder_Release(testIShellFolder);
1080 Cleanup();
1082 IMalloc_Free(ppM, newPIDL);
1084 IShellFolder_Release(IDesktopFolder);
1087 /* A simple implementation of an IPropertyBag, which returns fixed values for
1088 * 'Target' and 'Attributes' properties.
1090 static HRESULT WINAPI InitPropertyBag_IPropertyBag_QueryInterface(IPropertyBag *iface, REFIID riid,
1091 void **ppvObject)
1093 if (!ppvObject)
1094 return E_INVALIDARG;
1096 if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IPropertyBag, riid)) {
1097 *ppvObject = iface;
1098 } else {
1099 ok (FALSE, "InitPropertyBag asked for unknown interface!\n");
1100 return E_NOINTERFACE;
1103 IPropertyBag_AddRef(iface);
1104 return S_OK;
1107 static ULONG WINAPI InitPropertyBag_IPropertyBag_AddRef(IPropertyBag *iface) {
1108 return 2;
1111 static ULONG WINAPI InitPropertyBag_IPropertyBag_Release(IPropertyBag *iface) {
1112 return 1;
1115 static HRESULT WINAPI InitPropertyBag_IPropertyBag_Read(IPropertyBag *iface, LPCOLESTR pszPropName,
1116 VARIANT *pVar, IErrorLog *pErrorLog)
1118 static const WCHAR wszTargetSpecialFolder[] = {
1119 'T','a','r','g','e','t','S','p','e','c','i','a','l','F','o','l','d','e','r',0 };
1120 static const WCHAR wszTarget[] = {
1121 'T','a','r','g','e','t',0 };
1122 static const WCHAR wszAttributes[] = {
1123 'A','t','t','r','i','b','u','t','e','s',0 };
1124 static const WCHAR wszResolveLinkFlags[] = {
1125 'R','e','s','o','l','v','e','L','i','n','k','F','l','a','g','s',0 };
1126 static const WCHAR wszTargetKnownFolder[] = {
1127 'T','a','r','g','e','t','K','n','o','w','n','F','o','l','d','e','r',0 };
1128 static const WCHAR wszCLSID[] = {
1129 'C','L','S','I','D',0 };
1131 if (!lstrcmpW(pszPropName, wszTargetSpecialFolder)) {
1132 ok(V_VT(pVar) == VT_I4 ||
1133 broken(V_VT(pVar) == VT_BSTR), /* Win2k */
1134 "Wrong variant type for 'TargetSpecialFolder' property!\n");
1135 return E_INVALIDARG;
1138 if (!lstrcmpW(pszPropName, wszResolveLinkFlags))
1140 ok(V_VT(pVar) == VT_UI4, "Wrong variant type for 'ResolveLinkFlags' property!\n");
1141 return E_INVALIDARG;
1144 if (!lstrcmpW(pszPropName, wszTarget)) {
1145 WCHAR wszPath[MAX_PATH];
1146 BOOL result;
1148 ok(V_VT(pVar) == VT_BSTR ||
1149 broken(V_VT(pVar) == VT_EMPTY), /* Win2k */
1150 "Wrong variant type for 'Target' property!\n");
1151 if (V_VT(pVar) != VT_BSTR) return E_INVALIDARG;
1153 result = pSHGetSpecialFolderPathW(NULL, wszPath, CSIDL_DESKTOPDIRECTORY, FALSE);
1154 ok(result, "SHGetSpecialFolderPathW(DESKTOPDIRECTORY) failed! %u\n", GetLastError());
1155 if (!result) return E_INVALIDARG;
1157 V_BSTR(pVar) = SysAllocString(wszPath);
1158 return S_OK;
1161 if (!lstrcmpW(pszPropName, wszAttributes)) {
1162 ok(V_VT(pVar) == VT_UI4, "Wrong variant type for 'Attributes' property!\n");
1163 if (V_VT(pVar) != VT_UI4) return E_INVALIDARG;
1164 V_UI4(pVar) = SFGAO_FOLDER|SFGAO_HASSUBFOLDER|SFGAO_FILESYSANCESTOR|
1165 SFGAO_CANRENAME|SFGAO_FILESYSTEM;
1166 return S_OK;
1169 if (!lstrcmpW(pszPropName, wszTargetKnownFolder)) {
1170 ok(V_VT(pVar) == VT_BSTR, "Wrong variant type for 'TargetKnownFolder' property!\n");
1171 /* TODO */
1172 return E_INVALIDARG;
1175 if (!lstrcmpW(pszPropName, wszCLSID)) {
1176 ok(V_VT(pVar) == VT_EMPTY, "Wrong variant type for 'CLSID' property!\n");
1177 /* TODO */
1178 return E_INVALIDARG;
1181 ok(FALSE, "PropertyBag was asked for unknown property %s (vt=%d)!\n", wine_dbgstr_w(pszPropName), V_VT(pVar));
1182 return E_INVALIDARG;
1185 static HRESULT WINAPI InitPropertyBag_IPropertyBag_Write(IPropertyBag *iface, LPCOLESTR pszPropName,
1186 VARIANT *pVar)
1188 ok(FALSE, "Unexpected call to IPropertyBag_Write\n");
1189 return E_NOTIMPL;
1192 static const IPropertyBagVtbl InitPropertyBag_IPropertyBagVtbl = {
1193 InitPropertyBag_IPropertyBag_QueryInterface,
1194 InitPropertyBag_IPropertyBag_AddRef,
1195 InitPropertyBag_IPropertyBag_Release,
1196 InitPropertyBag_IPropertyBag_Read,
1197 InitPropertyBag_IPropertyBag_Write
1200 static struct IPropertyBag InitPropertyBag = {
1201 &InitPropertyBag_IPropertyBagVtbl
1204 static void test_FolderShortcut(void) {
1205 IPersistPropertyBag *pPersistPropertyBag;
1206 IShellFolder *pShellFolder, *pDesktopFolder;
1207 IPersistFolder3 *pPersistFolder3;
1208 HRESULT hr;
1209 STRRET strret;
1210 WCHAR wszDesktopPath[MAX_PATH], wszBuffer[MAX_PATH];
1211 BOOL result;
1212 CLSID clsid;
1213 LPITEMIDLIST pidlCurrentFolder, pidlWineTestFolder, pidlSubFolder;
1214 HKEY hShellExtKey;
1215 WCHAR wszWineTestFolder[] = {
1216 ':',':','{','9','B','3','5','2','E','B','F','-','2','7','6','5','-','4','5','C','1','-',
1217 'B','4','C','6','-','8','5','C','C','7','F','7','A','B','C','6','4','}',0 };
1218 WCHAR wszShellExtKey[] = { 'S','o','f','t','w','a','r','e','\\',
1219 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
1220 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
1221 'E','x','p','l','o','r','e','r','\\','D','e','s','k','t','o','p','\\',
1222 'N','a','m','e','S','p','a','c','e','\\',
1223 '{','9','b','3','5','2','e','b','f','-','2','7','6','5','-','4','5','c','1','-',
1224 'b','4','c','6','-','8','5','c','c','7','f','7','a','b','c','6','4','}',0 };
1226 WCHAR wszSomeSubFolder[] = { 'S','u','b','F','o','l','d','e','r', 0};
1227 static const GUID CLSID_UnixDosFolder =
1228 {0x9d20aae8, 0x0625, 0x44b0, {0x9c, 0xa7, 0x71, 0x88, 0x9c, 0x22, 0x54, 0xd9}};
1230 if (!pSHGetSpecialFolderPathW || !pStrRetToBufW) {
1231 win_skip("SHGetSpecialFolderPathW and/or StrRetToBufW are not available\n");
1232 return;
1235 if (!pSHGetFolderPathAndSubDirA)
1237 win_skip("FolderShortcut test doesn't work on Win2k\n");
1238 return;
1241 /* These tests basically show, that CLSID_FolderShortcuts are initialized
1242 * via their IPersistPropertyBag interface. And that the target folder
1243 * is taken from the IPropertyBag's 'Target' property.
1245 hr = CoCreateInstance(&CLSID_FolderShortcut, NULL, CLSCTX_INPROC_SERVER,
1246 &IID_IPersistPropertyBag, (LPVOID*)&pPersistPropertyBag);
1247 if (hr == REGDB_E_CLASSNOTREG) {
1248 win_skip("CLSID_FolderShortcut is not implemented\n");
1249 return;
1251 ok (SUCCEEDED(hr), "CoCreateInstance failed! hr = 0x%08x\n", hr);
1252 if (FAILED(hr)) return;
1254 hr = IPersistPropertyBag_Load(pPersistPropertyBag, &InitPropertyBag, NULL);
1255 ok(SUCCEEDED(hr), "IPersistPropertyBag_Load failed! hr = %08x\n", hr);
1256 if (FAILED(hr)) {
1257 IPersistPropertyBag_Release(pPersistPropertyBag);
1258 return;
1261 hr = IPersistPropertyBag_QueryInterface(pPersistPropertyBag, &IID_IShellFolder,
1262 (LPVOID*)&pShellFolder);
1263 IPersistPropertyBag_Release(pPersistPropertyBag);
1264 ok(SUCCEEDED(hr), "IPersistPropertyBag_QueryInterface(IShellFolder) failed! hr = %08x\n", hr);
1265 if (FAILED(hr)) return;
1267 hr = IShellFolder_GetDisplayNameOf(pShellFolder, NULL, SHGDN_FORPARSING, &strret);
1268 ok(SUCCEEDED(hr), "IShellFolder_GetDisplayNameOf(NULL) failed! hr = %08x\n", hr);
1269 if (FAILED(hr)) {
1270 IShellFolder_Release(pShellFolder);
1271 return;
1274 result = pSHGetSpecialFolderPathW(NULL, wszDesktopPath, CSIDL_DESKTOPDIRECTORY, FALSE);
1275 ok(result, "SHGetSpecialFolderPathW(CSIDL_DESKTOPDIRECTORY) failed! %u\n", GetLastError());
1276 if (!result) return;
1278 pStrRetToBufW(&strret, NULL, wszBuffer, MAX_PATH);
1279 ok(!lstrcmpiW(wszDesktopPath, wszBuffer), "FolderShortcut returned incorrect folder!\n");
1281 hr = IShellFolder_QueryInterface(pShellFolder, &IID_IPersistFolder3, (LPVOID*)&pPersistFolder3);
1282 IShellFolder_Release(pShellFolder);
1283 ok(SUCCEEDED(hr), "IShellFolder_QueryInterface(IID_IPersistFolder3 failed! hr = 0x%08x\n", hr);
1284 if (FAILED(hr)) return;
1286 hr = IPersistFolder3_GetClassID(pPersistFolder3, &clsid);
1287 ok(SUCCEEDED(hr), "IPersistFolder3_GetClassID failed! hr=0x%08x\n", hr);
1288 ok(IsEqualCLSID(&clsid, &CLSID_FolderShortcut), "Unexpected CLSID!\n");
1290 hr = IPersistFolder3_GetCurFolder(pPersistFolder3, &pidlCurrentFolder);
1291 ok(SUCCEEDED(hr), "IPersistFolder3_GetCurFolder failed! hr=0x%08x\n", hr);
1292 ok(!pidlCurrentFolder, "IPersistFolder3_GetCurFolder should return a NULL pidl!\n");
1294 /* For FolderShortcut objects, the Initialize method initialized the folder's position in the
1295 * shell namespace. The target folder, read from the property bag above, remains untouched.
1296 * The following tests show this: The itemidlist for some imaginary shellfolder object
1297 * is created and the FolderShortcut is initialized with it. GetCurFolder now returns this
1298 * itemidlist, but GetDisplayNameOf still returns the path from above.
1300 hr = SHGetDesktopFolder(&pDesktopFolder);
1301 ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08x\n", hr);
1302 if (FAILED(hr)) return;
1304 /* Temporarily register WineTestFolder as a shell namespace extension at the Desktop.
1305 * Otherwise ParseDisplayName fails on WinXP with E_INVALIDARG */
1306 RegCreateKeyW(HKEY_CURRENT_USER, wszShellExtKey, &hShellExtKey);
1307 RegCloseKey(hShellExtKey);
1308 hr = IShellFolder_ParseDisplayName(pDesktopFolder, NULL, NULL, wszWineTestFolder, NULL,
1309 &pidlWineTestFolder, NULL);
1310 RegDeleteKeyW(HKEY_CURRENT_USER, wszShellExtKey);
1311 IShellFolder_Release(pDesktopFolder);
1312 ok (SUCCEEDED(hr), "IShellFolder::ParseDisplayName failed! hr = %08x\n", hr);
1313 if (FAILED(hr)) return;
1315 hr = IPersistFolder3_Initialize(pPersistFolder3, pidlWineTestFolder);
1316 ok (SUCCEEDED(hr), "IPersistFolder3::Initialize failed! hr = %08x\n", hr);
1317 if (FAILED(hr)) {
1318 IPersistFolder3_Release(pPersistFolder3);
1319 pILFree(pidlWineTestFolder);
1320 return;
1323 hr = IPersistFolder3_GetCurFolder(pPersistFolder3, &pidlCurrentFolder);
1324 ok(SUCCEEDED(hr), "IPersistFolder3_GetCurFolder failed! hr=0x%08x\n", hr);
1325 ok(pILIsEqual(pidlCurrentFolder, pidlWineTestFolder),
1326 "IPersistFolder3_GetCurFolder should return pidlWineTestFolder!\n");
1327 pILFree(pidlCurrentFolder);
1328 pILFree(pidlWineTestFolder);
1330 hr = IPersistFolder3_QueryInterface(pPersistFolder3, &IID_IShellFolder, (LPVOID*)&pShellFolder);
1331 IPersistFolder3_Release(pPersistFolder3);
1332 ok(SUCCEEDED(hr), "IPersistFolder3_QueryInterface(IShellFolder) failed! hr = %08x\n", hr);
1333 if (FAILED(hr)) return;
1335 hr = IShellFolder_GetDisplayNameOf(pShellFolder, NULL, SHGDN_FORPARSING, &strret);
1336 ok(SUCCEEDED(hr), "IShellFolder_GetDisplayNameOf(NULL) failed! hr = %08x\n", hr);
1337 if (FAILED(hr)) {
1338 IShellFolder_Release(pShellFolder);
1339 return;
1342 pStrRetToBufW(&strret, NULL, wszBuffer, MAX_PATH);
1343 ok(!lstrcmpiW(wszDesktopPath, wszBuffer), "FolderShortcut returned incorrect folder!\n");
1345 /* Next few lines are meant to show that children of FolderShortcuts are not FolderShortcuts,
1346 * but ShellFSFolders. */
1347 myPathAddBackslashW(wszDesktopPath);
1348 lstrcatW(wszDesktopPath, wszSomeSubFolder);
1349 if (!CreateDirectoryW(wszDesktopPath, NULL)) {
1350 IShellFolder_Release(pShellFolder);
1351 return;
1354 hr = IShellFolder_ParseDisplayName(pShellFolder, NULL, NULL, wszSomeSubFolder, NULL,
1355 &pidlSubFolder, NULL);
1356 RemoveDirectoryW(wszDesktopPath);
1357 ok (SUCCEEDED(hr), "IShellFolder::ParseDisplayName failed! hr = %08x\n", hr);
1358 if (FAILED(hr)) {
1359 IShellFolder_Release(pShellFolder);
1360 return;
1363 hr = IShellFolder_BindToObject(pShellFolder, pidlSubFolder, NULL, &IID_IPersistFolder3,
1364 (LPVOID*)&pPersistFolder3);
1365 IShellFolder_Release(pShellFolder);
1366 pILFree(pidlSubFolder);
1367 ok (SUCCEEDED(hr), "IShellFolder::BindToObject failed! hr = %08x\n", hr);
1368 if (FAILED(hr))
1369 return;
1371 /* On windows, we expect CLSID_ShellFSFolder. On wine we relax this constraint
1372 * a little bit and also allow CLSID_UnixDosFolder. */
1373 hr = IPersistFolder3_GetClassID(pPersistFolder3, &clsid);
1374 ok(SUCCEEDED(hr), "IPersistFolder3_GetClassID failed! hr=0x%08x\n", hr);
1375 ok(IsEqualCLSID(&clsid, &CLSID_ShellFSFolder) || IsEqualCLSID(&clsid, &CLSID_UnixDosFolder),
1376 "IPersistFolder3::GetClassID returned unexpected CLSID!\n");
1378 IPersistFolder3_Release(pPersistFolder3);
1381 #include "pshpack1.h"
1382 struct FileStructA {
1383 BYTE type;
1384 BYTE dummy;
1385 DWORD dwFileSize;
1386 WORD uFileDate; /* In our current implementation this is */
1387 WORD uFileTime; /* FileTimeToDosDate(WIN32_FIND_DATA->ftLastWriteTime) */
1388 WORD uFileAttribs;
1389 CHAR szName[1];
1392 struct FileStructW {
1393 WORD cbLen; /* Length of this element. */
1394 BYTE abFooBar1[6]; /* Beyond any recognition. */
1395 WORD uDate; /* FileTimeToDosDate(WIN32_FIND_DATA->ftCreationTime)? */
1396 WORD uTime; /* (this is currently speculation) */
1397 WORD uDate2; /* FileTimeToDosDate(WIN32_FIND_DATA->ftLastAccessTime)? */
1398 WORD uTime2; /* (this is currently speculation) */
1399 BYTE abFooBar2[4]; /* Beyond any recognition. */
1400 WCHAR wszName[1]; /* The long filename in unicode. */
1401 /* Just for documentation: Right after the unicode string: */
1402 WORD cbOffset; /* FileStructW's offset from the beginning of the SHITMEID.
1403 * SHITEMID->cb == uOffset + cbLen */
1405 #include "poppack.h"
1407 static void test_ITEMIDLIST_format(void) {
1408 WCHAR wszPersonal[MAX_PATH];
1409 LPSHELLFOLDER psfDesktop, psfPersonal;
1410 LPITEMIDLIST pidlPersonal, pidlFile;
1411 HANDLE hFile;
1412 HRESULT hr;
1413 BOOL bResult;
1414 WCHAR wszFile[3][17] = { { 'e','v','e','n','_',0 }, { 'o','d','d','_',0 },
1415 { 'l','o','n','g','e','r','_','t','h','a','n','.','8','_','3',0 } };
1416 int i;
1418 if (!pSHGetSpecialFolderPathW) return;
1420 bResult = pSHGetSpecialFolderPathW(NULL, wszPersonal, CSIDL_PERSONAL, FALSE);
1421 ok(bResult, "SHGetSpecialFolderPathW failed! Last error: %u\n", GetLastError());
1422 if (!bResult) return;
1424 SetLastError(0xdeadbeef);
1425 bResult = SetCurrentDirectoryW(wszPersonal);
1426 if (!bResult && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) {
1427 win_skip("Most W-calls are not implemented\n");
1428 return;
1430 ok(bResult, "SetCurrentDirectory failed! Last error: %u\n", GetLastError());
1431 if (!bResult) return;
1433 hr = SHGetDesktopFolder(&psfDesktop);
1434 ok(SUCCEEDED(hr), "SHGetDesktopFolder failed! hr: %08x\n", hr);
1435 if (FAILED(hr)) return;
1437 hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszPersonal, NULL, &pidlPersonal, NULL);
1438 ok(SUCCEEDED(hr), "psfDesktop->ParseDisplayName failed! hr = %08x\n", hr);
1439 if (FAILED(hr)) {
1440 IShellFolder_Release(psfDesktop);
1441 return;
1444 hr = IShellFolder_BindToObject(psfDesktop, pidlPersonal, NULL, &IID_IShellFolder,
1445 (LPVOID*)&psfPersonal);
1446 IShellFolder_Release(psfDesktop);
1447 pILFree(pidlPersonal);
1448 ok(SUCCEEDED(hr), "psfDesktop->BindToObject failed! hr = %08x\n", hr);
1449 if (FAILED(hr)) return;
1451 for (i=0; i<3; i++) {
1452 CHAR szFile[MAX_PATH];
1453 struct FileStructA *pFileStructA;
1454 WORD cbOffset;
1456 WideCharToMultiByte(CP_ACP, 0, wszFile[i], -1, szFile, MAX_PATH, NULL, NULL);
1458 hFile = CreateFileW(wszFile[i], GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_FLAG_WRITE_THROUGH, NULL);
1459 ok(hFile != INVALID_HANDLE_VALUE, "CreateFile failed! (%u)\n", GetLastError());
1460 if (hFile == INVALID_HANDLE_VALUE) {
1461 IShellFolder_Release(psfPersonal);
1462 return;
1464 CloseHandle(hFile);
1466 hr = IShellFolder_ParseDisplayName(psfPersonal, NULL, NULL, wszFile[i], NULL, &pidlFile, NULL);
1467 DeleteFileW(wszFile[i]);
1468 ok(SUCCEEDED(hr), "psfPersonal->ParseDisplayName failed! hr: %08x\n", hr);
1469 if (FAILED(hr)) {
1470 IShellFolder_Release(psfPersonal);
1471 return;
1474 pFileStructA = (struct FileStructA *)pidlFile->mkid.abID;
1475 ok(pFileStructA->type == 0x32, "PIDLTYPE should be 0x32!\n");
1476 ok(pFileStructA->dummy == 0x00, "Dummy Byte should be 0x00!\n");
1477 ok(pFileStructA->dwFileSize == 0, "Filesize should be zero!\n");
1479 if (i < 2) /* First two file names are already in valid 8.3 format */
1480 ok(!strcmp(szFile, (CHAR*)&pidlFile->mkid.abID[12]), "Wrong file name!\n");
1481 else
1482 /* WinXP stores a derived 8.3 dos name (LONGER~1.8_3) here. We probably
1483 * can't implement this correctly, since unix filesystems don't support
1484 * this nasty short/long filename stuff. So we'll probably stay with our
1485 * current habbit of storing the long filename here, which seems to work
1486 * just fine. */
1487 todo_wine
1488 ok(pidlFile->mkid.abID[18] == '~' ||
1489 broken(pidlFile->mkid.abID[34] == '~'), /* Win2k */
1490 "Should be derived 8.3 name!\n");
1492 if (i == 0) /* First file name has an even number of chars. No need for alignment. */
1493 ok(pidlFile->mkid.abID[12 + strlen(szFile) + 1] != '\0' ||
1494 broken(pidlFile->mkid.cb == 2 + 12 + strlen(szFile) + 1 + 1), /* Win2k */
1495 "Alignment byte, where there shouldn't be!\n");
1497 if (i == 1) /* Second file name has an uneven number of chars => alignment byte */
1498 ok(pidlFile->mkid.abID[12 + strlen(szFile) + 1] == '\0',
1499 "There should be an alignment byte, but isn't!\n");
1501 /* The offset of the FileStructW member is stored as a WORD at the end of the pidl. */
1502 cbOffset = *(WORD*)(((LPBYTE)pidlFile)+pidlFile->mkid.cb-sizeof(WORD));
1503 ok ((cbOffset >= sizeof(struct FileStructA) &&
1504 cbOffset <= pidlFile->mkid.cb - sizeof(struct FileStructW)) ||
1505 broken(pidlFile->mkid.cb == 2 + 12 + strlen(szFile) + 1 + 1) || /* Win2k on short names */
1506 broken(pidlFile->mkid.cb == 2 + 12 + strlen(szFile) + 1 + 12 + 1), /* Win2k on long names */
1507 "Wrong offset value (%d) stored at the end of the PIDL\n", cbOffset);
1509 if (cbOffset >= sizeof(struct FileStructA) &&
1510 cbOffset <= pidlFile->mkid.cb - sizeof(struct FileStructW))
1512 struct FileStructW *pFileStructW = (struct FileStructW *)(((LPBYTE)pidlFile)+cbOffset);
1514 ok(pidlFile->mkid.cb == cbOffset + pFileStructW->cbLen,
1515 "FileStructW's offset and length should add up to the PIDL's length!\n");
1517 if (pidlFile->mkid.cb == cbOffset + pFileStructW->cbLen) {
1518 /* Since we just created the file, time of creation,
1519 * time of last access and time of last write access just be the same.
1520 * These tests seem to fail sometimes (on WinXP), if the test is run again shortly
1521 * after the first run. I do remember something with NTFS keeping the creation time
1522 * if a file is deleted and then created again within a couple of seconds or so.
1523 * Might be the reason. */
1524 ok (pFileStructA->uFileDate == pFileStructW->uDate &&
1525 pFileStructA->uFileTime == pFileStructW->uTime,
1526 "Last write time should match creation time!\n");
1528 /* On FAT filesystems the last access time is midnight
1529 local time, so the values of uDate2 and uTime2 will
1530 depend on the local timezone. If the times are exactly
1531 equal then the dates should be identical for both FAT
1532 and NTFS as no timezone is more than 1 day away from UTC.
1534 if (pFileStructA->uFileTime == pFileStructW->uTime2)
1536 ok (pFileStructA->uFileDate == pFileStructW->uDate2,
1537 "Last write date and time should match last access date and time!\n");
1539 else
1541 /* Filesystem may be FAT. Check date within 1 day
1542 and seconds are zero. */
1543 trace ("Filesystem may be FAT. Performing less strict atime test.\n");
1544 ok ((pFileStructW->uTime2 & 0x1F) == 0,
1545 "Last access time on FAT filesystems should have zero seconds.\n");
1546 /* TODO: Perform check for date being within one day.*/
1549 ok (!lstrcmpW(wszFile[i], pFileStructW->wszName) ||
1550 !lstrcmpW(wszFile[i], (WCHAR *)(pFileStructW->abFooBar2 + 22)) || /* Vista */
1551 !lstrcmpW(wszFile[i], (WCHAR *)(pFileStructW->abFooBar2 + 26)), /* Win7 */
1552 "The filename should be stored in unicode at this position!\n");
1556 pILFree(pidlFile);
1560 static void testSHGetFolderPathAndSubDirA(void)
1562 HRESULT ret;
1563 BOOL delret;
1564 DWORD dwret;
1565 int i;
1566 static char wine[] = "wine";
1567 static char winetemp[] = "wine\\temp";
1568 static char appdata[MAX_PATH];
1569 static char testpath[MAX_PATH];
1570 static char toolongpath[MAX_PATH+1];
1572 if(!pSHGetFolderPathA) {
1573 win_skip("SHGetFolderPathA not present!\n");
1574 return;
1576 if(FAILED(pSHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, appdata)))
1578 win_skip("SHGetFolderPathA failed for CSIDL_LOCAL_APPDATA!\n");
1579 return;
1582 sprintf(testpath, "%s\\%s", appdata, winetemp);
1583 delret = RemoveDirectoryA(testpath);
1584 if(!delret && (ERROR_PATH_NOT_FOUND != GetLastError()) ) {
1585 win_skip("RemoveDirectoryA(%s) failed with error %u\n", testpath, GetLastError());
1586 return;
1589 sprintf(testpath, "%s\\%s", appdata, wine);
1590 delret = RemoveDirectoryA(testpath);
1591 if(!delret && (ERROR_PATH_NOT_FOUND != GetLastError()) && (ERROR_FILE_NOT_FOUND != GetLastError())) {
1592 win_skip("RemoveDirectoryA(%s) failed with error %u\n", testpath, GetLastError());
1593 return;
1596 /* test invalid second parameter */
1597 ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | 0xff, NULL, SHGFP_TYPE_CURRENT, wine, testpath);
1598 ok(E_INVALIDARG == ret, "expected E_INVALIDARG, got %x\n", ret);
1600 /* test fourth parameter */
1601 ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, 2, winetemp, testpath);
1602 switch(ret) {
1603 case S_OK: /* winvista */
1604 ok(!strncmp(appdata, testpath, strlen(appdata)),
1605 "expected %s to start with %s\n", testpath, appdata);
1606 ok(!lstrcmpA(&testpath[1 + strlen(appdata)], winetemp),
1607 "expected %s to end with %s\n", testpath, winetemp);
1608 break;
1609 case E_INVALIDARG: /* winxp, win2k3 */
1610 break;
1611 default:
1612 ok(0, "expected S_OK or E_INVALIDARG, got %x\n", ret);
1615 /* test fifth parameter */
1616 testpath[0] = '\0';
1617 ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, NULL, testpath);
1618 ok(S_OK == ret, "expected S_OK, got %x\n", ret);
1619 ok(!lstrcmpA(appdata, testpath), "expected %s, got %s\n", appdata, testpath);
1621 testpath[0] = '\0';
1622 ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, "", testpath);
1623 ok(S_OK == ret, "expected S_OK, got %x\n", ret);
1624 ok(!lstrcmpA(appdata, testpath), "expected %s, got %s\n", appdata, testpath);
1626 testpath[0] = '\0';
1627 ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, "\\", testpath);
1628 ok(S_OK == ret, "expected S_OK, got %x\n", ret);
1629 ok(!lstrcmpA(appdata, testpath), "expected %s, got %s\n", appdata, testpath);
1631 for(i=0; i< MAX_PATH; i++)
1632 toolongpath[i] = '0' + i % 10;
1633 toolongpath[MAX_PATH] = '\0';
1634 ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, toolongpath, testpath);
1635 ok(HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE) == ret,
1636 "expected %x, got %x\n", HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE), ret);
1638 testpath[0] = '\0';
1639 ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, wine, NULL);
1640 ok((S_OK == ret) || (E_INVALIDARG == ret), "expected S_OK or E_INVALIDARG, got %x\n", ret);
1642 /* test a not existing path */
1643 testpath[0] = '\0';
1644 ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, winetemp, testpath);
1645 ok(HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == ret,
1646 "expected %x, got %x\n", HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND), ret);
1648 /* create a directory inside a not existing directory */
1649 testpath[0] = '\0';
1650 ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_CREATE | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, winetemp, testpath);
1651 ok(S_OK == ret, "expected S_OK, got %x\n", ret);
1652 ok(!strncmp(appdata, testpath, strlen(appdata)),
1653 "expected %s to start with %s\n", testpath, appdata);
1654 ok(!lstrcmpA(&testpath[1 + strlen(appdata)], winetemp),
1655 "expected %s to end with %s\n", testpath, winetemp);
1656 dwret = GetFileAttributes(testpath);
1657 ok(FILE_ATTRIBUTE_DIRECTORY | dwret, "expected %x to contain FILE_ATTRIBUTE_DIRECTORY\n", dwret);
1659 /* cleanup */
1660 sprintf(testpath, "%s\\%s", appdata, winetemp);
1661 RemoveDirectoryA(testpath);
1662 sprintf(testpath, "%s\\%s", appdata, wine);
1663 RemoveDirectoryA(testpath);
1666 static void test_LocalizedNames(void)
1668 static char cCurrDirA[MAX_PATH];
1669 WCHAR cCurrDirW[MAX_PATH], tempbufW[25];
1670 IShellFolder *IDesktopFolder, *testIShellFolder;
1671 ITEMIDLIST *newPIDL;
1672 int len;
1673 HRESULT hr;
1674 static char resourcefile[MAX_PATH];
1675 DWORD res;
1676 HANDLE file;
1677 STRRET strret;
1679 static const char desktopini_contents1[] =
1680 "[.ShellClassInfo]\r\n"
1681 "LocalizedResourceName=@";
1682 static const char desktopini_contents2[] =
1683 ",-1\r\n";
1684 static WCHAR foldernameW[] = {'t','e','s','t','f','o','l','d','e','r',0};
1685 static const WCHAR folderdisplayW[] = {'F','o','l','d','e','r',' ','N','a','m','e',' ','R','e','s','o','u','r','c','e',0};
1687 /* create folder with desktop.ini and localized name in GetModuleFileNameA(NULL) */
1688 CreateDirectoryA(".\\testfolder", NULL);
1690 SetFileAttributesA(".\\testfolder", GetFileAttributesA(".\\testfolder")|FILE_ATTRIBUTE_SYSTEM);
1692 GetModuleFileNameA(NULL, resourcefile, MAX_PATH);
1694 file = CreateFileA(".\\testfolder\\desktop.ini", GENERIC_WRITE, 0, NULL,
1695 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
1696 ok(file != INVALID_HANDLE_VALUE, "CreateFileA failed %i\n", GetLastError());
1697 ok(WriteFile(file, desktopini_contents1, strlen(desktopini_contents1), &res, NULL) &&
1698 WriteFile(file, resourcefile, strlen(resourcefile), &res, NULL) &&
1699 WriteFile(file, desktopini_contents2, strlen(desktopini_contents2), &res, NULL),
1700 "WriteFile failed %i\n", GetLastError());
1701 CloseHandle(file);
1703 /* get IShellFolder for parent */
1704 GetCurrentDirectoryA(MAX_PATH, cCurrDirA);
1705 len = lstrlenA(cCurrDirA);
1707 if (len == 0) {
1708 win_skip("GetCurrentDirectoryA returned empty string. Skipping test_LocalizedNames\n");
1709 goto cleanup;
1711 if(cCurrDirA[len-1] == '\\')
1712 cCurrDirA[len-1] = 0;
1714 MultiByteToWideChar(CP_ACP, 0, cCurrDirA, -1, cCurrDirW, MAX_PATH);
1716 hr = SHGetDesktopFolder(&IDesktopFolder);
1717 ok(hr == S_OK, "SHGetDesktopfolder failed %08x\n", hr);
1719 hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cCurrDirW, NULL, &newPIDL, 0);
1720 ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
1722 hr = IShellFolder_BindToObject(IDesktopFolder, newPIDL, NULL, (REFIID)&IID_IShellFolder, (LPVOID *)&testIShellFolder);
1723 ok(hr == S_OK, "BindToObject failed %08x\n", hr);
1725 IMalloc_Free(ppM, newPIDL);
1727 /* windows reads the display name from the resource */
1728 hr = IShellFolder_ParseDisplayName(testIShellFolder, NULL, NULL, foldernameW, NULL, &newPIDL, 0);
1729 ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
1731 hr = IShellFolder_GetDisplayNameOf(testIShellFolder, newPIDL, SHGDN_INFOLDER, &strret);
1732 ok(hr == S_OK, "GetDisplayNameOf failed %08x\n", hr);
1734 if (SUCCEEDED(hr) && pStrRetToBufW)
1736 hr = pStrRetToBufW(&strret, newPIDL, tempbufW, sizeof(tempbufW)/sizeof(WCHAR));
1737 ok (SUCCEEDED(hr), "StrRetToBufW failed! hr = %08x\n", hr);
1738 todo_wine
1739 ok (!lstrcmpiW(tempbufW, folderdisplayW) ||
1740 broken(!lstrcmpiW(tempbufW, foldernameW)), /* W2K */
1741 "GetDisplayNameOf returned %s\n", wine_dbgstr_w(tempbufW));
1744 /* editing name is also read from the resource */
1745 hr = IShellFolder_GetDisplayNameOf(testIShellFolder, newPIDL, SHGDN_INFOLDER|SHGDN_FOREDITING, &strret);
1746 ok(hr == S_OK, "GetDisplayNameOf failed %08x\n", hr);
1748 if (SUCCEEDED(hr) && pStrRetToBufW)
1750 hr = pStrRetToBufW(&strret, newPIDL, tempbufW, sizeof(tempbufW)/sizeof(WCHAR));
1751 ok (SUCCEEDED(hr), "StrRetToBufW failed! hr = %08x\n", hr);
1752 todo_wine
1753 ok (!lstrcmpiW(tempbufW, folderdisplayW) ||
1754 broken(!lstrcmpiW(tempbufW, foldernameW)), /* W2K */
1755 "GetDisplayNameOf returned %s\n", wine_dbgstr_w(tempbufW));
1758 /* parsing name is unchanged */
1759 hr = IShellFolder_GetDisplayNameOf(testIShellFolder, newPIDL, SHGDN_INFOLDER|SHGDN_FORPARSING, &strret);
1760 ok(hr == S_OK, "GetDisplayNameOf failed %08x\n", hr);
1762 if (SUCCEEDED(hr) && pStrRetToBufW)
1764 hr = pStrRetToBufW(&strret, newPIDL, tempbufW, sizeof(tempbufW)/sizeof(WCHAR));
1765 ok (SUCCEEDED(hr), "StrRetToBufW failed! hr = %08x\n", hr);
1766 ok (!lstrcmpiW(tempbufW, foldernameW), "GetDisplayNameOf returned %s\n", wine_dbgstr_w(tempbufW));
1769 IShellFolder_Release(IDesktopFolder);
1770 IShellFolder_Release(testIShellFolder);
1772 IMalloc_Free(ppM, newPIDL);
1774 cleanup:
1775 DeleteFileA(".\\testfolder\\desktop.ini");
1776 SetFileAttributesA(".\\testfolder", GetFileAttributesA(".\\testfolder")&~FILE_ATTRIBUTE_SYSTEM);
1777 RemoveDirectoryA(".\\testfolder");
1780 static void test_SHCreateShellItem(void)
1782 IShellItem *shellitem, *shellitem2;
1783 IPersistIDList *persistidl;
1784 LPITEMIDLIST pidl_cwd=NULL, pidl_testfile, pidl_abstestfile, pidl_test;
1785 HRESULT ret;
1786 char curdirA[MAX_PATH];
1787 WCHAR curdirW[MAX_PATH];
1788 IShellFolder *desktopfolder=NULL, *currentfolder=NULL;
1789 static WCHAR testfileW[] = {'t','e','s','t','f','i','l','e',0};
1791 GetCurrentDirectoryA(MAX_PATH, curdirA);
1793 if (!lstrlenA(curdirA))
1795 win_skip("GetCurrentDirectoryA returned empty string, skipping test_SHCreateShellItem\n");
1796 return;
1799 MultiByteToWideChar(CP_ACP, 0, curdirA, -1, curdirW, MAX_PATH);
1801 ret = SHGetDesktopFolder(&desktopfolder);
1802 ok(SUCCEEDED(ret), "SHGetShellFolder returned %x\n", ret);
1804 ret = IShellFolder_ParseDisplayName(desktopfolder, NULL, NULL, curdirW, NULL, &pidl_cwd, NULL);
1805 ok(SUCCEEDED(ret), "ParseDisplayName returned %x\n", ret);
1807 ret = IShellFolder_BindToObject(desktopfolder, pidl_cwd, NULL, &IID_IShellFolder, (void**)&currentfolder);
1808 ok(SUCCEEDED(ret), "BindToObject returned %x\n", ret);
1810 CreateTestFile(".\\testfile");
1812 ret = IShellFolder_ParseDisplayName(currentfolder, NULL, NULL, testfileW, NULL, &pidl_testfile, NULL);
1813 ok(SUCCEEDED(ret), "ParseDisplayName returned %x\n", ret);
1815 pidl_abstestfile = pILCombine(pidl_cwd, pidl_testfile);
1817 ret = pSHCreateShellItem(NULL, NULL, NULL, &shellitem);
1818 ok(ret == E_INVALIDARG, "SHCreateShellItem returned %x\n", ret);
1820 if (0) /* crashes on Windows XP */
1822 pSHCreateShellItem(NULL, NULL, pidl_cwd, NULL);
1823 pSHCreateShellItem(pidl_cwd, NULL, NULL, &shellitem);
1824 pSHCreateShellItem(NULL, currentfolder, NULL, &shellitem);
1825 pSHCreateShellItem(pidl_cwd, currentfolder, NULL, &shellitem);
1828 ret = pSHCreateShellItem(NULL, NULL, pidl_cwd, &shellitem);
1829 ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret);
1830 if (SUCCEEDED(ret))
1832 ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
1833 ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
1834 if (SUCCEEDED(ret))
1836 ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
1837 ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
1838 if (SUCCEEDED(ret))
1840 ok(ILIsEqual(pidl_cwd, pidl_test), "id lists are not equal\n");
1841 pILFree(pidl_test);
1843 IPersistIDList_Release(persistidl);
1845 IShellItem_Release(shellitem);
1848 ret = pSHCreateShellItem(pidl_cwd, NULL, pidl_testfile, &shellitem);
1849 ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret);
1850 if (SUCCEEDED(ret))
1852 ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
1853 ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
1854 if (SUCCEEDED(ret))
1856 ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
1857 ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
1858 if (SUCCEEDED(ret))
1860 ok(ILIsEqual(pidl_abstestfile, pidl_test), "id lists are not equal\n");
1861 pILFree(pidl_test);
1863 IPersistIDList_Release(persistidl);
1866 ret = IShellItem_GetParent(shellitem, &shellitem2);
1867 ok(SUCCEEDED(ret), "GetParent returned %x\n", ret);
1868 if (SUCCEEDED(ret))
1870 ret = IShellItem_QueryInterface(shellitem2, &IID_IPersistIDList, (void**)&persistidl);
1871 ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
1872 if (SUCCEEDED(ret))
1874 ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
1875 ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
1876 if (SUCCEEDED(ret))
1878 ok(ILIsEqual(pidl_cwd, pidl_test), "id lists are not equal\n");
1879 pILFree(pidl_test);
1881 IPersistIDList_Release(persistidl);
1883 IShellItem_Release(shellitem2);
1886 IShellItem_Release(shellitem);
1889 ret = pSHCreateShellItem(NULL, currentfolder, pidl_testfile, &shellitem);
1890 ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret);
1891 if (SUCCEEDED(ret))
1893 ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
1894 ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
1895 if (SUCCEEDED(ret))
1897 ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
1898 ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
1899 if (SUCCEEDED(ret))
1901 ok(ILIsEqual(pidl_abstestfile, pidl_test), "id lists are not equal\n");
1902 pILFree(pidl_test);
1904 IPersistIDList_Release(persistidl);
1906 IShellItem_Release(shellitem);
1909 /* if a parent pidl and shellfolder are specified, the shellfolder is ignored */
1910 ret = pSHCreateShellItem(pidl_cwd, desktopfolder, pidl_testfile, &shellitem);
1911 ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret);
1912 if (SUCCEEDED(ret))
1914 ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
1915 ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
1916 if (SUCCEEDED(ret))
1918 ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
1919 ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
1920 if (SUCCEEDED(ret))
1922 ok(ILIsEqual(pidl_abstestfile, pidl_test), "id lists are not equal\n");
1923 pILFree(pidl_test);
1925 IPersistIDList_Release(persistidl);
1927 IShellItem_Release(shellitem);
1930 DeleteFileA(".\\testfile");
1931 pILFree(pidl_abstestfile);
1932 pILFree(pidl_testfile);
1933 pILFree(pidl_cwd);
1934 IShellFolder_Release(currentfolder);
1935 IShellFolder_Release(desktopfolder);
1938 START_TEST(shlfolder)
1940 init_function_pointers();
1941 /* if OleInitialize doesn't get called, ParseDisplayName returns
1942 CO_E_NOTINITIALIZED for malformed directory names on win2k. */
1943 OleInitialize(NULL);
1945 test_ParseDisplayName();
1946 test_BindToObject();
1947 test_EnumObjects_and_CompareIDs();
1948 test_GetDisplayName();
1949 test_GetAttributesOf();
1950 test_SHGetPathFromIDList();
1951 test_CallForAttributes();
1952 test_FolderShortcut();
1953 test_ITEMIDLIST_format();
1954 if(pSHGetFolderPathAndSubDirA)
1955 testSHGetFolderPathAndSubDirA();
1956 else
1957 win_skip("SHGetFolderPathAndSubDirA not present\n");
1958 test_LocalizedNames();
1959 if(pSHCreateShellItem)
1960 test_SHCreateShellItem();
1961 else
1962 win_skip("SHCreateShellItem not present\n");
1964 OleUninitialize();