2 * Unit test of the IShellFolder functions.
4 * Copyright 2004 Vitaliy Margolen
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
40 #include "wine/test.h"
45 static HRESULT (WINAPI
*pSHBindToParent
)(LPCITEMIDLIST
, REFIID
, LPVOID
*, LPCITEMIDLIST
*);
46 static HRESULT (WINAPI
*pSHGetFolderPathA
)(HWND
, int, HANDLE
, DWORD
, LPSTR
);
47 static HRESULT (WINAPI
*pSHGetFolderPathAndSubDirA
)(HWND
, int, HANDLE
, DWORD
, LPCSTR
, LPSTR
);
48 static BOOL (WINAPI
*pSHGetPathFromIDListW
)(LPCITEMIDLIST
,LPWSTR
);
49 static BOOL (WINAPI
*pSHGetSpecialFolderPathA
)(HWND
, LPSTR
, int, BOOL
);
50 static BOOL (WINAPI
*pSHGetSpecialFolderPathW
)(HWND
, LPWSTR
, int, BOOL
);
51 static HRESULT (WINAPI
*pStrRetToBufW
)(STRRET
*,LPCITEMIDLIST
,LPWSTR
,UINT
);
52 static LPITEMIDLIST (WINAPI
*pILFindLastID
)(LPCITEMIDLIST
);
53 static void (WINAPI
*pILFree
)(LPITEMIDLIST
);
54 static BOOL (WINAPI
*pILIsEqual
)(LPCITEMIDLIST
, LPCITEMIDLIST
);
55 static HRESULT (WINAPI
*pSHCreateShellItem
)(LPCITEMIDLIST
,IShellFolder
*,LPCITEMIDLIST
,IShellItem
**);
56 static LPITEMIDLIST (WINAPI
*pILCombine
)(LPCITEMIDLIST
,LPCITEMIDLIST
);
59 static void init_function_pointers(void)
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)
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";
93 WCHAR cTestDirW
[MAX_PATH
] = {0};
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
);
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
);
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
);
162 IShellFolder_Release(IDesktopFolder
);
165 /* creates a file with the specified name for tests */
166 static void CreateTestFile(const CHAR
*name
)
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
);
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");
207 static void test_EnumObjects(IShellFolder
*iFolder
)
209 IEnumIDList
*iEnumList
;
210 LPITEMIDLIST newPIDL
, idlArr
[10];
215 static const WORD iResults
[5][5] =
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
)
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)
254 idlArr
[i
] = idlArr
[j
];
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
++)
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
]);
289 IMalloc_Free(ppM
, idlArr
[i
]);
292 static void test_BindToObject(void)
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
);
321 IShellFolder_Release(psfDesktop
);
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
);
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
);
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
);
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
);
351 IShellFolder_Release(psfMyComputer
);
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
);
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
);
372 IShellFolder_Release(psfSystemDir
);
375 /* Based on PathAddBackslashW from dlls/shlwapi/path.c */
376 static LPWSTR
myPathAddBackslashW( LPWSTR lpszPath
)
380 if (!lpszPath
|| (iLen
= lstrlenW(lpszPath
)) >= MAX_PATH
)
386 if (lpszPath
[-1] != '\\')
395 static void test_GetDisplayName(void)
400 WCHAR wszTestFile
[MAX_PATH
], wszTestFile2
[MAX_PATH
];
401 char szTestFile
[MAX_PATH
], szTestDir
[MAX_PATH
];
404 LPSHELLFOLDER psfDesktop
, psfPersonal
;
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");
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());
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
);
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
);
459 IShellFolder_Release(psfDesktop
);
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
);
484 ok (hr
== HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND
) ||
485 hr
== E_NOTIMPL
|| /* Vista */
486 broken(SUCCEEDED(hr
)), /* Win9x, W2K */
489 IShellFolder_Release(psfFile
);
492 if (!pSHBindToParent
)
494 win_skip("SHBindToParent is missing\n");
495 DeleteFileA(szTestFile
);
496 RemoveDirectoryA(szTestDir
);
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
);
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
);
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
);
532 IShellFolder_Release(psfPersonal
);
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");
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
);
565 IShellFolder_Release(psfDesktop
);
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
);
577 IShellFolder_Release(psfDesktop
);
578 IShellFolder_Release(psfPersonal
);
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 ILFree(pidlTestFile
);
590 IShellFolder_Release(psfDesktop
);
591 IShellFolder_Release(psfPersonal
);
594 static void test_CallForAttributes(void)
600 LPSHELLFOLDER psfDesktop
;
601 LPITEMIDLIST pidlMyDocuments
;
602 DWORD dwAttributes
, dwCallForAttributes
, dwOrigAttributes
, dwOrigCallForAttributes
;
603 static const WCHAR wszAttributes
[] = { 'A','t','t','r','i','b','u','t','e','s',0 };
604 static const WCHAR wszCallForAttributes
[] = {
605 'C','a','l','l','F','o','r','A','t','t','r','i','b','u','t','e','s',0 };
606 static const WCHAR wszMyDocumentsKey
[] = {
607 'C','L','S','I','D','\\','{','4','5','0','D','8','F','B','A','-','A','D','2','5','-',
608 '1','1','D','0','-','9','8','A','8','-','0','8','0','0','3','6','1','B','1','1','0','3','}',
609 '\\','S','h','e','l','l','F','o','l','d','e','r',0 };
610 WCHAR wszMyDocuments
[] = {
611 ':',':','{','4','5','0','D','8','F','B','A','-','A','D','2','5','-','1','1','D','0','-',
612 '9','8','A','8','-','0','8','0','0','3','6','1','B','1','1','0','3','}',0 };
614 /* For the root of a namespace extension, the attributes are not queried by binding
615 * to the object and calling GetAttributesOf. Instead, the attributes are read from
616 * the registry value HKCR/CLSID/{...}/ShellFolder/Attributes. This is documented on MSDN.
618 * The MyDocuments shellfolder on WinXP has a HKCR/CLSID/{...}/ShellFolder/CallForAttributes
619 * value. It seems that if the folder is queried for one of the flags set in CallForAttributes,
620 * the shell does bind to the folder object and calls GetAttributesOf. This is not documented
621 * on MSDN. This test is meant to document the observed behaviour on WinXP SP2.
623 hr
= SHGetDesktopFolder(&psfDesktop
);
624 ok (SUCCEEDED(hr
), "SHGetDesktopFolder failed! hr = %08x\n", hr
);
625 if (FAILED(hr
)) return;
627 hr
= IShellFolder_ParseDisplayName(psfDesktop
, NULL
, NULL
, wszMyDocuments
, NULL
,
628 &pidlMyDocuments
, NULL
);
630 broken(hr
== E_INVALIDARG
), /* Win95, NT4 */
631 "Desktop's ParseDisplayName failed to parse MyDocuments's CLSID! hr = %08x\n", hr
);
633 IShellFolder_Release(psfDesktop
);
637 dwAttributes
= 0xffffffff;
638 hr
= IShellFolder_GetAttributesOf(psfDesktop
, 1,
639 (LPCITEMIDLIST
*)&pidlMyDocuments
, &dwAttributes
);
640 ok (SUCCEEDED(hr
), "Desktop->GetAttributesOf(MyDocuments) failed! hr = %08x\n", hr
);
642 /* We need the following setup (as observed on WinXP SP2), for the tests to make sense. */
643 ok (dwAttributes
& SFGAO_FILESYSTEM
, "SFGAO_FILESYSTEM attribute is not set for MyDocuments!\n");
644 ok (!(dwAttributes
& SFGAO_ISSLOW
), "SFGAO_ISSLOW attribute is set for MyDocuments!\n");
645 ok (!(dwAttributes
& SFGAO_GHOSTED
), "SFGAO_GHOSTED attribute is set for MyDocuments!\n");
647 /* We don't have the MyDocuments shellfolder in wine yet, and thus we don't have the registry
648 * key. So the test will return at this point, if run on wine.
650 lResult
= RegOpenKeyExW(HKEY_CLASSES_ROOT
, wszMyDocumentsKey
, 0, KEY_WRITE
|KEY_READ
, &hKey
);
651 ok (lResult
== ERROR_SUCCESS
||
652 lResult
== ERROR_ACCESS_DENIED
,
653 "RegOpenKeyEx failed! result: %08x\n", lResult
);
654 if (lResult
!= ERROR_SUCCESS
) {
655 if (lResult
== ERROR_ACCESS_DENIED
)
656 skip("Not enough rights to open the registry key\n");
657 IMalloc_Free(ppM
, pidlMyDocuments
);
658 IShellFolder_Release(psfDesktop
);
662 /* Query MyDocuments' Attributes value, to be able to restore it later. */
663 dwSize
= sizeof(DWORD
);
664 lResult
= RegQueryValueExW(hKey
, wszAttributes
, NULL
, NULL
, (LPBYTE
)&dwOrigAttributes
, &dwSize
);
665 ok (lResult
== ERROR_SUCCESS
, "RegQueryValueEx failed! result: %08x\n", lResult
);
666 if (lResult
!= ERROR_SUCCESS
) {
668 IMalloc_Free(ppM
, pidlMyDocuments
);
669 IShellFolder_Release(psfDesktop
);
673 /* Query MyDocuments' CallForAttributes value, to be able to restore it later. */
674 dwSize
= sizeof(DWORD
);
675 lResult
= RegQueryValueExW(hKey
, wszCallForAttributes
, NULL
, NULL
,
676 (LPBYTE
)&dwOrigCallForAttributes
, &dwSize
);
677 ok (lResult
== ERROR_SUCCESS
, "RegQueryValueEx failed! result: %08x\n", lResult
);
678 if (lResult
!= ERROR_SUCCESS
) {
680 IMalloc_Free(ppM
, pidlMyDocuments
);
681 IShellFolder_Release(psfDesktop
);
685 /* Define via the Attributes value that MyDocuments attributes are SFGAO_ISSLOW and
686 * SFGAO_GHOSTED and that MyDocuments should be called for the SFGAO_ISSLOW and
687 * SFGAO_FILESYSTEM attributes. */
688 dwAttributes
= SFGAO_ISSLOW
|SFGAO_GHOSTED
;
689 RegSetValueExW(hKey
, wszAttributes
, 0, REG_DWORD
, (LPBYTE
)&dwAttributes
, sizeof(DWORD
));
690 dwCallForAttributes
= SFGAO_ISSLOW
|SFGAO_FILESYSTEM
;
691 RegSetValueExW(hKey
, wszCallForAttributes
, 0, REG_DWORD
,
692 (LPBYTE
)&dwCallForAttributes
, sizeof(DWORD
));
694 /* Although it is not set in CallForAttributes, the SFGAO_GHOSTED flag is reset by
695 * GetAttributesOf. It seems that once there is a single attribute queried, for which
696 * CallForAttributes is set, all flags are taken from the GetAttributesOf call and
697 * the flags in Attributes are ignored.
699 dwAttributes
= SFGAO_ISSLOW
|SFGAO_GHOSTED
|SFGAO_FILESYSTEM
;
700 hr
= IShellFolder_GetAttributesOf(psfDesktop
, 1,
701 (LPCITEMIDLIST
*)&pidlMyDocuments
, &dwAttributes
);
702 ok (SUCCEEDED(hr
), "Desktop->GetAttributesOf(MyDocuments) failed! hr = %08x\n", hr
);
704 ok (dwAttributes
== SFGAO_FILESYSTEM
,
705 "Desktop->GetAttributes(MyDocuments) returned unexpected attributes: %08x\n",
708 /* Restore MyDocuments' original Attributes and CallForAttributes registry values */
709 RegSetValueExW(hKey
, wszAttributes
, 0, REG_DWORD
, (LPBYTE
)&dwOrigAttributes
, sizeof(DWORD
));
710 RegSetValueExW(hKey
, wszCallForAttributes
, 0, REG_DWORD
,
711 (LPBYTE
)&dwOrigCallForAttributes
, sizeof(DWORD
));
713 IMalloc_Free(ppM
, pidlMyDocuments
);
714 IShellFolder_Release(psfDesktop
);
717 static void test_GetAttributesOf(void)
720 LPSHELLFOLDER psfDesktop
, psfMyComputer
;
721 SHITEMID emptyitem
= { 0, { 0 } };
722 LPCITEMIDLIST pidlEmpty
= (LPCITEMIDLIST
)&emptyitem
;
723 LPITEMIDLIST pidlMyComputer
;
725 static const DWORD desktopFlags
[] = {
727 SFGAO_STORAGE
| SFGAO_HASPROPSHEET
| SFGAO_STORAGEANCESTOR
| SFGAO_FILESYSANCESTOR
|
728 SFGAO_FOLDER
| SFGAO_FILESYSTEM
| SFGAO_HASSUBFOLDER
,
730 SFGAO_CANRENAME
| SFGAO_HASPROPSHEET
| SFGAO_STREAM
| SFGAO_FILESYSANCESTOR
|
731 SFGAO_FOLDER
| SFGAO_FILESYSTEM
| SFGAO_HASSUBFOLDER
,
732 /* WinMe, Win9x, WinNT*/
733 SFGAO_CANRENAME
| SFGAO_HASPROPSHEET
| SFGAO_FILESYSANCESTOR
|
734 SFGAO_FOLDER
| SFGAO_FILESYSTEM
| SFGAO_HASSUBFOLDER
736 static const DWORD myComputerFlags
[] = {
738 SFGAO_CANRENAME
| SFGAO_CANDELETE
| SFGAO_HASPROPSHEET
| SFGAO_DROPTARGET
|
739 SFGAO_FILESYSANCESTOR
| SFGAO_FOLDER
| SFGAO_HASSUBFOLDER
,
741 SFGAO_CANRENAME
| SFGAO_HASPROPSHEET
| SFGAO_DROPTARGET
| SFGAO_STREAM
|
742 SFGAO_FILESYSANCESTOR
| SFGAO_FOLDER
| SFGAO_HASSUBFOLDER
,
743 /* WinMe, Win9x, WinNT */
744 SFGAO_CANRENAME
| SFGAO_HASPROPSHEET
| SFGAO_DROPTARGET
| SFGAO_FILESYSANCESTOR
|
745 SFGAO_FOLDER
| SFGAO_HASSUBFOLDER
,
746 /* Win95, WinNT when queried directly */
747 SFGAO_CANLINK
| SFGAO_HASPROPSHEET
| SFGAO_DROPTARGET
| SFGAO_FILESYSANCESTOR
|
748 SFGAO_FOLDER
| SFGAO_FILESYSTEM
| SFGAO_HASSUBFOLDER
750 WCHAR wszMyComputer
[] = {
751 ':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
752 'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };
753 char cCurrDirA
[MAX_PATH
] = {0};
754 WCHAR cCurrDirW
[MAX_PATH
];
755 static WCHAR cTestDirW
[] = {'t','e','s','t','d','i','r',0};
756 IShellFolder
*IDesktopFolder
, *testIShellFolder
;
759 BOOL foundFlagsMatch
;
761 hr
= SHGetDesktopFolder(&psfDesktop
);
762 ok (SUCCEEDED(hr
), "SHGetDesktopFolder failed! hr = %08x\n", hr
);
763 if (FAILED(hr
)) return;
765 /* The Desktop attributes can be queried with a single empty itemidlist, .. */
766 dwFlags
= 0xffffffff;
767 hr
= IShellFolder_GetAttributesOf(psfDesktop
, 1, &pidlEmpty
, &dwFlags
);
768 ok (SUCCEEDED(hr
), "Desktop->GetAttributesOf(empty pidl) failed! hr = %08x\n", hr
);
769 for (i
= 0, foundFlagsMatch
= FALSE
; !foundFlagsMatch
&&
770 i
< sizeof(desktopFlags
) / sizeof(desktopFlags
[0]); i
++)
772 if (desktopFlags
[i
] == dwFlags
)
773 foundFlagsMatch
= TRUE
;
775 ok (foundFlagsMatch
, "Wrong Desktop attributes: %08x\n", dwFlags
);
777 /* .. or with no itemidlist at all. */
778 dwFlags
= 0xffffffff;
779 hr
= IShellFolder_GetAttributesOf(psfDesktop
, 0, NULL
, &dwFlags
);
780 ok (SUCCEEDED(hr
), "Desktop->GetAttributesOf(NULL) failed! hr = %08x\n", hr
);
781 for (i
= 0, foundFlagsMatch
= FALSE
; !foundFlagsMatch
&&
782 i
< sizeof(desktopFlags
) / sizeof(desktopFlags
[0]); i
++)
784 if (desktopFlags
[i
] == dwFlags
)
785 foundFlagsMatch
= TRUE
;
787 ok (foundFlagsMatch
, "Wrong Desktop attributes: %08x\n", dwFlags
);
789 /* Testing the attributes of the MyComputer shellfolder */
790 hr
= IShellFolder_ParseDisplayName(psfDesktop
, NULL
, NULL
, wszMyComputer
, NULL
, &pidlMyComputer
, NULL
);
791 ok (SUCCEEDED(hr
), "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08x\n", hr
);
793 IShellFolder_Release(psfDesktop
);
797 /* Windows sets the SFGAO_CANLINK flag, when MyComputer is queried via the Desktop
798 * folder object. It doesn't do this, if MyComputer is queried directly (see below).
800 dwFlags
= 0xffffffff;
801 hr
= IShellFolder_GetAttributesOf(psfDesktop
, 1, (LPCITEMIDLIST
*)&pidlMyComputer
, &dwFlags
);
802 ok (SUCCEEDED(hr
), "Desktop->GetAttributesOf(MyComputer) failed! hr = %08x\n", hr
);
803 for (i
= 0, foundFlagsMatch
= FALSE
; !foundFlagsMatch
&&
804 i
< sizeof(myComputerFlags
) / sizeof(myComputerFlags
[0]); i
++)
806 if ((myComputerFlags
[i
] | SFGAO_CANLINK
) == dwFlags
)
807 foundFlagsMatch
= TRUE
;
810 ok (foundFlagsMatch
, "Wrong MyComputer attributes: %08x\n", dwFlags
);
812 hr
= IShellFolder_BindToObject(psfDesktop
, pidlMyComputer
, NULL
, &IID_IShellFolder
, (LPVOID
*)&psfMyComputer
);
813 ok (SUCCEEDED(hr
), "Desktop failed to bind to MyComputer object! hr = %08x\n", hr
);
814 IShellFolder_Release(psfDesktop
);
815 IMalloc_Free(ppM
, pidlMyComputer
);
816 if (FAILED(hr
)) return;
818 hr
= IShellFolder_GetAttributesOf(psfMyComputer
, 1, &pidlEmpty
, &dwFlags
);
820 ok (hr
== E_INVALIDARG
||
821 broken(SUCCEEDED(hr
)), /* W2K and earlier */
822 "MyComputer->GetAttributesOf(emtpy pidl) should fail! hr = %08x\n", hr
);
824 dwFlags
= 0xffffffff;
825 hr
= IShellFolder_GetAttributesOf(psfMyComputer
, 0, NULL
, &dwFlags
);
826 ok (SUCCEEDED(hr
), "MyComputer->GetAttributesOf(NULL) failed! hr = %08x\n", hr
);
827 for (i
= 0, foundFlagsMatch
= FALSE
; !foundFlagsMatch
&&
828 i
< sizeof(myComputerFlags
) / sizeof(myComputerFlags
[0]); i
++)
830 if (myComputerFlags
[i
] == dwFlags
)
831 foundFlagsMatch
= TRUE
;
834 ok (foundFlagsMatch
, "Wrong MyComputer attributes: %08x\n", dwFlags
);
836 IShellFolder_Release(psfMyComputer
);
838 GetCurrentDirectoryA(MAX_PATH
, cCurrDirA
);
839 len
= lstrlenA(cCurrDirA
);
842 win_skip("GetCurrentDirectoryA returned empty string. Skipping test_GetAttributesOf\n");
845 if (len
> 3 && cCurrDirA
[len
-1] == '\\')
846 cCurrDirA
[len
-1] = 0;
848 /* create test directory */
849 CreateFilesFolders();
851 MultiByteToWideChar(CP_ACP
, 0, cCurrDirA
, -1, cCurrDirW
, MAX_PATH
);
853 hr
= SHGetDesktopFolder(&IDesktopFolder
);
854 ok(hr
== S_OK
, "SHGetDesktopfolder failed %08x\n", hr
);
856 hr
= IShellFolder_ParseDisplayName(IDesktopFolder
, NULL
, NULL
, cCurrDirW
, NULL
, &newPIDL
, 0);
857 ok(hr
== S_OK
, "ParseDisplayName failed %08x\n", hr
);
859 hr
= IShellFolder_BindToObject(IDesktopFolder
, newPIDL
, NULL
, (REFIID
)&IID_IShellFolder
, (LPVOID
*)&testIShellFolder
);
860 ok(hr
== S_OK
, "BindToObject failed %08x\n", hr
);
862 IMalloc_Free(ppM
, newPIDL
);
864 /* get relative PIDL */
865 hr
= IShellFolder_ParseDisplayName(testIShellFolder
, NULL
, NULL
, cTestDirW
, NULL
, &newPIDL
, 0);
866 ok(hr
== S_OK
, "ParseDisplayName failed %08x\n", hr
);
868 /* test the shell attributes of the test directory using the relative PIDL */
869 dwFlags
= SFGAO_FOLDER
;
870 hr
= IShellFolder_GetAttributesOf(testIShellFolder
, 1, (LPCITEMIDLIST
*)&newPIDL
, &dwFlags
);
871 ok (SUCCEEDED(hr
), "Desktop->GetAttributesOf() failed! hr = %08x\n", hr
);
872 ok ((dwFlags
&SFGAO_FOLDER
), "Wrong directory attribute for relative PIDL: %08x\n", dwFlags
);
875 IMalloc_Free(ppM
, newPIDL
);
877 /* append testdirectory name to path */
878 if (cCurrDirA
[len
-1] == '\\')
879 cCurrDirA
[len
-1] = 0;
880 lstrcatA(cCurrDirA
, "\\testdir");
881 MultiByteToWideChar(CP_ACP
, 0, cCurrDirA
, -1, cCurrDirW
, MAX_PATH
);
883 hr
= IShellFolder_ParseDisplayName(IDesktopFolder
, NULL
, NULL
, cCurrDirW
, NULL
, &newPIDL
, 0);
884 ok(hr
== S_OK
, "ParseDisplayName failed %08x\n", hr
);
886 /* test the shell attributes of the test directory using the absolute PIDL */
887 dwFlags
= SFGAO_FOLDER
;
888 hr
= IShellFolder_GetAttributesOf(IDesktopFolder
, 1, (LPCITEMIDLIST
*)&newPIDL
, &dwFlags
);
889 ok (SUCCEEDED(hr
), "Desktop->GetAttributesOf() failed! hr = %08x\n", hr
);
890 ok ((dwFlags
&SFGAO_FOLDER
), "Wrong directory attribute for absolute PIDL: %08x\n", dwFlags
);
893 IMalloc_Free(ppM
, newPIDL
);
895 IShellFolder_Release(testIShellFolder
);
899 IShellFolder_Release(IDesktopFolder
);
902 static void test_SHGetPathFromIDList(void)
904 SHITEMID emptyitem
= { 0, { 0 } };
905 LPCITEMIDLIST pidlEmpty
= (LPCITEMIDLIST
)&emptyitem
;
906 LPITEMIDLIST pidlMyComputer
;
907 WCHAR wszPath
[MAX_PATH
], wszDesktop
[MAX_PATH
];
910 LPSHELLFOLDER psfDesktop
;
911 WCHAR wszMyComputer
[] = {
912 ':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
913 'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };
914 WCHAR wszFileName
[MAX_PATH
];
915 LPITEMIDLIST pidlTestFile
;
918 static WCHAR wszTestFile
[] = {
919 'w','i','n','e','t','e','s','t','.','f','o','o',0 };
920 HRESULT (WINAPI
*pSHGetSpecialFolderLocation
)(HWND
, int, LPITEMIDLIST
*);
922 LPITEMIDLIST pidlPrograms
;
924 if(!pSHGetPathFromIDListW
|| !pSHGetSpecialFolderPathW
)
926 win_skip("SHGetPathFromIDListW() or SHGetSpecialFolderPathW() is missing\n");
930 /* Calling SHGetPathFromIDListW with no pidl should return the empty string */
933 result
= pSHGetPathFromIDListW(NULL
, wszPath
);
934 ok(!result
, "Expected failure\n");
935 ok(!wszPath
[0], "Expected empty string\n");
937 /* Calling SHGetPathFromIDListW with an empty pidl should return the desktop folder's path. */
938 result
= pSHGetSpecialFolderPathW(NULL
, wszDesktop
, CSIDL_DESKTOP
, FALSE
);
939 ok(result
, "SHGetSpecialFolderPathW(CSIDL_DESKTOP) failed! Last error: %u\n", GetLastError());
942 /* Check if we are on Win9x */
943 SetLastError(0xdeadbeef);
944 lstrcmpiW(wszDesktop
, wszDesktop
);
945 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED
)
947 win_skip("Most W-calls are not implemented\n");
951 result
= pSHGetPathFromIDListW(pidlEmpty
, wszPath
);
952 ok(result
, "SHGetPathFromIDListW failed! Last error: %u\n", GetLastError());
954 ok(!lstrcmpiW(wszDesktop
, wszPath
), "SHGetPathFromIDListW didn't return desktop path for empty pidl!\n");
956 /* MyComputer does not map to a filesystem path. SHGetPathFromIDListW should fail. */
957 hr
= SHGetDesktopFolder(&psfDesktop
);
958 ok (SUCCEEDED(hr
), "SHGetDesktopFolder failed! hr = %08x\n", hr
);
959 if (FAILED(hr
)) return;
961 hr
= IShellFolder_ParseDisplayName(psfDesktop
, NULL
, NULL
, wszMyComputer
, NULL
, &pidlMyComputer
, NULL
);
962 ok (SUCCEEDED(hr
), "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08x\n", hr
);
964 IShellFolder_Release(psfDesktop
);
968 SetLastError(0xdeadbeef);
971 result
= pSHGetPathFromIDListW(pidlMyComputer
, wszPath
);
972 ok (!result
, "SHGetPathFromIDListW succeeded where it shouldn't!\n");
973 ok (GetLastError()==0xdeadbeef ||
974 GetLastError()==ERROR_SUCCESS
, /* Vista and higher */
975 "Unexpected last error from SHGetPathFromIDListW: %u\n", GetLastError());
976 ok (!wszPath
[0], "Expected empty path\n");
978 IShellFolder_Release(psfDesktop
);
982 IMalloc_Free(ppM
, pidlMyComputer
);
984 result
= pSHGetSpecialFolderPathW(NULL
, wszFileName
, CSIDL_DESKTOPDIRECTORY
, FALSE
);
985 ok(result
, "SHGetSpecialFolderPathW failed! Last error: %u\n", GetLastError());
987 IShellFolder_Release(psfDesktop
);
990 myPathAddBackslashW(wszFileName
);
991 lstrcatW(wszFileName
, wszTestFile
);
992 hTestFile
= CreateFileW(wszFileName
, GENERIC_WRITE
, 0, NULL
, CREATE_NEW
, 0, NULL
);
993 ok(hTestFile
!= INVALID_HANDLE_VALUE
, "CreateFileW failed! Last error: %u\n", GetLastError());
994 if (hTestFile
== INVALID_HANDLE_VALUE
) {
995 IShellFolder_Release(psfDesktop
);
998 CloseHandle(hTestFile
);
1000 hr
= IShellFolder_ParseDisplayName(psfDesktop
, NULL
, NULL
, wszTestFile
, NULL
, &pidlTestFile
, NULL
);
1001 ok (SUCCEEDED(hr
), "Desktop's ParseDisplayName failed to parse filename hr = %08x\n", hr
);
1003 IShellFolder_Release(psfDesktop
);
1004 DeleteFileW(wszFileName
);
1005 IMalloc_Free(ppM
, pidlTestFile
);
1009 /* This test is to show that the Desktop shellfolder prepends the CSIDL_DESKTOPDIRECTORY
1010 * path for files placed on the desktop, if called with SHGDN_FORPARSING. */
1011 hr
= IShellFolder_GetDisplayNameOf(psfDesktop
, pidlTestFile
, SHGDN_FORPARSING
, &strret
);
1012 ok (SUCCEEDED(hr
), "Desktop's GetDisplayNamfOf failed! hr = %08x\n", hr
);
1013 IShellFolder_Release(psfDesktop
);
1014 DeleteFileW(wszFileName
);
1016 IMalloc_Free(ppM
, pidlTestFile
);
1021 pStrRetToBufW(&strret
, pidlTestFile
, wszPath
, MAX_PATH
);
1022 ok(0 == lstrcmpW(wszFileName
, wszPath
),
1023 "Desktop->GetDisplayNameOf(pidlTestFile, SHGDN_FORPARSING) "
1024 "returned incorrect path for file placed on desktop\n");
1027 result
= pSHGetPathFromIDListW(pidlTestFile
, wszPath
);
1028 ok(result
, "SHGetPathFromIDListW failed! Last error: %u\n", GetLastError());
1029 IMalloc_Free(ppM
, pidlTestFile
);
1030 if (!result
) return;
1031 ok(0 == lstrcmpW(wszFileName
, wszPath
), "SHGetPathFromIDListW returned incorrect path for file placed on desktop\n");
1034 /* Test if we can get the path from the start menu "program files" PIDL. */
1035 hShell32
= GetModuleHandleA("shell32");
1036 pSHGetSpecialFolderLocation
= (void *)GetProcAddress(hShell32
, "SHGetSpecialFolderLocation");
1038 hr
= pSHGetSpecialFolderLocation(NULL
, CSIDL_PROGRAM_FILES
, &pidlPrograms
);
1039 ok(SUCCEEDED(hr
), "SHGetFolderLocation failed: 0x%08x\n", hr
);
1041 SetLastError(0xdeadbeef);
1042 result
= pSHGetPathFromIDListW(pidlPrograms
, wszPath
);
1043 IMalloc_Free(ppM
, pidlPrograms
);
1044 ok(result
, "SHGetPathFromIDListW failed\n");
1047 static void test_EnumObjects_and_CompareIDs(void)
1049 ITEMIDLIST
*newPIDL
;
1050 IShellFolder
*IDesktopFolder
, *testIShellFolder
;
1051 char cCurrDirA
[MAX_PATH
] = {0};
1052 static const CHAR cTestDirA
[] = "\\testdir";
1053 WCHAR cTestDirW
[MAX_PATH
];
1057 GetCurrentDirectoryA(MAX_PATH
, cCurrDirA
);
1058 len
= lstrlenA(cCurrDirA
);
1061 win_skip("GetCurrentDirectoryA returned empty string. Skipping test_EnumObjects_and_CompareIDs\n");
1064 if(cCurrDirA
[len
-1] == '\\')
1065 cCurrDirA
[len
-1] = 0;
1067 lstrcatA(cCurrDirA
, cTestDirA
);
1068 MultiByteToWideChar(CP_ACP
, 0, cCurrDirA
, -1, cTestDirW
, MAX_PATH
);
1070 hr
= SHGetDesktopFolder(&IDesktopFolder
);
1071 ok(hr
== S_OK
, "SHGetDesktopfolder failed %08x\n", hr
);
1073 CreateFilesFolders();
1075 hr
= IShellFolder_ParseDisplayName(IDesktopFolder
, NULL
, NULL
, cTestDirW
, NULL
, &newPIDL
, 0);
1076 ok(hr
== S_OK
, "ParseDisplayName failed %08x\n", hr
);
1078 hr
= IShellFolder_BindToObject(IDesktopFolder
, newPIDL
, NULL
, (REFIID
)&IID_IShellFolder
, (LPVOID
*)&testIShellFolder
);
1079 ok(hr
== S_OK
, "BindToObject failed %08x\n", hr
);
1081 test_EnumObjects(testIShellFolder
);
1083 IShellFolder_Release(testIShellFolder
);
1087 IMalloc_Free(ppM
, newPIDL
);
1089 IShellFolder_Release(IDesktopFolder
);
1092 /* A simple implementation of an IPropertyBag, which returns fixed values for
1093 * 'Target' and 'Attributes' properties.
1095 static HRESULT WINAPI
InitPropertyBag_IPropertyBag_QueryInterface(IPropertyBag
*iface
, REFIID riid
,
1099 return E_INVALIDARG
;
1101 if (IsEqualIID(&IID_IUnknown
, riid
) || IsEqualIID(&IID_IPropertyBag
, riid
)) {
1104 ok (FALSE
, "InitPropertyBag asked for unknown interface!\n");
1105 return E_NOINTERFACE
;
1108 IPropertyBag_AddRef(iface
);
1112 static ULONG WINAPI
InitPropertyBag_IPropertyBag_AddRef(IPropertyBag
*iface
) {
1116 static ULONG WINAPI
InitPropertyBag_IPropertyBag_Release(IPropertyBag
*iface
) {
1120 static HRESULT WINAPI
InitPropertyBag_IPropertyBag_Read(IPropertyBag
*iface
, LPCOLESTR pszPropName
,
1121 VARIANT
*pVar
, IErrorLog
*pErrorLog
)
1123 static const WCHAR wszTargetSpecialFolder
[] = {
1124 'T','a','r','g','e','t','S','p','e','c','i','a','l','F','o','l','d','e','r',0 };
1125 static const WCHAR wszTarget
[] = {
1126 'T','a','r','g','e','t',0 };
1127 static const WCHAR wszAttributes
[] = {
1128 'A','t','t','r','i','b','u','t','e','s',0 };
1129 static const WCHAR wszResolveLinkFlags
[] = {
1130 'R','e','s','o','l','v','e','L','i','n','k','F','l','a','g','s',0 };
1131 static const WCHAR wszTargetKnownFolder
[] = {
1132 'T','a','r','g','e','t','K','n','o','w','n','F','o','l','d','e','r',0 };
1133 static const WCHAR wszCLSID
[] = {
1134 'C','L','S','I','D',0 };
1136 if (!lstrcmpW(pszPropName
, wszTargetSpecialFolder
)) {
1137 ok(V_VT(pVar
) == VT_I4
||
1138 broken(V_VT(pVar
) == VT_BSTR
), /* Win2k */
1139 "Wrong variant type for 'TargetSpecialFolder' property!\n");
1140 return E_INVALIDARG
;
1143 if (!lstrcmpW(pszPropName
, wszResolveLinkFlags
))
1145 ok(V_VT(pVar
) == VT_UI4
, "Wrong variant type for 'ResolveLinkFlags' property!\n");
1146 return E_INVALIDARG
;
1149 if (!lstrcmpW(pszPropName
, wszTarget
)) {
1150 WCHAR wszPath
[MAX_PATH
];
1153 ok(V_VT(pVar
) == VT_BSTR
||
1154 broken(V_VT(pVar
) == VT_EMPTY
), /* Win2k */
1155 "Wrong variant type for 'Target' property!\n");
1156 if (V_VT(pVar
) != VT_BSTR
) return E_INVALIDARG
;
1158 result
= pSHGetSpecialFolderPathW(NULL
, wszPath
, CSIDL_DESKTOPDIRECTORY
, FALSE
);
1159 ok(result
, "SHGetSpecialFolderPathW(DESKTOPDIRECTORY) failed! %u\n", GetLastError());
1160 if (!result
) return E_INVALIDARG
;
1162 V_BSTR(pVar
) = SysAllocString(wszPath
);
1166 if (!lstrcmpW(pszPropName
, wszAttributes
)) {
1167 ok(V_VT(pVar
) == VT_UI4
, "Wrong variant type for 'Attributes' property!\n");
1168 if (V_VT(pVar
) != VT_UI4
) return E_INVALIDARG
;
1169 V_UI4(pVar
) = SFGAO_FOLDER
|SFGAO_HASSUBFOLDER
|SFGAO_FILESYSANCESTOR
|
1170 SFGAO_CANRENAME
|SFGAO_FILESYSTEM
;
1174 if (!lstrcmpW(pszPropName
, wszTargetKnownFolder
)) {
1175 ok(V_VT(pVar
) == VT_BSTR
, "Wrong variant type for 'TargetKnownFolder' property!\n");
1177 return E_INVALIDARG
;
1180 if (!lstrcmpW(pszPropName
, wszCLSID
)) {
1181 ok(V_VT(pVar
) == VT_EMPTY
, "Wrong variant type for 'CLSID' property!\n");
1183 return E_INVALIDARG
;
1186 ok(FALSE
, "PropertyBag was asked for unknown property %s (vt=%d)!\n", wine_dbgstr_w(pszPropName
), V_VT(pVar
));
1187 return E_INVALIDARG
;
1190 static HRESULT WINAPI
InitPropertyBag_IPropertyBag_Write(IPropertyBag
*iface
, LPCOLESTR pszPropName
,
1193 ok(FALSE
, "Unexpected call to IPropertyBag_Write\n");
1197 static const IPropertyBagVtbl InitPropertyBag_IPropertyBagVtbl
= {
1198 InitPropertyBag_IPropertyBag_QueryInterface
,
1199 InitPropertyBag_IPropertyBag_AddRef
,
1200 InitPropertyBag_IPropertyBag_Release
,
1201 InitPropertyBag_IPropertyBag_Read
,
1202 InitPropertyBag_IPropertyBag_Write
1205 static struct IPropertyBag InitPropertyBag
= {
1206 &InitPropertyBag_IPropertyBagVtbl
1209 static void test_FolderShortcut(void) {
1210 IPersistPropertyBag
*pPersistPropertyBag
;
1211 IShellFolder
*pShellFolder
, *pDesktopFolder
;
1212 IPersistFolder3
*pPersistFolder3
;
1215 WCHAR wszDesktopPath
[MAX_PATH
], wszBuffer
[MAX_PATH
];
1218 LPITEMIDLIST pidlCurrentFolder
, pidlWineTestFolder
, pidlSubFolder
;
1220 WCHAR wszWineTestFolder
[] = {
1221 ':',':','{','9','B','3','5','2','E','B','F','-','2','7','6','5','-','4','5','C','1','-',
1222 'B','4','C','6','-','8','5','C','C','7','F','7','A','B','C','6','4','}',0 };
1223 WCHAR wszShellExtKey
[] = { 'S','o','f','t','w','a','r','e','\\',
1224 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
1225 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
1226 'E','x','p','l','o','r','e','r','\\','D','e','s','k','t','o','p','\\',
1227 'N','a','m','e','S','p','a','c','e','\\',
1228 '{','9','b','3','5','2','e','b','f','-','2','7','6','5','-','4','5','c','1','-',
1229 'b','4','c','6','-','8','5','c','c','7','f','7','a','b','c','6','4','}',0 };
1231 WCHAR wszSomeSubFolder
[] = { 'S','u','b','F','o','l','d','e','r', 0};
1232 static const GUID CLSID_UnixDosFolder
=
1233 {0x9d20aae8, 0x0625, 0x44b0, {0x9c, 0xa7, 0x71, 0x88, 0x9c, 0x22, 0x54, 0xd9}};
1235 if (!pSHGetSpecialFolderPathW
|| !pStrRetToBufW
) {
1236 win_skip("SHGetSpecialFolderPathW and/or StrRetToBufW are not available\n");
1240 if (!pSHGetFolderPathAndSubDirA
)
1242 win_skip("FolderShortcut test doesn't work on Win2k\n");
1246 /* These tests basically show, that CLSID_FolderShortcuts are initialized
1247 * via their IPersistPropertyBag interface. And that the target folder
1248 * is taken from the IPropertyBag's 'Target' property.
1250 hr
= CoCreateInstance(&CLSID_FolderShortcut
, NULL
, CLSCTX_INPROC_SERVER
,
1251 &IID_IPersistPropertyBag
, (LPVOID
*)&pPersistPropertyBag
);
1252 if (hr
== REGDB_E_CLASSNOTREG
) {
1253 win_skip("CLSID_FolderShortcut is not implemented\n");
1256 ok (SUCCEEDED(hr
), "CoCreateInstance failed! hr = 0x%08x\n", hr
);
1257 if (FAILED(hr
)) return;
1259 hr
= IPersistPropertyBag_Load(pPersistPropertyBag
, &InitPropertyBag
, NULL
);
1260 ok(SUCCEEDED(hr
), "IPersistPropertyBag_Load failed! hr = %08x\n", hr
);
1262 IPersistPropertyBag_Release(pPersistPropertyBag
);
1266 hr
= IPersistPropertyBag_QueryInterface(pPersistPropertyBag
, &IID_IShellFolder
,
1267 (LPVOID
*)&pShellFolder
);
1268 IPersistPropertyBag_Release(pPersistPropertyBag
);
1269 ok(SUCCEEDED(hr
), "IPersistPropertyBag_QueryInterface(IShellFolder) failed! hr = %08x\n", hr
);
1270 if (FAILED(hr
)) return;
1272 hr
= IShellFolder_GetDisplayNameOf(pShellFolder
, NULL
, SHGDN_FORPARSING
, &strret
);
1273 ok(SUCCEEDED(hr
), "IShellFolder_GetDisplayNameOf(NULL) failed! hr = %08x\n", hr
);
1275 IShellFolder_Release(pShellFolder
);
1279 result
= pSHGetSpecialFolderPathW(NULL
, wszDesktopPath
, CSIDL_DESKTOPDIRECTORY
, FALSE
);
1280 ok(result
, "SHGetSpecialFolderPathW(CSIDL_DESKTOPDIRECTORY) failed! %u\n", GetLastError());
1281 if (!result
) return;
1283 pStrRetToBufW(&strret
, NULL
, wszBuffer
, MAX_PATH
);
1284 ok(!lstrcmpiW(wszDesktopPath
, wszBuffer
), "FolderShortcut returned incorrect folder!\n");
1286 hr
= IShellFolder_QueryInterface(pShellFolder
, &IID_IPersistFolder3
, (LPVOID
*)&pPersistFolder3
);
1287 IShellFolder_Release(pShellFolder
);
1288 ok(SUCCEEDED(hr
), "IShellFolder_QueryInterface(IID_IPersistFolder3 failed! hr = 0x%08x\n", hr
);
1289 if (FAILED(hr
)) return;
1291 hr
= IPersistFolder3_GetClassID(pPersistFolder3
, &clsid
);
1292 ok(SUCCEEDED(hr
), "IPersistFolder3_GetClassID failed! hr=0x%08x\n", hr
);
1293 ok(IsEqualCLSID(&clsid
, &CLSID_FolderShortcut
), "Unexpected CLSID!\n");
1295 hr
= IPersistFolder3_GetCurFolder(pPersistFolder3
, &pidlCurrentFolder
);
1296 ok(SUCCEEDED(hr
), "IPersistFolder3_GetCurFolder failed! hr=0x%08x\n", hr
);
1297 ok(!pidlCurrentFolder
, "IPersistFolder3_GetCurFolder should return a NULL pidl!\n");
1299 /* For FolderShortcut objects, the Initialize method initialized the folder's position in the
1300 * shell namespace. The target folder, read from the property bag above, remains untouched.
1301 * The following tests show this: The itemidlist for some imaginary shellfolder object
1302 * is created and the FolderShortcut is initialized with it. GetCurFolder now returns this
1303 * itemidlist, but GetDisplayNameOf still returns the path from above.
1305 hr
= SHGetDesktopFolder(&pDesktopFolder
);
1306 ok (SUCCEEDED(hr
), "SHGetDesktopFolder failed! hr = %08x\n", hr
);
1307 if (FAILED(hr
)) return;
1309 /* Temporarily register WineTestFolder as a shell namespace extension at the Desktop.
1310 * Otherwise ParseDisplayName fails on WinXP with E_INVALIDARG */
1311 RegCreateKeyW(HKEY_CURRENT_USER
, wszShellExtKey
, &hShellExtKey
);
1312 RegCloseKey(hShellExtKey
);
1313 hr
= IShellFolder_ParseDisplayName(pDesktopFolder
, NULL
, NULL
, wszWineTestFolder
, NULL
,
1314 &pidlWineTestFolder
, NULL
);
1315 RegDeleteKeyW(HKEY_CURRENT_USER
, wszShellExtKey
);
1316 IShellFolder_Release(pDesktopFolder
);
1317 ok (SUCCEEDED(hr
), "IShellFolder::ParseDisplayName failed! hr = %08x\n", hr
);
1318 if (FAILED(hr
)) return;
1320 hr
= IPersistFolder3_Initialize(pPersistFolder3
, pidlWineTestFolder
);
1321 ok (SUCCEEDED(hr
), "IPersistFolder3::Initialize failed! hr = %08x\n", hr
);
1323 IPersistFolder3_Release(pPersistFolder3
);
1324 pILFree(pidlWineTestFolder
);
1328 hr
= IPersistFolder3_GetCurFolder(pPersistFolder3
, &pidlCurrentFolder
);
1329 ok(SUCCEEDED(hr
), "IPersistFolder3_GetCurFolder failed! hr=0x%08x\n", hr
);
1330 ok(pILIsEqual(pidlCurrentFolder
, pidlWineTestFolder
),
1331 "IPersistFolder3_GetCurFolder should return pidlWineTestFolder!\n");
1332 pILFree(pidlCurrentFolder
);
1333 pILFree(pidlWineTestFolder
);
1335 hr
= IPersistFolder3_QueryInterface(pPersistFolder3
, &IID_IShellFolder
, (LPVOID
*)&pShellFolder
);
1336 IPersistFolder3_Release(pPersistFolder3
);
1337 ok(SUCCEEDED(hr
), "IPersistFolder3_QueryInterface(IShellFolder) failed! hr = %08x\n", hr
);
1338 if (FAILED(hr
)) return;
1340 hr
= IShellFolder_GetDisplayNameOf(pShellFolder
, NULL
, SHGDN_FORPARSING
, &strret
);
1341 ok(SUCCEEDED(hr
), "IShellFolder_GetDisplayNameOf(NULL) failed! hr = %08x\n", hr
);
1343 IShellFolder_Release(pShellFolder
);
1347 pStrRetToBufW(&strret
, NULL
, wszBuffer
, MAX_PATH
);
1348 ok(!lstrcmpiW(wszDesktopPath
, wszBuffer
), "FolderShortcut returned incorrect folder!\n");
1350 /* Next few lines are meant to show that children of FolderShortcuts are not FolderShortcuts,
1351 * but ShellFSFolders. */
1352 myPathAddBackslashW(wszDesktopPath
);
1353 lstrcatW(wszDesktopPath
, wszSomeSubFolder
);
1354 if (!CreateDirectoryW(wszDesktopPath
, NULL
)) {
1355 IShellFolder_Release(pShellFolder
);
1359 hr
= IShellFolder_ParseDisplayName(pShellFolder
, NULL
, NULL
, wszSomeSubFolder
, NULL
,
1360 &pidlSubFolder
, NULL
);
1361 RemoveDirectoryW(wszDesktopPath
);
1362 ok (SUCCEEDED(hr
), "IShellFolder::ParseDisplayName failed! hr = %08x\n", hr
);
1364 IShellFolder_Release(pShellFolder
);
1368 hr
= IShellFolder_BindToObject(pShellFolder
, pidlSubFolder
, NULL
, &IID_IPersistFolder3
,
1369 (LPVOID
*)&pPersistFolder3
);
1370 IShellFolder_Release(pShellFolder
);
1371 pILFree(pidlSubFolder
);
1372 ok (SUCCEEDED(hr
), "IShellFolder::BindToObject failed! hr = %08x\n", hr
);
1376 /* On windows, we expect CLSID_ShellFSFolder. On wine we relax this constraint
1377 * a little bit and also allow CLSID_UnixDosFolder. */
1378 hr
= IPersistFolder3_GetClassID(pPersistFolder3
, &clsid
);
1379 ok(SUCCEEDED(hr
), "IPersistFolder3_GetClassID failed! hr=0x%08x\n", hr
);
1380 ok(IsEqualCLSID(&clsid
, &CLSID_ShellFSFolder
) || IsEqualCLSID(&clsid
, &CLSID_UnixDosFolder
),
1381 "IPersistFolder3::GetClassID returned unexpected CLSID!\n");
1383 IPersistFolder3_Release(pPersistFolder3
);
1386 #include "pshpack1.h"
1387 struct FileStructA
{
1391 WORD uFileDate
; /* In our current implementation this is */
1392 WORD uFileTime
; /* FileTimeToDosDate(WIN32_FIND_DATA->ftLastWriteTime) */
1397 struct FileStructW
{
1398 WORD cbLen
; /* Length of this element. */
1399 BYTE abFooBar1
[6]; /* Beyond any recognition. */
1400 WORD uDate
; /* FileTimeToDosDate(WIN32_FIND_DATA->ftCreationTime)? */
1401 WORD uTime
; /* (this is currently speculation) */
1402 WORD uDate2
; /* FileTimeToDosDate(WIN32_FIND_DATA->ftLastAccessTime)? */
1403 WORD uTime2
; /* (this is currently speculation) */
1404 BYTE abFooBar2
[4]; /* Beyond any recognition. */
1405 WCHAR wszName
[1]; /* The long filename in unicode. */
1406 /* Just for documentation: Right after the unicode string: */
1407 WORD cbOffset
; /* FileStructW's offset from the beginning of the SHITMEID.
1408 * SHITEMID->cb == uOffset + cbLen */
1410 #include "poppack.h"
1412 static void test_ITEMIDLIST_format(void) {
1413 WCHAR wszPersonal
[MAX_PATH
];
1414 LPSHELLFOLDER psfDesktop
, psfPersonal
;
1415 LPITEMIDLIST pidlPersonal
, pidlFile
;
1419 WCHAR wszFile
[3][17] = { { 'e','v','e','n','_',0 }, { 'o','d','d','_',0 },
1420 { 'l','o','n','g','e','r','_','t','h','a','n','.','8','_','3',0 } };
1423 if (!pSHGetSpecialFolderPathW
) return;
1425 bResult
= pSHGetSpecialFolderPathW(NULL
, wszPersonal
, CSIDL_PERSONAL
, FALSE
);
1426 ok(bResult
, "SHGetSpecialFolderPathW failed! Last error: %u\n", GetLastError());
1427 if (!bResult
) return;
1429 SetLastError(0xdeadbeef);
1430 bResult
= SetCurrentDirectoryW(wszPersonal
);
1431 if (!bResult
&& GetLastError() == ERROR_CALL_NOT_IMPLEMENTED
) {
1432 win_skip("Most W-calls are not implemented\n");
1435 ok(bResult
, "SetCurrentDirectory failed! Last error: %u\n", GetLastError());
1436 if (!bResult
) return;
1438 hr
= SHGetDesktopFolder(&psfDesktop
);
1439 ok(SUCCEEDED(hr
), "SHGetDesktopFolder failed! hr: %08x\n", hr
);
1440 if (FAILED(hr
)) return;
1442 hr
= IShellFolder_ParseDisplayName(psfDesktop
, NULL
, NULL
, wszPersonal
, NULL
, &pidlPersonal
, NULL
);
1443 ok(SUCCEEDED(hr
), "psfDesktop->ParseDisplayName failed! hr = %08x\n", hr
);
1445 IShellFolder_Release(psfDesktop
);
1449 hr
= IShellFolder_BindToObject(psfDesktop
, pidlPersonal
, NULL
, &IID_IShellFolder
,
1450 (LPVOID
*)&psfPersonal
);
1451 IShellFolder_Release(psfDesktop
);
1452 pILFree(pidlPersonal
);
1453 ok(SUCCEEDED(hr
), "psfDesktop->BindToObject failed! hr = %08x\n", hr
);
1454 if (FAILED(hr
)) return;
1456 for (i
=0; i
<3; i
++) {
1457 CHAR szFile
[MAX_PATH
];
1458 struct FileStructA
*pFileStructA
;
1461 WideCharToMultiByte(CP_ACP
, 0, wszFile
[i
], -1, szFile
, MAX_PATH
, NULL
, NULL
);
1463 hFile
= CreateFileW(wszFile
[i
], GENERIC_WRITE
, 0, NULL
, CREATE_NEW
, FILE_FLAG_WRITE_THROUGH
, NULL
);
1464 ok(hFile
!= INVALID_HANDLE_VALUE
, "CreateFile failed! (%u)\n", GetLastError());
1465 if (hFile
== INVALID_HANDLE_VALUE
) {
1466 IShellFolder_Release(psfPersonal
);
1471 hr
= IShellFolder_ParseDisplayName(psfPersonal
, NULL
, NULL
, wszFile
[i
], NULL
, &pidlFile
, NULL
);
1472 DeleteFileW(wszFile
[i
]);
1473 ok(SUCCEEDED(hr
), "psfPersonal->ParseDisplayName failed! hr: %08x\n", hr
);
1475 IShellFolder_Release(psfPersonal
);
1479 pFileStructA
= (struct FileStructA
*)pidlFile
->mkid
.abID
;
1480 ok(pFileStructA
->type
== 0x32, "PIDLTYPE should be 0x32!\n");
1481 ok(pFileStructA
->dummy
== 0x00, "Dummy Byte should be 0x00!\n");
1482 ok(pFileStructA
->dwFileSize
== 0, "Filesize should be zero!\n");
1484 if (i
< 2) /* First two file names are already in valid 8.3 format */
1485 ok(!strcmp(szFile
, (CHAR
*)&pidlFile
->mkid
.abID
[12]), "Wrong file name!\n");
1487 /* WinXP stores a derived 8.3 dos name (LONGER~1.8_3) here. We probably
1488 * can't implement this correctly, since unix filesystems don't support
1489 * this nasty short/long filename stuff. So we'll probably stay with our
1490 * current habbit of storing the long filename here, which seems to work
1493 ok(pidlFile
->mkid
.abID
[18] == '~' ||
1494 broken(pidlFile
->mkid
.abID
[34] == '~'), /* Win2k */
1495 "Should be derived 8.3 name!\n");
1497 if (i
== 0) /* First file name has an even number of chars. No need for alignment. */
1498 ok(pidlFile
->mkid
.abID
[12 + strlen(szFile
) + 1] != '\0' ||
1499 broken(pidlFile
->mkid
.cb
== 2 + 12 + strlen(szFile
) + 1 + 1), /* Win2k */
1500 "Alignment byte, where there shouldn't be!\n");
1502 if (i
== 1) /* Second file name has an uneven number of chars => alignment byte */
1503 ok(pidlFile
->mkid
.abID
[12 + strlen(szFile
) + 1] == '\0',
1504 "There should be an alignment byte, but isn't!\n");
1506 /* The offset of the FileStructW member is stored as a WORD at the end of the pidl. */
1507 cbOffset
= *(WORD
*)(((LPBYTE
)pidlFile
)+pidlFile
->mkid
.cb
-sizeof(WORD
));
1508 ok ((cbOffset
>= sizeof(struct FileStructA
) &&
1509 cbOffset
<= pidlFile
->mkid
.cb
- sizeof(struct FileStructW
)) ||
1510 broken(pidlFile
->mkid
.cb
== 2 + 12 + strlen(szFile
) + 1 + 1) || /* Win2k on short names */
1511 broken(pidlFile
->mkid
.cb
== 2 + 12 + strlen(szFile
) + 1 + 12 + 1), /* Win2k on long names */
1512 "Wrong offset value (%d) stored at the end of the PIDL\n", cbOffset
);
1514 if (cbOffset
>= sizeof(struct FileStructA
) &&
1515 cbOffset
<= pidlFile
->mkid
.cb
- sizeof(struct FileStructW
))
1517 struct FileStructW
*pFileStructW
= (struct FileStructW
*)(((LPBYTE
)pidlFile
)+cbOffset
);
1519 ok(pidlFile
->mkid
.cb
== cbOffset
+ pFileStructW
->cbLen
,
1520 "FileStructW's offset and length should add up to the PIDL's length!\n");
1522 if (pidlFile
->mkid
.cb
== cbOffset
+ pFileStructW
->cbLen
) {
1523 /* Since we just created the file, time of creation,
1524 * time of last access and time of last write access just be the same.
1525 * These tests seem to fail sometimes (on WinXP), if the test is run again shortly
1526 * after the first run. I do remember something with NTFS keeping the creation time
1527 * if a file is deleted and then created again within a couple of seconds or so.
1528 * Might be the reason. */
1529 ok (pFileStructA
->uFileDate
== pFileStructW
->uDate
&&
1530 pFileStructA
->uFileTime
== pFileStructW
->uTime
,
1531 "Last write time should match creation time!\n");
1533 /* On FAT filesystems the last access time is midnight
1534 local time, so the values of uDate2 and uTime2 will
1535 depend on the local timezone. If the times are exactly
1536 equal then the dates should be identical for both FAT
1537 and NTFS as no timezone is more than 1 day away from UTC.
1539 if (pFileStructA
->uFileTime
== pFileStructW
->uTime2
)
1541 ok (pFileStructA
->uFileDate
== pFileStructW
->uDate2
,
1542 "Last write date and time should match last access date and time!\n");
1546 /* Filesystem may be FAT. Check date within 1 day
1547 and seconds are zero. */
1548 trace ("Filesystem may be FAT. Performing less strict atime test.\n");
1549 ok ((pFileStructW
->uTime2
& 0x1F) == 0,
1550 "Last access time on FAT filesystems should have zero seconds.\n");
1551 /* TODO: Perform check for date being within one day.*/
1554 ok (!lstrcmpW(wszFile
[i
], pFileStructW
->wszName
) ||
1555 !lstrcmpW(wszFile
[i
], (WCHAR
*)(pFileStructW
->abFooBar2
+ 22)) || /* Vista */
1556 !lstrcmpW(wszFile
[i
], (WCHAR
*)(pFileStructW
->abFooBar2
+ 26)), /* Win7 */
1557 "The filename should be stored in unicode at this position!\n");
1564 IShellFolder_Release(psfPersonal
);
1567 static void testSHGetFolderPathAndSubDirA(void)
1573 static char wine
[] = "wine";
1574 static char winetemp
[] = "wine\\temp";
1575 static char appdata
[MAX_PATH
];
1576 static char testpath
[MAX_PATH
];
1577 static char toolongpath
[MAX_PATH
+1];
1579 if(!pSHGetFolderPathA
) {
1580 win_skip("SHGetFolderPathA not present!\n");
1583 if(FAILED(pSHGetFolderPathA(NULL
, CSIDL_LOCAL_APPDATA
, NULL
, SHGFP_TYPE_CURRENT
, appdata
)))
1585 win_skip("SHGetFolderPathA failed for CSIDL_LOCAL_APPDATA!\n");
1589 sprintf(testpath
, "%s\\%s", appdata
, winetemp
);
1590 delret
= RemoveDirectoryA(testpath
);
1591 if(!delret
&& (ERROR_PATH_NOT_FOUND
!= GetLastError()) ) {
1592 win_skip("RemoveDirectoryA(%s) failed with error %u\n", testpath
, GetLastError());
1596 sprintf(testpath
, "%s\\%s", appdata
, wine
);
1597 delret
= RemoveDirectoryA(testpath
);
1598 if(!delret
&& (ERROR_PATH_NOT_FOUND
!= GetLastError()) && (ERROR_FILE_NOT_FOUND
!= GetLastError())) {
1599 win_skip("RemoveDirectoryA(%s) failed with error %u\n", testpath
, GetLastError());
1603 /* test invalid second parameter */
1604 ret
= pSHGetFolderPathAndSubDirA(NULL
, CSIDL_FLAG_DONT_VERIFY
| 0xff, NULL
, SHGFP_TYPE_CURRENT
, wine
, testpath
);
1605 ok(E_INVALIDARG
== ret
, "expected E_INVALIDARG, got %x\n", ret
);
1607 /* test fourth parameter */
1608 ret
= pSHGetFolderPathAndSubDirA(NULL
, CSIDL_FLAG_DONT_VERIFY
| CSIDL_LOCAL_APPDATA
, NULL
, 2, winetemp
, testpath
);
1610 case S_OK
: /* winvista */
1611 ok(!strncmp(appdata
, testpath
, strlen(appdata
)),
1612 "expected %s to start with %s\n", testpath
, appdata
);
1613 ok(!lstrcmpA(&testpath
[1 + strlen(appdata
)], winetemp
),
1614 "expected %s to end with %s\n", testpath
, winetemp
);
1616 case E_INVALIDARG
: /* winxp, win2k3 */
1619 ok(0, "expected S_OK or E_INVALIDARG, got %x\n", ret
);
1622 /* test fifth parameter */
1624 ret
= pSHGetFolderPathAndSubDirA(NULL
, CSIDL_FLAG_DONT_VERIFY
| CSIDL_LOCAL_APPDATA
, NULL
, SHGFP_TYPE_CURRENT
, NULL
, testpath
);
1625 ok(S_OK
== ret
, "expected S_OK, got %x\n", ret
);
1626 ok(!lstrcmpA(appdata
, testpath
), "expected %s, got %s\n", appdata
, testpath
);
1629 ret
= pSHGetFolderPathAndSubDirA(NULL
, CSIDL_FLAG_DONT_VERIFY
| CSIDL_LOCAL_APPDATA
, NULL
, SHGFP_TYPE_CURRENT
, "", testpath
);
1630 ok(S_OK
== ret
, "expected S_OK, got %x\n", ret
);
1631 ok(!lstrcmpA(appdata
, testpath
), "expected %s, got %s\n", appdata
, testpath
);
1634 ret
= pSHGetFolderPathAndSubDirA(NULL
, CSIDL_FLAG_DONT_VERIFY
| CSIDL_LOCAL_APPDATA
, NULL
, SHGFP_TYPE_CURRENT
, "\\", testpath
);
1635 ok(S_OK
== ret
, "expected S_OK, got %x\n", ret
);
1636 ok(!lstrcmpA(appdata
, testpath
), "expected %s, got %s\n", appdata
, testpath
);
1638 for(i
=0; i
< MAX_PATH
; i
++)
1639 toolongpath
[i
] = '0' + i
% 10;
1640 toolongpath
[MAX_PATH
] = '\0';
1641 ret
= pSHGetFolderPathAndSubDirA(NULL
, CSIDL_FLAG_DONT_VERIFY
| CSIDL_LOCAL_APPDATA
, NULL
, SHGFP_TYPE_CURRENT
, toolongpath
, testpath
);
1642 ok(HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE
) == ret
,
1643 "expected %x, got %x\n", HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE
), ret
);
1646 ret
= pSHGetFolderPathAndSubDirA(NULL
, CSIDL_FLAG_DONT_VERIFY
| CSIDL_LOCAL_APPDATA
, NULL
, SHGFP_TYPE_CURRENT
, wine
, NULL
);
1647 ok((S_OK
== ret
) || (E_INVALIDARG
== ret
), "expected S_OK or E_INVALIDARG, got %x\n", ret
);
1649 /* test a not existing path */
1651 ret
= pSHGetFolderPathAndSubDirA(NULL
, CSIDL_LOCAL_APPDATA
, NULL
, SHGFP_TYPE_CURRENT
, winetemp
, testpath
);
1652 ok(HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND
) == ret
,
1653 "expected %x, got %x\n", HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND
), ret
);
1655 /* create a directory inside a not existing directory */
1657 ret
= pSHGetFolderPathAndSubDirA(NULL
, CSIDL_FLAG_CREATE
| CSIDL_LOCAL_APPDATA
, NULL
, SHGFP_TYPE_CURRENT
, winetemp
, testpath
);
1658 ok(S_OK
== ret
, "expected S_OK, got %x\n", ret
);
1659 ok(!strncmp(appdata
, testpath
, strlen(appdata
)),
1660 "expected %s to start with %s\n", testpath
, appdata
);
1661 ok(!lstrcmpA(&testpath
[1 + strlen(appdata
)], winetemp
),
1662 "expected %s to end with %s\n", testpath
, winetemp
);
1663 dwret
= GetFileAttributes(testpath
);
1664 ok(FILE_ATTRIBUTE_DIRECTORY
| dwret
, "expected %x to contain FILE_ATTRIBUTE_DIRECTORY\n", dwret
);
1667 sprintf(testpath
, "%s\\%s", appdata
, winetemp
);
1668 RemoveDirectoryA(testpath
);
1669 sprintf(testpath
, "%s\\%s", appdata
, wine
);
1670 RemoveDirectoryA(testpath
);
1673 static void test_LocalizedNames(void)
1675 static char cCurrDirA
[MAX_PATH
];
1676 WCHAR cCurrDirW
[MAX_PATH
], tempbufW
[25];
1677 IShellFolder
*IDesktopFolder
, *testIShellFolder
;
1678 ITEMIDLIST
*newPIDL
;
1681 static char resourcefile
[MAX_PATH
];
1686 static const char desktopini_contents1
[] =
1687 "[.ShellClassInfo]\r\n"
1688 "LocalizedResourceName=@";
1689 static const char desktopini_contents2
[] =
1691 static WCHAR foldernameW
[] = {'t','e','s','t','f','o','l','d','e','r',0};
1692 static const WCHAR folderdisplayW
[] = {'F','o','l','d','e','r',' ','N','a','m','e',' ','R','e','s','o','u','r','c','e',0};
1694 /* create folder with desktop.ini and localized name in GetModuleFileNameA(NULL) */
1695 CreateDirectoryA(".\\testfolder", NULL
);
1697 SetFileAttributesA(".\\testfolder", GetFileAttributesA(".\\testfolder")|FILE_ATTRIBUTE_SYSTEM
);
1699 GetModuleFileNameA(NULL
, resourcefile
, MAX_PATH
);
1701 file
= CreateFileA(".\\testfolder\\desktop.ini", GENERIC_WRITE
, 0, NULL
,
1702 CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
1703 ok(file
!= INVALID_HANDLE_VALUE
, "CreateFileA failed %i\n", GetLastError());
1704 ok(WriteFile(file
, desktopini_contents1
, strlen(desktopini_contents1
), &res
, NULL
) &&
1705 WriteFile(file
, resourcefile
, strlen(resourcefile
), &res
, NULL
) &&
1706 WriteFile(file
, desktopini_contents2
, strlen(desktopini_contents2
), &res
, NULL
),
1707 "WriteFile failed %i\n", GetLastError());
1710 /* get IShellFolder for parent */
1711 GetCurrentDirectoryA(MAX_PATH
, cCurrDirA
);
1712 len
= lstrlenA(cCurrDirA
);
1715 win_skip("GetCurrentDirectoryA returned empty string. Skipping test_LocalizedNames\n");
1718 if(cCurrDirA
[len
-1] == '\\')
1719 cCurrDirA
[len
-1] = 0;
1721 MultiByteToWideChar(CP_ACP
, 0, cCurrDirA
, -1, cCurrDirW
, MAX_PATH
);
1723 hr
= SHGetDesktopFolder(&IDesktopFolder
);
1724 ok(hr
== S_OK
, "SHGetDesktopfolder failed %08x\n", hr
);
1726 hr
= IShellFolder_ParseDisplayName(IDesktopFolder
, NULL
, NULL
, cCurrDirW
, NULL
, &newPIDL
, 0);
1727 ok(hr
== S_OK
, "ParseDisplayName failed %08x\n", hr
);
1729 hr
= IShellFolder_BindToObject(IDesktopFolder
, newPIDL
, NULL
, (REFIID
)&IID_IShellFolder
, (LPVOID
*)&testIShellFolder
);
1730 ok(hr
== S_OK
, "BindToObject failed %08x\n", hr
);
1732 IMalloc_Free(ppM
, newPIDL
);
1734 /* windows reads the display name from the resource */
1735 hr
= IShellFolder_ParseDisplayName(testIShellFolder
, NULL
, NULL
, foldernameW
, NULL
, &newPIDL
, 0);
1736 ok(hr
== S_OK
, "ParseDisplayName failed %08x\n", hr
);
1738 hr
= IShellFolder_GetDisplayNameOf(testIShellFolder
, newPIDL
, SHGDN_INFOLDER
, &strret
);
1739 ok(hr
== S_OK
, "GetDisplayNameOf failed %08x\n", hr
);
1741 if (SUCCEEDED(hr
) && pStrRetToBufW
)
1743 hr
= pStrRetToBufW(&strret
, newPIDL
, tempbufW
, sizeof(tempbufW
)/sizeof(WCHAR
));
1744 ok (SUCCEEDED(hr
), "StrRetToBufW failed! hr = %08x\n", hr
);
1746 ok (!lstrcmpiW(tempbufW
, folderdisplayW
) ||
1747 broken(!lstrcmpiW(tempbufW
, foldernameW
)), /* W2K */
1748 "GetDisplayNameOf returned %s\n", wine_dbgstr_w(tempbufW
));
1751 /* editing name is also read from the resource */
1752 hr
= IShellFolder_GetDisplayNameOf(testIShellFolder
, newPIDL
, SHGDN_INFOLDER
|SHGDN_FOREDITING
, &strret
);
1753 ok(hr
== S_OK
, "GetDisplayNameOf failed %08x\n", hr
);
1755 if (SUCCEEDED(hr
) && pStrRetToBufW
)
1757 hr
= pStrRetToBufW(&strret
, newPIDL
, tempbufW
, sizeof(tempbufW
)/sizeof(WCHAR
));
1758 ok (SUCCEEDED(hr
), "StrRetToBufW failed! hr = %08x\n", hr
);
1760 ok (!lstrcmpiW(tempbufW
, folderdisplayW
) ||
1761 broken(!lstrcmpiW(tempbufW
, foldernameW
)), /* W2K */
1762 "GetDisplayNameOf returned %s\n", wine_dbgstr_w(tempbufW
));
1765 /* parsing name is unchanged */
1766 hr
= IShellFolder_GetDisplayNameOf(testIShellFolder
, newPIDL
, SHGDN_INFOLDER
|SHGDN_FORPARSING
, &strret
);
1767 ok(hr
== S_OK
, "GetDisplayNameOf failed %08x\n", hr
);
1769 if (SUCCEEDED(hr
) && pStrRetToBufW
)
1771 hr
= pStrRetToBufW(&strret
, newPIDL
, tempbufW
, sizeof(tempbufW
)/sizeof(WCHAR
));
1772 ok (SUCCEEDED(hr
), "StrRetToBufW failed! hr = %08x\n", hr
);
1773 ok (!lstrcmpiW(tempbufW
, foldernameW
), "GetDisplayNameOf returned %s\n", wine_dbgstr_w(tempbufW
));
1776 IShellFolder_Release(IDesktopFolder
);
1777 IShellFolder_Release(testIShellFolder
);
1779 IMalloc_Free(ppM
, newPIDL
);
1782 DeleteFileA(".\\testfolder\\desktop.ini");
1783 SetFileAttributesA(".\\testfolder", GetFileAttributesA(".\\testfolder")&~FILE_ATTRIBUTE_SYSTEM
);
1784 RemoveDirectoryA(".\\testfolder");
1787 static void test_SHCreateShellItem(void)
1789 IShellItem
*shellitem
, *shellitem2
;
1790 IPersistIDList
*persistidl
;
1791 LPITEMIDLIST pidl_cwd
=NULL
, pidl_testfile
, pidl_abstestfile
, pidl_test
;
1793 char curdirA
[MAX_PATH
];
1794 WCHAR curdirW
[MAX_PATH
];
1795 IShellFolder
*desktopfolder
=NULL
, *currentfolder
=NULL
;
1796 static WCHAR testfileW
[] = {'t','e','s','t','f','i','l','e',0};
1798 GetCurrentDirectoryA(MAX_PATH
, curdirA
);
1800 if (!lstrlenA(curdirA
))
1802 win_skip("GetCurrentDirectoryA returned empty string, skipping test_SHCreateShellItem\n");
1806 MultiByteToWideChar(CP_ACP
, 0, curdirA
, -1, curdirW
, MAX_PATH
);
1808 ret
= SHGetDesktopFolder(&desktopfolder
);
1809 ok(SUCCEEDED(ret
), "SHGetShellFolder returned %x\n", ret
);
1811 ret
= IShellFolder_ParseDisplayName(desktopfolder
, NULL
, NULL
, curdirW
, NULL
, &pidl_cwd
, NULL
);
1812 ok(SUCCEEDED(ret
), "ParseDisplayName returned %x\n", ret
);
1814 ret
= IShellFolder_BindToObject(desktopfolder
, pidl_cwd
, NULL
, &IID_IShellFolder
, (void**)¤tfolder
);
1815 ok(SUCCEEDED(ret
), "BindToObject returned %x\n", ret
);
1817 CreateTestFile(".\\testfile");
1819 ret
= IShellFolder_ParseDisplayName(currentfolder
, NULL
, NULL
, testfileW
, NULL
, &pidl_testfile
, NULL
);
1820 ok(SUCCEEDED(ret
), "ParseDisplayName returned %x\n", ret
);
1822 pidl_abstestfile
= pILCombine(pidl_cwd
, pidl_testfile
);
1824 ret
= pSHCreateShellItem(NULL
, NULL
, NULL
, &shellitem
);
1825 ok(ret
== E_INVALIDARG
, "SHCreateShellItem returned %x\n", ret
);
1827 if (0) /* crashes on Windows XP */
1829 pSHCreateShellItem(NULL
, NULL
, pidl_cwd
, NULL
);
1830 pSHCreateShellItem(pidl_cwd
, NULL
, NULL
, &shellitem
);
1831 pSHCreateShellItem(NULL
, currentfolder
, NULL
, &shellitem
);
1832 pSHCreateShellItem(pidl_cwd
, currentfolder
, NULL
, &shellitem
);
1835 ret
= pSHCreateShellItem(NULL
, NULL
, pidl_cwd
, &shellitem
);
1836 ok(SUCCEEDED(ret
), "SHCreateShellItem returned %x\n", ret
);
1839 ret
= IShellItem_QueryInterface(shellitem
, &IID_IPersistIDList
, (void**)&persistidl
);
1840 ok(SUCCEEDED(ret
), "QueryInterface returned %x\n", ret
);
1843 ret
= IPersistIDList_GetIDList(persistidl
, &pidl_test
);
1844 ok(SUCCEEDED(ret
), "GetIDList returned %x\n", ret
);
1847 ok(ILIsEqual(pidl_cwd
, pidl_test
), "id lists are not equal\n");
1850 IPersistIDList_Release(persistidl
);
1852 IShellItem_Release(shellitem
);
1855 ret
= pSHCreateShellItem(pidl_cwd
, NULL
, pidl_testfile
, &shellitem
);
1856 ok(SUCCEEDED(ret
), "SHCreateShellItem returned %x\n", ret
);
1859 ret
= IShellItem_QueryInterface(shellitem
, &IID_IPersistIDList
, (void**)&persistidl
);
1860 ok(SUCCEEDED(ret
), "QueryInterface returned %x\n", ret
);
1863 ret
= IPersistIDList_GetIDList(persistidl
, &pidl_test
);
1864 ok(SUCCEEDED(ret
), "GetIDList returned %x\n", ret
);
1867 ok(ILIsEqual(pidl_abstestfile
, pidl_test
), "id lists are not equal\n");
1870 IPersistIDList_Release(persistidl
);
1873 ret
= IShellItem_GetParent(shellitem
, &shellitem2
);
1874 ok(SUCCEEDED(ret
), "GetParent returned %x\n", ret
);
1877 ret
= IShellItem_QueryInterface(shellitem2
, &IID_IPersistIDList
, (void**)&persistidl
);
1878 ok(SUCCEEDED(ret
), "QueryInterface returned %x\n", ret
);
1881 ret
= IPersistIDList_GetIDList(persistidl
, &pidl_test
);
1882 ok(SUCCEEDED(ret
), "GetIDList returned %x\n", ret
);
1885 ok(ILIsEqual(pidl_cwd
, pidl_test
), "id lists are not equal\n");
1888 IPersistIDList_Release(persistidl
);
1890 IShellItem_Release(shellitem2
);
1893 IShellItem_Release(shellitem
);
1896 ret
= pSHCreateShellItem(NULL
, currentfolder
, pidl_testfile
, &shellitem
);
1897 ok(SUCCEEDED(ret
), "SHCreateShellItem returned %x\n", ret
);
1900 ret
= IShellItem_QueryInterface(shellitem
, &IID_IPersistIDList
, (void**)&persistidl
);
1901 ok(SUCCEEDED(ret
), "QueryInterface returned %x\n", ret
);
1904 ret
= IPersistIDList_GetIDList(persistidl
, &pidl_test
);
1905 ok(SUCCEEDED(ret
), "GetIDList returned %x\n", ret
);
1908 ok(ILIsEqual(pidl_abstestfile
, pidl_test
), "id lists are not equal\n");
1911 IPersistIDList_Release(persistidl
);
1913 IShellItem_Release(shellitem
);
1916 /* if a parent pidl and shellfolder are specified, the shellfolder is ignored */
1917 ret
= pSHCreateShellItem(pidl_cwd
, desktopfolder
, pidl_testfile
, &shellitem
);
1918 ok(SUCCEEDED(ret
), "SHCreateShellItem returned %x\n", ret
);
1921 ret
= IShellItem_QueryInterface(shellitem
, &IID_IPersistIDList
, (void**)&persistidl
);
1922 ok(SUCCEEDED(ret
), "QueryInterface returned %x\n", ret
);
1925 ret
= IPersistIDList_GetIDList(persistidl
, &pidl_test
);
1926 ok(SUCCEEDED(ret
), "GetIDList returned %x\n", ret
);
1929 ok(ILIsEqual(pidl_abstestfile
, pidl_test
), "id lists are not equal\n");
1932 IPersistIDList_Release(persistidl
);
1934 IShellItem_Release(shellitem
);
1937 DeleteFileA(".\\testfile");
1938 pILFree(pidl_abstestfile
);
1939 pILFree(pidl_testfile
);
1941 IShellFolder_Release(currentfolder
);
1942 IShellFolder_Release(desktopfolder
);
1945 START_TEST(shlfolder
)
1947 init_function_pointers();
1948 /* if OleInitialize doesn't get called, ParseDisplayName returns
1949 CO_E_NOTINITIALIZED for malformed directory names on win2k. */
1950 OleInitialize(NULL
);
1952 test_ParseDisplayName();
1953 test_BindToObject();
1954 test_EnumObjects_and_CompareIDs();
1955 test_GetDisplayName();
1956 test_GetAttributesOf();
1957 test_SHGetPathFromIDList();
1958 test_CallForAttributes();
1959 test_FolderShortcut();
1960 test_ITEMIDLIST_format();
1961 if(pSHGetFolderPathAndSubDirA
)
1962 testSHGetFolderPathAndSubDirA();
1964 win_skip("SHGetFolderPathAndSubDirA not present\n");
1965 test_LocalizedNames();
1966 if(pSHCreateShellItem
)
1967 test_SHCreateShellItem();
1969 win_skip("SHCreateShellItem not present\n");