push 5b1efc32b5a8acb1d5b5e60584746392dd0c436e
[wine/hacks.git] / dlls / shell32 / tests / shlfolder.c
blobf676a2a76641d6e97851ff2b9a40d299c52613b0
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 ||
651 lResult == ERROR_ACCESS_DENIED,
652 "RegOpenKeyEx failed! result: %08x\n", lResult);
653 if (lResult != ERROR_SUCCESS) {
654 if (lResult == ERROR_ACCESS_DENIED)
655 skip("Not enough rights to open the registry key\n");
656 IMalloc_Free(ppM, pidlMyDocuments);
657 IShellFolder_Release(psfDesktop);
658 return;
661 /* Query MyDocuments' Attributes value, to be able to restore it later. */
662 dwSize = sizeof(DWORD);
663 lResult = RegQueryValueExW(hKey, wszAttributes, NULL, NULL, (LPBYTE)&dwOrigAttributes, &dwSize);
664 ok (lResult == ERROR_SUCCESS, "RegQueryValueEx failed! result: %08x\n", lResult);
665 if (lResult != ERROR_SUCCESS) {
666 RegCloseKey(hKey);
667 IMalloc_Free(ppM, pidlMyDocuments);
668 IShellFolder_Release(psfDesktop);
669 return;
672 /* Query MyDocuments' CallForAttributes value, to be able to restore it later. */
673 dwSize = sizeof(DWORD);
674 lResult = RegQueryValueExW(hKey, wszCallForAttributes, NULL, NULL,
675 (LPBYTE)&dwOrigCallForAttributes, &dwSize);
676 ok (lResult == ERROR_SUCCESS, "RegQueryValueEx failed! result: %08x\n", lResult);
677 if (lResult != ERROR_SUCCESS) {
678 RegCloseKey(hKey);
679 IMalloc_Free(ppM, pidlMyDocuments);
680 IShellFolder_Release(psfDesktop);
681 return;
684 /* Define via the Attributes value that MyDocuments attributes are SFGAO_ISSLOW and
685 * SFGAO_GHOSTED and that MyDocuments should be called for the SFGAO_ISSLOW and
686 * SFGAO_FILESYSTEM attributes. */
687 dwAttributes = SFGAO_ISSLOW|SFGAO_GHOSTED;
688 RegSetValueExW(hKey, wszAttributes, 0, REG_DWORD, (LPBYTE)&dwAttributes, sizeof(DWORD));
689 dwCallForAttributes = SFGAO_ISSLOW|SFGAO_FILESYSTEM;
690 RegSetValueExW(hKey, wszCallForAttributes, 0, REG_DWORD,
691 (LPBYTE)&dwCallForAttributes, sizeof(DWORD));
693 /* Although it is not set in CallForAttributes, the SFGAO_GHOSTED flag is reset by
694 * GetAttributesOf. It seems that once there is a single attribute queried, for which
695 * CallForAttributes is set, all flags are taken from the GetAttributesOf call and
696 * the flags in Attributes are ignored.
698 dwAttributes = SFGAO_ISSLOW|SFGAO_GHOSTED|SFGAO_FILESYSTEM;
699 hr = IShellFolder_GetAttributesOf(psfDesktop, 1,
700 (LPCITEMIDLIST*)&pidlMyDocuments, &dwAttributes);
701 ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(MyDocuments) failed! hr = %08x\n", hr);
702 if (SUCCEEDED(hr))
703 ok (dwAttributes == SFGAO_FILESYSTEM,
704 "Desktop->GetAttributes(MyDocuments) returned unexpected attributes: %08x\n",
705 dwAttributes);
707 /* Restore MyDocuments' original Attributes and CallForAttributes registry values */
708 RegSetValueExW(hKey, wszAttributes, 0, REG_DWORD, (LPBYTE)&dwOrigAttributes, sizeof(DWORD));
709 RegSetValueExW(hKey, wszCallForAttributes, 0, REG_DWORD,
710 (LPBYTE)&dwOrigCallForAttributes, sizeof(DWORD));
711 RegCloseKey(hKey);
712 IMalloc_Free(ppM, pidlMyDocuments);
713 IShellFolder_Release(psfDesktop);
716 static void test_GetAttributesOf(void)
718 HRESULT hr;
719 LPSHELLFOLDER psfDesktop, psfMyComputer;
720 SHITEMID emptyitem = { 0, { 0 } };
721 LPCITEMIDLIST pidlEmpty = (LPCITEMIDLIST)&emptyitem;
722 LPITEMIDLIST pidlMyComputer;
723 DWORD dwFlags;
724 static const DWORD desktopFlags[] = {
725 /* WinXP */
726 SFGAO_STORAGE | SFGAO_HASPROPSHEET | SFGAO_STORAGEANCESTOR | SFGAO_FILESYSANCESTOR |
727 SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_HASSUBFOLDER,
728 /* Win2k */
729 SFGAO_CANRENAME | SFGAO_HASPROPSHEET | SFGAO_STREAM | SFGAO_FILESYSANCESTOR |
730 SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_HASSUBFOLDER,
731 /* WinMe, Win9x, WinNT*/
732 SFGAO_CANRENAME | SFGAO_HASPROPSHEET | SFGAO_FILESYSANCESTOR |
733 SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_HASSUBFOLDER
735 static const DWORD myComputerFlags[] = {
736 /* WinXP */
737 SFGAO_CANRENAME | SFGAO_CANDELETE | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET |
738 SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER,
739 /* Win2k */
740 SFGAO_CANRENAME | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET | SFGAO_STREAM |
741 SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER,
742 /* WinMe, Win9x, WinNT */
743 SFGAO_CANRENAME | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET | SFGAO_FILESYSANCESTOR |
744 SFGAO_FOLDER | SFGAO_HASSUBFOLDER,
745 /* Win95, WinNT when queried directly */
746 SFGAO_CANLINK | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET | SFGAO_FILESYSANCESTOR |
747 SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_HASSUBFOLDER
749 WCHAR wszMyComputer[] = {
750 ':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
751 'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };
752 char cCurrDirA [MAX_PATH] = {0};
753 WCHAR cCurrDirW [MAX_PATH];
754 static WCHAR cTestDirW[] = {'t','e','s','t','d','i','r',0};
755 IShellFolder *IDesktopFolder, *testIShellFolder;
756 ITEMIDLIST *newPIDL;
757 int len, i;
758 BOOL foundFlagsMatch;
760 hr = SHGetDesktopFolder(&psfDesktop);
761 ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08x\n", hr);
762 if (FAILED(hr)) return;
764 /* The Desktop attributes can be queried with a single empty itemidlist, .. */
765 dwFlags = 0xffffffff;
766 hr = IShellFolder_GetAttributesOf(psfDesktop, 1, &pidlEmpty, &dwFlags);
767 ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(empty pidl) failed! hr = %08x\n", hr);
768 for (i = 0, foundFlagsMatch = FALSE; !foundFlagsMatch &&
769 i < sizeof(desktopFlags) / sizeof(desktopFlags[0]); i++)
771 if (desktopFlags[i] == dwFlags)
772 foundFlagsMatch = TRUE;
774 ok (foundFlagsMatch, "Wrong Desktop attributes: %08x\n", dwFlags);
776 /* .. or with no itemidlist at all. */
777 dwFlags = 0xffffffff;
778 hr = IShellFolder_GetAttributesOf(psfDesktop, 0, NULL, &dwFlags);
779 ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(NULL) failed! hr = %08x\n", hr);
780 for (i = 0, foundFlagsMatch = FALSE; !foundFlagsMatch &&
781 i < sizeof(desktopFlags) / sizeof(desktopFlags[0]); i++)
783 if (desktopFlags[i] == dwFlags)
784 foundFlagsMatch = TRUE;
786 ok (foundFlagsMatch, "Wrong Desktop attributes: %08x\n", dwFlags);
788 /* Testing the attributes of the MyComputer shellfolder */
789 hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyComputer, NULL, &pidlMyComputer, NULL);
790 ok (SUCCEEDED(hr), "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08x\n", hr);
791 if (FAILED(hr)) {
792 IShellFolder_Release(psfDesktop);
793 return;
796 /* Windows sets the SFGAO_CANLINK flag, when MyComputer is queried via the Desktop
797 * folder object. It doesn't do this, if MyComputer is queried directly (see below).
799 dwFlags = 0xffffffff;
800 hr = IShellFolder_GetAttributesOf(psfDesktop, 1, (LPCITEMIDLIST*)&pidlMyComputer, &dwFlags);
801 ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(MyComputer) failed! hr = %08x\n", hr);
802 for (i = 0, foundFlagsMatch = FALSE; !foundFlagsMatch &&
803 i < sizeof(myComputerFlags) / sizeof(myComputerFlags[0]); i++)
805 if ((myComputerFlags[i] | SFGAO_CANLINK) == dwFlags)
806 foundFlagsMatch = TRUE;
808 todo_wine
809 ok (foundFlagsMatch, "Wrong MyComputer attributes: %08x\n", dwFlags);
811 hr = IShellFolder_BindToObject(psfDesktop, pidlMyComputer, NULL, &IID_IShellFolder, (LPVOID*)&psfMyComputer);
812 ok (SUCCEEDED(hr), "Desktop failed to bind to MyComputer object! hr = %08x\n", hr);
813 IShellFolder_Release(psfDesktop);
814 IMalloc_Free(ppM, pidlMyComputer);
815 if (FAILED(hr)) return;
817 hr = IShellFolder_GetAttributesOf(psfMyComputer, 1, &pidlEmpty, &dwFlags);
818 todo_wine
819 ok (hr == E_INVALIDARG ||
820 broken(SUCCEEDED(hr)), /* W2K and earlier */
821 "MyComputer->GetAttributesOf(emtpy pidl) should fail! hr = %08x\n", hr);
823 dwFlags = 0xffffffff;
824 hr = IShellFolder_GetAttributesOf(psfMyComputer, 0, NULL, &dwFlags);
825 ok (SUCCEEDED(hr), "MyComputer->GetAttributesOf(NULL) failed! hr = %08x\n", hr);
826 for (i = 0, foundFlagsMatch = FALSE; !foundFlagsMatch &&
827 i < sizeof(myComputerFlags) / sizeof(myComputerFlags[0]); i++)
829 if (myComputerFlags[i] == dwFlags)
830 foundFlagsMatch = TRUE;
832 todo_wine
833 ok (foundFlagsMatch, "Wrong MyComputer attributes: %08x\n", dwFlags);
835 IShellFolder_Release(psfMyComputer);
837 GetCurrentDirectoryA(MAX_PATH, cCurrDirA);
838 len = lstrlenA(cCurrDirA);
840 if (len == 0) {
841 win_skip("GetCurrentDirectoryA returned empty string. Skipping test_GetAttributesOf\n");
842 return;
844 if (len > 3 && cCurrDirA[len-1] == '\\')
845 cCurrDirA[len-1] = 0;
847 /* create test directory */
848 CreateFilesFolders();
850 MultiByteToWideChar(CP_ACP, 0, cCurrDirA, -1, cCurrDirW, MAX_PATH);
852 hr = SHGetDesktopFolder(&IDesktopFolder);
853 ok(hr == S_OK, "SHGetDesktopfolder failed %08x\n", hr);
855 hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cCurrDirW, NULL, &newPIDL, 0);
856 ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
858 hr = IShellFolder_BindToObject(IDesktopFolder, newPIDL, NULL, (REFIID)&IID_IShellFolder, (LPVOID *)&testIShellFolder);
859 ok(hr == S_OK, "BindToObject failed %08x\n", hr);
861 IMalloc_Free(ppM, newPIDL);
863 /* get relative PIDL */
864 hr = IShellFolder_ParseDisplayName(testIShellFolder, NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
865 ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
867 /* test the shell attributes of the test directory using the relative PIDL */
868 dwFlags = SFGAO_FOLDER;
869 hr = IShellFolder_GetAttributesOf(testIShellFolder, 1, (LPCITEMIDLIST*)&newPIDL, &dwFlags);
870 ok (SUCCEEDED(hr), "Desktop->GetAttributesOf() failed! hr = %08x\n", hr);
871 ok ((dwFlags&SFGAO_FOLDER), "Wrong directory attribute for relative PIDL: %08x\n", dwFlags);
873 /* free memory */
874 IMalloc_Free(ppM, newPIDL);
876 /* append testdirectory name to path */
877 if (cCurrDirA[len-1] == '\\')
878 cCurrDirA[len-1] = 0;
879 lstrcatA(cCurrDirA, "\\testdir");
880 MultiByteToWideChar(CP_ACP, 0, cCurrDirA, -1, cCurrDirW, MAX_PATH);
882 hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cCurrDirW, NULL, &newPIDL, 0);
883 ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
885 /* test the shell attributes of the test directory using the absolute PIDL */
886 dwFlags = SFGAO_FOLDER;
887 hr = IShellFolder_GetAttributesOf(IDesktopFolder, 1, (LPCITEMIDLIST*)&newPIDL, &dwFlags);
888 ok (SUCCEEDED(hr), "Desktop->GetAttributesOf() failed! hr = %08x\n", hr);
889 ok ((dwFlags&SFGAO_FOLDER), "Wrong directory attribute for absolute PIDL: %08x\n", dwFlags);
891 /* free memory */
892 IMalloc_Free(ppM, newPIDL);
894 IShellFolder_Release(testIShellFolder);
896 Cleanup();
898 IShellFolder_Release(IDesktopFolder);
901 static void test_SHGetPathFromIDList(void)
903 SHITEMID emptyitem = { 0, { 0 } };
904 LPCITEMIDLIST pidlEmpty = (LPCITEMIDLIST)&emptyitem;
905 LPITEMIDLIST pidlMyComputer;
906 WCHAR wszPath[MAX_PATH], wszDesktop[MAX_PATH];
907 BOOL result;
908 HRESULT hr;
909 LPSHELLFOLDER psfDesktop;
910 WCHAR wszMyComputer[] = {
911 ':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
912 'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };
913 WCHAR wszFileName[MAX_PATH];
914 LPITEMIDLIST pidlTestFile;
915 HANDLE hTestFile;
916 STRRET strret;
917 static WCHAR wszTestFile[] = {
918 'w','i','n','e','t','e','s','t','.','f','o','o',0 };
919 HRESULT (WINAPI *pSHGetSpecialFolderLocation)(HWND, int, LPITEMIDLIST *);
920 HMODULE hShell32;
921 LPITEMIDLIST pidlPrograms;
923 if(!pSHGetPathFromIDListW || !pSHGetSpecialFolderPathW)
925 win_skip("SHGetPathFromIDListW() or SHGetSpecialFolderPathW() is missing\n");
926 return;
929 /* Calling SHGetPathFromIDListW with no pidl should return the empty string */
930 wszPath[0] = 'a';
931 wszPath[1] = '\0';
932 result = pSHGetPathFromIDListW(NULL, wszPath);
933 ok(!result, "Expected failure\n");
934 ok(!wszPath[0], "Expected empty string\n");
936 /* Calling SHGetPathFromIDListW with an empty pidl should return the desktop folder's path. */
937 result = pSHGetSpecialFolderPathW(NULL, wszDesktop, CSIDL_DESKTOP, FALSE);
938 ok(result, "SHGetSpecialFolderPathW(CSIDL_DESKTOP) failed! Last error: %u\n", GetLastError());
939 if (!result) return;
941 /* Check if we are on Win9x */
942 SetLastError(0xdeadbeef);
943 lstrcmpiW(wszDesktop, wszDesktop);
944 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
946 win_skip("Most W-calls are not implemented\n");
947 return;
950 result = pSHGetPathFromIDListW(pidlEmpty, wszPath);
951 ok(result, "SHGetPathFromIDListW failed! Last error: %u\n", GetLastError());
952 if (!result) return;
953 ok(!lstrcmpiW(wszDesktop, wszPath), "SHGetPathFromIDListW didn't return desktop path for empty pidl!\n");
955 /* MyComputer does not map to a filesystem path. SHGetPathFromIDListW should fail. */
956 hr = SHGetDesktopFolder(&psfDesktop);
957 ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08x\n", hr);
958 if (FAILED(hr)) return;
960 hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyComputer, NULL, &pidlMyComputer, NULL);
961 ok (SUCCEEDED(hr), "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08x\n", hr);
962 if (FAILED(hr)) {
963 IShellFolder_Release(psfDesktop);
964 return;
967 SetLastError(0xdeadbeef);
968 wszPath[0] = 'a';
969 wszPath[1] = '\0';
970 result = pSHGetPathFromIDListW(pidlMyComputer, wszPath);
971 ok (!result, "SHGetPathFromIDListW succeeded where it shouldn't!\n");
972 ok (GetLastError()==0xdeadbeef ||
973 GetLastError()==ERROR_SUCCESS, /* Vista and higher */
974 "Unexpected last error from SHGetPathFromIDListW: %u\n", GetLastError());
975 ok (!wszPath[0], "Expected empty path\n");
976 if (result) {
977 IShellFolder_Release(psfDesktop);
978 return;
981 IMalloc_Free(ppM, pidlMyComputer);
983 result = pSHGetSpecialFolderPathW(NULL, wszFileName, CSIDL_DESKTOPDIRECTORY, FALSE);
984 ok(result, "SHGetSpecialFolderPathW failed! Last error: %u\n", GetLastError());
985 if (!result) {
986 IShellFolder_Release(psfDesktop);
987 return;
989 myPathAddBackslashW(wszFileName);
990 lstrcatW(wszFileName, wszTestFile);
991 hTestFile = CreateFileW(wszFileName, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL);
992 ok(hTestFile != INVALID_HANDLE_VALUE, "CreateFileW failed! Last error: %u\n", GetLastError());
993 if (hTestFile == INVALID_HANDLE_VALUE) {
994 IShellFolder_Release(psfDesktop);
995 return;
997 CloseHandle(hTestFile);
999 hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszTestFile, NULL, &pidlTestFile, NULL);
1000 ok (SUCCEEDED(hr), "Desktop's ParseDisplayName failed to parse filename hr = %08x\n", hr);
1001 if (FAILED(hr)) {
1002 IShellFolder_Release(psfDesktop);
1003 DeleteFileW(wszFileName);
1004 IMalloc_Free(ppM, pidlTestFile);
1005 return;
1008 /* This test is to show that the Desktop shellfolder prepends the CSIDL_DESKTOPDIRECTORY
1009 * path for files placed on the desktop, if called with SHGDN_FORPARSING. */
1010 hr = IShellFolder_GetDisplayNameOf(psfDesktop, pidlTestFile, SHGDN_FORPARSING, &strret);
1011 ok (SUCCEEDED(hr), "Desktop's GetDisplayNamfOf failed! hr = %08x\n", hr);
1012 IShellFolder_Release(psfDesktop);
1013 DeleteFileW(wszFileName);
1014 if (FAILED(hr)) {
1015 IMalloc_Free(ppM, pidlTestFile);
1016 return;
1018 if (pStrRetToBufW)
1020 pStrRetToBufW(&strret, pidlTestFile, wszPath, MAX_PATH);
1021 ok(0 == lstrcmpW(wszFileName, wszPath),
1022 "Desktop->GetDisplayNameOf(pidlTestFile, SHGDN_FORPARSING) "
1023 "returned incorrect path for file placed on desktop\n");
1026 result = pSHGetPathFromIDListW(pidlTestFile, wszPath);
1027 ok(result, "SHGetPathFromIDListW failed! Last error: %u\n", GetLastError());
1028 IMalloc_Free(ppM, pidlTestFile);
1029 if (!result) return;
1030 ok(0 == lstrcmpW(wszFileName, wszPath), "SHGetPathFromIDListW returned incorrect path for file placed on desktop\n");
1033 /* Test if we can get the path from the start menu "program files" PIDL. */
1034 hShell32 = GetModuleHandleA("shell32");
1035 pSHGetSpecialFolderLocation = (void *)GetProcAddress(hShell32, "SHGetSpecialFolderLocation");
1037 hr = pSHGetSpecialFolderLocation(NULL, CSIDL_PROGRAM_FILES, &pidlPrograms);
1038 ok(SUCCEEDED(hr), "SHGetFolderLocation failed: 0x%08x\n", hr);
1040 SetLastError(0xdeadbeef);
1041 result = pSHGetPathFromIDListW(pidlPrograms, wszPath);
1042 IMalloc_Free(ppM, pidlPrograms);
1043 ok(result, "SHGetPathFromIDListW failed\n");
1046 static void test_EnumObjects_and_CompareIDs(void)
1048 ITEMIDLIST *newPIDL;
1049 IShellFolder *IDesktopFolder, *testIShellFolder;
1050 char cCurrDirA [MAX_PATH] = {0};
1051 static const CHAR cTestDirA[] = "\\testdir";
1052 WCHAR cTestDirW[MAX_PATH];
1053 int len;
1054 HRESULT hr;
1056 GetCurrentDirectoryA(MAX_PATH, cCurrDirA);
1057 len = lstrlenA(cCurrDirA);
1059 if(len == 0) {
1060 win_skip("GetCurrentDirectoryA returned empty string. Skipping test_EnumObjects_and_CompareIDs\n");
1061 return;
1063 if(cCurrDirA[len-1] == '\\')
1064 cCurrDirA[len-1] = 0;
1066 lstrcatA(cCurrDirA, cTestDirA);
1067 MultiByteToWideChar(CP_ACP, 0, cCurrDirA, -1, cTestDirW, MAX_PATH);
1069 hr = SHGetDesktopFolder(&IDesktopFolder);
1070 ok(hr == S_OK, "SHGetDesktopfolder failed %08x\n", hr);
1072 CreateFilesFolders();
1074 hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
1075 ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
1077 hr = IShellFolder_BindToObject(IDesktopFolder, newPIDL, NULL, (REFIID)&IID_IShellFolder, (LPVOID *)&testIShellFolder);
1078 ok(hr == S_OK, "BindToObject failed %08x\n", hr);
1080 test_EnumObjects(testIShellFolder);
1082 IShellFolder_Release(testIShellFolder);
1084 Cleanup();
1086 IMalloc_Free(ppM, newPIDL);
1088 IShellFolder_Release(IDesktopFolder);
1091 /* A simple implementation of an IPropertyBag, which returns fixed values for
1092 * 'Target' and 'Attributes' properties.
1094 static HRESULT WINAPI InitPropertyBag_IPropertyBag_QueryInterface(IPropertyBag *iface, REFIID riid,
1095 void **ppvObject)
1097 if (!ppvObject)
1098 return E_INVALIDARG;
1100 if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IPropertyBag, riid)) {
1101 *ppvObject = iface;
1102 } else {
1103 ok (FALSE, "InitPropertyBag asked for unknown interface!\n");
1104 return E_NOINTERFACE;
1107 IPropertyBag_AddRef(iface);
1108 return S_OK;
1111 static ULONG WINAPI InitPropertyBag_IPropertyBag_AddRef(IPropertyBag *iface) {
1112 return 2;
1115 static ULONG WINAPI InitPropertyBag_IPropertyBag_Release(IPropertyBag *iface) {
1116 return 1;
1119 static HRESULT WINAPI InitPropertyBag_IPropertyBag_Read(IPropertyBag *iface, LPCOLESTR pszPropName,
1120 VARIANT *pVar, IErrorLog *pErrorLog)
1122 static const WCHAR wszTargetSpecialFolder[] = {
1123 'T','a','r','g','e','t','S','p','e','c','i','a','l','F','o','l','d','e','r',0 };
1124 static const WCHAR wszTarget[] = {
1125 'T','a','r','g','e','t',0 };
1126 static const WCHAR wszAttributes[] = {
1127 'A','t','t','r','i','b','u','t','e','s',0 };
1128 static const WCHAR wszResolveLinkFlags[] = {
1129 'R','e','s','o','l','v','e','L','i','n','k','F','l','a','g','s',0 };
1130 static const WCHAR wszTargetKnownFolder[] = {
1131 'T','a','r','g','e','t','K','n','o','w','n','F','o','l','d','e','r',0 };
1132 static const WCHAR wszCLSID[] = {
1133 'C','L','S','I','D',0 };
1135 if (!lstrcmpW(pszPropName, wszTargetSpecialFolder)) {
1136 ok(V_VT(pVar) == VT_I4 ||
1137 broken(V_VT(pVar) == VT_BSTR), /* Win2k */
1138 "Wrong variant type for 'TargetSpecialFolder' property!\n");
1139 return E_INVALIDARG;
1142 if (!lstrcmpW(pszPropName, wszResolveLinkFlags))
1144 ok(V_VT(pVar) == VT_UI4, "Wrong variant type for 'ResolveLinkFlags' property!\n");
1145 return E_INVALIDARG;
1148 if (!lstrcmpW(pszPropName, wszTarget)) {
1149 WCHAR wszPath[MAX_PATH];
1150 BOOL result;
1152 ok(V_VT(pVar) == VT_BSTR ||
1153 broken(V_VT(pVar) == VT_EMPTY), /* Win2k */
1154 "Wrong variant type for 'Target' property!\n");
1155 if (V_VT(pVar) != VT_BSTR) return E_INVALIDARG;
1157 result = pSHGetSpecialFolderPathW(NULL, wszPath, CSIDL_DESKTOPDIRECTORY, FALSE);
1158 ok(result, "SHGetSpecialFolderPathW(DESKTOPDIRECTORY) failed! %u\n", GetLastError());
1159 if (!result) return E_INVALIDARG;
1161 V_BSTR(pVar) = SysAllocString(wszPath);
1162 return S_OK;
1165 if (!lstrcmpW(pszPropName, wszAttributes)) {
1166 ok(V_VT(pVar) == VT_UI4, "Wrong variant type for 'Attributes' property!\n");
1167 if (V_VT(pVar) != VT_UI4) return E_INVALIDARG;
1168 V_UI4(pVar) = SFGAO_FOLDER|SFGAO_HASSUBFOLDER|SFGAO_FILESYSANCESTOR|
1169 SFGAO_CANRENAME|SFGAO_FILESYSTEM;
1170 return S_OK;
1173 if (!lstrcmpW(pszPropName, wszTargetKnownFolder)) {
1174 ok(V_VT(pVar) == VT_BSTR, "Wrong variant type for 'TargetKnownFolder' property!\n");
1175 /* TODO */
1176 return E_INVALIDARG;
1179 if (!lstrcmpW(pszPropName, wszCLSID)) {
1180 ok(V_VT(pVar) == VT_EMPTY, "Wrong variant type for 'CLSID' property!\n");
1181 /* TODO */
1182 return E_INVALIDARG;
1185 ok(FALSE, "PropertyBag was asked for unknown property %s (vt=%d)!\n", wine_dbgstr_w(pszPropName), V_VT(pVar));
1186 return E_INVALIDARG;
1189 static HRESULT WINAPI InitPropertyBag_IPropertyBag_Write(IPropertyBag *iface, LPCOLESTR pszPropName,
1190 VARIANT *pVar)
1192 ok(FALSE, "Unexpected call to IPropertyBag_Write\n");
1193 return E_NOTIMPL;
1196 static const IPropertyBagVtbl InitPropertyBag_IPropertyBagVtbl = {
1197 InitPropertyBag_IPropertyBag_QueryInterface,
1198 InitPropertyBag_IPropertyBag_AddRef,
1199 InitPropertyBag_IPropertyBag_Release,
1200 InitPropertyBag_IPropertyBag_Read,
1201 InitPropertyBag_IPropertyBag_Write
1204 static struct IPropertyBag InitPropertyBag = {
1205 &InitPropertyBag_IPropertyBagVtbl
1208 static void test_FolderShortcut(void) {
1209 IPersistPropertyBag *pPersistPropertyBag;
1210 IShellFolder *pShellFolder, *pDesktopFolder;
1211 IPersistFolder3 *pPersistFolder3;
1212 HRESULT hr;
1213 STRRET strret;
1214 WCHAR wszDesktopPath[MAX_PATH], wszBuffer[MAX_PATH];
1215 BOOL result;
1216 CLSID clsid;
1217 LPITEMIDLIST pidlCurrentFolder, pidlWineTestFolder, pidlSubFolder;
1218 HKEY hShellExtKey;
1219 WCHAR wszWineTestFolder[] = {
1220 ':',':','{','9','B','3','5','2','E','B','F','-','2','7','6','5','-','4','5','C','1','-',
1221 'B','4','C','6','-','8','5','C','C','7','F','7','A','B','C','6','4','}',0 };
1222 WCHAR wszShellExtKey[] = { 'S','o','f','t','w','a','r','e','\\',
1223 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
1224 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
1225 'E','x','p','l','o','r','e','r','\\','D','e','s','k','t','o','p','\\',
1226 'N','a','m','e','S','p','a','c','e','\\',
1227 '{','9','b','3','5','2','e','b','f','-','2','7','6','5','-','4','5','c','1','-',
1228 'b','4','c','6','-','8','5','c','c','7','f','7','a','b','c','6','4','}',0 };
1230 WCHAR wszSomeSubFolder[] = { 'S','u','b','F','o','l','d','e','r', 0};
1231 static const GUID CLSID_UnixDosFolder =
1232 {0x9d20aae8, 0x0625, 0x44b0, {0x9c, 0xa7, 0x71, 0x88, 0x9c, 0x22, 0x54, 0xd9}};
1234 if (!pSHGetSpecialFolderPathW || !pStrRetToBufW) {
1235 win_skip("SHGetSpecialFolderPathW and/or StrRetToBufW are not available\n");
1236 return;
1239 if (!pSHGetFolderPathAndSubDirA)
1241 win_skip("FolderShortcut test doesn't work on Win2k\n");
1242 return;
1245 /* These tests basically show, that CLSID_FolderShortcuts are initialized
1246 * via their IPersistPropertyBag interface. And that the target folder
1247 * is taken from the IPropertyBag's 'Target' property.
1249 hr = CoCreateInstance(&CLSID_FolderShortcut, NULL, CLSCTX_INPROC_SERVER,
1250 &IID_IPersistPropertyBag, (LPVOID*)&pPersistPropertyBag);
1251 if (hr == REGDB_E_CLASSNOTREG) {
1252 win_skip("CLSID_FolderShortcut is not implemented\n");
1253 return;
1255 ok (SUCCEEDED(hr), "CoCreateInstance failed! hr = 0x%08x\n", hr);
1256 if (FAILED(hr)) return;
1258 hr = IPersistPropertyBag_Load(pPersistPropertyBag, &InitPropertyBag, NULL);
1259 ok(SUCCEEDED(hr), "IPersistPropertyBag_Load failed! hr = %08x\n", hr);
1260 if (FAILED(hr)) {
1261 IPersistPropertyBag_Release(pPersistPropertyBag);
1262 return;
1265 hr = IPersistPropertyBag_QueryInterface(pPersistPropertyBag, &IID_IShellFolder,
1266 (LPVOID*)&pShellFolder);
1267 IPersistPropertyBag_Release(pPersistPropertyBag);
1268 ok(SUCCEEDED(hr), "IPersistPropertyBag_QueryInterface(IShellFolder) failed! hr = %08x\n", hr);
1269 if (FAILED(hr)) return;
1271 hr = IShellFolder_GetDisplayNameOf(pShellFolder, NULL, SHGDN_FORPARSING, &strret);
1272 ok(SUCCEEDED(hr), "IShellFolder_GetDisplayNameOf(NULL) failed! hr = %08x\n", hr);
1273 if (FAILED(hr)) {
1274 IShellFolder_Release(pShellFolder);
1275 return;
1278 result = pSHGetSpecialFolderPathW(NULL, wszDesktopPath, CSIDL_DESKTOPDIRECTORY, FALSE);
1279 ok(result, "SHGetSpecialFolderPathW(CSIDL_DESKTOPDIRECTORY) failed! %u\n", GetLastError());
1280 if (!result) return;
1282 pStrRetToBufW(&strret, NULL, wszBuffer, MAX_PATH);
1283 ok(!lstrcmpiW(wszDesktopPath, wszBuffer), "FolderShortcut returned incorrect folder!\n");
1285 hr = IShellFolder_QueryInterface(pShellFolder, &IID_IPersistFolder3, (LPVOID*)&pPersistFolder3);
1286 IShellFolder_Release(pShellFolder);
1287 ok(SUCCEEDED(hr), "IShellFolder_QueryInterface(IID_IPersistFolder3 failed! hr = 0x%08x\n", hr);
1288 if (FAILED(hr)) return;
1290 hr = IPersistFolder3_GetClassID(pPersistFolder3, &clsid);
1291 ok(SUCCEEDED(hr), "IPersistFolder3_GetClassID failed! hr=0x%08x\n", hr);
1292 ok(IsEqualCLSID(&clsid, &CLSID_FolderShortcut), "Unexpected CLSID!\n");
1294 hr = IPersistFolder3_GetCurFolder(pPersistFolder3, &pidlCurrentFolder);
1295 ok(SUCCEEDED(hr), "IPersistFolder3_GetCurFolder failed! hr=0x%08x\n", hr);
1296 ok(!pidlCurrentFolder, "IPersistFolder3_GetCurFolder should return a NULL pidl!\n");
1298 /* For FolderShortcut objects, the Initialize method initialized the folder's position in the
1299 * shell namespace. The target folder, read from the property bag above, remains untouched.
1300 * The following tests show this: The itemidlist for some imaginary shellfolder object
1301 * is created and the FolderShortcut is initialized with it. GetCurFolder now returns this
1302 * itemidlist, but GetDisplayNameOf still returns the path from above.
1304 hr = SHGetDesktopFolder(&pDesktopFolder);
1305 ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08x\n", hr);
1306 if (FAILED(hr)) return;
1308 /* Temporarily register WineTestFolder as a shell namespace extension at the Desktop.
1309 * Otherwise ParseDisplayName fails on WinXP with E_INVALIDARG */
1310 RegCreateKeyW(HKEY_CURRENT_USER, wszShellExtKey, &hShellExtKey);
1311 RegCloseKey(hShellExtKey);
1312 hr = IShellFolder_ParseDisplayName(pDesktopFolder, NULL, NULL, wszWineTestFolder, NULL,
1313 &pidlWineTestFolder, NULL);
1314 RegDeleteKeyW(HKEY_CURRENT_USER, wszShellExtKey);
1315 IShellFolder_Release(pDesktopFolder);
1316 ok (SUCCEEDED(hr), "IShellFolder::ParseDisplayName failed! hr = %08x\n", hr);
1317 if (FAILED(hr)) return;
1319 hr = IPersistFolder3_Initialize(pPersistFolder3, pidlWineTestFolder);
1320 ok (SUCCEEDED(hr), "IPersistFolder3::Initialize failed! hr = %08x\n", hr);
1321 if (FAILED(hr)) {
1322 IPersistFolder3_Release(pPersistFolder3);
1323 pILFree(pidlWineTestFolder);
1324 return;
1327 hr = IPersistFolder3_GetCurFolder(pPersistFolder3, &pidlCurrentFolder);
1328 ok(SUCCEEDED(hr), "IPersistFolder3_GetCurFolder failed! hr=0x%08x\n", hr);
1329 ok(pILIsEqual(pidlCurrentFolder, pidlWineTestFolder),
1330 "IPersistFolder3_GetCurFolder should return pidlWineTestFolder!\n");
1331 pILFree(pidlCurrentFolder);
1332 pILFree(pidlWineTestFolder);
1334 hr = IPersistFolder3_QueryInterface(pPersistFolder3, &IID_IShellFolder, (LPVOID*)&pShellFolder);
1335 IPersistFolder3_Release(pPersistFolder3);
1336 ok(SUCCEEDED(hr), "IPersistFolder3_QueryInterface(IShellFolder) failed! hr = %08x\n", hr);
1337 if (FAILED(hr)) return;
1339 hr = IShellFolder_GetDisplayNameOf(pShellFolder, NULL, SHGDN_FORPARSING, &strret);
1340 ok(SUCCEEDED(hr), "IShellFolder_GetDisplayNameOf(NULL) failed! hr = %08x\n", hr);
1341 if (FAILED(hr)) {
1342 IShellFolder_Release(pShellFolder);
1343 return;
1346 pStrRetToBufW(&strret, NULL, wszBuffer, MAX_PATH);
1347 ok(!lstrcmpiW(wszDesktopPath, wszBuffer), "FolderShortcut returned incorrect folder!\n");
1349 /* Next few lines are meant to show that children of FolderShortcuts are not FolderShortcuts,
1350 * but ShellFSFolders. */
1351 myPathAddBackslashW(wszDesktopPath);
1352 lstrcatW(wszDesktopPath, wszSomeSubFolder);
1353 if (!CreateDirectoryW(wszDesktopPath, NULL)) {
1354 IShellFolder_Release(pShellFolder);
1355 return;
1358 hr = IShellFolder_ParseDisplayName(pShellFolder, NULL, NULL, wszSomeSubFolder, NULL,
1359 &pidlSubFolder, NULL);
1360 RemoveDirectoryW(wszDesktopPath);
1361 ok (SUCCEEDED(hr), "IShellFolder::ParseDisplayName failed! hr = %08x\n", hr);
1362 if (FAILED(hr)) {
1363 IShellFolder_Release(pShellFolder);
1364 return;
1367 hr = IShellFolder_BindToObject(pShellFolder, pidlSubFolder, NULL, &IID_IPersistFolder3,
1368 (LPVOID*)&pPersistFolder3);
1369 IShellFolder_Release(pShellFolder);
1370 pILFree(pidlSubFolder);
1371 ok (SUCCEEDED(hr), "IShellFolder::BindToObject failed! hr = %08x\n", hr);
1372 if (FAILED(hr))
1373 return;
1375 /* On windows, we expect CLSID_ShellFSFolder. On wine we relax this constraint
1376 * a little bit and also allow CLSID_UnixDosFolder. */
1377 hr = IPersistFolder3_GetClassID(pPersistFolder3, &clsid);
1378 ok(SUCCEEDED(hr), "IPersistFolder3_GetClassID failed! hr=0x%08x\n", hr);
1379 ok(IsEqualCLSID(&clsid, &CLSID_ShellFSFolder) || IsEqualCLSID(&clsid, &CLSID_UnixDosFolder),
1380 "IPersistFolder3::GetClassID returned unexpected CLSID!\n");
1382 IPersistFolder3_Release(pPersistFolder3);
1385 #include "pshpack1.h"
1386 struct FileStructA {
1387 BYTE type;
1388 BYTE dummy;
1389 DWORD dwFileSize;
1390 WORD uFileDate; /* In our current implementation this is */
1391 WORD uFileTime; /* FileTimeToDosDate(WIN32_FIND_DATA->ftLastWriteTime) */
1392 WORD uFileAttribs;
1393 CHAR szName[1];
1396 struct FileStructW {
1397 WORD cbLen; /* Length of this element. */
1398 BYTE abFooBar1[6]; /* Beyond any recognition. */
1399 WORD uDate; /* FileTimeToDosDate(WIN32_FIND_DATA->ftCreationTime)? */
1400 WORD uTime; /* (this is currently speculation) */
1401 WORD uDate2; /* FileTimeToDosDate(WIN32_FIND_DATA->ftLastAccessTime)? */
1402 WORD uTime2; /* (this is currently speculation) */
1403 BYTE abFooBar2[4]; /* Beyond any recognition. */
1404 WCHAR wszName[1]; /* The long filename in unicode. */
1405 /* Just for documentation: Right after the unicode string: */
1406 WORD cbOffset; /* FileStructW's offset from the beginning of the SHITMEID.
1407 * SHITEMID->cb == uOffset + cbLen */
1409 #include "poppack.h"
1411 static void test_ITEMIDLIST_format(void) {
1412 WCHAR wszPersonal[MAX_PATH];
1413 LPSHELLFOLDER psfDesktop, psfPersonal;
1414 LPITEMIDLIST pidlPersonal, pidlFile;
1415 HANDLE hFile;
1416 HRESULT hr;
1417 BOOL bResult;
1418 WCHAR wszFile[3][17] = { { 'e','v','e','n','_',0 }, { 'o','d','d','_',0 },
1419 { 'l','o','n','g','e','r','_','t','h','a','n','.','8','_','3',0 } };
1420 int i;
1422 if (!pSHGetSpecialFolderPathW) return;
1424 bResult = pSHGetSpecialFolderPathW(NULL, wszPersonal, CSIDL_PERSONAL, FALSE);
1425 ok(bResult, "SHGetSpecialFolderPathW failed! Last error: %u\n", GetLastError());
1426 if (!bResult) return;
1428 SetLastError(0xdeadbeef);
1429 bResult = SetCurrentDirectoryW(wszPersonal);
1430 if (!bResult && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) {
1431 win_skip("Most W-calls are not implemented\n");
1432 return;
1434 ok(bResult, "SetCurrentDirectory failed! Last error: %u\n", GetLastError());
1435 if (!bResult) return;
1437 hr = SHGetDesktopFolder(&psfDesktop);
1438 ok(SUCCEEDED(hr), "SHGetDesktopFolder failed! hr: %08x\n", hr);
1439 if (FAILED(hr)) return;
1441 hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszPersonal, NULL, &pidlPersonal, NULL);
1442 ok(SUCCEEDED(hr), "psfDesktop->ParseDisplayName failed! hr = %08x\n", hr);
1443 if (FAILED(hr)) {
1444 IShellFolder_Release(psfDesktop);
1445 return;
1448 hr = IShellFolder_BindToObject(psfDesktop, pidlPersonal, NULL, &IID_IShellFolder,
1449 (LPVOID*)&psfPersonal);
1450 IShellFolder_Release(psfDesktop);
1451 pILFree(pidlPersonal);
1452 ok(SUCCEEDED(hr), "psfDesktop->BindToObject failed! hr = %08x\n", hr);
1453 if (FAILED(hr)) return;
1455 for (i=0; i<3; i++) {
1456 CHAR szFile[MAX_PATH];
1457 struct FileStructA *pFileStructA;
1458 WORD cbOffset;
1460 WideCharToMultiByte(CP_ACP, 0, wszFile[i], -1, szFile, MAX_PATH, NULL, NULL);
1462 hFile = CreateFileW(wszFile[i], GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_FLAG_WRITE_THROUGH, NULL);
1463 ok(hFile != INVALID_HANDLE_VALUE, "CreateFile failed! (%u)\n", GetLastError());
1464 if (hFile == INVALID_HANDLE_VALUE) {
1465 IShellFolder_Release(psfPersonal);
1466 return;
1468 CloseHandle(hFile);
1470 hr = IShellFolder_ParseDisplayName(psfPersonal, NULL, NULL, wszFile[i], NULL, &pidlFile, NULL);
1471 DeleteFileW(wszFile[i]);
1472 ok(SUCCEEDED(hr), "psfPersonal->ParseDisplayName failed! hr: %08x\n", hr);
1473 if (FAILED(hr)) {
1474 IShellFolder_Release(psfPersonal);
1475 return;
1478 pFileStructA = (struct FileStructA *)pidlFile->mkid.abID;
1479 ok(pFileStructA->type == 0x32, "PIDLTYPE should be 0x32!\n");
1480 ok(pFileStructA->dummy == 0x00, "Dummy Byte should be 0x00!\n");
1481 ok(pFileStructA->dwFileSize == 0, "Filesize should be zero!\n");
1483 if (i < 2) /* First two file names are already in valid 8.3 format */
1484 ok(!strcmp(szFile, (CHAR*)&pidlFile->mkid.abID[12]), "Wrong file name!\n");
1485 else
1486 /* WinXP stores a derived 8.3 dos name (LONGER~1.8_3) here. We probably
1487 * can't implement this correctly, since unix filesystems don't support
1488 * this nasty short/long filename stuff. So we'll probably stay with our
1489 * current habbit of storing the long filename here, which seems to work
1490 * just fine. */
1491 todo_wine
1492 ok(pidlFile->mkid.abID[18] == '~' ||
1493 broken(pidlFile->mkid.abID[34] == '~'), /* Win2k */
1494 "Should be derived 8.3 name!\n");
1496 if (i == 0) /* First file name has an even number of chars. No need for alignment. */
1497 ok(pidlFile->mkid.abID[12 + strlen(szFile) + 1] != '\0' ||
1498 broken(pidlFile->mkid.cb == 2 + 12 + strlen(szFile) + 1 + 1), /* Win2k */
1499 "Alignment byte, where there shouldn't be!\n");
1501 if (i == 1) /* Second file name has an uneven number of chars => alignment byte */
1502 ok(pidlFile->mkid.abID[12 + strlen(szFile) + 1] == '\0',
1503 "There should be an alignment byte, but isn't!\n");
1505 /* The offset of the FileStructW member is stored as a WORD at the end of the pidl. */
1506 cbOffset = *(WORD*)(((LPBYTE)pidlFile)+pidlFile->mkid.cb-sizeof(WORD));
1507 ok ((cbOffset >= sizeof(struct FileStructA) &&
1508 cbOffset <= pidlFile->mkid.cb - sizeof(struct FileStructW)) ||
1509 broken(pidlFile->mkid.cb == 2 + 12 + strlen(szFile) + 1 + 1) || /* Win2k on short names */
1510 broken(pidlFile->mkid.cb == 2 + 12 + strlen(szFile) + 1 + 12 + 1), /* Win2k on long names */
1511 "Wrong offset value (%d) stored at the end of the PIDL\n", cbOffset);
1513 if (cbOffset >= sizeof(struct FileStructA) &&
1514 cbOffset <= pidlFile->mkid.cb - sizeof(struct FileStructW))
1516 struct FileStructW *pFileStructW = (struct FileStructW *)(((LPBYTE)pidlFile)+cbOffset);
1518 ok(pidlFile->mkid.cb == cbOffset + pFileStructW->cbLen,
1519 "FileStructW's offset and length should add up to the PIDL's length!\n");
1521 if (pidlFile->mkid.cb == cbOffset + pFileStructW->cbLen) {
1522 /* Since we just created the file, time of creation,
1523 * time of last access and time of last write access just be the same.
1524 * These tests seem to fail sometimes (on WinXP), if the test is run again shortly
1525 * after the first run. I do remember something with NTFS keeping the creation time
1526 * if a file is deleted and then created again within a couple of seconds or so.
1527 * Might be the reason. */
1528 ok (pFileStructA->uFileDate == pFileStructW->uDate &&
1529 pFileStructA->uFileTime == pFileStructW->uTime,
1530 "Last write time should match creation time!\n");
1532 /* On FAT filesystems the last access time is midnight
1533 local time, so the values of uDate2 and uTime2 will
1534 depend on the local timezone. If the times are exactly
1535 equal then the dates should be identical for both FAT
1536 and NTFS as no timezone is more than 1 day away from UTC.
1538 if (pFileStructA->uFileTime == pFileStructW->uTime2)
1540 ok (pFileStructA->uFileDate == pFileStructW->uDate2,
1541 "Last write date and time should match last access date and time!\n");
1543 else
1545 /* Filesystem may be FAT. Check date within 1 day
1546 and seconds are zero. */
1547 trace ("Filesystem may be FAT. Performing less strict atime test.\n");
1548 ok ((pFileStructW->uTime2 & 0x1F) == 0,
1549 "Last access time on FAT filesystems should have zero seconds.\n");
1550 /* TODO: Perform check for date being within one day.*/
1553 ok (!lstrcmpW(wszFile[i], pFileStructW->wszName) ||
1554 !lstrcmpW(wszFile[i], (WCHAR *)(pFileStructW->abFooBar2 + 22)) || /* Vista */
1555 !lstrcmpW(wszFile[i], (WCHAR *)(pFileStructW->abFooBar2 + 26)), /* Win7 */
1556 "The filename should be stored in unicode at this position!\n");
1560 pILFree(pidlFile);
1564 static void testSHGetFolderPathAndSubDirA(void)
1566 HRESULT ret;
1567 BOOL delret;
1568 DWORD dwret;
1569 int i;
1570 static char wine[] = "wine";
1571 static char winetemp[] = "wine\\temp";
1572 static char appdata[MAX_PATH];
1573 static char testpath[MAX_PATH];
1574 static char toolongpath[MAX_PATH+1];
1576 if(!pSHGetFolderPathA) {
1577 win_skip("SHGetFolderPathA not present!\n");
1578 return;
1580 if(FAILED(pSHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, appdata)))
1582 win_skip("SHGetFolderPathA failed for CSIDL_LOCAL_APPDATA!\n");
1583 return;
1586 sprintf(testpath, "%s\\%s", appdata, winetemp);
1587 delret = RemoveDirectoryA(testpath);
1588 if(!delret && (ERROR_PATH_NOT_FOUND != GetLastError()) ) {
1589 win_skip("RemoveDirectoryA(%s) failed with error %u\n", testpath, GetLastError());
1590 return;
1593 sprintf(testpath, "%s\\%s", appdata, wine);
1594 delret = RemoveDirectoryA(testpath);
1595 if(!delret && (ERROR_PATH_NOT_FOUND != GetLastError()) && (ERROR_FILE_NOT_FOUND != GetLastError())) {
1596 win_skip("RemoveDirectoryA(%s) failed with error %u\n", testpath, GetLastError());
1597 return;
1600 /* test invalid second parameter */
1601 ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | 0xff, NULL, SHGFP_TYPE_CURRENT, wine, testpath);
1602 ok(E_INVALIDARG == ret, "expected E_INVALIDARG, got %x\n", ret);
1604 /* test fourth parameter */
1605 ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, 2, winetemp, testpath);
1606 switch(ret) {
1607 case S_OK: /* winvista */
1608 ok(!strncmp(appdata, testpath, strlen(appdata)),
1609 "expected %s to start with %s\n", testpath, appdata);
1610 ok(!lstrcmpA(&testpath[1 + strlen(appdata)], winetemp),
1611 "expected %s to end with %s\n", testpath, winetemp);
1612 break;
1613 case E_INVALIDARG: /* winxp, win2k3 */
1614 break;
1615 default:
1616 ok(0, "expected S_OK or E_INVALIDARG, got %x\n", ret);
1619 /* test fifth parameter */
1620 testpath[0] = '\0';
1621 ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, NULL, testpath);
1622 ok(S_OK == ret, "expected S_OK, got %x\n", ret);
1623 ok(!lstrcmpA(appdata, testpath), "expected %s, got %s\n", appdata, testpath);
1625 testpath[0] = '\0';
1626 ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, "", testpath);
1627 ok(S_OK == ret, "expected S_OK, got %x\n", ret);
1628 ok(!lstrcmpA(appdata, testpath), "expected %s, got %s\n", appdata, testpath);
1630 testpath[0] = '\0';
1631 ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, "\\", testpath);
1632 ok(S_OK == ret, "expected S_OK, got %x\n", ret);
1633 ok(!lstrcmpA(appdata, testpath), "expected %s, got %s\n", appdata, testpath);
1635 for(i=0; i< MAX_PATH; i++)
1636 toolongpath[i] = '0' + i % 10;
1637 toolongpath[MAX_PATH] = '\0';
1638 ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, toolongpath, testpath);
1639 ok(HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE) == ret,
1640 "expected %x, got %x\n", HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE), ret);
1642 testpath[0] = '\0';
1643 ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, wine, NULL);
1644 ok((S_OK == ret) || (E_INVALIDARG == ret), "expected S_OK or E_INVALIDARG, got %x\n", ret);
1646 /* test a not existing path */
1647 testpath[0] = '\0';
1648 ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, winetemp, testpath);
1649 ok(HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == ret,
1650 "expected %x, got %x\n", HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND), ret);
1652 /* create a directory inside a not existing directory */
1653 testpath[0] = '\0';
1654 ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_CREATE | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, winetemp, testpath);
1655 ok(S_OK == ret, "expected S_OK, got %x\n", ret);
1656 ok(!strncmp(appdata, testpath, strlen(appdata)),
1657 "expected %s to start with %s\n", testpath, appdata);
1658 ok(!lstrcmpA(&testpath[1 + strlen(appdata)], winetemp),
1659 "expected %s to end with %s\n", testpath, winetemp);
1660 dwret = GetFileAttributes(testpath);
1661 ok(FILE_ATTRIBUTE_DIRECTORY | dwret, "expected %x to contain FILE_ATTRIBUTE_DIRECTORY\n", dwret);
1663 /* cleanup */
1664 sprintf(testpath, "%s\\%s", appdata, winetemp);
1665 RemoveDirectoryA(testpath);
1666 sprintf(testpath, "%s\\%s", appdata, wine);
1667 RemoveDirectoryA(testpath);
1670 static void test_LocalizedNames(void)
1672 static char cCurrDirA[MAX_PATH];
1673 WCHAR cCurrDirW[MAX_PATH], tempbufW[25];
1674 IShellFolder *IDesktopFolder, *testIShellFolder;
1675 ITEMIDLIST *newPIDL;
1676 int len;
1677 HRESULT hr;
1678 static char resourcefile[MAX_PATH];
1679 DWORD res;
1680 HANDLE file;
1681 STRRET strret;
1683 static const char desktopini_contents1[] =
1684 "[.ShellClassInfo]\r\n"
1685 "LocalizedResourceName=@";
1686 static const char desktopini_contents2[] =
1687 ",-1\r\n";
1688 static WCHAR foldernameW[] = {'t','e','s','t','f','o','l','d','e','r',0};
1689 static const WCHAR folderdisplayW[] = {'F','o','l','d','e','r',' ','N','a','m','e',' ','R','e','s','o','u','r','c','e',0};
1691 /* create folder with desktop.ini and localized name in GetModuleFileNameA(NULL) */
1692 CreateDirectoryA(".\\testfolder", NULL);
1694 SetFileAttributesA(".\\testfolder", GetFileAttributesA(".\\testfolder")|FILE_ATTRIBUTE_SYSTEM);
1696 GetModuleFileNameA(NULL, resourcefile, MAX_PATH);
1698 file = CreateFileA(".\\testfolder\\desktop.ini", GENERIC_WRITE, 0, NULL,
1699 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
1700 ok(file != INVALID_HANDLE_VALUE, "CreateFileA failed %i\n", GetLastError());
1701 ok(WriteFile(file, desktopini_contents1, strlen(desktopini_contents1), &res, NULL) &&
1702 WriteFile(file, resourcefile, strlen(resourcefile), &res, NULL) &&
1703 WriteFile(file, desktopini_contents2, strlen(desktopini_contents2), &res, NULL),
1704 "WriteFile failed %i\n", GetLastError());
1705 CloseHandle(file);
1707 /* get IShellFolder for parent */
1708 GetCurrentDirectoryA(MAX_PATH, cCurrDirA);
1709 len = lstrlenA(cCurrDirA);
1711 if (len == 0) {
1712 win_skip("GetCurrentDirectoryA returned empty string. Skipping test_LocalizedNames\n");
1713 goto cleanup;
1715 if(cCurrDirA[len-1] == '\\')
1716 cCurrDirA[len-1] = 0;
1718 MultiByteToWideChar(CP_ACP, 0, cCurrDirA, -1, cCurrDirW, MAX_PATH);
1720 hr = SHGetDesktopFolder(&IDesktopFolder);
1721 ok(hr == S_OK, "SHGetDesktopfolder failed %08x\n", hr);
1723 hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cCurrDirW, NULL, &newPIDL, 0);
1724 ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
1726 hr = IShellFolder_BindToObject(IDesktopFolder, newPIDL, NULL, (REFIID)&IID_IShellFolder, (LPVOID *)&testIShellFolder);
1727 ok(hr == S_OK, "BindToObject failed %08x\n", hr);
1729 IMalloc_Free(ppM, newPIDL);
1731 /* windows reads the display name from the resource */
1732 hr = IShellFolder_ParseDisplayName(testIShellFolder, NULL, NULL, foldernameW, NULL, &newPIDL, 0);
1733 ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
1735 hr = IShellFolder_GetDisplayNameOf(testIShellFolder, newPIDL, SHGDN_INFOLDER, &strret);
1736 ok(hr == S_OK, "GetDisplayNameOf failed %08x\n", hr);
1738 if (SUCCEEDED(hr) && pStrRetToBufW)
1740 hr = pStrRetToBufW(&strret, newPIDL, tempbufW, sizeof(tempbufW)/sizeof(WCHAR));
1741 ok (SUCCEEDED(hr), "StrRetToBufW failed! hr = %08x\n", hr);
1742 todo_wine
1743 ok (!lstrcmpiW(tempbufW, folderdisplayW) ||
1744 broken(!lstrcmpiW(tempbufW, foldernameW)), /* W2K */
1745 "GetDisplayNameOf returned %s\n", wine_dbgstr_w(tempbufW));
1748 /* editing name is also read from the resource */
1749 hr = IShellFolder_GetDisplayNameOf(testIShellFolder, newPIDL, SHGDN_INFOLDER|SHGDN_FOREDITING, &strret);
1750 ok(hr == S_OK, "GetDisplayNameOf failed %08x\n", hr);
1752 if (SUCCEEDED(hr) && pStrRetToBufW)
1754 hr = pStrRetToBufW(&strret, newPIDL, tempbufW, sizeof(tempbufW)/sizeof(WCHAR));
1755 ok (SUCCEEDED(hr), "StrRetToBufW failed! hr = %08x\n", hr);
1756 todo_wine
1757 ok (!lstrcmpiW(tempbufW, folderdisplayW) ||
1758 broken(!lstrcmpiW(tempbufW, foldernameW)), /* W2K */
1759 "GetDisplayNameOf returned %s\n", wine_dbgstr_w(tempbufW));
1762 /* parsing name is unchanged */
1763 hr = IShellFolder_GetDisplayNameOf(testIShellFolder, newPIDL, SHGDN_INFOLDER|SHGDN_FORPARSING, &strret);
1764 ok(hr == S_OK, "GetDisplayNameOf failed %08x\n", hr);
1766 if (SUCCEEDED(hr) && pStrRetToBufW)
1768 hr = pStrRetToBufW(&strret, newPIDL, tempbufW, sizeof(tempbufW)/sizeof(WCHAR));
1769 ok (SUCCEEDED(hr), "StrRetToBufW failed! hr = %08x\n", hr);
1770 ok (!lstrcmpiW(tempbufW, foldernameW), "GetDisplayNameOf returned %s\n", wine_dbgstr_w(tempbufW));
1773 IShellFolder_Release(IDesktopFolder);
1774 IShellFolder_Release(testIShellFolder);
1776 IMalloc_Free(ppM, newPIDL);
1778 cleanup:
1779 DeleteFileA(".\\testfolder\\desktop.ini");
1780 SetFileAttributesA(".\\testfolder", GetFileAttributesA(".\\testfolder")&~FILE_ATTRIBUTE_SYSTEM);
1781 RemoveDirectoryA(".\\testfolder");
1784 static void test_SHCreateShellItem(void)
1786 IShellItem *shellitem, *shellitem2;
1787 IPersistIDList *persistidl;
1788 LPITEMIDLIST pidl_cwd=NULL, pidl_testfile, pidl_abstestfile, pidl_test;
1789 HRESULT ret;
1790 char curdirA[MAX_PATH];
1791 WCHAR curdirW[MAX_PATH];
1792 IShellFolder *desktopfolder=NULL, *currentfolder=NULL;
1793 static WCHAR testfileW[] = {'t','e','s','t','f','i','l','e',0};
1795 GetCurrentDirectoryA(MAX_PATH, curdirA);
1797 if (!lstrlenA(curdirA))
1799 win_skip("GetCurrentDirectoryA returned empty string, skipping test_SHCreateShellItem\n");
1800 return;
1803 MultiByteToWideChar(CP_ACP, 0, curdirA, -1, curdirW, MAX_PATH);
1805 ret = SHGetDesktopFolder(&desktopfolder);
1806 ok(SUCCEEDED(ret), "SHGetShellFolder returned %x\n", ret);
1808 ret = IShellFolder_ParseDisplayName(desktopfolder, NULL, NULL, curdirW, NULL, &pidl_cwd, NULL);
1809 ok(SUCCEEDED(ret), "ParseDisplayName returned %x\n", ret);
1811 ret = IShellFolder_BindToObject(desktopfolder, pidl_cwd, NULL, &IID_IShellFolder, (void**)&currentfolder);
1812 ok(SUCCEEDED(ret), "BindToObject returned %x\n", ret);
1814 CreateTestFile(".\\testfile");
1816 ret = IShellFolder_ParseDisplayName(currentfolder, NULL, NULL, testfileW, NULL, &pidl_testfile, NULL);
1817 ok(SUCCEEDED(ret), "ParseDisplayName returned %x\n", ret);
1819 pidl_abstestfile = pILCombine(pidl_cwd, pidl_testfile);
1821 ret = pSHCreateShellItem(NULL, NULL, NULL, &shellitem);
1822 ok(ret == E_INVALIDARG, "SHCreateShellItem returned %x\n", ret);
1824 if (0) /* crashes on Windows XP */
1826 pSHCreateShellItem(NULL, NULL, pidl_cwd, NULL);
1827 pSHCreateShellItem(pidl_cwd, NULL, NULL, &shellitem);
1828 pSHCreateShellItem(NULL, currentfolder, NULL, &shellitem);
1829 pSHCreateShellItem(pidl_cwd, currentfolder, NULL, &shellitem);
1832 ret = pSHCreateShellItem(NULL, NULL, pidl_cwd, &shellitem);
1833 ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret);
1834 if (SUCCEEDED(ret))
1836 ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
1837 ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
1838 if (SUCCEEDED(ret))
1840 ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
1841 ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
1842 if (SUCCEEDED(ret))
1844 ok(ILIsEqual(pidl_cwd, pidl_test), "id lists are not equal\n");
1845 pILFree(pidl_test);
1847 IPersistIDList_Release(persistidl);
1849 IShellItem_Release(shellitem);
1852 ret = pSHCreateShellItem(pidl_cwd, NULL, pidl_testfile, &shellitem);
1853 ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret);
1854 if (SUCCEEDED(ret))
1856 ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
1857 ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
1858 if (SUCCEEDED(ret))
1860 ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
1861 ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
1862 if (SUCCEEDED(ret))
1864 ok(ILIsEqual(pidl_abstestfile, pidl_test), "id lists are not equal\n");
1865 pILFree(pidl_test);
1867 IPersistIDList_Release(persistidl);
1870 ret = IShellItem_GetParent(shellitem, &shellitem2);
1871 ok(SUCCEEDED(ret), "GetParent returned %x\n", ret);
1872 if (SUCCEEDED(ret))
1874 ret = IShellItem_QueryInterface(shellitem2, &IID_IPersistIDList, (void**)&persistidl);
1875 ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
1876 if (SUCCEEDED(ret))
1878 ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
1879 ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
1880 if (SUCCEEDED(ret))
1882 ok(ILIsEqual(pidl_cwd, pidl_test), "id lists are not equal\n");
1883 pILFree(pidl_test);
1885 IPersistIDList_Release(persistidl);
1887 IShellItem_Release(shellitem2);
1890 IShellItem_Release(shellitem);
1893 ret = pSHCreateShellItem(NULL, currentfolder, pidl_testfile, &shellitem);
1894 ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret);
1895 if (SUCCEEDED(ret))
1897 ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
1898 ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
1899 if (SUCCEEDED(ret))
1901 ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
1902 ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
1903 if (SUCCEEDED(ret))
1905 ok(ILIsEqual(pidl_abstestfile, pidl_test), "id lists are not equal\n");
1906 pILFree(pidl_test);
1908 IPersistIDList_Release(persistidl);
1910 IShellItem_Release(shellitem);
1913 /* if a parent pidl and shellfolder are specified, the shellfolder is ignored */
1914 ret = pSHCreateShellItem(pidl_cwd, desktopfolder, pidl_testfile, &shellitem);
1915 ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret);
1916 if (SUCCEEDED(ret))
1918 ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
1919 ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
1920 if (SUCCEEDED(ret))
1922 ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
1923 ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
1924 if (SUCCEEDED(ret))
1926 ok(ILIsEqual(pidl_abstestfile, pidl_test), "id lists are not equal\n");
1927 pILFree(pidl_test);
1929 IPersistIDList_Release(persistidl);
1931 IShellItem_Release(shellitem);
1934 DeleteFileA(".\\testfile");
1935 pILFree(pidl_abstestfile);
1936 pILFree(pidl_testfile);
1937 pILFree(pidl_cwd);
1938 IShellFolder_Release(currentfolder);
1939 IShellFolder_Release(desktopfolder);
1942 START_TEST(shlfolder)
1944 init_function_pointers();
1945 /* if OleInitialize doesn't get called, ParseDisplayName returns
1946 CO_E_NOTINITIALIZED for malformed directory names on win2k. */
1947 OleInitialize(NULL);
1949 test_ParseDisplayName();
1950 test_BindToObject();
1951 test_EnumObjects_and_CompareIDs();
1952 test_GetDisplayName();
1953 test_GetAttributesOf();
1954 test_SHGetPathFromIDList();
1955 test_CallForAttributes();
1956 test_FolderShortcut();
1957 test_ITEMIDLIST_format();
1958 if(pSHGetFolderPathAndSubDirA)
1959 testSHGetFolderPathAndSubDirA();
1960 else
1961 win_skip("SHGetFolderPathAndSubDirA not present\n");
1962 test_LocalizedNames();
1963 if(pSHCreateShellItem)
1964 test_SHCreateShellItem();
1965 else
1966 win_skip("SHCreateShellItem not present\n");
1968 OleUninitialize();