spaces -> tabs in rbuild file
[reactos.git] / rostests / winetests / shell32 / shlfolder.c
1 /*
2 * Unit test of the IShellFolder functions.
3 *
4 * Copyright 2004 Vitaliy Margolen
5 *
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.
10 *
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.
15 *
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
19 */
20
21 #include <stdarg.h>
22 #include <stdio.h>
23
24 #define COBJMACROS
25 #define CONST_VTABLE
26
27 #include "windef.h"
28 #include "winbase.h"
29 #include "wtypes.h"
30 #include "shellapi.h"
31
32
33 #include "shlguid.h"
34 #include "shlobj.h"
35 #include "shobjidl.h"
36 #include "shlwapi.h"
37 #include "ocidl.h"
38 #include "oleauto.h"
39
40 #include "wine/test.h"
41
42
43 static IMalloc *ppM;
44
45 static HRESULT (WINAPI *pSHBindToParent)(LPCITEMIDLIST, REFIID, LPVOID*, LPCITEMIDLIST*);
46 static HRESULT (WINAPI *pSHGetFolderPathA)(HWND, int, HANDLE, DWORD, LPSTR);
47 static HRESULT (WINAPI *pSHGetFolderPathAndSubDirA)(HWND, int, HANDLE, DWORD, LPCSTR, LPSTR);
48 static BOOL (WINAPI *pSHGetPathFromIDListW)(LPCITEMIDLIST,LPWSTR);
49 static BOOL (WINAPI *pSHGetSpecialFolderPathA)(HWND, LPSTR, int, BOOL);
50 static BOOL (WINAPI *pSHGetSpecialFolderPathW)(HWND, LPWSTR, int, BOOL);
51 static HRESULT (WINAPI *pStrRetToBufW)(STRRET*,LPCITEMIDLIST,LPWSTR,UINT);
52 static LPITEMIDLIST (WINAPI *pILFindLastID)(LPCITEMIDLIST);
53 static void (WINAPI *pILFree)(LPITEMIDLIST);
54 static BOOL (WINAPI *pILIsEqual)(LPCITEMIDLIST, LPCITEMIDLIST);
55 static HRESULT (WINAPI *pSHCreateShellItem)(LPCITEMIDLIST,IShellFolder*,LPCITEMIDLIST,IShellItem**);
56 static LPITEMIDLIST (WINAPI *pILCombine)(LPCITEMIDLIST,LPCITEMIDLIST);
57
58
59 static void init_function_pointers(void)
60 {
61 HMODULE hmod;
62 HRESULT hr;
63
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);
76
77 hmod = GetModuleHandleA("shlwapi.dll");
78 pStrRetToBufW = (void*)GetProcAddress(hmod, "StrRetToBufW");
79
80 hr = SHGetMalloc(&ppM);
81 ok(hr == S_OK, "SHGetMalloc failed %08x\n", hr);
82 }
83
84 static void test_ParseDisplayName(void)
85 {
86 HRESULT hr;
87 IShellFolder *IDesktopFolder;
88 static const char *cNonExistDir1A = "c:\\nonexist_subdir";
89 static const char *cNonExistDir2A = "c:\\\\nonexist_subdir";
90 static const char *cInetTestA = "http:\\yyy";
91 static const char *cInetTest2A = "xx:yyy";
92 DWORD res;
93 WCHAR cTestDirW [MAX_PATH] = {0};
94 ITEMIDLIST *newPIDL;
95 BOOL bRes;
96
97 hr = SHGetDesktopFolder(&IDesktopFolder);
98 if(hr != S_OK) return;
99
100 MultiByteToWideChar(CP_ACP, 0, cInetTestA, -1, cTestDirW, MAX_PATH);
101 hr = IShellFolder_ParseDisplayName(IDesktopFolder,
102 NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
103 todo_wine ok((SUCCEEDED(hr) || broken(hr == E_FAIL) /* NT4 */),
104 "ParseDisplayName returned %08x, expected SUCCESS or E_FAIL\n", hr);
105 if (SUCCEEDED(hr))
106 {
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);
110 }
111
112 MultiByteToWideChar(CP_ACP, 0, cInetTest2A, -1, cTestDirW, MAX_PATH);
113 hr = IShellFolder_ParseDisplayName(IDesktopFolder,
114 NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
115 todo_wine ok((SUCCEEDED(hr) || broken(hr == E_FAIL) /* NT4 */),
116 "ParseDisplayName returned %08x, expected SUCCESS or E_FAIL\n", hr);
117 if (SUCCEEDED(hr))
118 {
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);
122 }
123
124 res = GetFileAttributesA(cNonExistDir1A);
125 if(res != INVALID_FILE_ATTRIBUTES) return;
126
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);
132
133 res = GetFileAttributesA(cNonExistDir2A);
134 if(res != INVALID_FILE_ATTRIBUTES) return;
135
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);
141
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;
146
147 bRes = pSHGetSpecialFolderPathW(NULL, cTestDirW, CSIDL_PERSONAL, FALSE);
148 ok(bRes, "SHGetSpecialFolderPath(CSIDL_PERSONAL) failed! %u\n", GetLastError());
149 if (!bRes) goto finished;
150
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;
154
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);
160
161 finished:
162 IShellFolder_Release(IDesktopFolder);
163 }
164
165 /* creates a file with the specified name for tests */
166 static void CreateTestFile(const CHAR *name)
167 {
168 HANDLE file;
169 DWORD written;
170
171 file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
172 if (file != INVALID_HANDLE_VALUE)
173 {
174 WriteFile(file, name, strlen(name), &written, NULL);
175 WriteFile(file, "\n", strlen("\n"), &written, NULL);
176 CloseHandle(file);
177 }
178 }
179
180
181 /* initializes the tests */
182 static void CreateFilesFolders(void)
183 {
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);
191 }
192
193 /* cleans after tests */
194 static void Cleanup(void)
195 {
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");
203 }
204
205
206 /* perform test */
207 static void test_EnumObjects(IShellFolder *iFolder)
208 {
209 IEnumIDList *iEnumList;
210 LPITEMIDLIST newPIDL, idlArr[10];
211 ULONG NumPIDLs;
212 int i=0, j;
213 HRESULT hr;
214
215 static const WORD iResults [5][5] =
216 {
217 { 0,-1,-1,-1,-1},
218 { 1, 0,-1,-1,-1},
219 { 1, 1, 0,-1,-1},
220 { 1, 1, 1, 0,-1},
221 { 1, 1, 1, 1, 0}
222 };
223
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] =
227 {
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,
233 };
234
235 hr = IShellFolder_EnumObjects(iFolder, NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN, &iEnumList);
236 ok(hr == S_OK, "EnumObjects failed %08x\n", hr);
237
238 /* This is to show that, contrary to what is said on MSDN, on IEnumIDList::Next,
239 * the filesystem shellfolders return S_OK even if less than 'celt' items are
240 * returned (in contrast to S_FALSE). We have to do it in a loop since WinXP
241 * only ever returns a single entry per call. */
242 while (IEnumIDList_Next(iEnumList, 10-i, &idlArr[i], &NumPIDLs) == S_OK)
243 i += NumPIDLs;
244 ok (i == 5, "i: %d\n", i);
245
246 hr = IEnumIDList_Release(iEnumList);
247 ok(hr == S_OK, "IEnumIDList_Release failed %08x\n", hr);
248
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)
252 {
253 newPIDL = idlArr[i];
254 idlArr[i] = idlArr[j];
255 idlArr[j] = newPIDL;
256 }
257
258 for (i=0;i<5;i++) for (j=0;j<5;j++)
259 {
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]);
262 }
263
264
265 for (i = 0; i < 5; i++)
266 {
267 SFGAOF flags;
268 #define SFGAO_VISTA SFGAO_DROPTARGET | SFGAO_CANLINK | SFGAO_CANCOPY
269 /* Native returns all flags no matter what we ask for */
270 flags = SFGAO_CANCOPY;
271 hr = IShellFolder_GetAttributesOf(iFolder, 1, (LPCITEMIDLIST*)(idlArr + i), &flags);
272 flags &= SFGAO_testfor;
273 ok(hr == S_OK, "GetAttributesOf returns %08x\n", hr);
274 ok(flags == (attrs[i]) ||
275 flags == (attrs[i] & ~SFGAO_FILESYSANCESTOR) || /* Win9x, NT4 */
276 flags == ((attrs[i] & ~SFGAO_CAPABILITYMASK) | SFGAO_VISTA), /* Vista and higher */
277 "GetAttributesOf[%i] got %08x, expected %08x\n", i, flags, attrs[i]);
278
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]);
286 }
287
288 for (i=0;i<5;i++)
289 IMalloc_Free(ppM, idlArr[i]);
290 }
291
292 static void test_BindToObject(void)
293 {
294 HRESULT hr;
295 UINT cChars;
296 IShellFolder *psfDesktop, *psfChild, *psfMyComputer, *psfSystemDir;
297 SHITEMID emptyitem = { 0, { 0 } };
298 LPITEMIDLIST pidlMyComputer, pidlSystemDir, pidlEmpty = (LPITEMIDLIST)&emptyitem;
299 WCHAR wszSystemDir[MAX_PATH];
300 char szSystemDir[MAX_PATH];
301 WCHAR wszMyComputer[] = {
302 ':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
303 'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };
304
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
307 */
308 hr = SHGetDesktopFolder(&psfDesktop);
309 ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08x\n", hr);
310 if (FAILED(hr)) return;
311
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);
314
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);
317
318 hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyComputer, NULL, &pidlMyComputer, NULL);
319 ok (SUCCEEDED(hr), "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08x\n", hr);
320 if (FAILED(hr)) {
321 IShellFolder_Release(psfDesktop);
322 return;
323 }
324
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;
330
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);
333
334 #if 0
335 /* this call segfaults on 98SE */
336 hr = IShellFolder_BindToObject(psfMyComputer, NULL, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
337 ok (hr == E_INVALIDARG, "MyComputers's BindToObject should fail, when called with NULL pidl! hr = %08x\n", hr);
338 #endif
339
340 cChars = GetSystemDirectoryA(szSystemDir, MAX_PATH);
341 ok (cChars > 0 && cChars < MAX_PATH, "GetSystemDirectoryA failed! LastError: %u\n", GetLastError());
342 if (cChars == 0 || cChars >= MAX_PATH) {
343 IShellFolder_Release(psfMyComputer);
344 return;
345 }
346 MultiByteToWideChar(CP_ACP, 0, szSystemDir, -1, wszSystemDir, MAX_PATH);
347
348 hr = IShellFolder_ParseDisplayName(psfMyComputer, NULL, NULL, wszSystemDir, NULL, &pidlSystemDir, NULL);
349 ok (SUCCEEDED(hr), "MyComputers's ParseDisplayName failed to parse the SystemDirectory! hr = %08x\n", hr);
350 if (FAILED(hr)) {
351 IShellFolder_Release(psfMyComputer);
352 return;
353 }
354
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;
360
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);
364
365 #if 0
366 /* this call segfaults on 98SE */
367 hr = IShellFolder_BindToObject(psfSystemDir, NULL, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
368 ok (hr == E_INVALIDARG,
369 "FileSystem ShellFolder's BindToObject should fail, when called with NULL pidl! hr = %08x\n", hr);
370 #endif
371
372 IShellFolder_Release(psfSystemDir);
373 }
374
375 /* Based on PathAddBackslashW from dlls/shlwapi/path.c */
376 static LPWSTR myPathAddBackslashW( LPWSTR lpszPath )
377 {
378 size_t iLen;
379
380 if (!lpszPath || (iLen = lstrlenW(lpszPath)) >= MAX_PATH)
381 return NULL;
382
383 if (iLen)
384 {
385 lpszPath += iLen;
386 if (lpszPath[-1] != '\\')
387 {
388 *lpszPath++ = '\\';
389 *lpszPath = '\0';
390 }
391 }
392 return lpszPath;
393 }
394
395 static void test_GetDisplayName(void)
396 {
397 BOOL result;
398 HRESULT hr;
399 HANDLE hTestFile;
400 WCHAR wszTestFile[MAX_PATH], wszTestFile2[MAX_PATH];
401 char szTestFile[MAX_PATH], szTestDir[MAX_PATH];
402 DWORD attr;
403 STRRET strret;
404 LPSHELLFOLDER psfDesktop, psfPersonal;
405 IUnknown *psfFile;
406 SHITEMID emptyitem = { 0, { 0 } };
407 LPITEMIDLIST pidlTestFile, pidlEmpty = (LPITEMIDLIST)&emptyitem;
408 LPCITEMIDLIST pidlLast;
409 static const CHAR szFileName[] = "winetest.foo";
410 static const WCHAR wszFileName[] = { 'w','i','n','e','t','e','s','t','.','f','o','o',0 };
411 static const WCHAR wszDirName[] = { 'w','i','n','e','t','e','s','t',0 };
412
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.
419 */
420
421 if(!pSHGetSpecialFolderPathA) {
422 win_skip("SHGetSpecialFolderPathA is not available\n");
423 return;
424 }
425
426 /* First creating a directory in MyDocuments and a file in this directory. */
427 result = pSHGetSpecialFolderPathA(NULL, szTestDir, CSIDL_PERSONAL, FALSE);
428 ok(result, "SHGetSpecialFolderPathA failed! Last error: %u\n", GetLastError());
429 if (!result) return;
430
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))
436 {
437 ok(0, "unable to create the '%s' directory\n", szTestDir);
438 return;
439 }
440
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);
448
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;
453
454 MultiByteToWideChar(CP_ACP, 0, szTestFile, -1, wszTestFile, MAX_PATH);
455
456 hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszTestFile, NULL, &pidlTestFile, NULL);
457 ok(SUCCEEDED(hr), "Desktop->ParseDisplayName failed! hr = %08x\n", hr);
458 if (FAILED(hr)) {
459 IShellFolder_Release(psfDesktop);
460 return;
461 }
462
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");
471 }
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");
478 }
479
480 /* It seems as if we cannot bind to regular files on windows, but only directories.
481 */
482 hr = IShellFolder_BindToObject(psfDesktop, pidlTestFile, NULL, &IID_IUnknown, (VOID**)&psfFile);
483 todo_wine
484 ok (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) ||
485 hr == E_NOTIMPL || /* Vista */
486 broken(SUCCEEDED(hr)), /* Win9x, W2K */
487 "hr = %08x\n", hr);
488 if (SUCCEEDED(hr)) {
489 IShellFolder_Release(psfFile);
490 }
491
492 if (!pSHBindToParent)
493 {
494 win_skip("SHBindToParent is missing\n");
495 DeleteFileA(szTestFile);
496 RemoveDirectoryA(szTestDir);
497 return;
498 }
499
500 /* Some tests for IShellFolder::SetNameOf */
501 if (pSHGetFolderPathAndSubDirA)
502 {
503 hr = pSHBindToParent(pidlTestFile, &IID_IShellFolder, (VOID**)&psfPersonal, &pidlLast);
504 ok(SUCCEEDED(hr), "SHBindToParent failed! hr = %08x\n", hr);
505 if (SUCCEEDED(hr)) {
506 /* It's ok to use this fixed path. Call will fail anyway. */
507 WCHAR wszAbsoluteFilename[] = { 'C',':','\\','w','i','n','e','t','e','s','t', 0 };
508 LPITEMIDLIST pidlNew;
509
510 /* The pidl returned through the last parameter of SetNameOf is a simple one. */
511 hr = IShellFolder_SetNameOf(psfPersonal, NULL, pidlLast, wszDirName, SHGDN_NORMAL, &pidlNew);
512 ok (SUCCEEDED(hr), "SetNameOf failed! hr = %08x\n", hr);
513 if (hr == S_OK)
514 {
515 ok (((LPITEMIDLIST)((LPBYTE)pidlNew+pidlNew->mkid.cb))->mkid.cb == 0,
516 "pidl returned from SetNameOf should be simple!\n");
517
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);
523
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);
528
529 pILFree(pidlNew);
530 }
531
532 IShellFolder_Release(psfPersonal);
533 }
534 }
535 else
536 win_skip("Avoid needs of interaction on Win2k\n");
537
538 /* Deleting the file and the directory */
539 DeleteFileA(szTestFile);
540 RemoveDirectoryA(szTestDir);
541
542 /* SHGetPathFromIDListW still works, although the file is not present anymore. */
543 if (pSHGetPathFromIDListW)
544 {
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");
548 }
549
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");
553
554 /* But it succeeds with an empty PIDL. */
555 hr = pSHBindToParent(pidlEmpty, &IID_IShellFolder, (VOID**)&psfPersonal, &pidlLast);
556 ok (SUCCEEDED(hr), "SHBindToParent(empty PIDL) should succeed! hr = %08x\n", hr);
557 ok (pidlLast == pidlEmpty, "The last element of an empty PIDL should be the PIDL itself!\n");
558 if (SUCCEEDED(hr))
559 IShellFolder_Release(psfPersonal);
560
561 /* Binding to the folder and querying the display name of the file also works. */
562 hr = pSHBindToParent(pidlTestFile, &IID_IShellFolder, (VOID**)&psfPersonal, &pidlLast);
563 ok (SUCCEEDED(hr), "SHBindToParent failed! hr = %08x\n", hr);
564 if (FAILED(hr)) {
565 IShellFolder_Release(psfDesktop);
566 return;
567 }
568
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");
573
574 hr = IShellFolder_GetDisplayNameOf(psfPersonal, pidlLast, SHGDN_FORPARSING, &strret);
575 ok (SUCCEEDED(hr), "Personal->GetDisplayNameOf failed! hr = %08x\n", hr);
576 if (FAILED(hr)) {
577 IShellFolder_Release(psfDesktop);
578 IShellFolder_Release(psfPersonal);
579 return;
580 }
581
582 if (pStrRetToBufW)
583 {
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");
587 }
588
589 ILFree(pidlTestFile);
590 IShellFolder_Release(psfDesktop);
591 IShellFolder_Release(psfPersonal);
592 }
593
594 static void test_CallForAttributes(void)
595 {
596 HKEY hKey;
597 LONG lResult;
598 HRESULT hr;
599 DWORD dwSize;
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 };
613
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.
617 *
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.
622 */
623 hr = SHGetDesktopFolder(&psfDesktop);
624 ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08x\n", hr);
625 if (FAILED(hr)) return;
626
627 hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyDocuments, NULL,
628 &pidlMyDocuments, NULL);
629 ok (SUCCEEDED(hr) ||
630 broken(hr == E_INVALIDARG), /* Win95, NT4 */
631 "Desktop's ParseDisplayName failed to parse MyDocuments's CLSID! hr = %08x\n", hr);
632 if (FAILED(hr)) {
633 IShellFolder_Release(psfDesktop);
634 return;
635 }
636
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);
641
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");
646
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.
649 */
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);
659 return;
660 }
661
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) {
667 RegCloseKey(hKey);
668 IMalloc_Free(ppM, pidlMyDocuments);
669 IShellFolder_Release(psfDesktop);
670 return;
671 }
672
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) {
679 RegCloseKey(hKey);
680 IMalloc_Free(ppM, pidlMyDocuments);
681 IShellFolder_Release(psfDesktop);
682 return;
683 }
684
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));
693
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.
698 */
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);
703 if (SUCCEEDED(hr))
704 ok (dwAttributes == SFGAO_FILESYSTEM,
705 "Desktop->GetAttributes(MyDocuments) returned unexpected attributes: %08x\n",
706 dwAttributes);
707
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));
712 RegCloseKey(hKey);
713 IMalloc_Free(ppM, pidlMyDocuments);
714 IShellFolder_Release(psfDesktop);
715 }
716
717 static void test_GetAttributesOf(void)
718 {
719 HRESULT hr;
720 LPSHELLFOLDER psfDesktop, psfMyComputer;
721 SHITEMID emptyitem = { 0, { 0 } };
722 LPCITEMIDLIST pidlEmpty = (LPCITEMIDLIST)&emptyitem;
723 LPITEMIDLIST pidlMyComputer;
724 DWORD dwFlags;
725 static const DWORD desktopFlags[] = {
726 /* WinXP */
727 SFGAO_STORAGE | SFGAO_HASPROPSHEET | SFGAO_STORAGEANCESTOR | SFGAO_FILESYSANCESTOR |
728 SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_HASSUBFOLDER,
729 /* Win2k */
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
735 };
736 static const DWORD myComputerFlags[] = {
737 /* WinXP */
738 SFGAO_CANRENAME | SFGAO_CANDELETE | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET |
739 SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER,
740 /* Win2k */
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
749 };
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;
757 ITEMIDLIST *newPIDL;
758 int len, i;
759 BOOL foundFlagsMatch;
760
761 hr = SHGetDesktopFolder(&psfDesktop);
762 ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08x\n", hr);
763 if (FAILED(hr)) return;
764
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++)
771 {
772 if (desktopFlags[i] == dwFlags)
773 foundFlagsMatch = TRUE;
774 }
775 ok (foundFlagsMatch, "Wrong Desktop attributes: %08x\n", dwFlags);
776
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++)
783 {
784 if (desktopFlags[i] == dwFlags)
785 foundFlagsMatch = TRUE;
786 }
787 ok (foundFlagsMatch, "Wrong Desktop attributes: %08x\n", dwFlags);
788
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);
792 if (FAILED(hr)) {
793 IShellFolder_Release(psfDesktop);
794 return;
795 }
796
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).
799 */
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++)
805 {
806 if ((myComputerFlags[i] | SFGAO_CANLINK) == dwFlags)
807 foundFlagsMatch = TRUE;
808 }
809 todo_wine
810 ok (foundFlagsMatch, "Wrong MyComputer attributes: %08x\n", dwFlags);
811
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;
817
818 hr = IShellFolder_GetAttributesOf(psfMyComputer, 1, &pidlEmpty, &dwFlags);
819 todo_wine
820 ok (hr == E_INVALIDARG ||
821 broken(SUCCEEDED(hr)), /* W2K and earlier */
822 "MyComputer->GetAttributesOf(emtpy pidl) should fail! hr = %08x\n", hr);
823
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++)
829 {
830 if (myComputerFlags[i] == dwFlags)
831 foundFlagsMatch = TRUE;
832 }
833 todo_wine
834 ok (foundFlagsMatch, "Wrong MyComputer attributes: %08x\n", dwFlags);
835
836 IShellFolder_Release(psfMyComputer);
837
838 GetCurrentDirectoryA(MAX_PATH, cCurrDirA);
839 len = lstrlenA(cCurrDirA);
840
841 if (len == 0) {
842 win_skip("GetCurrentDirectoryA returned empty string. Skipping test_GetAttributesOf\n");
843 return;
844 }
845 if (len > 3 && cCurrDirA[len-1] == '\\')
846 cCurrDirA[len-1] = 0;
847
848 /* create test directory */
849 CreateFilesFolders();
850
851 MultiByteToWideChar(CP_ACP, 0, cCurrDirA, -1, cCurrDirW, MAX_PATH);
852
853 hr = SHGetDesktopFolder(&IDesktopFolder);
854 ok(hr == S_OK, "SHGetDesktopfolder failed %08x\n", hr);
855
856 hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cCurrDirW, NULL, &newPIDL, 0);
857 ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
858
859 hr = IShellFolder_BindToObject(IDesktopFolder, newPIDL, NULL, (REFIID)&IID_IShellFolder, (LPVOID *)&testIShellFolder);
860 ok(hr == S_OK, "BindToObject failed %08x\n", hr);
861
862 IMalloc_Free(ppM, newPIDL);
863
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);
867
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);
873
874 /* free memory */
875 IMalloc_Free(ppM, newPIDL);
876
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);
882
883 hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cCurrDirW, NULL, &newPIDL, 0);
884 ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
885
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);
891
892 /* free memory */
893 IMalloc_Free(ppM, newPIDL);
894
895 IShellFolder_Release(testIShellFolder);
896
897 Cleanup();
898
899 IShellFolder_Release(IDesktopFolder);
900 }
901
902 static void test_SHGetPathFromIDList(void)
903 {
904 SHITEMID emptyitem = { 0, { 0 } };
905 LPCITEMIDLIST pidlEmpty = (LPCITEMIDLIST)&emptyitem;
906 LPITEMIDLIST pidlMyComputer;
907 WCHAR wszPath[MAX_PATH], wszDesktop[MAX_PATH];
908 BOOL result;
909 HRESULT hr;
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;
916 HANDLE hTestFile;
917 STRRET strret;
918 static WCHAR wszTestFile[] = {
919 'w','i','n','e','t','e','s','t','.','f','o','o',0 };
920 HRESULT (WINAPI *pSHGetSpecialFolderLocation)(HWND, int, LPITEMIDLIST *);
921 HMODULE hShell32;
922 LPITEMIDLIST pidlPrograms;
923
924 if(!pSHGetPathFromIDListW || !pSHGetSpecialFolderPathW)
925 {
926 win_skip("SHGetPathFromIDListW() or SHGetSpecialFolderPathW() is missing\n");
927 return;
928 }
929
930 /* Calling SHGetPathFromIDListW with no pidl should return the empty string */
931 wszPath[0] = 'a';
932 wszPath[1] = '\0';
933 result = pSHGetPathFromIDListW(NULL, wszPath);
934 ok(!result, "Expected failure\n");
935 ok(!wszPath[0], "Expected empty string\n");
936
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());
940 if (!result) return;
941
942 /* Check if we are on Win9x */
943 SetLastError(0xdeadbeef);
944 lstrcmpiW(wszDesktop, wszDesktop);
945 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
946 {
947 win_skip("Most W-calls are not implemented\n");
948 return;
949 }
950
951 result = pSHGetPathFromIDListW(pidlEmpty, wszPath);
952 ok(result, "SHGetPathFromIDListW failed! Last error: %u\n", GetLastError());
953 if (!result) return;
954 ok(!lstrcmpiW(wszDesktop, wszPath), "SHGetPathFromIDListW didn't return desktop path for empty pidl!\n");
955
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;
960
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);
963 if (FAILED(hr)) {
964 IShellFolder_Release(psfDesktop);
965 return;
966 }
967
968 SetLastError(0xdeadbeef);
969 wszPath[0] = 'a';
970 wszPath[1] = '\0';
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");
977 if (result) {
978 IShellFolder_Release(psfDesktop);
979 return;
980 }
981
982 IMalloc_Free(ppM, pidlMyComputer);
983
984 result = pSHGetSpecialFolderPathW(NULL, wszFileName, CSIDL_DESKTOPDIRECTORY, FALSE);
985 ok(result, "SHGetSpecialFolderPathW failed! Last error: %u\n", GetLastError());
986 if (!result) {
987 IShellFolder_Release(psfDesktop);
988 return;
989 }
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);
996 return;
997 }
998 CloseHandle(hTestFile);
999
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);
1002 if (FAILED(hr)) {
1003 IShellFolder_Release(psfDesktop);
1004 DeleteFileW(wszFileName);
1005 IMalloc_Free(ppM, pidlTestFile);
1006 return;
1007 }
1008
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);
1015 if (FAILED(hr)) {
1016 IMalloc_Free(ppM, pidlTestFile);
1017 return;
1018 }
1019 if (pStrRetToBufW)
1020 {
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");
1025 }
1026
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");
1032
1033
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");
1037
1038 hr = pSHGetSpecialFolderLocation(NULL, CSIDL_PROGRAM_FILES, &pidlPrograms);
1039 ok(SUCCEEDED(hr), "SHGetFolderLocation failed: 0x%08x\n", hr);
1040
1041 SetLastError(0xdeadbeef);
1042 result = pSHGetPathFromIDListW(pidlPrograms, wszPath);
1043 IMalloc_Free(ppM, pidlPrograms);
1044 ok(result, "SHGetPathFromIDListW failed\n");
1045 }
1046
1047 static void test_EnumObjects_and_CompareIDs(void)
1048 {
1049 ITEMIDLIST *newPIDL;
1050 IShellFolder *IDesktopFolder, *testIShellFolder;
1051 char cCurrDirA [MAX_PATH] = {0};
1052 static const CHAR cTestDirA[] = "\\testdir";
1053 WCHAR cTestDirW[MAX_PATH];
1054 int len;
1055 HRESULT hr;
1056
1057 GetCurrentDirectoryA(MAX_PATH, cCurrDirA);
1058 len = lstrlenA(cCurrDirA);
1059
1060 if(len == 0) {
1061 win_skip("GetCurrentDirectoryA returned empty string. Skipping test_EnumObjects_and_CompareIDs\n");
1062 return;
1063 }
1064 if(cCurrDirA[len-1] == '\\')
1065 cCurrDirA[len-1] = 0;
1066
1067 lstrcatA(cCurrDirA, cTestDirA);
1068 MultiByteToWideChar(CP_ACP, 0, cCurrDirA, -1, cTestDirW, MAX_PATH);
1069
1070 hr = SHGetDesktopFolder(&IDesktopFolder);
1071 ok(hr == S_OK, "SHGetDesktopfolder failed %08x\n", hr);
1072
1073 CreateFilesFolders();
1074
1075 hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
1076 ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
1077
1078 hr = IShellFolder_BindToObject(IDesktopFolder, newPIDL, NULL, (REFIID)&IID_IShellFolder, (LPVOID *)&testIShellFolder);
1079 ok(hr == S_OK, "BindToObject failed %08x\n", hr);
1080
1081 test_EnumObjects(testIShellFolder);
1082
1083 IShellFolder_Release(testIShellFolder);
1084
1085 Cleanup();
1086
1087 IMalloc_Free(ppM, newPIDL);
1088
1089 IShellFolder_Release(IDesktopFolder);
1090 }
1091
1092 /* A simple implementation of an IPropertyBag, which returns fixed values for
1093 * 'Target' and 'Attributes' properties.
1094 */
1095 static HRESULT WINAPI InitPropertyBag_IPropertyBag_QueryInterface(IPropertyBag *iface, REFIID riid,
1096 void **ppvObject)
1097 {
1098 if (!ppvObject)
1099 return E_INVALIDARG;
1100
1101 if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IPropertyBag, riid)) {
1102 *ppvObject = iface;
1103 } else {
1104 ok (FALSE, "InitPropertyBag asked for unknown interface!\n");
1105 return E_NOINTERFACE;
1106 }
1107
1108 IPropertyBag_AddRef(iface);
1109 return S_OK;
1110 }
1111
1112 static ULONG WINAPI InitPropertyBag_IPropertyBag_AddRef(IPropertyBag *iface) {
1113 return 2;
1114 }
1115
1116 static ULONG WINAPI InitPropertyBag_IPropertyBag_Release(IPropertyBag *iface) {
1117 return 1;
1118 }
1119
1120 static HRESULT WINAPI InitPropertyBag_IPropertyBag_Read(IPropertyBag *iface, LPCOLESTR pszPropName,
1121 VARIANT *pVar, IErrorLog *pErrorLog)
1122 {
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 };
1135
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;
1141 }
1142
1143 if (!lstrcmpW(pszPropName, wszResolveLinkFlags))
1144 {
1145 ok(V_VT(pVar) == VT_UI4, "Wrong variant type for 'ResolveLinkFlags' property!\n");
1146 return E_INVALIDARG;
1147 }
1148
1149 if (!lstrcmpW(pszPropName, wszTarget)) {
1150 WCHAR wszPath[MAX_PATH];
1151 BOOL result;
1152
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;
1157
1158 result = pSHGetSpecialFolderPathW(NULL, wszPath, CSIDL_DESKTOPDIRECTORY, FALSE);
1159 ok(result, "SHGetSpecialFolderPathW(DESKTOPDIRECTORY) failed! %u\n", GetLastError());
1160 if (!result) return E_INVALIDARG;
1161
1162 V_BSTR(pVar) = SysAllocString(wszPath);
1163 return S_OK;
1164 }
1165
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;
1171 return S_OK;
1172 }
1173
1174 if (!lstrcmpW(pszPropName, wszTargetKnownFolder)) {
1175 ok(V_VT(pVar) == VT_BSTR, "Wrong variant type for 'TargetKnownFolder' property!\n");
1176 /* TODO */
1177 return E_INVALIDARG;
1178 }
1179
1180 if (!lstrcmpW(pszPropName, wszCLSID)) {
1181 ok(V_VT(pVar) == VT_EMPTY, "Wrong variant type for 'CLSID' property!\n");
1182 /* TODO */
1183 return E_INVALIDARG;
1184 }
1185
1186 ok(FALSE, "PropertyBag was asked for unknown property %s (vt=%d)!\n", wine_dbgstr_w(pszPropName), V_VT(pVar));
1187 return E_INVALIDARG;
1188 }
1189
1190 static HRESULT WINAPI InitPropertyBag_IPropertyBag_Write(IPropertyBag *iface, LPCOLESTR pszPropName,
1191 VARIANT *pVar)
1192 {
1193 ok(FALSE, "Unexpected call to IPropertyBag_Write\n");
1194 return E_NOTIMPL;
1195 }
1196
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
1203 };
1204
1205 static struct IPropertyBag InitPropertyBag = {
1206 &InitPropertyBag_IPropertyBagVtbl
1207 };
1208
1209 static void test_FolderShortcut(void) {
1210 IPersistPropertyBag *pPersistPropertyBag;
1211 IShellFolder *pShellFolder, *pDesktopFolder;
1212 IPersistFolder3 *pPersistFolder3;
1213 HRESULT hr;
1214 STRRET strret;
1215 WCHAR wszDesktopPath[MAX_PATH], wszBuffer[MAX_PATH];
1216 BOOL result;
1217 CLSID clsid;
1218 LPITEMIDLIST pidlCurrentFolder, pidlWineTestFolder, pidlSubFolder;
1219 HKEY hShellExtKey;
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 };
1230
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}};
1234
1235 if (!pSHGetSpecialFolderPathW || !pStrRetToBufW) {
1236 win_skip("SHGetSpecialFolderPathW and/or StrRetToBufW are not available\n");
1237 return;
1238 }
1239
1240 if (!pSHGetFolderPathAndSubDirA)
1241 {
1242 win_skip("FolderShortcut test doesn't work on Win2k\n");
1243 return;
1244 }
1245
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.
1249 */
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");
1254 return;
1255 }
1256 ok (SUCCEEDED(hr), "CoCreateInstance failed! hr = 0x%08x\n", hr);
1257 if (FAILED(hr)) return;
1258
1259 hr = IPersistPropertyBag_Load(pPersistPropertyBag, &InitPropertyBag, NULL);
1260 ok(SUCCEEDED(hr), "IPersistPropertyBag_Load failed! hr = %08x\n", hr);
1261 if (FAILED(hr)) {
1262 IPersistPropertyBag_Release(pPersistPropertyBag);
1263 return;
1264 }
1265
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;
1271
1272 hr = IShellFolder_GetDisplayNameOf(pShellFolder, NULL, SHGDN_FORPARSING, &strret);
1273 ok(SUCCEEDED(hr), "IShellFolder_GetDisplayNameOf(NULL) failed! hr = %08x\n", hr);
1274 if (FAILED(hr)) {
1275 IShellFolder_Release(pShellFolder);
1276 return;
1277 }
1278
1279 result = pSHGetSpecialFolderPathW(NULL, wszDesktopPath, CSIDL_DESKTOPDIRECTORY, FALSE);
1280 ok(result, "SHGetSpecialFolderPathW(CSIDL_DESKTOPDIRECTORY) failed! %u\n", GetLastError());
1281 if (!result) return;
1282
1283 pStrRetToBufW(&strret, NULL, wszBuffer, MAX_PATH);
1284 ok(!lstrcmpiW(wszDesktopPath, wszBuffer), "FolderShortcut returned incorrect folder!\n");
1285
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;
1290
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");
1294
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");
1298
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.
1304 */
1305 hr = SHGetDesktopFolder(&pDesktopFolder);
1306 ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08x\n", hr);
1307 if (FAILED(hr)) return;
1308
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;
1319
1320 hr = IPersistFolder3_Initialize(pPersistFolder3, pidlWineTestFolder);
1321 ok (SUCCEEDED(hr), "IPersistFolder3::Initialize failed! hr = %08x\n", hr);
1322 if (FAILED(hr)) {
1323 IPersistFolder3_Release(pPersistFolder3);
1324 pILFree(pidlWineTestFolder);
1325 return;
1326 }
1327
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);
1334
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;
1339
1340 hr = IShellFolder_GetDisplayNameOf(pShellFolder, NULL, SHGDN_FORPARSING, &strret);
1341 ok(SUCCEEDED(hr), "IShellFolder_GetDisplayNameOf(NULL) failed! hr = %08x\n", hr);
1342 if (FAILED(hr)) {
1343 IShellFolder_Release(pShellFolder);
1344 return;
1345 }
1346
1347 pStrRetToBufW(&strret, NULL, wszBuffer, MAX_PATH);
1348 ok(!lstrcmpiW(wszDesktopPath, wszBuffer), "FolderShortcut returned incorrect folder!\n");
1349
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);
1356 return;
1357 }
1358
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);
1363 if (FAILED(hr)) {
1364 IShellFolder_Release(pShellFolder);
1365 return;
1366 }
1367
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);
1373 if (FAILED(hr))
1374 return;
1375
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");
1382
1383 IPersistFolder3_Release(pPersistFolder3);
1384 }
1385
1386 #include "pshpack1.h"
1387 struct FileStructA {
1388 BYTE type;
1389 BYTE dummy;
1390 DWORD dwFileSize;
1391 WORD uFileDate; /* In our current implementation this is */
1392 WORD uFileTime; /* FileTimeToDosDate(WIN32_FIND_DATA->ftLastWriteTime) */
1393 WORD uFileAttribs;
1394 CHAR szName[1];
1395 };
1396
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 */
1409 };
1410 #include "poppack.h"
1411
1412 static void test_ITEMIDLIST_format(void) {
1413 WCHAR wszPersonal[MAX_PATH];
1414 LPSHELLFOLDER psfDesktop, psfPersonal;
1415 LPITEMIDLIST pidlPersonal, pidlFile;
1416 HANDLE hFile;
1417 HRESULT hr;
1418 BOOL bResult;
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 } };
1421 int i;
1422
1423 if (!pSHGetSpecialFolderPathW) return;
1424
1425 bResult = pSHGetSpecialFolderPathW(NULL, wszPersonal, CSIDL_PERSONAL, FALSE);
1426 ok(bResult, "SHGetSpecialFolderPathW failed! Last error: %u\n", GetLastError());
1427 if (!bResult) return;
1428
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");
1433 return;
1434 }
1435 ok(bResult, "SetCurrentDirectory failed! Last error: %u\n", GetLastError());
1436 if (!bResult) return;
1437
1438 hr = SHGetDesktopFolder(&psfDesktop);
1439 ok(SUCCEEDED(hr), "SHGetDesktopFolder failed! hr: %08x\n", hr);
1440 if (FAILED(hr)) return;
1441
1442 hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszPersonal, NULL, &pidlPersonal, NULL);
1443 ok(SUCCEEDED(hr), "psfDesktop->ParseDisplayName failed! hr = %08x\n", hr);
1444 if (FAILED(hr)) {
1445 IShellFolder_Release(psfDesktop);
1446 return;
1447 }
1448
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;
1455
1456 for (i=0; i<3; i++) {
1457 CHAR szFile[MAX_PATH];
1458 struct FileStructA *pFileStructA;
1459 WORD cbOffset;
1460
1461 WideCharToMultiByte(CP_ACP, 0, wszFile[i], -1, szFile, MAX_PATH, NULL, NULL);
1462
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);
1467 return;
1468 }
1469 CloseHandle(hFile);
1470
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);
1474 if (FAILED(hr)) {
1475 IShellFolder_Release(psfPersonal);
1476 return;
1477 }
1478
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");
1483
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");
1486 else
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
1491 * just fine. */
1492 todo_wine
1493 ok(pidlFile->mkid.abID[18] == '~' ||
1494 broken(pidlFile->mkid.abID[34] == '~'), /* Win2k */
1495 "Should be derived 8.3 name!\n");
1496
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");
1501
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");
1505
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);
1513
1514 if (cbOffset >= sizeof(struct FileStructA) &&
1515 cbOffset <= pidlFile->mkid.cb - sizeof(struct FileStructW))
1516 {
1517 struct FileStructW *pFileStructW = (struct FileStructW *)(((LPBYTE)pidlFile)+cbOffset);
1518
1519 ok(pidlFile->mkid.cb == cbOffset + pFileStructW->cbLen,
1520 "FileStructW's offset and length should add up to the PIDL's length!\n");
1521
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");
1532
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.
1538 */
1539 if (pFileStructA->uFileTime == pFileStructW->uTime2)
1540 {
1541 ok (pFileStructA->uFileDate == pFileStructW->uDate2,
1542 "Last write date and time should match last access date and time!\n");
1543 }
1544 else
1545 {
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.*/
1552 }
1553
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");
1558 }
1559 }
1560
1561 pILFree(pidlFile);
1562 }
1563
1564 IShellFolder_Release(psfPersonal);
1565 }
1566
1567 static void testSHGetFolderPathAndSubDirA(void)
1568 {
1569 HRESULT ret;
1570 BOOL delret;
1571 DWORD dwret;
1572 int i;
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];
1578
1579 if(!pSHGetFolderPathA) {
1580 win_skip("SHGetFolderPathA not present!\n");
1581 return;
1582 }
1583 if(FAILED(pSHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, appdata)))
1584 {
1585 win_skip("SHGetFolderPathA failed for CSIDL_LOCAL_APPDATA!\n");
1586 return;
1587 }
1588
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());
1593 return;
1594 }
1595
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());
1600 return;
1601 }
1602
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);
1606
1607 /* test fourth parameter */
1608 ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, 2, winetemp, testpath);
1609 switch(ret) {
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);
1615 break;
1616 case E_INVALIDARG: /* winxp, win2k3 */
1617 break;
1618 default:
1619 ok(0, "expected S_OK or E_INVALIDARG, got %x\n", ret);
1620 }
1621
1622 /* test fifth parameter */
1623 testpath[0] = '\0';
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);
1627
1628 testpath[0] = '\0';
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);
1632
1633 testpath[0] = '\0';
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);
1637
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);
1644
1645 testpath[0] = '\0';
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);
1648
1649 /* test a not existing path */
1650 testpath[0] = '\0';
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);
1654
1655 /* create a directory inside a not existing directory */
1656 testpath[0] = '\0';
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);
1665
1666 /* cleanup */
1667 sprintf(testpath, "%s\\%s", appdata, winetemp);
1668 RemoveDirectoryA(testpath);
1669 sprintf(testpath, "%s\\%s", appdata, wine);
1670 RemoveDirectoryA(testpath);
1671 }
1672
1673 static void test_LocalizedNames(void)
1674 {
1675 static char cCurrDirA[MAX_PATH];
1676 WCHAR cCurrDirW[MAX_PATH], tempbufW[25];
1677 IShellFolder *IDesktopFolder, *testIShellFolder;
1678 ITEMIDLIST *newPIDL;
1679 int len;
1680 HRESULT hr;
1681 static char resourcefile[MAX_PATH];
1682 DWORD res;
1683 HANDLE file;
1684 STRRET strret;
1685
1686 static const char desktopini_contents1[] =
1687 "[.ShellClassInfo]\r\n"
1688 "LocalizedResourceName=@";
1689 static const char desktopini_contents2[] =
1690 ",-1\r\n";
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};
1693
1694 /* create folder with desktop.ini and localized name in GetModuleFileNameA(NULL) */
1695 CreateDirectoryA(".\\testfolder", NULL);
1696
1697 SetFileAttributesA(".\\testfolder", GetFileAttributesA(".\\testfolder")|FILE_ATTRIBUTE_SYSTEM);
1698
1699 GetModuleFileNameA(NULL, resourcefile, MAX_PATH);
1700
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());
1708 CloseHandle(file);
1709
1710 /* get IShellFolder for parent */
1711 GetCurrentDirectoryA(MAX_PATH, cCurrDirA);
1712 len = lstrlenA(cCurrDirA);
1713
1714 if (len == 0) {
1715 win_skip("GetCurrentDirectoryA returned empty string. Skipping test_LocalizedNames\n");
1716 goto cleanup;
1717 }
1718 if(cCurrDirA[len-1] == '\\')
1719 cCurrDirA[len-1] = 0;
1720
1721 MultiByteToWideChar(CP_ACP, 0, cCurrDirA, -1, cCurrDirW, MAX_PATH);
1722
1723 hr = SHGetDesktopFolder(&IDesktopFolder);
1724 ok(hr == S_OK, "SHGetDesktopfolder failed %08x\n", hr);
1725
1726 hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cCurrDirW, NULL, &newPIDL, 0);
1727 ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
1728
1729 hr = IShellFolder_BindToObject(IDesktopFolder, newPIDL, NULL, (REFIID)&IID_IShellFolder, (LPVOID *)&testIShellFolder);
1730 ok(hr == S_OK, "BindToObject failed %08x\n", hr);
1731
1732 IMalloc_Free(ppM, newPIDL);
1733
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);
1737
1738 hr = IShellFolder_GetDisplayNameOf(testIShellFolder, newPIDL, SHGDN_INFOLDER, &strret);
1739 ok(hr == S_OK, "GetDisplayNameOf failed %08x\n", hr);
1740
1741 if (SUCCEEDED(hr) && pStrRetToBufW)
1742 {
1743 hr = pStrRetToBufW(&strret, newPIDL, tempbufW, sizeof(tempbufW)/sizeof(WCHAR));
1744 ok (SUCCEEDED(hr), "StrRetToBufW failed! hr = %08x\n", hr);
1745 todo_wine
1746 ok (!lstrcmpiW(tempbufW, folderdisplayW) ||
1747 broken(!lstrcmpiW(tempbufW, foldernameW)), /* W2K */
1748 "GetDisplayNameOf returned %s\n", wine_dbgstr_w(tempbufW));
1749 }
1750
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);
1754
1755 if (SUCCEEDED(hr) && pStrRetToBufW)
1756 {
1757 hr = pStrRetToBufW(&strret, newPIDL, tempbufW, sizeof(tempbufW)/sizeof(WCHAR));
1758 ok (SUCCEEDED(hr), "StrRetToBufW failed! hr = %08x\n", hr);
1759 todo_wine
1760 ok (!lstrcmpiW(tempbufW, folderdisplayW) ||
1761 broken(!lstrcmpiW(tempbufW, foldernameW)), /* W2K */
1762 "GetDisplayNameOf returned %s\n", wine_dbgstr_w(tempbufW));
1763 }
1764
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);
1768
1769 if (SUCCEEDED(hr) && pStrRetToBufW)
1770 {
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));
1774 }
1775
1776 IShellFolder_Release(IDesktopFolder);
1777 IShellFolder_Release(testIShellFolder);
1778
1779 IMalloc_Free(ppM, newPIDL);
1780
1781 cleanup:
1782 DeleteFileA(".\\testfolder\\desktop.ini");
1783 SetFileAttributesA(".\\testfolder", GetFileAttributesA(".\\testfolder")&~FILE_ATTRIBUTE_SYSTEM);
1784 RemoveDirectoryA(".\\testfolder");
1785 }
1786
1787 static void test_SHCreateShellItem(void)
1788 {
1789 IShellItem *shellitem, *shellitem2;
1790 IPersistIDList *persistidl;
1791 LPITEMIDLIST pidl_cwd=NULL, pidl_testfile, pidl_abstestfile, pidl_test;
1792 HRESULT ret;
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};
1797
1798 GetCurrentDirectoryA(MAX_PATH, curdirA);
1799
1800 if (!lstrlenA(curdirA))
1801 {
1802 win_skip("GetCurrentDirectoryA returned empty string, skipping test_SHCreateShellItem\n");
1803 return;
1804 }
1805
1806 MultiByteToWideChar(CP_ACP, 0, curdirA, -1, curdirW, MAX_PATH);
1807
1808 ret = SHGetDesktopFolder(&desktopfolder);
1809 ok(SUCCEEDED(ret), "SHGetShellFolder returned %x\n", ret);
1810
1811 ret = IShellFolder_ParseDisplayName(desktopfolder, NULL, NULL, curdirW, NULL, &pidl_cwd, NULL);
1812 ok(SUCCEEDED(ret), "ParseDisplayName returned %x\n", ret);
1813
1814 ret = IShellFolder_BindToObject(desktopfolder, pidl_cwd, NULL, &IID_IShellFolder, (void**)&currentfolder);
1815 ok(SUCCEEDED(ret), "BindToObject returned %x\n", ret);
1816
1817 CreateTestFile(".\\testfile");
1818
1819 ret = IShellFolder_ParseDisplayName(currentfolder, NULL, NULL, testfileW, NULL, &pidl_testfile, NULL);
1820 ok(SUCCEEDED(ret), "ParseDisplayName returned %x\n", ret);
1821
1822 pidl_abstestfile = pILCombine(pidl_cwd, pidl_testfile);
1823
1824 ret = pSHCreateShellItem(NULL, NULL, NULL, &shellitem);
1825 ok(ret == E_INVALIDARG, "SHCreateShellItem returned %x\n", ret);
1826
1827 if (0) /* crashes on Windows XP */
1828 {
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);
1833 }
1834
1835 ret = pSHCreateShellItem(NULL, NULL, pidl_cwd, &shellitem);
1836 ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret);
1837 if (SUCCEEDED(ret))
1838 {
1839 ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
1840 ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
1841 if (SUCCEEDED(ret))
1842 {
1843 ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
1844 ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
1845 if (SUCCEEDED(ret))
1846 {
1847 ok(ILIsEqual(pidl_cwd, pidl_test), "id lists are not equal\n");
1848 pILFree(pidl_test);
1849 }
1850 IPersistIDList_Release(persistidl);
1851 }
1852 IShellItem_Release(shellitem);
1853 }
1854
1855 ret = pSHCreateShellItem(pidl_cwd, NULL, pidl_testfile, &shellitem);
1856 ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret);
1857 if (SUCCEEDED(ret))
1858 {
1859 ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
1860 ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
1861 if (SUCCEEDED(ret))
1862 {
1863 ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
1864 ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
1865 if (SUCCEEDED(ret))
1866 {
1867 ok(ILIsEqual(pidl_abstestfile, pidl_test), "id lists are not equal\n");
1868 pILFree(pidl_test);
1869 }
1870 IPersistIDList_Release(persistidl);
1871 }
1872
1873 ret = IShellItem_GetParent(shellitem, &shellitem2);
1874 ok(SUCCEEDED(ret), "GetParent returned %x\n", ret);
1875 if (SUCCEEDED(ret))
1876 {
1877 ret = IShellItem_QueryInterface(shellitem2, &IID_IPersistIDList, (void**)&persistidl);
1878 ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
1879 if (SUCCEEDED(ret))
1880 {
1881 ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
1882 ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
1883 if (SUCCEEDED(ret))
1884 {
1885 ok(ILIsEqual(pidl_cwd, pidl_test), "id lists are not equal\n");
1886 pILFree(pidl_test);
1887 }
1888 IPersistIDList_Release(persistidl);
1889 }
1890 IShellItem_Release(shellitem2);
1891 }
1892
1893 IShellItem_Release(shellitem);
1894 }
1895
1896 ret = pSHCreateShellItem(NULL, currentfolder, pidl_testfile, &shellitem);
1897 ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret);
1898 if (SUCCEEDED(ret))
1899 {
1900 ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
1901 ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
1902 if (SUCCEEDED(ret))
1903 {
1904 ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
1905 ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
1906 if (SUCCEEDED(ret))
1907 {
1908 ok(ILIsEqual(pidl_abstestfile, pidl_test), "id lists are not equal\n");
1909 pILFree(pidl_test);
1910 }
1911 IPersistIDList_Release(persistidl);
1912 }
1913 IShellItem_Release(shellitem);
1914 }
1915
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);
1919 if (SUCCEEDED(ret))
1920 {
1921 ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
1922 ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
1923 if (SUCCEEDED(ret))
1924 {
1925 ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
1926 ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
1927 if (SUCCEEDED(ret))
1928 {
1929 ok(ILIsEqual(pidl_abstestfile, pidl_test), "id lists are not equal\n");
1930 pILFree(pidl_test);
1931 }
1932 IPersistIDList_Release(persistidl);
1933 }
1934 IShellItem_Release(shellitem);
1935 }
1936
1937 DeleteFileA(".\\testfile");
1938 pILFree(pidl_abstestfile);
1939 pILFree(pidl_testfile);
1940 pILFree(pidl_cwd);
1941 IShellFolder_Release(currentfolder);
1942 IShellFolder_Release(desktopfolder);
1943 }
1944
1945 START_TEST(shlfolder)
1946 {
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);
1951
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();
1963 else
1964 win_skip("SHGetFolderPathAndSubDirA not present\n");
1965 test_LocalizedNames();
1966 if(pSHCreateShellItem)
1967 test_SHCreateShellItem();
1968 else
1969 win_skip("SHCreateShellItem not present\n");
1970
1971 OleUninitialize();
1972 }