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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
40 #include "wine/unicode.h"
41 #include "wine/test.h"
46 static HRESULT (WINAPI
*pSHBindToParent
)(LPCITEMIDLIST
, REFIID
, LPVOID
*, LPCITEMIDLIST
*);
47 static BOOL (WINAPI
*pSHGetSpecialFolderPathW
)(HWND
, LPWSTR
, int, BOOL
);
48 static HRESULT (WINAPI
*pStrRetToBufW
)(STRRET
*,LPCITEMIDLIST
,LPWSTR
,UINT
);
49 static LPITEMIDLIST (WINAPI
*pILFindLastID
)(LPCITEMIDLIST
);
51 static void init_function_pointers(void)
56 hmod
= GetModuleHandleA("shell32.dll");
59 pSHBindToParent
= (void*)GetProcAddress(hmod
, "SHBindToParent");
60 pSHGetSpecialFolderPathW
= (void*)GetProcAddress(hmod
, "SHGetSpecialFolderPathW");
61 pILFindLastID
= (void *)GetProcAddress(hmod
, (LPCSTR
)16);
65 hmod
= GetModuleHandleA("shlwapi.dll");
68 pStrRetToBufW
= (void*)GetProcAddress(hmod
, "StrRetToBufW");
71 hr
= SHGetMalloc(&ppM
);
72 ok(hr
== S_OK
, "SHGetMalloc failed %08lx\n", hr
);
75 static void test_ParseDisplayName(void)
78 IShellFolder
*IDesktopFolder
;
79 static const char *cNonExistDir1A
= "c:\\nonexist_subdir";
80 static const char *cNonExistDir2A
= "c:\\\\nonexist_subdir";
82 WCHAR cTestDirW
[MAX_PATH
] = {0};
85 hr
= SHGetDesktopFolder(&IDesktopFolder
);
86 if(hr
!= S_OK
) return;
88 res
= GetFileAttributesA(cNonExistDir1A
);
89 if(res
!= INVALID_FILE_ATTRIBUTES
) return;
91 MultiByteToWideChar(CP_ACP
, 0, cNonExistDir1A
, -1, cTestDirW
, MAX_PATH
);
92 hr
= IShellFolder_ParseDisplayName(IDesktopFolder
,
93 NULL
, NULL
, cTestDirW
, NULL
, &newPIDL
, 0);
94 ok((hr
== HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND
)) || (hr
== E_FAIL
),
95 "ParseDisplayName returned %08lx, expected 80070002 or E_FAIL\n", hr
);
97 res
= GetFileAttributesA(cNonExistDir2A
);
98 if(res
!= INVALID_FILE_ATTRIBUTES
) return;
100 MultiByteToWideChar(CP_ACP
, 0, cNonExistDir2A
, -1, cTestDirW
, MAX_PATH
);
101 hr
= IShellFolder_ParseDisplayName(IDesktopFolder
,
102 NULL
, NULL
, cTestDirW
, NULL
, &newPIDL
, 0);
103 ok((hr
== HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND
)) || (hr
== E_FAIL
) || (hr
== E_INVALIDARG
),
104 "ParseDisplayName returned %08lx, expected 80070002, E_FAIL or E_INVALIDARG\n", hr
);
107 /* creates a file with the specified name for tests */
108 static void CreateTestFile(const CHAR
*name
)
113 file
= CreateFileA(name
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, 0, NULL
);
114 if (file
!= INVALID_HANDLE_VALUE
)
116 WriteFile(file
, name
, strlen(name
), &written
, NULL
);
117 WriteFile(file
, "\n", strlen("\n"), &written
, NULL
);
123 /* initializes the tests */
124 static void CreateFilesFolders(void)
126 CreateDirectoryA(".\\testdir", NULL
);
127 CreateDirectoryA(".\\testdir\\test.txt", NULL
);
128 CreateTestFile (".\\testdir\\test1.txt ");
129 CreateTestFile (".\\testdir\\test2.txt ");
130 CreateTestFile (".\\testdir\\test3.txt ");
131 CreateDirectoryA(".\\testdir\\testdir2 ", NULL
);
132 CreateDirectoryA(".\\testdir\\testdir2\\subdir", NULL
);
135 /* cleans after tests */
136 static void Cleanup(void)
138 DeleteFileA(".\\testdir\\test1.txt");
139 DeleteFileA(".\\testdir\\test2.txt");
140 DeleteFileA(".\\testdir\\test3.txt");
141 RemoveDirectoryA(".\\testdir\\test.txt");
142 RemoveDirectoryA(".\\testdir\\testdir2\\subdir");
143 RemoveDirectoryA(".\\testdir\\testdir2");
144 RemoveDirectoryA(".\\testdir");
149 static void test_EnumObjects(IShellFolder
*iFolder
)
151 IEnumIDList
*iEnumList
;
152 LPITEMIDLIST newPIDL
, idlArr
[10];
157 static const WORD iResults
[5][5] =
166 /* Just test SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR for now */
167 static const ULONG attrs
[5] =
169 SFGAO_FILESYSTEM
| SFGAO_FOLDER
| SFGAO_FILESYSANCESTOR
,
170 SFGAO_FILESYSTEM
| SFGAO_FOLDER
| SFGAO_FILESYSANCESTOR
,
176 hr
= IShellFolder_EnumObjects(iFolder
, NULL
, SHCONTF_FOLDERS
| SHCONTF_NONFOLDERS
| SHCONTF_INCLUDEHIDDEN
, &iEnumList
);
177 ok(hr
== S_OK
, "EnumObjects failed %08lx\n", hr
);
179 /* This is to show that, contrary to what is said on MSDN, on IEnumIDList::Next,
180 * the filesystem shellfolders return S_OK even if less than 'celt' items are
181 * returned (in contrast to S_FALSE). We have to do it in a loop since WinXP
182 * only ever returns a single entry per call. */
183 while (IEnumIDList_Next(iEnumList
, 10-i
, &idlArr
[i
], &NumPIDLs
) == S_OK
)
185 ok (i
== 5, "i: %d\n", i
);
187 hr
= IEnumIDList_Release(iEnumList
);
188 ok(hr
== S_OK
, "IEnumIDList_Release failed %08lx\n", hr
);
190 /* Sort them first in case of wrong order from system */
191 for (i
=0;i
<5;i
++) for (j
=0;j
<5;j
++)
192 if ((SHORT
)IShellFolder_CompareIDs(iFolder
, 0, idlArr
[i
], idlArr
[j
]) < 0)
195 idlArr
[i
] = idlArr
[j
];
199 for (i
=0;i
<5;i
++) for (j
=0;j
<5;j
++)
201 hr
= IShellFolder_CompareIDs(iFolder
, 0, idlArr
[i
], idlArr
[j
]);
202 ok(hr
== iResults
[i
][j
], "Got %lx expected [%d]-[%d]=%x\n", hr
, i
, j
, iResults
[i
][j
]);
206 for (i
= 0; i
< 5; i
++)
209 flags
= SFGAO_FILESYSTEM
| SFGAO_FOLDER
| SFGAO_FILESYSANCESTOR
;
210 hr
= IShellFolder_GetAttributesOf(iFolder
, 1, (LPCITEMIDLIST
*)(idlArr
+ i
), &flags
);
211 flags
&= SFGAO_FILESYSTEM
| SFGAO_FOLDER
| SFGAO_FILESYSANCESTOR
;
212 ok(hr
== S_OK
, "GetAttributesOf returns %08lx\n", hr
);
213 ok(flags
== attrs
[i
], "GetAttributesOf gets attrs %08lx, expects %08lx\n", flags
, attrs
[i
]);
217 IMalloc_Free(ppM
, idlArr
[i
]);
220 static void test_BindToObject(void)
224 IShellFolder
*psfDesktop
, *psfChild
, *psfMyComputer
, *psfSystemDir
;
225 SHITEMID emptyitem
= { 0, { 0 } };
226 LPITEMIDLIST pidlMyComputer
, pidlSystemDir
, pidlEmpty
= (LPITEMIDLIST
)&emptyitem
;
227 WCHAR wszSystemDir
[MAX_PATH
];
228 char szSystemDir
[MAX_PATH
];
229 WCHAR wszMyComputer
[] = {
230 ':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
231 'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };
233 /* The following tests shows that BindToObject should fail with E_INVALIDARG if called
234 * with an empty pidl. This is tested for Desktop, MyComputer and the FS ShellFolder
236 hr
= SHGetDesktopFolder(&psfDesktop
);
237 ok (SUCCEEDED(hr
), "SHGetDesktopFolder failed! hr = %08lx\n", hr
);
238 if (FAILED(hr
)) return;
240 hr
= IShellFolder_BindToObject(psfDesktop
, pidlEmpty
, NULL
, &IID_IShellFolder
, (LPVOID
*)&psfChild
);
241 ok (hr
== E_INVALIDARG
, "Desktop's BindToObject should fail, when called with empty pidl! hr = %08lx\n", hr
);
243 hr
= IShellFolder_BindToObject(psfDesktop
, NULL
, NULL
, &IID_IShellFolder
, (LPVOID
*)&psfChild
);
244 ok (hr
== E_INVALIDARG
, "Desktop's BindToObject should fail, when called with NULL pidl! hr = %08lx\n", hr
);
246 hr
= IShellFolder_ParseDisplayName(psfDesktop
, NULL
, NULL
, wszMyComputer
, NULL
, &pidlMyComputer
, NULL
);
247 ok (SUCCEEDED(hr
), "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08lx\n", hr
);
249 IShellFolder_Release(psfDesktop
);
253 hr
= IShellFolder_BindToObject(psfDesktop
, pidlMyComputer
, NULL
, &IID_IShellFolder
, (LPVOID
*)&psfMyComputer
);
254 ok (SUCCEEDED(hr
), "Desktop failed to bind to MyComputer object! hr = %08lx\n", hr
);
255 IShellFolder_Release(psfDesktop
);
256 IMalloc_Free(ppM
, pidlMyComputer
);
257 if (FAILED(hr
)) return;
259 hr
= IShellFolder_BindToObject(psfMyComputer
, pidlEmpty
, NULL
, &IID_IShellFolder
, (LPVOID
*)&psfChild
);
260 ok (hr
== E_INVALIDARG
, "MyComputers's BindToObject should fail, when called with empty pidl! hr = %08lx\n", hr
);
262 hr
= IShellFolder_BindToObject(psfMyComputer
, NULL
, NULL
, &IID_IShellFolder
, (LPVOID
*)&psfChild
);
263 ok (hr
== E_INVALIDARG
, "MyComputers's BindToObject should fail, when called with NULL pidl! hr = %08lx\n", hr
);
265 cChars
= GetSystemDirectoryA(szSystemDir
, MAX_PATH
);
266 ok (cChars
> 0 && cChars
< MAX_PATH
, "GetSystemDirectoryA failed! LastError: %08lx\n", GetLastError());
267 if (cChars
== 0 || cChars
>= MAX_PATH
) {
268 IShellFolder_Release(psfMyComputer
);
271 MultiByteToWideChar(CP_ACP
, 0, szSystemDir
, -1, wszSystemDir
, MAX_PATH
);
273 hr
= IShellFolder_ParseDisplayName(psfMyComputer
, NULL
, NULL
, wszSystemDir
, NULL
, &pidlSystemDir
, NULL
);
274 ok (SUCCEEDED(hr
), "MyComputers's ParseDisplayName failed to parse the SystemDirectory! hr = %08lx\n", hr
);
276 IShellFolder_Release(psfMyComputer
);
280 hr
= IShellFolder_BindToObject(psfMyComputer
, pidlSystemDir
, NULL
, &IID_IShellFolder
, (LPVOID
*)&psfSystemDir
);
281 ok (SUCCEEDED(hr
), "MyComputer failed to bind to a FileSystem ShellFolder! hr = %08lx\n", hr
);
282 IShellFolder_Release(psfMyComputer
);
283 IMalloc_Free(ppM
, pidlSystemDir
);
284 if (FAILED(hr
)) return;
286 hr
= IShellFolder_BindToObject(psfSystemDir
, pidlEmpty
, NULL
, &IID_IShellFolder
, (LPVOID
*)&psfChild
);
287 ok (hr
== E_INVALIDARG
,
288 "FileSystem ShellFolder's BindToObject should fail, when called with empty pidl! hr = %08lx\n", hr
);
290 hr
= IShellFolder_BindToObject(psfSystemDir
, NULL
, NULL
, &IID_IShellFolder
, (LPVOID
*)&psfChild
);
291 ok (hr
== E_INVALIDARG
,
292 "FileSystem ShellFolder's BindToObject should fail, when called with NULL pidl! hr = %08lx\n", hr
);
294 IShellFolder_Release(psfSystemDir
);
297 static void test_GetDisplayName(void)
302 WCHAR wszTestFile
[MAX_PATH
], wszTestFile2
[MAX_PATH
], wszTestDir
[MAX_PATH
];
303 char szTestFile
[MAX_PATH
], szTestDir
[MAX_PATH
];
305 LPSHELLFOLDER psfDesktop
, psfPersonal
;
307 SHITEMID emptyitem
= { 0, { 0 } };
308 LPITEMIDLIST pidlTestFile
, pidlEmpty
= (LPITEMIDLIST
)&emptyitem
;
309 LPCITEMIDLIST pidlLast
;
310 static const WCHAR wszFileName
[] = { 'w','i','n','e','t','e','s','t','.','f','o','o',0 };
311 static const WCHAR wszDirName
[] = { 'w','i','n','e','t','e','s','t',0 };
313 /* I'm trying to figure if there is a functional difference between calling
314 * SHGetPathFromIDList and calling GetDisplayNameOf(SHGDN_FORPARSING) after
315 * binding to the shellfolder. One thing I thought of was that perhaps
316 * SHGetPathFromIDList would be able to get the path to a file, which does
317 * not exist anymore, while the other method would'nt. It turns out there's
318 * no functional difference in this respect.
321 if(!pSHGetSpecialFolderPathW
) return;
323 /* First creating a directory in MyDocuments and a file in this directory. */
324 result
= pSHGetSpecialFolderPathW(NULL
, wszTestDir
, CSIDL_PERSONAL
, FALSE
);
325 ok(result
, "SHGetSpecialFolderPathW failed! Last error: %08lx\n", GetLastError());
328 PathAddBackslashW(wszTestDir
);
329 lstrcatW(wszTestDir
, wszDirName
);
330 WideCharToMultiByte(CP_ACP
, 0, wszTestDir
, -1, szTestDir
, MAX_PATH
, 0, 0);
331 result
= CreateDirectoryA(szTestDir
, NULL
);
332 ok(result
, "CreateDirectoryA failed! Last error: %08lx\n", GetLastError());
335 lstrcpyW(wszTestFile
, wszTestDir
);
336 PathAddBackslashW(wszTestFile
);
337 lstrcatW(wszTestFile
, wszFileName
);
338 WideCharToMultiByte(CP_ACP
, 0, wszTestFile
, -1, szTestFile
, MAX_PATH
, 0, 0);
340 hTestFile
= CreateFileA(szTestFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, 0, NULL
);
341 ok((hTestFile
!= INVALID_HANDLE_VALUE
), "CreateFileA failed! Last error: %08lx\n", GetLastError());
342 if (hTestFile
== INVALID_HANDLE_VALUE
) return;
343 CloseHandle(hTestFile
);
345 /* Getting an itemidlist for the file. */
346 hr
= SHGetDesktopFolder(&psfDesktop
);
347 ok(SUCCEEDED(hr
), "SHGetDesktopFolder failed! hr = %08lx\n", hr
);
348 if (FAILED(hr
)) return;
350 hr
= IShellFolder_ParseDisplayName(psfDesktop
, NULL
, NULL
, wszTestFile
, NULL
, &pidlTestFile
, NULL
);
351 ok(SUCCEEDED(hr
), "Desktop->ParseDisplayName failed! hr = %08lx\n", hr
);
353 IShellFolder_Release(psfDesktop
);
357 /* It seems as if we cannot bind to regular files on windows, but only directories.
359 hr
= IShellFolder_BindToObject(psfDesktop
, pidlTestFile
, NULL
, &IID_IUnknown
, (VOID
**)&psfFile
);
360 todo_wine
{ ok (hr
== HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND
), "hr = %08lx\n", hr
); }
362 IShellFolder_Release(psfFile
);
365 /* Deleting the file and the directory */
366 DeleteFileA(szTestFile
);
367 RemoveDirectoryA(szTestDir
);
369 /* SHGetPathFromIDListW still works, although the file is not present anymore. */
370 result
= SHGetPathFromIDListW(pidlTestFile
, wszTestFile2
);
371 ok (result
, "SHGetPathFromIDListW failed! Last error: %08lx\n", GetLastError());
372 ok (!lstrcmpiW(wszTestFile
, wszTestFile2
), "SHGetPathFromIDListW returns incorrect path!\n");
374 if(!pSHBindToParent
) return;
376 /* SHBindToParent fails, if called with a NULL PIDL. */
377 hr
= pSHBindToParent(NULL
, &IID_IShellFolder
, (VOID
**)&psfPersonal
, &pidlLast
);
378 ok (FAILED(hr
), "SHBindToParent(NULL) should fail!\n");
380 /* But it succeeds with an empty PIDL. */
381 hr
= pSHBindToParent(pidlEmpty
, &IID_IShellFolder
, (VOID
**)&psfPersonal
, &pidlLast
);
382 ok (SUCCEEDED(hr
), "SHBindToParent(empty PIDL) should succeed! hr = %08lx\n", hr
);
383 ok (pidlLast
== pidlEmpty
, "The last element of an empty PIDL should be the PIDL itself!\n");
385 IShellFolder_Release(psfPersonal
);
387 /* Binding to the folder and querying the display name of the file also works. */
388 hr
= pSHBindToParent(pidlTestFile
, &IID_IShellFolder
, (VOID
**)&psfPersonal
, &pidlLast
);
389 ok (SUCCEEDED(hr
), "SHBindToParent failed! hr = %08lx\n", hr
);
391 IShellFolder_Release(psfDesktop
);
395 /* This test shows that Windows doesn't allocate a new pidlLast, but returns a pointer into
396 * pidlTestFile (In accordance with MSDN). */
397 ok (pILFindLastID(pidlTestFile
) == pidlLast
,
398 "SHBindToParent doesn't return the last id of the pidl param!\n");
400 hr
= IShellFolder_GetDisplayNameOf(psfPersonal
, pidlLast
, SHGDN_FORPARSING
, &strret
);
401 ok (SUCCEEDED(hr
), "Personal->GetDisplayNameOf failed! hr = %08lx\n", hr
);
403 IShellFolder_Release(psfDesktop
);
404 IShellFolder_Release(psfPersonal
);
410 hr
= pStrRetToBufW(&strret
, pidlLast
, wszTestFile2
, MAX_PATH
);
411 ok (SUCCEEDED(hr
), "StrRetToBufW failed! hr = %08lx\n", hr
);
412 ok (!lstrcmpiW(wszTestFile
, wszTestFile2
), "GetDisplayNameOf returns incorrect path!\n");
415 IShellFolder_Release(psfDesktop
);
416 IShellFolder_Release(psfPersonal
);
419 static void test_CallForAttributes(void)
425 LPSHELLFOLDER psfDesktop
;
426 LPITEMIDLIST pidlMyDocuments
;
427 DWORD dwAttributes
, dwCallForAttributes
, dwOrigAttributes
, dwOrigCallForAttributes
;
428 static const WCHAR wszAttributes
[] = { 'A','t','t','r','i','b','u','t','e','s',0 };
429 static const WCHAR wszCallForAttributes
[] = {
430 'C','a','l','l','F','o','r','A','t','t','r','i','b','u','t','e','s',0 };
431 static const WCHAR wszMyDocumentsKey
[] = {
432 'C','L','S','I','D','\\','{','4','5','0','D','8','F','B','A','-','A','D','2','5','-',
433 '1','1','D','0','-','9','8','A','8','-','0','8','0','0','3','6','1','B','1','1','0','3','}',
434 '\\','S','h','e','l','l','F','o','l','d','e','r',0 };
435 WCHAR wszMyDocuments
[] = {
436 ':',':','{','4','5','0','D','8','F','B','A','-','A','D','2','5','-','1','1','D','0','-',
437 '9','8','A','8','-','0','8','0','0','3','6','1','B','1','1','0','3','}',0 };
439 /* For the root of a namespace extension, the attributes are not queried by binding
440 * to the object and calling GetAttributesOf. Instead, the attributes are read from
441 * the registry value HKCR/CLSID/{...}/ShellFolder/Attributes. This is documented on MSDN.
443 * The MyDocuments shellfolder on WinXP has a HKCR/CLSID/{...}/ShellFolder/CallForAttributes
444 * value. It seems that if the folder is queried for one of the flags set in CallForAttributes,
445 * the shell does bind to the folder object and calls GetAttributesOf. This is not documented
446 * on MSDN. This test is meant to document the observed behaviour on WinXP SP2.
448 hr
= SHGetDesktopFolder(&psfDesktop
);
449 ok (SUCCEEDED(hr
), "SHGetDesktopFolder failed! hr = %08lx\n", hr
);
450 if (FAILED(hr
)) return;
452 hr
= IShellFolder_ParseDisplayName(psfDesktop
, NULL
, NULL
, wszMyDocuments
, NULL
,
453 &pidlMyDocuments
, NULL
);
455 "Desktop's ParseDisplayName failed to parse MyDocuments's CLSID! hr = %08lx\n", hr
);
457 IShellFolder_Release(psfDesktop
);
461 dwAttributes
= 0xffffffff;
462 hr
= IShellFolder_GetAttributesOf(psfDesktop
, 1,
463 (LPCITEMIDLIST
*)&pidlMyDocuments
, &dwAttributes
);
464 ok (SUCCEEDED(hr
), "Desktop->GetAttributesOf(MyDocuments) failed! hr = %08lx\n", hr
);
466 /* We need the following setup (as observed on WinXP SP2), for the tests to make sense. */
467 ok (dwAttributes
& SFGAO_FILESYSTEM
, "SFGAO_FILESYSTEM attribute is not set for MyDocuments!\n");
468 ok (!(dwAttributes
& SFGAO_ISSLOW
), "SFGAO_ISSLOW attribute is set for MyDocuments!\n");
469 ok (!(dwAttributes
& SFGAO_GHOSTED
), "SFGAO_GHOSTED attribute is set for MyDocuments!\n");
471 /* We don't have the MyDocuments shellfolder in wine yet, and thus we don't have the registry
472 * key. So the test will return at this point, if run on wine.
474 lResult
= RegOpenKeyExW(HKEY_CLASSES_ROOT
, wszMyDocumentsKey
, 0, KEY_WRITE
|KEY_READ
, &hKey
);
475 ok (lResult
== ERROR_SUCCESS
, "RegOpenKeyEx failed! result: %08lx\n", lResult
);
476 if (lResult
!= ERROR_SUCCESS
) {
477 IMalloc_Free(ppM
, pidlMyDocuments
);
478 IShellFolder_Release(psfDesktop
);
482 /* Query MyDocuments' Attributes value, to be able to restore it later. */
483 dwSize
= sizeof(DWORD
);
484 lResult
= RegQueryValueExW(hKey
, wszAttributes
, NULL
, NULL
, (LPBYTE
)&dwOrigAttributes
, &dwSize
);
485 ok (lResult
== ERROR_SUCCESS
, "RegQueryValueEx failed! result: %08lx\n", lResult
);
486 if (lResult
!= ERROR_SUCCESS
) {
488 IMalloc_Free(ppM
, pidlMyDocuments
);
489 IShellFolder_Release(psfDesktop
);
493 /* Query MyDocuments' CallForAttributes value, to be able to restore it later. */
494 dwSize
= sizeof(DWORD
);
495 lResult
= RegQueryValueExW(hKey
, wszCallForAttributes
, NULL
, NULL
,
496 (LPBYTE
)&dwOrigCallForAttributes
, &dwSize
);
497 ok (lResult
== ERROR_SUCCESS
, "RegQueryValueEx failed! result: %08lx\n", lResult
);
498 if (lResult
!= ERROR_SUCCESS
) {
500 IMalloc_Free(ppM
, pidlMyDocuments
);
501 IShellFolder_Release(psfDesktop
);
505 /* Define via the Attributes value that MyDocuments attributes are SFGAO_ISSLOW and
506 * SFGAO_GHOSTED and that MyDocuments should be called for the SFGAO_ISSLOW and
507 * SFGAO_FILESYSTEM attributes. */
508 dwAttributes
= SFGAO_ISSLOW
|SFGAO_GHOSTED
;
509 RegSetValueExW(hKey
, wszAttributes
, 0, REG_DWORD
, (LPBYTE
)&dwAttributes
, sizeof(DWORD
));
510 dwCallForAttributes
= SFGAO_ISSLOW
|SFGAO_FILESYSTEM
;
511 RegSetValueExW(hKey
, wszCallForAttributes
, 0, REG_DWORD
,
512 (LPBYTE
)&dwCallForAttributes
, sizeof(DWORD
));
514 /* Although it is not set in CallForAttributes, the SFGAO_GHOSTED flag is reset by
515 * GetAttributesOf. It seems that once there is a single attribute queried, for which
516 * CallForAttributes is set, all flags are taken from the GetAttributesOf call and
517 * the flags in Attributes are ignored.
519 dwAttributes
= SFGAO_ISSLOW
|SFGAO_GHOSTED
|SFGAO_FILESYSTEM
;
520 hr
= IShellFolder_GetAttributesOf(psfDesktop
, 1,
521 (LPCITEMIDLIST
*)&pidlMyDocuments
, &dwAttributes
);
522 ok (SUCCEEDED(hr
), "Desktop->GetAttributesOf(MyDocuments) failed! hr = %08lx\n", hr
);
524 ok (dwAttributes
== SFGAO_FILESYSTEM
,
525 "Desktop->GetAttributes(MyDocuments) returned unexpected attributes: %08lx\n",
528 /* Restore MyDocuments' original Attributes and CallForAttributes registry values */
529 RegSetValueExW(hKey
, wszAttributes
, 0, REG_DWORD
, (LPBYTE
)&dwOrigAttributes
, sizeof(DWORD
));
530 RegSetValueExW(hKey
, wszCallForAttributes
, 0, REG_DWORD
,
531 (LPBYTE
)&dwOrigCallForAttributes
, sizeof(DWORD
));
533 IMalloc_Free(ppM
, pidlMyDocuments
);
534 IShellFolder_Release(psfDesktop
);
537 static void test_GetAttributesOf(void)
540 LPSHELLFOLDER psfDesktop
, psfMyComputer
;
541 SHITEMID emptyitem
= { 0, { 0 } };
542 LPCITEMIDLIST pidlEmpty
= (LPCITEMIDLIST
)&emptyitem
;
543 LPITEMIDLIST pidlMyComputer
;
545 const static DWORD dwDesktopFlags
= /* As observed on WinXP SP2 */
546 SFGAO_STORAGE
| SFGAO_HASPROPSHEET
| SFGAO_STORAGEANCESTOR
|
547 SFGAO_FILESYSANCESTOR
| SFGAO_FOLDER
| SFGAO_FILESYSTEM
| SFGAO_HASSUBFOLDER
;
548 const static DWORD dwMyComputerFlags
= /* As observed on WinXP SP2 */
549 SFGAO_CANRENAME
| SFGAO_CANDELETE
| SFGAO_HASPROPSHEET
|
550 SFGAO_DROPTARGET
| SFGAO_FILESYSANCESTOR
| SFGAO_FOLDER
| SFGAO_HASSUBFOLDER
;
551 WCHAR wszMyComputer
[] = {
552 ':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
553 'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };
555 hr
= SHGetDesktopFolder(&psfDesktop
);
556 ok (SUCCEEDED(hr
), "SHGetDesktopFolder failed! hr = %08lx\n", hr
);
557 if (FAILED(hr
)) return;
559 /* The Desktop attributes can be queried with a single empty itemidlist, .. */
560 dwFlags
= 0xffffffff;
561 hr
= IShellFolder_GetAttributesOf(psfDesktop
, 1, &pidlEmpty
, &dwFlags
);
562 ok (SUCCEEDED(hr
), "Desktop->GetAttributesOf(empty pidl) failed! hr = %08lx\n", hr
);
563 ok (dwFlags
== dwDesktopFlags
, "Wrong Desktop attributes: %08lx, expected: %08lx\n",
564 dwFlags
, dwDesktopFlags
);
566 /* .. or with no itemidlist at all. */
567 dwFlags
= 0xffffffff;
568 hr
= IShellFolder_GetAttributesOf(psfDesktop
, 0, NULL
, &dwFlags
);
569 ok (SUCCEEDED(hr
), "Desktop->GetAttributesOf(NULL) failed! hr = %08lx\n", hr
);
570 ok (dwFlags
== dwDesktopFlags
, "Wrong Desktop attributes: %08lx, expected: %08lx\n",
571 dwFlags
, dwDesktopFlags
);
573 /* Testing the attributes of the MyComputer shellfolder */
574 hr
= IShellFolder_ParseDisplayName(psfDesktop
, NULL
, NULL
, wszMyComputer
, NULL
, &pidlMyComputer
, NULL
);
575 ok (SUCCEEDED(hr
), "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08lx\n", hr
);
577 IShellFolder_Release(psfDesktop
);
581 /* WinXP SP2 sets the SFGAO_CANLINK flag, when MyComputer is queried via the Desktop
582 * folder object. It doesn't do this, if MyComputer is queried directly (see below).
583 * SFGAO_CANLINK is the same as DROPEFFECT_LINK, which MSDN says means: "Drag source
584 * should create a link to the original data". You can't create links on MyComputer on
585 * Windows, so this flag shouldn't be set. Seems like a bug in Windows. As long as nobody
586 * depends on this bug, we probably shouldn't imitate it.
588 dwFlags
= 0xffffffff;
589 hr
= IShellFolder_GetAttributesOf(psfDesktop
, 1, (LPCITEMIDLIST
*)&pidlMyComputer
, &dwFlags
);
590 ok (SUCCEEDED(hr
), "Desktop->GetAttributesOf(MyComputer) failed! hr = %08lx\n", hr
);
591 todo_wine
{ ok ((dwFlags
& ~(DWORD
)SFGAO_CANLINK
) == dwMyComputerFlags
,
592 "Wrong MyComputer attributes: %08lx, expected: %08lx\n", dwFlags
, dwMyComputerFlags
); }
594 hr
= IShellFolder_BindToObject(psfDesktop
, pidlMyComputer
, NULL
, &IID_IShellFolder
, (LPVOID
*)&psfMyComputer
);
595 ok (SUCCEEDED(hr
), "Desktop failed to bind to MyComputer object! hr = %08lx\n", hr
);
596 IShellFolder_Release(psfDesktop
);
597 IMalloc_Free(ppM
, pidlMyComputer
);
598 if (FAILED(hr
)) return;
600 hr
= IShellFolder_GetAttributesOf(psfMyComputer
, 1, &pidlEmpty
, &dwFlags
);
601 todo_wine
{ok (hr
== E_INVALIDARG
, "MyComputer->GetAttributesOf(emtpy pidl) should fail! hr = %08lx\n", hr
); }
603 dwFlags
= 0xffffffff;
604 hr
= IShellFolder_GetAttributesOf(psfMyComputer
, 0, NULL
, &dwFlags
);
605 ok (SUCCEEDED(hr
), "MyComputer->GetAttributesOf(NULL) failed! hr = %08lx\n", hr
);
606 todo_wine
{ ok (dwFlags
== dwMyComputerFlags
,
607 "Wrong MyComputer attributes: %08lx, expected: %08lx\n", dwFlags
, dwMyComputerFlags
); }
609 IShellFolder_Release(psfMyComputer
);
612 static void test_SHGetPathFromIDList(void)
614 SHITEMID emptyitem
= { 0, { 0 } };
615 LPCITEMIDLIST pidlEmpty
= (LPCITEMIDLIST
)&emptyitem
;
616 LPITEMIDLIST pidlMyComputer
;
617 WCHAR wszPath
[MAX_PATH
], wszDesktop
[MAX_PATH
];
620 LPSHELLFOLDER psfDesktop
;
621 WCHAR wszMyComputer
[] = {
622 ':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
623 'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };
624 WCHAR wszFileName
[MAX_PATH
];
625 LPITEMIDLIST pidlTestFile
;
628 static WCHAR wszTestFile
[] = {
629 'w','i','n','e','t','e','s','t','.','f','o','o',0 };
631 if(!pSHGetSpecialFolderPathW
) return;
633 /* Calling SHGetPathFromIDList with an empty pidl should return the desktop folder's path. */
634 result
= pSHGetSpecialFolderPathW(NULL
, wszDesktop
, CSIDL_DESKTOP
, FALSE
);
635 ok(result
, "SHGetSpecialFolderPathW(CSIDL_DESKTOP) failed! Last error: %08lx\n", GetLastError());
638 result
= SHGetPathFromIDListW(pidlEmpty
, wszPath
);
639 ok(result
, "SHGetPathFromIDListW failed! Last error: %08lx\n", GetLastError());
641 ok(!lstrcmpiW(wszDesktop
, wszPath
), "SHGetPathFromIDList didn't return desktop path for empty pidl!\n");
643 /* MyComputer does not map to a filesystem path. SHGetPathFromIDList should fail. */
644 hr
= SHGetDesktopFolder(&psfDesktop
);
645 ok (SUCCEEDED(hr
), "SHGetDesktopFolder failed! hr = %08lx\n", hr
);
646 if (FAILED(hr
)) return;
648 hr
= IShellFolder_ParseDisplayName(psfDesktop
, NULL
, NULL
, wszMyComputer
, NULL
, &pidlMyComputer
, NULL
);
649 ok (SUCCEEDED(hr
), "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08lx\n", hr
);
651 IShellFolder_Release(psfDesktop
);
655 SetLastError(0xdeadbeef);
656 result
= SHGetPathFromIDListW(pidlMyComputer
, wszPath
);
657 ok (!result
, "SHGetPathFromIDList succeeded where it shouldn't!\n");
658 ok (GetLastError()==0xdeadbeef, "SHGetPathFromIDList shouldn't set last error! Last error: %08lx\n", GetLastError());
660 IShellFolder_Release(psfDesktop
);
664 IMalloc_Free(ppM
, pidlMyComputer
);
666 result
= pSHGetSpecialFolderPathW(NULL
, wszFileName
, CSIDL_DESKTOPDIRECTORY
, FALSE
);
667 ok(result
, "SHGetSpecialFolderPathW failed! Last error: %08lx\n", GetLastError());
669 IShellFolder_Release(psfDesktop
);
672 PathAddBackslashW(wszFileName
);
673 lstrcatW(wszFileName
, wszTestFile
);
674 hTestFile
= CreateFileW(wszFileName
, GENERIC_WRITE
, 0, NULL
, CREATE_NEW
, 0, NULL
);
675 ok(hTestFile
!= INVALID_HANDLE_VALUE
, "CreateFileW failed! Last error: %08lx\n", GetLastError());
676 if (hTestFile
== INVALID_HANDLE_VALUE
) {
677 IShellFolder_Release(psfDesktop
);
680 CloseHandle(hTestFile
);
682 hr
= IShellFolder_ParseDisplayName(psfDesktop
, NULL
, NULL
, wszTestFile
, NULL
, &pidlTestFile
, NULL
);
683 ok (SUCCEEDED(hr
), "Desktop's ParseDisplayName failed to parse filename hr = %08lx\n", hr
);
685 IShellFolder_Release(psfDesktop
);
686 DeleteFileW(wszFileName
);
687 IMalloc_Free(ppM
, pidlTestFile
);
691 /* This test is to show that the Desktop shellfolder prepends the CSIDL_DESKTOPDIRECTORY
692 * path for files placed on the desktop, if called with SHGDN_FORPARSING. */
693 hr
= IShellFolder_GetDisplayNameOf(psfDesktop
, pidlTestFile
, SHGDN_FORPARSING
, &strret
);
694 ok (SUCCEEDED(hr
), "Desktop's GetDisplayNamfOf failed! hr = %08lx\n", hr
);
695 IShellFolder_Release(psfDesktop
);
696 DeleteFileW(wszFileName
);
698 IMalloc_Free(ppM
, pidlTestFile
);
703 pStrRetToBufW(&strret
, pidlTestFile
, wszPath
, MAX_PATH
);
704 ok(0 == lstrcmpW(wszFileName
, wszPath
),
705 "Desktop->GetDisplayNameOf(pidlTestFile, SHGDN_FORPARSING) "
706 "returned incorrect path for file placed on desktop\n");
709 result
= SHGetPathFromIDListW(pidlTestFile
, wszPath
);
710 ok(result
, "SHGetPathFromIDListW failed! Last error: %08lx\n", GetLastError());
711 IMalloc_Free(ppM
, pidlTestFile
);
713 ok(0 == lstrcmpW(wszFileName
, wszPath
), "SHGetPathFromIDListW returned incorrect path for file placed on desktop\n");
716 static void test_EnumObjects_and_CompareIDs(void)
719 IShellFolder
*IDesktopFolder
, *testIShellFolder
;
720 char cCurrDirA
[MAX_PATH
] = {0};
721 WCHAR cCurrDirW
[MAX_PATH
];
722 static const WCHAR cTestDirW
[] = {'\\','t','e','s','t','d','i','r',0};
726 GetCurrentDirectoryA(MAX_PATH
, cCurrDirA
);
727 len
= lstrlenA(cCurrDirA
);
730 trace("GetCurrentDirectoryA returned empty string. Skipping test_EnumObjects_and_CompareIDs\n");
733 if(cCurrDirA
[len
-1] == '\\')
734 cCurrDirA
[len
-1] = 0;
736 MultiByteToWideChar(CP_ACP
, 0, cCurrDirA
, -1, cCurrDirW
, MAX_PATH
);
737 strcatW(cCurrDirW
, cTestDirW
);
739 hr
= SHGetDesktopFolder(&IDesktopFolder
);
740 ok(hr
== S_OK
, "SHGetDesktopfolder failed %08lx\n", hr
);
742 CreateFilesFolders();
744 hr
= IShellFolder_ParseDisplayName(IDesktopFolder
, NULL
, NULL
, cCurrDirW
, NULL
, &newPIDL
, 0);
745 ok(hr
== S_OK
, "ParseDisplayName failed %08lx\n", hr
);
747 hr
= IShellFolder_BindToObject(IDesktopFolder
, newPIDL
, NULL
, (REFIID
)&IID_IShellFolder
, (LPVOID
*)&testIShellFolder
);
748 ok(hr
== S_OK
, "BindToObject failed %08lx\n", hr
);
750 test_EnumObjects(testIShellFolder
);
752 hr
= IShellFolder_Release(testIShellFolder
);
753 ok(hr
== S_OK
, "IShellFolder_Release failed %08lx\n", hr
);
757 IMalloc_Free(ppM
, newPIDL
);
760 /* A simple implementation of an IPropertyBag, which returns fixed values for
761 * 'Target' and 'Attributes' properties.
763 static HRESULT WINAPI
InitPropertyBag_IPropertyBag_QueryInterface(IPropertyBag
*iface
, REFIID riid
,
769 if (IsEqualIID(&IID_IUnknown
, riid
) || IsEqualIID(&IID_IPropertyBag
, riid
)) {
772 ok (FALSE
, "InitPropertyBag asked for unknown interface!\n");
773 return E_NOINTERFACE
;
776 IPropertyBag_AddRef(iface
);
780 static ULONG WINAPI
InitPropertyBag_IPropertyBag_AddRef(IPropertyBag
*iface
) {
784 static ULONG WINAPI
InitPropertyBag_IPropertyBag_Release(IPropertyBag
*iface
) {
788 static HRESULT WINAPI
InitPropertyBag_IPropertyBag_Read(IPropertyBag
*iface
, LPCOLESTR pszPropName
,
789 VARIANT
*pVar
, IErrorLog
*pErrorLog
)
791 static const WCHAR wszTargetSpecialFolder
[] = {
792 'T','a','r','g','e','t','S','p','e','c','i','a','l','F','o','l','d','e','r',0 };
793 static const WCHAR wszTarget
[] = {
794 'T','a','r','g','e','t',0 };
795 static const WCHAR wszAttributes
[] = {
796 'A','t','t','r','i','b','u','t','e','s',0 };
797 static const WCHAR wszResolveLinkFlags
[] = {
798 'R','e','s','o','l','v','e','L','i','n','k','F','l','a','g','s',0 };
800 if (!lstrcmpW(pszPropName
, wszTargetSpecialFolder
) ||
801 !lstrcmpW(pszPropName
, wszResolveLinkFlags
))
806 if (!lstrcmpW(pszPropName
, wszTarget
)) {
807 WCHAR wszPath
[MAX_PATH
];
810 ok(V_VT(pVar
) == VT_BSTR
, "Wrong variant type for 'Target' property!\n");
811 if (V_VT(pVar
) != VT_BSTR
) return E_INVALIDARG
;
813 result
= pSHGetSpecialFolderPathW(NULL
, wszPath
, CSIDL_DESKTOPDIRECTORY
, FALSE
);
814 ok(result
, "SHGetSpecialFolderPathW(DESKTOPDIRECTORY) failed! x%08lx\n", GetLastError());
815 if (!result
) return E_INVALIDARG
;
817 V_BSTR(pVar
) = SysAllocString(wszPath
);
821 if (!lstrcmpW(pszPropName
, wszAttributes
)) {
822 ok(V_VT(pVar
) == VT_UI4
, "Wrong variant type for 'Attributes' property!\n");
823 if (V_VT(pVar
) != VT_UI4
) return E_INVALIDARG
;
824 V_UI4(pVar
) = SFGAO_FOLDER
|SFGAO_HASSUBFOLDER
|SFGAO_FILESYSANCESTOR
|
825 SFGAO_CANRENAME
|SFGAO_FILESYSTEM
;
829 ok(FALSE
, "PropertyBag was asked for unknown property (vt=%d)!\n", V_VT(pVar
));
833 static HRESULT WINAPI
InitPropertyBag_IPropertyBag_Write(IPropertyBag
*iface
, LPCOLESTR pszPropName
,
836 ok(FALSE
, "Unexpected call to IPropertyBag_Write\n");
840 static const IPropertyBagVtbl InitPropertyBag_IPropertyBagVtbl
= {
841 InitPropertyBag_IPropertyBag_QueryInterface
,
842 InitPropertyBag_IPropertyBag_AddRef
,
843 InitPropertyBag_IPropertyBag_Release
,
844 InitPropertyBag_IPropertyBag_Read
,
845 InitPropertyBag_IPropertyBag_Write
848 struct IPropertyBag InitPropertyBag
= {
849 &InitPropertyBag_IPropertyBagVtbl
852 void test_FolderShortcut(void) {
853 IPersistPropertyBag
*pPersistPropertyBag
;
854 IShellFolder
*pShellFolder
;
855 IPersistFolder3
*pPersistFolder3
;
858 WCHAR wszPath
[MAX_PATH
], wszBuffer
[MAX_PATH
];
861 LPITEMIDLIST pidlCurrentFolder
;
863 if (!pSHGetSpecialFolderPathW
|| !pStrRetToBufW
) return;
865 /* These tests basically show, that CLSID_FolderShortcuts are initialized
866 * via their IPersistPropertyBag interface. And that the target folder
867 * is taken from the IPropertyBag's 'Target' property.
869 hr
= CoCreateInstance(&CLSID_FolderShortcut
, NULL
, CLSCTX_INPROC_SERVER
,
870 &IID_IPersistPropertyBag
, (LPVOID
*)&pPersistPropertyBag
);
871 ok (SUCCEEDED(hr
), "CoCreateInstance failed! hr = 0x%08lx\n", hr
);
872 if (FAILED(hr
)) return;
874 hr
= IPersistPropertyBag_Load(pPersistPropertyBag
, &InitPropertyBag
, NULL
);
875 todo_wine
{ ok(SUCCEEDED(hr
), "IPersistPropertyBag_Load failed! hr = %08lx\n", hr
); }
877 IPersistPropertyBag_Release(pPersistPropertyBag
);
881 hr
= IPersistPropertyBag_QueryInterface(pPersistPropertyBag
, &IID_IShellFolder
,
882 (LPVOID
*)&pShellFolder
);
883 IPersistPropertyBag_Release(pPersistPropertyBag
);
884 ok(SUCCEEDED(hr
), "IPersistPropertyBag_QueryInterface(IShellFolder) failed! hr = %08lx\n", hr
);
885 if (FAILED(hr
)) return;
887 hr
= IShellFolder_GetDisplayNameOf(pShellFolder
, NULL
, SHGDN_FORPARSING
, &strret
);
888 ok(SUCCEEDED(hr
), "IShellFolder_GetDisplayNameOf(NULL) failed! hr = %08lx\n", hr
);
890 IShellFolder_Release(pShellFolder
);
894 result
= pSHGetSpecialFolderPathW(NULL
, wszPath
, CSIDL_DESKTOPDIRECTORY
, FALSE
);
895 ok(result
, "SHGetSpecialFolderPathW(CSIDL_MYDOCUMENTS) failed! 0x%08lx\n", GetLastError());
898 pStrRetToBufW(&strret
, NULL
, wszBuffer
, MAX_PATH
);
899 ok(!lstrcmpiW(wszPath
, wszBuffer
), "FolderShortcut returned incorrect folder!\n");
901 hr
= IShellFolder_QueryInterface(pShellFolder
, &IID_IPersistFolder3
, (LPVOID
*)&pPersistFolder3
);
902 IShellFolder_Release(pShellFolder
);
903 ok(SUCCEEDED(hr
), "IShellFolder_QueryInterface(IID_IPersistFolder3 failed! hr = 0x%08lx\n", hr
);
904 if (FAILED(hr
)) return;
906 hr
= IPersistFolder3_GetClassID(pPersistFolder3
, &clsid
);
907 ok(SUCCEEDED(hr
), "IPersistFolder3_GetClassID failed! hr=0x%08lx\n", hr
);
908 ok(IsEqualCLSID(&clsid
, &CLSID_FolderShortcut
), "Unexpected CLSID!\n");
910 hr
= IPersistFolder3_GetCurFolder(pPersistFolder3
, &pidlCurrentFolder
);
911 ok(SUCCEEDED(hr
), "IPersistFolder3_GetCurFolder failed! hr=0x%08lx\n", hr
);
912 ok(!pidlCurrentFolder
, "IPersistFolder3_GetCurFolder should return a NULL pidl!\n");
914 IPersistFolder3_Release(pPersistFolder3
);
917 START_TEST(shlfolder
)
919 init_function_pointers();
920 /* if OleInitialize doesn't get called, ParseDisplayName returns
921 CO_E_NOTINITIALIZED for malformed directory names on win2k. */
924 test_ParseDisplayName();
926 test_EnumObjects_and_CompareIDs();
927 test_GetDisplayName();
928 test_GetAttributesOf();
929 test_SHGetPathFromIDList();
930 test_CallForAttributes();
931 test_FolderShortcut();