urlmon: Implemented IUri_GetUserName.
[wine] / dlls / shell32 / tests / 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 static HRESULT (WINAPI *pSHParseDisplayName)(LPCWSTR,IBindCtx*,LPITEMIDLIST*,SFGAOF,SFGAOF*);
58 static LPITEMIDLIST (WINAPI *pSHSimpleIDListFromPathAW)(LPCVOID);
59
60 static void init_function_pointers(void)
61 {
62     HMODULE hmod;
63     HRESULT hr;
64     void *ptr;
65
66     hmod = GetModuleHandleA("shell32.dll");
67
68 #define MAKEFUNC(f) (p##f = (void*)GetProcAddress(hmod, #f))
69     MAKEFUNC(SHBindToParent);
70     MAKEFUNC(SHCreateShellItem);
71     MAKEFUNC(SHGetFolderPathA);
72     MAKEFUNC(SHGetFolderPathAndSubDirA);
73     MAKEFUNC(SHGetPathFromIDListW);
74     MAKEFUNC(SHGetSpecialFolderPathA);
75     MAKEFUNC(SHGetSpecialFolderPathW);
76     MAKEFUNC(SHParseDisplayName);
77 #undef MAKEFUNC
78
79 #define MAKEFUNC_ORD(f, ord) (p##f = (void*)GetProcAddress(hmod, (LPSTR)(ord)))
80     MAKEFUNC_ORD(ILFindLastID, 16);
81     MAKEFUNC_ORD(ILIsEqual, 21);
82     MAKEFUNC_ORD(ILCombine, 25);
83     MAKEFUNC_ORD(ILFree, 155);
84     MAKEFUNC_ORD(SHSimpleIDListFromPathAW, 162);
85 #undef MAKEFUNC_ORD
86
87     /* test named exports */
88     ptr = GetProcAddress(hmod, "ILFree");
89     ok(broken(ptr == 0) || ptr != 0, "expected named export for ILFree\n");
90     if (ptr)
91     {
92 #define TESTNAMED(f) \
93     ptr = (void*)GetProcAddress(hmod, #f); \
94     ok(ptr != 0, "expected named export for " #f "\n");
95
96         TESTNAMED(ILAppendID);
97         TESTNAMED(ILClone);
98         TESTNAMED(ILCloneFirst);
99         TESTNAMED(ILCombine);
100         TESTNAMED(ILCreateFromPath);
101         TESTNAMED(ILCreateFromPathA);
102         TESTNAMED(ILCreateFromPathW);
103         TESTNAMED(ILFindChild);
104         TESTNAMED(ILFindLastID);
105         TESTNAMED(ILGetNext);
106         TESTNAMED(ILGetSize);
107         TESTNAMED(ILIsEqual);
108         TESTNAMED(ILIsParent);
109         TESTNAMED(ILRemoveLastID);
110         TESTNAMED(ILSaveToStream);
111 #undef TESTNAMED
112     }
113
114     hmod = GetModuleHandleA("shlwapi.dll");
115     pStrRetToBufW = (void*)GetProcAddress(hmod, "StrRetToBufW");
116
117     hr = SHGetMalloc(&ppM);
118     ok(hr == S_OK, "SHGetMalloc failed %08x\n", hr);
119 }
120
121 static void test_ParseDisplayName(void)
122 {
123     HRESULT hr;
124     IShellFolder *IDesktopFolder;
125     static const char *cNonExistDir1A = "c:\\nonexist_subdir";
126     static const char *cNonExistDir2A = "c:\\\\nonexist_subdir";
127     static const char *cInetTestA = "http:\\yyy";
128     static const char *cInetTest2A = "xx:yyy";
129     DWORD res;
130     WCHAR cTestDirW [MAX_PATH] = {0};
131     ITEMIDLIST *newPIDL;
132     BOOL bRes;
133
134     hr = SHGetDesktopFolder(&IDesktopFolder);
135     ok(hr == S_OK, "Expected SHGetDesktopFolder to return S_OK, got 0x%08x\n", hr);
136     if(hr != S_OK) return;
137
138     /* Tests crash on W2K and below (SHCreateShellItem available as of XP) */
139     if (pSHCreateShellItem)
140     {
141         /* null name and pidl */
142         hr = IShellFolder_ParseDisplayName(IDesktopFolder,
143             NULL, NULL, NULL, NULL, NULL, 0);
144         ok(hr == E_INVALIDARG, "returned %08x, expected E_INVALIDARG\n", hr);
145
146         /* null name */
147         newPIDL = (ITEMIDLIST*)0xdeadbeef;
148         hr = IShellFolder_ParseDisplayName(IDesktopFolder,
149             NULL, NULL, NULL, NULL, &newPIDL, 0);
150         ok(newPIDL == 0, "expected null, got %p\n", newPIDL);
151         ok(hr == E_INVALIDARG, "returned %08x, expected E_INVALIDARG\n", hr);
152     }
153     else
154         win_skip("Tests would crash on W2K and below\n");
155
156     MultiByteToWideChar(CP_ACP, 0, cInetTestA, -1, cTestDirW, MAX_PATH);
157     hr = IShellFolder_ParseDisplayName(IDesktopFolder,
158         NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
159     todo_wine ok(hr == S_OK || broken(hr == E_FAIL) /* NT4 */,
160         "ParseDisplayName returned %08x, expected SUCCESS or E_FAIL\n", hr);
161     if (hr == S_OK)
162     {
163         ok(pILFindLastID(newPIDL)->mkid.abID[0] == 0x61, "Last pidl should be of type "
164            "PT_IESPECIAL1, but is: %02x\n", pILFindLastID(newPIDL)->mkid.abID[0]);
165         IMalloc_Free(ppM, newPIDL);
166     }
167
168     MultiByteToWideChar(CP_ACP, 0, cInetTest2A, -1, cTestDirW, MAX_PATH);
169     hr = IShellFolder_ParseDisplayName(IDesktopFolder,
170         NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
171     todo_wine ok(hr == S_OK || broken(hr == E_FAIL) /* NT4 */,
172         "ParseDisplayName returned %08x, expected SUCCESS or E_FAIL\n", hr);
173     if (hr == S_OK)
174     {
175         ok(pILFindLastID(newPIDL)->mkid.abID[0] == 0x61, "Last pidl should be of type "
176            "PT_IESPECIAL1, but is: %02x\n", pILFindLastID(newPIDL)->mkid.abID[0]);
177         IMalloc_Free(ppM, newPIDL);
178     }
179
180     res = GetFileAttributesA(cNonExistDir1A);
181     if(res != INVALID_FILE_ATTRIBUTES)
182     {
183         skip("Test directory unexpectedly exists\n");
184         goto finished;
185     }
186
187     MultiByteToWideChar(CP_ACP, 0, cNonExistDir1A, -1, cTestDirW, MAX_PATH);
188     hr = IShellFolder_ParseDisplayName(IDesktopFolder, 
189         NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
190     ok((hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) || (hr == E_FAIL), 
191         "ParseDisplayName returned %08x, expected 80070002 or E_FAIL\n", hr);
192
193     res = GetFileAttributesA(cNonExistDir2A);
194     if(res != INVALID_FILE_ATTRIBUTES)
195     {
196         skip("Test directory unexpectedly exists\n");
197         goto finished;
198     }
199
200     MultiByteToWideChar(CP_ACP, 0, cNonExistDir2A, -1, cTestDirW, MAX_PATH);
201     hr = IShellFolder_ParseDisplayName(IDesktopFolder, 
202         NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
203     ok((hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) || (hr == E_FAIL) || (hr == E_INVALIDARG), 
204         "ParseDisplayName returned %08x, expected 80070002, E_FAIL or E_INVALIDARG\n", hr);
205
206     /* I thought that perhaps the DesktopFolder's ParseDisplayName would recognize the
207      * path corresponding to CSIDL_PERSONAL and return a CLSID_MyDocuments PIDL. Turns
208      * out it doesn't. The magic seems to happen in the file dialogs, then. */
209     if (!pSHGetSpecialFolderPathW || !pILFindLastID)
210     {
211         win_skip("SHGetSpecialFolderPathW and/or ILFindLastID are not available\n");
212         goto finished;
213     }
214
215     bRes = pSHGetSpecialFolderPathW(NULL, cTestDirW, CSIDL_PERSONAL, FALSE);
216     ok(bRes, "SHGetSpecialFolderPath(CSIDL_PERSONAL) failed! %u\n", GetLastError());
217     if (!bRes) goto finished;
218
219     hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
220     ok(hr == S_OK, "DesktopFolder->ParseDisplayName failed. hr = %08x.\n", hr);
221     if (hr != S_OK) goto finished;
222
223     ok(pILFindLastID(newPIDL)->mkid.abID[0] == 0x31 ||
224        pILFindLastID(newPIDL)->mkid.abID[0] == 0xb1, /* Win98 */
225        "Last pidl should be of type PT_FOLDER or PT_IESPECIAL2, but is: %02x\n",
226        pILFindLastID(newPIDL)->mkid.abID[0]);
227     IMalloc_Free(ppM, newPIDL);
228     
229 finished:
230     IShellFolder_Release(IDesktopFolder);
231 }
232
233 /* creates a file with the specified name for tests */
234 static void CreateTestFile(const CHAR *name)
235 {
236     HANDLE file;
237     DWORD written;
238
239     file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
240     if (file != INVALID_HANDLE_VALUE)
241     {
242         WriteFile(file, name, strlen(name), &written, NULL);
243         WriteFile(file, "\n", strlen("\n"), &written, NULL);
244         CloseHandle(file);
245     }
246 }
247
248
249 /* initializes the tests */
250 static void CreateFilesFolders(void)
251 {
252     CreateDirectoryA(".\\testdir", NULL);
253     CreateDirectoryA(".\\testdir\\test.txt", NULL);
254     CreateTestFile  (".\\testdir\\test1.txt ");
255     CreateTestFile  (".\\testdir\\test2.txt ");
256     CreateTestFile  (".\\testdir\\test3.txt ");
257     CreateDirectoryA(".\\testdir\\testdir2 ", NULL);
258     CreateDirectoryA(".\\testdir\\testdir2\\subdir", NULL);
259 }
260
261 /* cleans after tests */
262 static void Cleanup(void)
263 {
264     DeleteFileA(".\\testdir\\test1.txt");
265     DeleteFileA(".\\testdir\\test2.txt");
266     DeleteFileA(".\\testdir\\test3.txt");
267     RemoveDirectoryA(".\\testdir\\test.txt");
268     RemoveDirectoryA(".\\testdir\\testdir2\\subdir");
269     RemoveDirectoryA(".\\testdir\\testdir2");
270     RemoveDirectoryA(".\\testdir");
271 }
272
273
274 /* perform test */
275 static void test_EnumObjects(IShellFolder *iFolder)
276 {
277     IEnumIDList *iEnumList;
278     LPITEMIDLIST newPIDL, idlArr[10];
279     ULONG NumPIDLs;
280     int i=0, j;
281     HRESULT hr;
282
283     static const WORD iResults [5][5] =
284     {
285         { 0,-1,-1,-1,-1},
286         { 1, 0,-1,-1,-1},
287         { 1, 1, 0,-1,-1},
288         { 1, 1, 1, 0,-1},
289         { 1, 1, 1, 1, 0}
290     };
291
292 #define SFGAO_testfor SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR | SFGAO_CAPABILITYMASK
293     /* Don't test for SFGAO_HASSUBFOLDER since we return real state and native cached */
294     static const ULONG attrs[5] =
295     {
296         SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR,
297         SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR,
298         SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM,
299         SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM,
300         SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM,
301     };
302
303     hr = IShellFolder_EnumObjects(iFolder, NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN, &iEnumList);
304     ok(hr == S_OK, "EnumObjects failed %08x\n", hr);
305
306     /* This is to show that, contrary to what is said on MSDN, on IEnumIDList::Next,
307      * the filesystem shellfolders return S_OK even if less than 'celt' items are
308      * returned (in contrast to S_FALSE). We have to do it in a loop since WinXP
309      * only ever returns a single entry per call. */
310     while (IEnumIDList_Next(iEnumList, 10-i, &idlArr[i], &NumPIDLs) == S_OK) 
311         i += NumPIDLs;
312     ok (i == 5, "i: %d\n", i);
313
314     hr = IEnumIDList_Release(iEnumList);
315     ok(hr == S_OK, "IEnumIDList_Release failed %08x\n", hr);
316     
317     /* Sort them first in case of wrong order from system */
318     for (i=0;i<5;i++) for (j=0;j<5;j++)
319         if ((SHORT)IShellFolder_CompareIDs(iFolder, 0, idlArr[i], idlArr[j]) < 0)
320         {
321             newPIDL = idlArr[i];
322             idlArr[i] = idlArr[j];
323             idlArr[j] = newPIDL;
324         }
325             
326     for (i=0;i<5;i++) for (j=0;j<5;j++)
327     {
328         hr = IShellFolder_CompareIDs(iFolder, 0, idlArr[i], idlArr[j]);
329         ok(hr == iResults[i][j], "Got %x expected [%d]-[%d]=%x\n", hr, i, j, iResults[i][j]);
330     }
331
332
333     for (i = 0; i < 5; i++)
334     {
335         SFGAOF flags;
336 #define SFGAO_VISTA SFGAO_DROPTARGET | SFGAO_CANLINK | SFGAO_CANCOPY
337         /* Native returns all flags no matter what we ask for */
338         flags = SFGAO_CANCOPY;
339         hr = IShellFolder_GetAttributesOf(iFolder, 1, (LPCITEMIDLIST*)(idlArr + i), &flags);
340         flags &= SFGAO_testfor;
341         ok(hr == S_OK, "GetAttributesOf returns %08x\n", hr);
342         ok(flags == (attrs[i]) ||
343            flags == (attrs[i] & ~SFGAO_FILESYSANCESTOR) || /* Win9x, NT4 */
344            flags == ((attrs[i] & ~SFGAO_CAPABILITYMASK) | SFGAO_VISTA), /* Vista and higher */
345            "GetAttributesOf[%i] got %08x, expected %08x\n", i, flags, attrs[i]);
346
347         flags = SFGAO_testfor;
348         hr = IShellFolder_GetAttributesOf(iFolder, 1, (LPCITEMIDLIST*)(idlArr + i), &flags);
349         flags &= SFGAO_testfor;
350         ok(hr == S_OK, "GetAttributesOf returns %08x\n", hr);
351         ok(flags == attrs[i] ||
352            flags == (attrs[i] & ~SFGAO_FILESYSANCESTOR), /* Win9x, NT4 */
353            "GetAttributesOf[%i] got %08x, expected %08x\n", i, flags, attrs[i]);
354     }
355
356     for (i=0;i<5;i++)
357         IMalloc_Free(ppM, idlArr[i]);
358 }
359
360 static void test_BindToObject(void)
361 {
362     HRESULT hr;
363     UINT cChars;
364     IShellFolder *psfDesktop, *psfChild, *psfMyComputer, *psfSystemDir;
365     SHITEMID emptyitem = { 0, { 0 } };
366     LPITEMIDLIST pidlMyComputer, pidlSystemDir, pidlEmpty = (LPITEMIDLIST)&emptyitem;
367     WCHAR wszSystemDir[MAX_PATH];
368     char szSystemDir[MAX_PATH];
369     WCHAR wszMyComputer[] = { 
370         ':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
371         'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };
372
373     /* The following tests shows that BindToObject should fail with E_INVALIDARG if called
374      * with an empty pidl. This is tested for Desktop, MyComputer and the FS ShellFolder
375      */
376     hr = SHGetDesktopFolder(&psfDesktop);
377     ok (hr == S_OK, "SHGetDesktopFolder failed! hr = %08x\n", hr);
378     if (hr != S_OK) return;
379     
380     hr = IShellFolder_BindToObject(psfDesktop, pidlEmpty, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
381     ok (hr == E_INVALIDARG, "Desktop's BindToObject should fail, when called with empty pidl! hr = %08x\n", hr);
382
383     hr = IShellFolder_BindToObject(psfDesktop, NULL, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
384     ok (hr == E_INVALIDARG, "Desktop's BindToObject should fail, when called with NULL pidl! hr = %08x\n", hr);
385
386     hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyComputer, NULL, &pidlMyComputer, NULL);
387     ok (hr == S_OK, "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08x\n", hr);
388     if (hr != S_OK) {
389         IShellFolder_Release(psfDesktop);
390         return;
391     }
392     
393     hr = IShellFolder_BindToObject(psfDesktop, pidlMyComputer, NULL, &IID_IShellFolder, (LPVOID*)&psfMyComputer);
394     ok (hr == S_OK, "Desktop failed to bind to MyComputer object! hr = %08x\n", hr);
395     IShellFolder_Release(psfDesktop);
396     IMalloc_Free(ppM, pidlMyComputer);
397     if (hr != S_OK) return;
398
399     hr = IShellFolder_BindToObject(psfMyComputer, pidlEmpty, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
400     ok (hr == E_INVALIDARG, "MyComputers's BindToObject should fail, when called with empty pidl! hr = %08x\n", hr);
401
402 if (0)
403 {
404     /* this call segfaults on 98SE */
405     hr = IShellFolder_BindToObject(psfMyComputer, NULL, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
406     ok (hr == E_INVALIDARG, "MyComputers's BindToObject should fail, when called with NULL pidl! hr = %08x\n", hr);
407 }
408
409     cChars = GetSystemDirectoryA(szSystemDir, MAX_PATH);
410     ok (cChars > 0 && cChars < MAX_PATH, "GetSystemDirectoryA failed! LastError: %u\n", GetLastError());
411     if (cChars == 0 || cChars >= MAX_PATH) {
412         IShellFolder_Release(psfMyComputer);
413         return;
414     }
415     MultiByteToWideChar(CP_ACP, 0, szSystemDir, -1, wszSystemDir, MAX_PATH);
416     
417     hr = IShellFolder_ParseDisplayName(psfMyComputer, NULL, NULL, wszSystemDir, NULL, &pidlSystemDir, NULL);
418     ok (hr == S_OK, "MyComputers's ParseDisplayName failed to parse the SystemDirectory! hr = %08x\n", hr);
419     if (hr != S_OK) {
420         IShellFolder_Release(psfMyComputer);
421         return;
422     }
423
424     hr = IShellFolder_BindToObject(psfMyComputer, pidlSystemDir, NULL, &IID_IShellFolder, (LPVOID*)&psfSystemDir);
425     ok (hr == S_OK, "MyComputer failed to bind to a FileSystem ShellFolder! hr = %08x\n", hr);
426     IShellFolder_Release(psfMyComputer);
427     IMalloc_Free(ppM, pidlSystemDir);
428     if (hr != S_OK) return;
429
430     hr = IShellFolder_BindToObject(psfSystemDir, pidlEmpty, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
431     ok (hr == E_INVALIDARG, 
432         "FileSystem ShellFolder's BindToObject should fail, when called with empty pidl! hr = %08x\n", hr);
433
434 if (0)
435 {
436     /* this call segfaults on 98SE */
437     hr = IShellFolder_BindToObject(psfSystemDir, NULL, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
438     ok (hr == E_INVALIDARG,
439         "FileSystem ShellFolder's BindToObject should fail, when called with NULL pidl! hr = %08x\n", hr);
440 }
441
442     IShellFolder_Release(psfSystemDir);
443 }
444
445 /* Based on PathAddBackslashW from dlls/shlwapi/path.c */
446 static LPWSTR myPathAddBackslashW( LPWSTR lpszPath )
447 {
448   size_t iLen;
449
450   if (!lpszPath || (iLen = lstrlenW(lpszPath)) >= MAX_PATH)
451     return NULL;
452
453   if (iLen)
454   {
455     lpszPath += iLen;
456     if (lpszPath[-1] != '\\')
457     {
458       *lpszPath++ = '\\';
459       *lpszPath = '\0';
460     }
461   }
462   return lpszPath;
463 }
464
465 static void test_GetDisplayName(void)
466 {
467     BOOL result;
468     HRESULT hr;
469     HANDLE hTestFile;
470     WCHAR wszTestFile[MAX_PATH], wszTestFile2[MAX_PATH];
471     char szTestFile[MAX_PATH], szTestDir[MAX_PATH];
472     DWORD attr;
473     STRRET strret;
474     LPSHELLFOLDER psfDesktop, psfPersonal;
475     IUnknown *psfFile;
476     SHITEMID emptyitem = { 0, { 0 } };
477     LPITEMIDLIST pidlTestFile, pidlEmpty = (LPITEMIDLIST)&emptyitem;
478     LPCITEMIDLIST pidlLast;
479     static const CHAR szFileName[] = "winetest.foo";
480     static const WCHAR wszFileName[] = { 'w','i','n','e','t','e','s','t','.','f','o','o',0 };
481     static const WCHAR wszDirName[] = { 'w','i','n','e','t','e','s','t',0 };
482
483     /* I'm trying to figure if there is a functional difference between calling
484      * SHGetPathFromIDListW and calling GetDisplayNameOf(SHGDN_FORPARSING) after
485      * binding to the shellfolder. One thing I thought of was that perhaps 
486      * SHGetPathFromIDListW would be able to get the path to a file, which does
487      * not exist anymore, while the other method wouldn't. It turns out there's
488      * no functional difference in this respect.
489      */
490
491     if(!pSHGetSpecialFolderPathA) {
492         win_skip("SHGetSpecialFolderPathA is not available\n");
493         return;
494     }
495
496     /* First creating a directory in MyDocuments and a file in this directory. */
497     result = pSHGetSpecialFolderPathA(NULL, szTestDir, CSIDL_PERSONAL, FALSE);
498     ok(result, "SHGetSpecialFolderPathA failed! Last error: %u\n", GetLastError());
499     if (!result) return;
500
501     /* Use ANSI file functions so this works on Windows 9x */
502     lstrcatA(szTestDir, "\\winetest");
503     CreateDirectoryA(szTestDir, NULL);
504     attr=GetFileAttributesA(szTestDir);
505     if (attr == INVALID_FILE_ATTRIBUTES || !(attr & FILE_ATTRIBUTE_DIRECTORY))
506     {
507         ok(0, "unable to create the '%s' directory\n", szTestDir);
508         return;
509     }
510
511     lstrcpyA(szTestFile, szTestDir);
512     lstrcatA(szTestFile, "\\");
513     lstrcatA(szTestFile, szFileName);
514     hTestFile = CreateFileA(szTestFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
515     ok((hTestFile != INVALID_HANDLE_VALUE), "CreateFileA failed! Last error: %u\n", GetLastError());
516     if (hTestFile == INVALID_HANDLE_VALUE) return;
517     CloseHandle(hTestFile);
518
519     /* Getting an itemidlist for the file. */
520     hr = SHGetDesktopFolder(&psfDesktop);
521     ok(hr == S_OK, "SHGetDesktopFolder failed! hr = %08x\n", hr);
522     if (hr != S_OK) return;
523
524     MultiByteToWideChar(CP_ACP, 0, szTestFile, -1, wszTestFile, MAX_PATH);
525
526     hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszTestFile, NULL, &pidlTestFile, NULL);
527     ok(hr == S_OK, "Desktop->ParseDisplayName failed! hr = %08x\n", hr);
528     if (hr != S_OK) {
529         IShellFolder_Release(psfDesktop);
530         return;
531     }
532
533     pidlLast = pILFindLastID(pidlTestFile);
534     ok(pidlLast->mkid.cb >=76 ||
535         broken(pidlLast->mkid.cb == 28) || /* W2K */
536         broken(pidlLast->mkid.cb == 40), /* Win9x, WinME */
537         "Expected pidl length of at least 76, got %d.\n", pidlLast->mkid.cb);
538     if (pidlLast->mkid.cb >= 28) {
539         ok(!lstrcmpA((CHAR*)&pidlLast->mkid.abID[12], szFileName),
540             "Filename should be stored as ansi-string at this position!\n");
541     }
542     /* WinXP and up store the filenames as both ANSI and UNICODE in the pidls */
543     if (pidlLast->mkid.cb >= 76) {
544         ok(!lstrcmpW((WCHAR*)&pidlLast->mkid.abID[46], wszFileName) ||
545             (pidlLast->mkid.cb >= 94 && !lstrcmpW((WCHAR*)&pidlLast->mkid.abID[64], wszFileName)) ||  /* Vista */
546             (pidlLast->mkid.cb >= 98 && !lstrcmpW((WCHAR*)&pidlLast->mkid.abID[68], wszFileName)), /* Win7 */
547             "Filename should be stored as wchar-string at this position!\n");
548     }
549     
550     /* It seems as if we cannot bind to regular files on windows, but only directories. 
551      */
552     hr = IShellFolder_BindToObject(psfDesktop, pidlTestFile, NULL, &IID_IUnknown, (VOID**)&psfFile);
553     todo_wine
554     ok (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) ||
555         hr == E_NOTIMPL || /* Vista */
556         broken(hr == S_OK), /* Win9x, W2K */
557         "hr = %08x\n", hr);
558     if (hr == S_OK) {
559         IShellFolder_Release(psfFile);
560     }
561
562     if (!pSHBindToParent)
563     {
564         win_skip("SHBindToParent is missing\n");
565         DeleteFileA(szTestFile);
566         RemoveDirectoryA(szTestDir);
567         return;
568     }
569   
570     /* Some tests for IShellFolder::SetNameOf */
571     if (pSHGetFolderPathAndSubDirA)
572     {
573         hr = pSHBindToParent(pidlTestFile, &IID_IShellFolder, (VOID**)&psfPersonal, &pidlLast);
574         ok(hr == S_OK, "SHBindToParent failed! hr = %08x\n", hr);
575         if (hr == S_OK) {
576             /* It's ok to use this fixed path. Call will fail anyway. */
577             WCHAR wszAbsoluteFilename[] = { 'C',':','\\','w','i','n','e','t','e','s','t', 0 };
578             LPITEMIDLIST pidlNew;
579
580             /* The pidl returned through the last parameter of SetNameOf is a simple one. */
581             hr = IShellFolder_SetNameOf(psfPersonal, NULL, pidlLast, wszDirName, SHGDN_NORMAL, &pidlNew);
582             ok (hr == S_OK, "SetNameOf failed! hr = %08x\n", hr);
583             if (hr == S_OK)
584             {
585                 ok (((LPITEMIDLIST)((LPBYTE)pidlNew+pidlNew->mkid.cb))->mkid.cb == 0,
586                     "pidl returned from SetNameOf should be simple!\n");
587
588                 /* Passing an absolute path to SetNameOf fails. The HRESULT code indicates that SetNameOf
589                  * is implemented on top of SHFileOperation in WinXP. */
590                 hr = IShellFolder_SetNameOf(psfPersonal, NULL, pidlNew, wszAbsoluteFilename,
591                         SHGDN_FORPARSING, NULL);
592                 ok (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED), "SetNameOf succeeded! hr = %08x\n", hr);
593
594                 /* Rename the file back to its original name. SetNameOf ignores the fact, that the
595                  * SHGDN flags specify an absolute path. */
596                 hr = IShellFolder_SetNameOf(psfPersonal, NULL, pidlNew, wszFileName, SHGDN_FORPARSING, NULL);
597                 ok (hr == S_OK, "SetNameOf failed! hr = %08x\n", hr);
598
599                 pILFree(pidlNew);
600             }
601
602             IShellFolder_Release(psfPersonal);
603         }
604     }
605     else
606         win_skip("Avoid needs of interaction on Win2k\n");
607
608     /* Deleting the file and the directory */
609     DeleteFileA(szTestFile);
610     RemoveDirectoryA(szTestDir);
611
612     /* SHGetPathFromIDListW still works, although the file is not present anymore. */
613     if (pSHGetPathFromIDListW)
614     {
615         result = pSHGetPathFromIDListW(pidlTestFile, wszTestFile2);
616         ok (result, "SHGetPathFromIDListW failed! Last error: %u\n", GetLastError());
617         ok (!lstrcmpiW(wszTestFile, wszTestFile2), "SHGetPathFromIDListW returns incorrect path!\n");
618     }
619
620     /* SHBindToParent fails, if called with a NULL PIDL. */
621     hr = pSHBindToParent(NULL, &IID_IShellFolder, (VOID**)&psfPersonal, &pidlLast);
622     ok (hr != S_OK, "SHBindToParent(NULL) should fail!\n");
623
624     /* But it succeeds with an empty PIDL. */
625     hr = pSHBindToParent(pidlEmpty, &IID_IShellFolder, (VOID**)&psfPersonal, &pidlLast);
626     ok (hr == S_OK, "SHBindToParent(empty PIDL) should succeed! hr = %08x\n", hr);
627     ok (pidlLast == pidlEmpty, "The last element of an empty PIDL should be the PIDL itself!\n");
628     if (hr == S_OK)
629         IShellFolder_Release(psfPersonal);
630     
631     /* Binding to the folder and querying the display name of the file also works. */
632     hr = pSHBindToParent(pidlTestFile, &IID_IShellFolder, (VOID**)&psfPersonal, &pidlLast); 
633     ok (hr == S_OK, "SHBindToParent failed! hr = %08x\n", hr);
634     if (hr != S_OK) {
635         IShellFolder_Release(psfDesktop);
636         return;
637     }
638
639     /* This test shows that Windows doesn't allocate a new pidlLast, but returns a pointer into 
640      * pidlTestFile (In accordance with MSDN). */
641     ok (pILFindLastID(pidlTestFile) == pidlLast, 
642                                 "SHBindToParent doesn't return the last id of the pidl param!\n");
643     
644     hr = IShellFolder_GetDisplayNameOf(psfPersonal, pidlLast, SHGDN_FORPARSING, &strret);
645     ok (hr == S_OK, "Personal->GetDisplayNameOf failed! hr = %08x\n", hr);
646     if (hr != S_OK) {
647         IShellFolder_Release(psfDesktop);
648         IShellFolder_Release(psfPersonal);
649         return;
650     }
651
652     if (pStrRetToBufW)
653     {
654         hr = pStrRetToBufW(&strret, pidlLast, wszTestFile2, MAX_PATH);
655         ok (hr == S_OK, "StrRetToBufW failed! hr = %08x\n", hr);
656         ok (!lstrcmpiW(wszTestFile, wszTestFile2), "GetDisplayNameOf returns incorrect path!\n");
657     }
658     
659     ILFree(pidlTestFile);
660     IShellFolder_Release(psfDesktop);
661     IShellFolder_Release(psfPersonal);
662 }
663
664 static void test_CallForAttributes(void)
665 {
666     HKEY hKey;
667     LONG lResult;
668     HRESULT hr;
669     DWORD dwSize;
670     LPSHELLFOLDER psfDesktop;
671     LPITEMIDLIST pidlMyDocuments;
672     DWORD dwAttributes, dwCallForAttributes, dwOrigAttributes, dwOrigCallForAttributes;
673     static const WCHAR wszAttributes[] = { 'A','t','t','r','i','b','u','t','e','s',0 };
674     static const WCHAR wszCallForAttributes[] = { 
675         'C','a','l','l','F','o','r','A','t','t','r','i','b','u','t','e','s',0 };
676     static const WCHAR wszMyDocumentsKey[] = {
677         'C','L','S','I','D','\\','{','4','5','0','D','8','F','B','A','-','A','D','2','5','-',
678         '1','1','D','0','-','9','8','A','8','-','0','8','0','0','3','6','1','B','1','1','0','3','}',
679         '\\','S','h','e','l','l','F','o','l','d','e','r',0 };
680     WCHAR wszMyDocuments[] = {
681         ':',':','{','4','5','0','D','8','F','B','A','-','A','D','2','5','-','1','1','D','0','-',
682         '9','8','A','8','-','0','8','0','0','3','6','1','B','1','1','0','3','}',0 };
683     
684     /* For the root of a namespace extension, the attributes are not queried by binding
685      * to the object and calling GetAttributesOf. Instead, the attributes are read from 
686      * the registry value HKCR/CLSID/{...}/ShellFolder/Attributes. This is documented on MSDN.
687      *
688      * The MyDocuments shellfolder on WinXP has a HKCR/CLSID/{...}/ShellFolder/CallForAttributes
689      * value. It seems that if the folder is queried for one of the flags set in CallForAttributes,
690      * the shell does bind to the folder object and calls GetAttributesOf. This is not documented
691      * on MSDN. This test is meant to document the observed behaviour on WinXP SP2.
692      */
693     hr = SHGetDesktopFolder(&psfDesktop);
694     ok (hr == S_OK, "SHGetDesktopFolder failed! hr = %08x\n", hr);
695     if (hr != S_OK) return;
696     
697     hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyDocuments, NULL, 
698                                        &pidlMyDocuments, NULL);
699     ok (hr == S_OK ||
700         broken(hr == E_INVALIDARG), /* Win95, NT4 */
701         "Desktop's ParseDisplayName failed to parse MyDocuments's CLSID! hr = %08x\n", hr);
702     if (hr != S_OK) {
703         IShellFolder_Release(psfDesktop);
704         return;
705     }
706
707     dwAttributes = 0xffffffff;
708     hr = IShellFolder_GetAttributesOf(psfDesktop, 1, 
709                                       (LPCITEMIDLIST*)&pidlMyDocuments, &dwAttributes);
710     ok (hr == S_OK, "Desktop->GetAttributesOf(MyDocuments) failed! hr = %08x\n", hr);
711
712     /* We need the following setup (as observed on WinXP SP2), for the tests to make sense. */
713     ok (dwAttributes & SFGAO_FILESYSTEM, "SFGAO_FILESYSTEM attribute is not set for MyDocuments!\n");
714     ok (!(dwAttributes & SFGAO_ISSLOW), "SFGAO_ISSLOW attribute is set for MyDocuments!\n");
715     ok (!(dwAttributes & SFGAO_GHOSTED), "SFGAO_GHOSTED attribute is set for MyDocuments!\n");
716
717     /* We don't have the MyDocuments shellfolder in wine yet, and thus we don't have the registry
718      * key. So the test will return at this point, if run on wine. 
719      */
720     lResult = RegOpenKeyExW(HKEY_CLASSES_ROOT, wszMyDocumentsKey, 0, KEY_WRITE|KEY_READ, &hKey);
721     ok (lResult == ERROR_SUCCESS ||
722         lResult == ERROR_ACCESS_DENIED,
723         "RegOpenKeyEx failed! result: %08x\n", lResult);
724     if (lResult != ERROR_SUCCESS) {
725         if (lResult == ERROR_ACCESS_DENIED)
726             skip("Not enough rights to open the registry key\n");
727         IMalloc_Free(ppM, pidlMyDocuments);
728         IShellFolder_Release(psfDesktop);
729         return;
730     }
731     
732     /* Query MyDocuments' Attributes value, to be able to restore it later. */
733     dwSize = sizeof(DWORD);
734     lResult = RegQueryValueExW(hKey, wszAttributes, NULL, NULL, (LPBYTE)&dwOrigAttributes, &dwSize);
735     ok (lResult == ERROR_SUCCESS, "RegQueryValueEx failed! result: %08x\n", lResult);
736     if (lResult != ERROR_SUCCESS) {
737         RegCloseKey(hKey);
738         IMalloc_Free(ppM, pidlMyDocuments);
739         IShellFolder_Release(psfDesktop);
740         return;
741     }
742
743     /* Query MyDocuments' CallForAttributes value, to be able to restore it later. */
744     dwSize = sizeof(DWORD);
745     lResult = RegQueryValueExW(hKey, wszCallForAttributes, NULL, NULL, 
746                               (LPBYTE)&dwOrigCallForAttributes, &dwSize);
747     ok (lResult == ERROR_SUCCESS, "RegQueryValueEx failed! result: %08x\n", lResult);
748     if (lResult != ERROR_SUCCESS) {
749         RegCloseKey(hKey);
750         IMalloc_Free(ppM, pidlMyDocuments);
751         IShellFolder_Release(psfDesktop);
752         return;
753     }
754     
755     /* Define via the Attributes value that MyDocuments attributes are SFGAO_ISSLOW and 
756      * SFGAO_GHOSTED and that MyDocuments should be called for the SFGAO_ISSLOW and
757      * SFGAO_FILESYSTEM attributes. */
758     dwAttributes = SFGAO_ISSLOW|SFGAO_GHOSTED;
759     RegSetValueExW(hKey, wszAttributes, 0, REG_DWORD, (LPBYTE)&dwAttributes, sizeof(DWORD));
760     dwCallForAttributes = SFGAO_ISSLOW|SFGAO_FILESYSTEM;
761     RegSetValueExW(hKey, wszCallForAttributes, 0, REG_DWORD, 
762                    (LPBYTE)&dwCallForAttributes, sizeof(DWORD));
763
764     /* Although it is not set in CallForAttributes, the SFGAO_GHOSTED flag is reset by 
765      * GetAttributesOf. It seems that once there is a single attribute queried, for which
766      * CallForAttributes is set, all flags are taken from the GetAttributesOf call and
767      * the flags in Attributes are ignored. 
768      */
769     dwAttributes = SFGAO_ISSLOW|SFGAO_GHOSTED|SFGAO_FILESYSTEM;
770     hr = IShellFolder_GetAttributesOf(psfDesktop, 1, 
771                                       (LPCITEMIDLIST*)&pidlMyDocuments, &dwAttributes);
772     ok (hr == S_OK, "Desktop->GetAttributesOf(MyDocuments) failed! hr = %08x\n", hr);
773     if (hr == S_OK)
774         ok (dwAttributes == SFGAO_FILESYSTEM, 
775             "Desktop->GetAttributes(MyDocuments) returned unexpected attributes: %08x\n", 
776             dwAttributes);
777
778     /* Restore MyDocuments' original Attributes and CallForAttributes registry values */
779     RegSetValueExW(hKey, wszAttributes, 0, REG_DWORD, (LPBYTE)&dwOrigAttributes, sizeof(DWORD));
780     RegSetValueExW(hKey, wszCallForAttributes, 0, REG_DWORD, 
781                    (LPBYTE)&dwOrigCallForAttributes, sizeof(DWORD));
782     RegCloseKey(hKey);
783     IMalloc_Free(ppM, pidlMyDocuments);
784     IShellFolder_Release(psfDesktop);
785 }
786
787 static void test_GetAttributesOf(void) 
788 {
789     HRESULT hr;
790     LPSHELLFOLDER psfDesktop, psfMyComputer;
791     SHITEMID emptyitem = { 0, { 0 } };
792     LPCITEMIDLIST pidlEmpty = (LPCITEMIDLIST)&emptyitem;
793     LPITEMIDLIST pidlMyComputer;
794     DWORD dwFlags;
795     static const DWORD desktopFlags[] = {
796         /* WinXP */
797         SFGAO_STORAGE | SFGAO_HASPROPSHEET | SFGAO_STORAGEANCESTOR | SFGAO_FILESYSANCESTOR |
798         SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_HASSUBFOLDER,
799         /* Win2k */
800         SFGAO_CANRENAME | SFGAO_HASPROPSHEET | SFGAO_STREAM | SFGAO_FILESYSANCESTOR |
801         SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_HASSUBFOLDER,
802         /* WinMe, Win9x, WinNT*/
803         SFGAO_CANRENAME | SFGAO_HASPROPSHEET | SFGAO_FILESYSANCESTOR |
804         SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_HASSUBFOLDER
805     };
806     static const DWORD myComputerFlags[] = {
807         /* WinXP */
808         SFGAO_CANRENAME | SFGAO_CANDELETE | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET |
809         SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER,
810         /* Win2k */
811         SFGAO_CANRENAME | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET | SFGAO_STREAM |
812         SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER,
813         /* WinMe, Win9x, WinNT */
814         SFGAO_CANRENAME | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET | SFGAO_FILESYSANCESTOR |
815         SFGAO_FOLDER | SFGAO_HASSUBFOLDER,
816         /* Win95, WinNT when queried directly */
817         SFGAO_CANLINK | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET | SFGAO_FILESYSANCESTOR |
818         SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_HASSUBFOLDER
819     };
820     WCHAR wszMyComputer[] = { 
821         ':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
822         'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };
823     char  cCurrDirA [MAX_PATH] = {0};
824     WCHAR cCurrDirW [MAX_PATH];
825     static WCHAR cTestDirW[] = {'t','e','s','t','d','i','r',0};
826     IShellFolder *IDesktopFolder, *testIShellFolder;
827     ITEMIDLIST *newPIDL;
828     int len, i;
829     BOOL foundFlagsMatch;
830
831     hr = SHGetDesktopFolder(&psfDesktop);
832     ok (hr == S_OK, "SHGetDesktopFolder failed! hr = %08x\n", hr);
833     if (hr != S_OK) return;
834
835     /* The Desktop attributes can be queried with a single empty itemidlist, .. */
836     dwFlags = 0xffffffff;
837     hr = IShellFolder_GetAttributesOf(psfDesktop, 1, &pidlEmpty, &dwFlags);
838     ok (hr == S_OK, "Desktop->GetAttributesOf(empty pidl) failed! hr = %08x\n", hr);
839     for (i = 0, foundFlagsMatch = FALSE; !foundFlagsMatch &&
840          i < sizeof(desktopFlags) / sizeof(desktopFlags[0]); i++)
841     {
842         if (desktopFlags[i] == dwFlags)
843             foundFlagsMatch = TRUE;
844     }
845     ok (foundFlagsMatch, "Wrong Desktop attributes: %08x\n", dwFlags);
846
847     /* .. or with no itemidlist at all. */
848     dwFlags = 0xffffffff;
849     hr = IShellFolder_GetAttributesOf(psfDesktop, 0, NULL, &dwFlags);
850     ok (hr == S_OK, "Desktop->GetAttributesOf(NULL) failed! hr = %08x\n", hr);
851     for (i = 0, foundFlagsMatch = FALSE; !foundFlagsMatch &&
852          i < sizeof(desktopFlags) / sizeof(desktopFlags[0]); i++)
853     {
854         if (desktopFlags[i] == dwFlags)
855             foundFlagsMatch = TRUE;
856     }
857     ok (foundFlagsMatch, "Wrong Desktop attributes: %08x\n", dwFlags);
858    
859     /* Testing the attributes of the MyComputer shellfolder */
860     hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyComputer, NULL, &pidlMyComputer, NULL);
861     ok (hr == S_OK, "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08x\n", hr);
862     if (hr != S_OK) {
863         IShellFolder_Release(psfDesktop);
864         return;
865     }
866
867     /* Windows sets the SFGAO_CANLINK flag, when MyComputer is queried via the Desktop
868      * folder object. It doesn't do this, if MyComputer is queried directly (see below).
869      */
870     dwFlags = 0xffffffff;
871     hr = IShellFolder_GetAttributesOf(psfDesktop, 1, (LPCITEMIDLIST*)&pidlMyComputer, &dwFlags);
872     ok (hr == S_OK, "Desktop->GetAttributesOf(MyComputer) failed! hr = %08x\n", hr);
873     for (i = 0, foundFlagsMatch = FALSE; !foundFlagsMatch &&
874          i < sizeof(myComputerFlags) / sizeof(myComputerFlags[0]); i++)
875     {
876         if ((myComputerFlags[i] | SFGAO_CANLINK) == dwFlags)
877             foundFlagsMatch = TRUE;
878     }
879     todo_wine
880     ok (foundFlagsMatch, "Wrong MyComputer attributes: %08x\n", dwFlags);
881
882     hr = IShellFolder_BindToObject(psfDesktop, pidlMyComputer, NULL, &IID_IShellFolder, (LPVOID*)&psfMyComputer);
883     ok (hr == S_OK, "Desktop failed to bind to MyComputer object! hr = %08x\n", hr);
884     IShellFolder_Release(psfDesktop);
885     IMalloc_Free(ppM, pidlMyComputer);
886     if (hr != S_OK) return;
887
888     hr = IShellFolder_GetAttributesOf(psfMyComputer, 1, &pidlEmpty, &dwFlags);
889     todo_wine
890     ok (hr == E_INVALIDARG ||
891         broken(hr == S_OK), /* W2K and earlier */
892         "MyComputer->GetAttributesOf(emtpy pidl) should fail! hr = %08x\n", hr);
893
894     dwFlags = 0xffffffff;
895     hr = IShellFolder_GetAttributesOf(psfMyComputer, 0, NULL, &dwFlags);
896     ok (hr == S_OK, "MyComputer->GetAttributesOf(NULL) failed! hr = %08x\n", hr);
897     for (i = 0, foundFlagsMatch = FALSE; !foundFlagsMatch &&
898          i < sizeof(myComputerFlags) / sizeof(myComputerFlags[0]); i++)
899     {
900         if (myComputerFlags[i] == dwFlags)
901             foundFlagsMatch = TRUE;
902     }
903     todo_wine
904     ok (foundFlagsMatch, "Wrong MyComputer attributes: %08x\n", dwFlags);
905
906     IShellFolder_Release(psfMyComputer);
907
908     GetCurrentDirectoryA(MAX_PATH, cCurrDirA);
909     len = lstrlenA(cCurrDirA);
910
911     if (len == 0) {
912         win_skip("GetCurrentDirectoryA returned empty string. Skipping test_GetAttributesOf\n");
913         return;
914     }
915     if (len > 3 && cCurrDirA[len-1] == '\\')
916         cCurrDirA[len-1] = 0;
917
918     /* create test directory */
919     CreateFilesFolders();
920
921     MultiByteToWideChar(CP_ACP, 0, cCurrDirA, -1, cCurrDirW, MAX_PATH);
922  
923     hr = SHGetDesktopFolder(&IDesktopFolder);
924     ok(hr == S_OK, "SHGetDesktopfolder failed %08x\n", hr);
925
926     hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cCurrDirW, NULL, &newPIDL, 0);
927     ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
928
929     hr = IShellFolder_BindToObject(IDesktopFolder, newPIDL, NULL, (REFIID)&IID_IShellFolder, (LPVOID *)&testIShellFolder);
930     ok(hr == S_OK, "BindToObject failed %08x\n", hr);
931
932     IMalloc_Free(ppM, newPIDL);
933
934     /* get relative PIDL */
935     hr = IShellFolder_ParseDisplayName(testIShellFolder, NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
936     ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
937
938     /* test the shell attributes of the test directory using the relative PIDL */
939     dwFlags = SFGAO_FOLDER;
940     hr = IShellFolder_GetAttributesOf(testIShellFolder, 1, (LPCITEMIDLIST*)&newPIDL, &dwFlags);
941     ok (hr == S_OK, "Desktop->GetAttributesOf() failed! hr = %08x\n", hr);
942     ok ((dwFlags&SFGAO_FOLDER), "Wrong directory attribute for relative PIDL: %08x\n", dwFlags);
943
944     /* free memory */
945     IMalloc_Free(ppM, newPIDL);
946
947     /* append testdirectory name to path */
948     if (cCurrDirA[len-1] == '\\')
949         cCurrDirA[len-1] = 0;
950     lstrcatA(cCurrDirA, "\\testdir");
951     MultiByteToWideChar(CP_ACP, 0, cCurrDirA, -1, cCurrDirW, MAX_PATH);
952
953     hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cCurrDirW, NULL, &newPIDL, 0);
954     ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
955
956     /* test the shell attributes of the test directory using the absolute PIDL */
957     dwFlags = SFGAO_FOLDER;
958     hr = IShellFolder_GetAttributesOf(IDesktopFolder, 1, (LPCITEMIDLIST*)&newPIDL, &dwFlags);
959     ok (hr == S_OK, "Desktop->GetAttributesOf() failed! hr = %08x\n", hr);
960     ok ((dwFlags&SFGAO_FOLDER), "Wrong directory attribute for absolute PIDL: %08x\n", dwFlags);
961
962     /* free memory */
963     IMalloc_Free(ppM, newPIDL);
964
965     IShellFolder_Release(testIShellFolder);
966
967     Cleanup();
968
969     IShellFolder_Release(IDesktopFolder);
970 }
971
972 static void test_SHGetPathFromIDList(void)
973 {
974     SHITEMID emptyitem = { 0, { 0 } };
975     LPCITEMIDLIST pidlEmpty = (LPCITEMIDLIST)&emptyitem;
976     LPITEMIDLIST pidlMyComputer;
977     WCHAR wszPath[MAX_PATH], wszDesktop[MAX_PATH];
978     BOOL result;
979     HRESULT hr;
980     LPSHELLFOLDER psfDesktop;
981     WCHAR wszMyComputer[] = { 
982         ':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
983         'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };
984     WCHAR wszFileName[MAX_PATH];
985     LPITEMIDLIST pidlTestFile;
986     HANDLE hTestFile;
987     STRRET strret;
988     static WCHAR wszTestFile[] = {
989         'w','i','n','e','t','e','s','t','.','f','o','o',0 };
990         HRESULT (WINAPI *pSHGetSpecialFolderLocation)(HWND, int, LPITEMIDLIST *);
991         HMODULE hShell32;
992         LPITEMIDLIST pidlPrograms;
993
994     if(!pSHGetPathFromIDListW || !pSHGetSpecialFolderPathW)
995     {
996         win_skip("SHGetPathFromIDListW() or SHGetSpecialFolderPathW() is missing\n");
997         return;
998     }
999
1000     /* Calling SHGetPathFromIDListW with no pidl should return the empty string */
1001     wszPath[0] = 'a';
1002     wszPath[1] = '\0';
1003     result = pSHGetPathFromIDListW(NULL, wszPath);
1004     ok(!result, "Expected failure\n");
1005     ok(!wszPath[0], "Expected empty string\n");
1006
1007     /* Calling SHGetPathFromIDListW with an empty pidl should return the desktop folder's path. */
1008     result = pSHGetSpecialFolderPathW(NULL, wszDesktop, CSIDL_DESKTOP, FALSE);
1009     ok(result, "SHGetSpecialFolderPathW(CSIDL_DESKTOP) failed! Last error: %u\n", GetLastError());
1010     if (!result) return;
1011
1012     /* Check if we are on Win9x */
1013     SetLastError(0xdeadbeef);
1014     lstrcmpiW(wszDesktop, wszDesktop);
1015     if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
1016     {
1017         win_skip("Most W-calls are not implemented\n");
1018         return;
1019     }
1020
1021     result = pSHGetPathFromIDListW(pidlEmpty, wszPath);
1022     ok(result, "SHGetPathFromIDListW failed! Last error: %u\n", GetLastError());
1023     if (!result) return;
1024     ok(!lstrcmpiW(wszDesktop, wszPath), "SHGetPathFromIDListW didn't return desktop path for empty pidl!\n");
1025
1026     /* MyComputer does not map to a filesystem path. SHGetPathFromIDListW should fail. */
1027     hr = SHGetDesktopFolder(&psfDesktop);
1028     ok (hr == S_OK, "SHGetDesktopFolder failed! hr = %08x\n", hr);
1029     if (hr != S_OK) return;
1030
1031     hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyComputer, NULL, &pidlMyComputer, NULL);
1032     ok (hr == S_OK, "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08x\n", hr);
1033     if (hr != S_OK) {
1034         IShellFolder_Release(psfDesktop);
1035         return;
1036     }
1037
1038     SetLastError(0xdeadbeef);
1039     wszPath[0] = 'a';
1040     wszPath[1] = '\0';
1041     result = pSHGetPathFromIDListW(pidlMyComputer, wszPath);
1042     ok (!result, "SHGetPathFromIDListW succeeded where it shouldn't!\n");
1043     ok (GetLastError()==0xdeadbeef ||
1044         GetLastError()==ERROR_SUCCESS, /* Vista and higher */
1045         "Unexpected last error from SHGetPathFromIDListW: %u\n", GetLastError());
1046     ok (!wszPath[0], "Expected empty path\n");
1047     if (result) {
1048         IShellFolder_Release(psfDesktop);
1049         return;
1050     }
1051
1052     IMalloc_Free(ppM, pidlMyComputer);
1053
1054     result = pSHGetSpecialFolderPathW(NULL, wszFileName, CSIDL_DESKTOPDIRECTORY, FALSE);
1055     ok(result, "SHGetSpecialFolderPathW failed! Last error: %u\n", GetLastError());
1056     if (!result) {
1057         IShellFolder_Release(psfDesktop);
1058         return;
1059     }
1060     myPathAddBackslashW(wszFileName);
1061     lstrcatW(wszFileName, wszTestFile);
1062     hTestFile = CreateFileW(wszFileName, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL);
1063     ok(hTestFile != INVALID_HANDLE_VALUE, "CreateFileW failed! Last error: %u\n", GetLastError());
1064     if (hTestFile == INVALID_HANDLE_VALUE) {
1065         IShellFolder_Release(psfDesktop);
1066         return;
1067     }
1068     CloseHandle(hTestFile);
1069
1070     hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszTestFile, NULL, &pidlTestFile, NULL);
1071     ok (hr == S_OK, "Desktop's ParseDisplayName failed to parse filename hr = %08x\n", hr);
1072     if (hr != S_OK) {
1073         IShellFolder_Release(psfDesktop);
1074         DeleteFileW(wszFileName);
1075         IMalloc_Free(ppM, pidlTestFile);
1076         return;
1077     }
1078
1079     /* This test is to show that the Desktop shellfolder prepends the CSIDL_DESKTOPDIRECTORY
1080      * path for files placed on the desktop, if called with SHGDN_FORPARSING. */
1081     hr = IShellFolder_GetDisplayNameOf(psfDesktop, pidlTestFile, SHGDN_FORPARSING, &strret);
1082     ok (hr == S_OK, "Desktop's GetDisplayNamfOf failed! hr = %08x\n", hr);
1083     IShellFolder_Release(psfDesktop);
1084     DeleteFileW(wszFileName);
1085     if (hr != S_OK) {
1086         IMalloc_Free(ppM, pidlTestFile);
1087         return;
1088     }
1089     if (pStrRetToBufW)
1090     {
1091         pStrRetToBufW(&strret, pidlTestFile, wszPath, MAX_PATH);
1092         ok(0 == lstrcmpW(wszFileName, wszPath), 
1093            "Desktop->GetDisplayNameOf(pidlTestFile, SHGDN_FORPARSING) "
1094            "returned incorrect path for file placed on desktop\n");
1095     }
1096
1097     result = pSHGetPathFromIDListW(pidlTestFile, wszPath);
1098     ok(result, "SHGetPathFromIDListW failed! Last error: %u\n", GetLastError());
1099     IMalloc_Free(ppM, pidlTestFile);
1100     if (!result) return;
1101     ok(0 == lstrcmpW(wszFileName, wszPath), "SHGetPathFromIDListW returned incorrect path for file placed on desktop\n");
1102
1103
1104         /* Test if we can get the path from the start menu "program files" PIDL. */
1105     hShell32 = GetModuleHandleA("shell32");
1106     pSHGetSpecialFolderLocation = (void *)GetProcAddress(hShell32, "SHGetSpecialFolderLocation");
1107
1108     hr = pSHGetSpecialFolderLocation(NULL, CSIDL_PROGRAM_FILES, &pidlPrograms);
1109     ok(hr == S_OK, "SHGetFolderLocation failed: 0x%08x\n", hr);
1110
1111     SetLastError(0xdeadbeef);
1112     result = pSHGetPathFromIDListW(pidlPrograms, wszPath);
1113         IMalloc_Free(ppM, pidlPrograms);
1114     ok(result, "SHGetPathFromIDListW failed\n");
1115 }
1116
1117 static void test_EnumObjects_and_CompareIDs(void)
1118 {
1119     ITEMIDLIST *newPIDL;
1120     IShellFolder *IDesktopFolder, *testIShellFolder;
1121     char  cCurrDirA [MAX_PATH] = {0};
1122     static const CHAR cTestDirA[] = "\\testdir";
1123     WCHAR cTestDirW[MAX_PATH];
1124     int len;
1125     HRESULT hr;
1126
1127     GetCurrentDirectoryA(MAX_PATH, cCurrDirA);
1128     len = lstrlenA(cCurrDirA);
1129
1130     if(len == 0) {
1131         win_skip("GetCurrentDirectoryA returned empty string. Skipping test_EnumObjects_and_CompareIDs\n");
1132         return;
1133     }
1134     if(cCurrDirA[len-1] == '\\')
1135         cCurrDirA[len-1] = 0;
1136
1137     lstrcatA(cCurrDirA, cTestDirA);
1138     MultiByteToWideChar(CP_ACP, 0, cCurrDirA, -1, cTestDirW, MAX_PATH);
1139
1140     hr = SHGetDesktopFolder(&IDesktopFolder);
1141     ok(hr == S_OK, "SHGetDesktopfolder failed %08x\n", hr);
1142
1143     CreateFilesFolders();
1144
1145     hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
1146     ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
1147
1148     hr = IShellFolder_BindToObject(IDesktopFolder, newPIDL, NULL, (REFIID)&IID_IShellFolder, (LPVOID *)&testIShellFolder);
1149     ok(hr == S_OK, "BindToObject failed %08x\n", hr);
1150
1151     test_EnumObjects(testIShellFolder);
1152
1153     IShellFolder_Release(testIShellFolder);
1154
1155     Cleanup();
1156
1157     IMalloc_Free(ppM, newPIDL);
1158
1159     IShellFolder_Release(IDesktopFolder);
1160 }
1161
1162 /* A simple implementation of an IPropertyBag, which returns fixed values for
1163  * 'Target' and 'Attributes' properties.
1164  */
1165 static HRESULT WINAPI InitPropertyBag_IPropertyBag_QueryInterface(IPropertyBag *iface, REFIID riid,
1166     void **ppvObject) 
1167 {
1168     if (!ppvObject)
1169         return E_INVALIDARG;
1170
1171     if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IPropertyBag, riid)) {
1172         *ppvObject = iface;
1173     } else {
1174         ok (FALSE, "InitPropertyBag asked for unknown interface!\n");
1175         return E_NOINTERFACE;
1176     }
1177
1178     IPropertyBag_AddRef(iface);
1179     return S_OK;
1180 }
1181
1182 static ULONG WINAPI InitPropertyBag_IPropertyBag_AddRef(IPropertyBag *iface) {
1183     return 2;
1184 }
1185
1186 static ULONG WINAPI InitPropertyBag_IPropertyBag_Release(IPropertyBag *iface) {
1187     return 1;
1188 }
1189
1190 static HRESULT WINAPI InitPropertyBag_IPropertyBag_Read(IPropertyBag *iface, LPCOLESTR pszPropName,
1191     VARIANT *pVar, IErrorLog *pErrorLog)
1192 {
1193     static const WCHAR wszTargetSpecialFolder[] = {
1194         'T','a','r','g','e','t','S','p','e','c','i','a','l','F','o','l','d','e','r',0 };
1195     static const WCHAR wszTarget[] = {
1196         'T','a','r','g','e','t',0 };
1197     static const WCHAR wszAttributes[] = {
1198         'A','t','t','r','i','b','u','t','e','s',0 };
1199     static const WCHAR wszResolveLinkFlags[] = {
1200         'R','e','s','o','l','v','e','L','i','n','k','F','l','a','g','s',0 };
1201     static const WCHAR wszTargetKnownFolder[] = {
1202         'T','a','r','g','e','t','K','n','o','w','n','F','o','l','d','e','r',0 };
1203     static const WCHAR wszCLSID[] = {
1204         'C','L','S','I','D',0 };
1205        
1206     if (!lstrcmpW(pszPropName, wszTargetSpecialFolder)) {
1207         ok(V_VT(pVar) == VT_I4 ||
1208            broken(V_VT(pVar) == VT_BSTR),   /* Win2k */
1209            "Wrong variant type for 'TargetSpecialFolder' property!\n");
1210         return E_INVALIDARG;
1211     }
1212     
1213     if (!lstrcmpW(pszPropName, wszResolveLinkFlags)) 
1214     {
1215         ok(V_VT(pVar) == VT_UI4, "Wrong variant type for 'ResolveLinkFlags' property!\n");
1216         return E_INVALIDARG;
1217     }
1218
1219     if (!lstrcmpW(pszPropName, wszTarget)) {
1220         WCHAR wszPath[MAX_PATH];
1221         BOOL result;
1222         
1223         ok(V_VT(pVar) == VT_BSTR ||
1224            broken(V_VT(pVar) == VT_EMPTY),  /* Win2k */
1225            "Wrong variant type for 'Target' property!\n");
1226         if (V_VT(pVar) != VT_BSTR) return E_INVALIDARG;
1227
1228         result = pSHGetSpecialFolderPathW(NULL, wszPath, CSIDL_DESKTOPDIRECTORY, FALSE);
1229         ok(result, "SHGetSpecialFolderPathW(DESKTOPDIRECTORY) failed! %u\n", GetLastError());
1230         if (!result) return E_INVALIDARG;
1231
1232         V_BSTR(pVar) = SysAllocString(wszPath);
1233         return S_OK;
1234     }
1235
1236     if (!lstrcmpW(pszPropName, wszAttributes)) {
1237         ok(V_VT(pVar) == VT_UI4, "Wrong variant type for 'Attributes' property!\n");
1238         if (V_VT(pVar) != VT_UI4) return E_INVALIDARG;
1239         V_UI4(pVar) = SFGAO_FOLDER|SFGAO_HASSUBFOLDER|SFGAO_FILESYSANCESTOR|
1240                       SFGAO_CANRENAME|SFGAO_FILESYSTEM;
1241         return S_OK;
1242     }
1243
1244     if (!lstrcmpW(pszPropName, wszTargetKnownFolder)) {
1245         ok(V_VT(pVar) == VT_BSTR, "Wrong variant type for 'TargetKnownFolder' property!\n");
1246         /* TODO */
1247         return E_INVALIDARG;
1248     }
1249
1250     if (!lstrcmpW(pszPropName, wszCLSID)) {
1251         ok(V_VT(pVar) == VT_EMPTY, "Wrong variant type for 'CLSID' property!\n");
1252         /* TODO */
1253         return E_INVALIDARG;
1254     }
1255
1256     ok(FALSE, "PropertyBag was asked for unknown property %s (vt=%d)!\n", wine_dbgstr_w(pszPropName), V_VT(pVar));
1257     return E_INVALIDARG;
1258 }
1259
1260 static HRESULT WINAPI InitPropertyBag_IPropertyBag_Write(IPropertyBag *iface, LPCOLESTR pszPropName,
1261     VARIANT *pVar)
1262 {
1263     ok(FALSE, "Unexpected call to IPropertyBag_Write\n");
1264     return E_NOTIMPL;
1265 }
1266     
1267 static const IPropertyBagVtbl InitPropertyBag_IPropertyBagVtbl = {
1268     InitPropertyBag_IPropertyBag_QueryInterface,
1269     InitPropertyBag_IPropertyBag_AddRef,
1270     InitPropertyBag_IPropertyBag_Release,
1271     InitPropertyBag_IPropertyBag_Read,
1272     InitPropertyBag_IPropertyBag_Write
1273 };
1274
1275 static struct IPropertyBag InitPropertyBag = {
1276     &InitPropertyBag_IPropertyBagVtbl
1277 };
1278
1279 static void test_FolderShortcut(void) {
1280     IPersistPropertyBag *pPersistPropertyBag;
1281     IShellFolder *pShellFolder, *pDesktopFolder;
1282     IPersistFolder3 *pPersistFolder3;
1283     HRESULT hr;
1284     STRRET strret;
1285     WCHAR wszDesktopPath[MAX_PATH], wszBuffer[MAX_PATH];
1286     BOOL result;
1287     CLSID clsid;
1288     LPITEMIDLIST pidlCurrentFolder, pidlWineTestFolder, pidlSubFolder;
1289     HKEY hShellExtKey;
1290     WCHAR wszWineTestFolder[] = {
1291         ':',':','{','9','B','3','5','2','E','B','F','-','2','7','6','5','-','4','5','C','1','-',
1292         'B','4','C','6','-','8','5','C','C','7','F','7','A','B','C','6','4','}',0 };
1293     WCHAR wszShellExtKey[] = { 'S','o','f','t','w','a','r','e','\\',
1294         'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
1295         'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
1296         'E','x','p','l','o','r','e','r','\\','D','e','s','k','t','o','p','\\',
1297         'N','a','m','e','S','p','a','c','e','\\',
1298         '{','9','b','3','5','2','e','b','f','-','2','7','6','5','-','4','5','c','1','-',
1299         'b','4','c','6','-','8','5','c','c','7','f','7','a','b','c','6','4','}',0 };
1300     
1301     WCHAR wszSomeSubFolder[] = { 'S','u','b','F','o','l','d','e','r', 0};
1302     static const GUID CLSID_UnixDosFolder = 
1303         {0x9d20aae8, 0x0625, 0x44b0, {0x9c, 0xa7, 0x71, 0x88, 0x9c, 0x22, 0x54, 0xd9}};
1304
1305     if (!pSHGetSpecialFolderPathW || !pStrRetToBufW) {
1306         win_skip("SHGetSpecialFolderPathW and/or StrRetToBufW are not available\n");
1307         return;
1308     }
1309
1310     if (!pSHGetFolderPathAndSubDirA)
1311     {
1312         win_skip("FolderShortcut test doesn't work on Win2k\n");
1313         return;
1314     }
1315
1316     /* These tests basically show, that CLSID_FolderShortcuts are initialized
1317      * via their IPersistPropertyBag interface. And that the target folder
1318      * is taken from the IPropertyBag's 'Target' property.
1319      */
1320     hr = CoCreateInstance(&CLSID_FolderShortcut, NULL, CLSCTX_INPROC_SERVER, 
1321                           &IID_IPersistPropertyBag, (LPVOID*)&pPersistPropertyBag);
1322     if (hr == REGDB_E_CLASSNOTREG) {
1323         win_skip("CLSID_FolderShortcut is not implemented\n");
1324         return;
1325     }
1326     ok (hr == S_OK, "CoCreateInstance failed! hr = 0x%08x\n", hr);
1327     if (hr != S_OK) return;
1328
1329     hr = IPersistPropertyBag_Load(pPersistPropertyBag, &InitPropertyBag, NULL);
1330     ok(hr == S_OK, "IPersistPropertyBag_Load failed! hr = %08x\n", hr);
1331     if (hr != S_OK) {
1332         IPersistPropertyBag_Release(pPersistPropertyBag);
1333         return;
1334     }
1335
1336     hr = IPersistPropertyBag_QueryInterface(pPersistPropertyBag, &IID_IShellFolder, 
1337                                             (LPVOID*)&pShellFolder);
1338     IPersistPropertyBag_Release(pPersistPropertyBag);
1339     ok(hr == S_OK, "IPersistPropertyBag_QueryInterface(IShellFolder) failed! hr = %08x\n", hr);
1340     if (hr != S_OK) return;
1341
1342     hr = IShellFolder_GetDisplayNameOf(pShellFolder, NULL, SHGDN_FORPARSING, &strret);
1343     ok(hr == S_OK, "IShellFolder_GetDisplayNameOf(NULL) failed! hr = %08x\n", hr);
1344     if (hr != S_OK) {
1345         IShellFolder_Release(pShellFolder);
1346         return;
1347     }
1348
1349     result = pSHGetSpecialFolderPathW(NULL, wszDesktopPath, CSIDL_DESKTOPDIRECTORY, FALSE);
1350     ok(result, "SHGetSpecialFolderPathW(CSIDL_DESKTOPDIRECTORY) failed! %u\n", GetLastError());
1351     if (!result) return;
1352
1353     pStrRetToBufW(&strret, NULL, wszBuffer, MAX_PATH);
1354     ok(!lstrcmpiW(wszDesktopPath, wszBuffer), "FolderShortcut returned incorrect folder!\n");
1355
1356     hr = IShellFolder_QueryInterface(pShellFolder, &IID_IPersistFolder3, (LPVOID*)&pPersistFolder3);
1357     IShellFolder_Release(pShellFolder);
1358     ok(hr == S_OK, "IShellFolder_QueryInterface(IID_IPersistFolder3 failed! hr = 0x%08x\n", hr);
1359     if (hr != S_OK) return;
1360
1361     hr = IPersistFolder3_GetClassID(pPersistFolder3, &clsid);
1362     ok(hr == S_OK, "IPersistFolder3_GetClassID failed! hr=0x%08x\n", hr);
1363     ok(IsEqualCLSID(&clsid, &CLSID_FolderShortcut), "Unexpected CLSID!\n");
1364
1365     hr = IPersistFolder3_GetCurFolder(pPersistFolder3, &pidlCurrentFolder);
1366     todo_wine ok(hr == S_FALSE, "IPersistFolder3_GetCurFolder failed! hr=0x%08x\n", hr);
1367     ok(!pidlCurrentFolder, "IPersistFolder3_GetCurFolder should return a NULL pidl!\n");
1368                     
1369     /* For FolderShortcut objects, the Initialize method initialized the folder's position in the
1370      * shell namespace. The target folder, read from the property bag above, remains untouched. 
1371      * The following tests show this: The itemidlist for some imaginary shellfolder object
1372      * is created and the FolderShortcut is initialized with it. GetCurFolder now returns this
1373      * itemidlist, but GetDisplayNameOf still returns the path from above.
1374      */
1375     hr = SHGetDesktopFolder(&pDesktopFolder);
1376     ok (hr == S_OK, "SHGetDesktopFolder failed! hr = %08x\n", hr);
1377     if (hr != S_OK) return;
1378
1379     /* Temporarily register WineTestFolder as a shell namespace extension at the Desktop. 
1380      * Otherwise ParseDisplayName fails on WinXP with E_INVALIDARG */
1381     RegCreateKeyW(HKEY_CURRENT_USER, wszShellExtKey, &hShellExtKey);
1382     RegCloseKey(hShellExtKey);
1383     hr = IShellFolder_ParseDisplayName(pDesktopFolder, NULL, NULL, wszWineTestFolder, NULL,
1384                                        &pidlWineTestFolder, NULL);
1385     RegDeleteKeyW(HKEY_CURRENT_USER, wszShellExtKey);
1386     IShellFolder_Release(pDesktopFolder);
1387     ok (hr == S_OK, "IShellFolder::ParseDisplayName failed! hr = %08x\n", hr);
1388     if (hr != S_OK) return;
1389
1390     hr = IPersistFolder3_Initialize(pPersistFolder3, pidlWineTestFolder);
1391     ok (hr == S_OK, "IPersistFolder3::Initialize failed! hr = %08x\n", hr);
1392     if (hr != S_OK) {
1393         IPersistFolder3_Release(pPersistFolder3);
1394         pILFree(pidlWineTestFolder);
1395         return;
1396     }
1397
1398     hr = IPersistFolder3_GetCurFolder(pPersistFolder3, &pidlCurrentFolder);
1399     ok(hr == S_OK, "IPersistFolder3_GetCurFolder failed! hr=0x%08x\n", hr);
1400     ok(pILIsEqual(pidlCurrentFolder, pidlWineTestFolder),
1401         "IPersistFolder3_GetCurFolder should return pidlWineTestFolder!\n");
1402     pILFree(pidlCurrentFolder);
1403     pILFree(pidlWineTestFolder);
1404
1405     hr = IPersistFolder3_QueryInterface(pPersistFolder3, &IID_IShellFolder, (LPVOID*)&pShellFolder);
1406     IPersistFolder3_Release(pPersistFolder3);
1407     ok(hr == S_OK, "IPersistFolder3_QueryInterface(IShellFolder) failed! hr = %08x\n", hr);
1408     if (hr != S_OK) return;
1409
1410     hr = IShellFolder_GetDisplayNameOf(pShellFolder, NULL, SHGDN_FORPARSING, &strret);
1411     ok(hr == S_OK, "IShellFolder_GetDisplayNameOf(NULL) failed! hr = %08x\n", hr);
1412     if (hr != S_OK) {
1413         IShellFolder_Release(pShellFolder);
1414         return;
1415     }
1416
1417     pStrRetToBufW(&strret, NULL, wszBuffer, MAX_PATH);
1418     ok(!lstrcmpiW(wszDesktopPath, wszBuffer), "FolderShortcut returned incorrect folder!\n");
1419
1420     /* Next few lines are meant to show that children of FolderShortcuts are not FolderShortcuts,
1421      * but ShellFSFolders. */
1422     myPathAddBackslashW(wszDesktopPath);
1423     lstrcatW(wszDesktopPath, wszSomeSubFolder);
1424     if (!CreateDirectoryW(wszDesktopPath, NULL)) {
1425         IShellFolder_Release(pShellFolder);
1426         return;
1427     }
1428     
1429     hr = IShellFolder_ParseDisplayName(pShellFolder, NULL, NULL, wszSomeSubFolder, NULL, 
1430                                        &pidlSubFolder, NULL);
1431     RemoveDirectoryW(wszDesktopPath);
1432     ok (hr == S_OK, "IShellFolder::ParseDisplayName failed! hr = %08x\n", hr);
1433     if (hr != S_OK) {
1434         IShellFolder_Release(pShellFolder);
1435         return;
1436     }
1437
1438     hr = IShellFolder_BindToObject(pShellFolder, pidlSubFolder, NULL, &IID_IPersistFolder3,
1439                                    (LPVOID*)&pPersistFolder3);
1440     IShellFolder_Release(pShellFolder);
1441     pILFree(pidlSubFolder);
1442     ok (hr == S_OK, "IShellFolder::BindToObject failed! hr = %08x\n", hr);
1443     if (hr != S_OK)
1444         return;
1445
1446     /* On windows, we expect CLSID_ShellFSFolder. On wine we relax this constraint
1447      * a little bit and also allow CLSID_UnixDosFolder. */
1448     hr = IPersistFolder3_GetClassID(pPersistFolder3, &clsid);
1449     ok(hr == S_OK, "IPersistFolder3_GetClassID failed! hr=0x%08x\n", hr);
1450     ok(IsEqualCLSID(&clsid, &CLSID_ShellFSFolder) || IsEqualCLSID(&clsid, &CLSID_UnixDosFolder),
1451         "IPersistFolder3::GetClassID returned unexpected CLSID!\n");
1452
1453     IPersistFolder3_Release(pPersistFolder3);
1454 }
1455
1456 #include "pshpack1.h"
1457 struct FileStructA {
1458     BYTE  type;
1459     BYTE  dummy;
1460     DWORD dwFileSize;
1461     WORD  uFileDate;    /* In our current implementation this is */
1462     WORD  uFileTime;    /* FileTimeToDosDate(WIN32_FIND_DATA->ftLastWriteTime) */
1463     WORD  uFileAttribs;
1464     CHAR  szName[1];
1465 };
1466
1467 struct FileStructW {
1468     WORD  cbLen;        /* Length of this element. */
1469     BYTE  abFooBar1[6]; /* Beyond any recognition. */
1470     WORD  uDate;        /* FileTimeToDosDate(WIN32_FIND_DATA->ftCreationTime)? */
1471     WORD  uTime;        /* (this is currently speculation) */
1472     WORD  uDate2;       /* FileTimeToDosDate(WIN32_FIND_DATA->ftLastAccessTime)? */
1473     WORD  uTime2;       /* (this is currently speculation) */
1474     BYTE  abFooBar2[4]; /* Beyond any recognition. */
1475     WCHAR wszName[1];   /* The long filename in unicode. */
1476     /* Just for documentation: Right after the unicode string: */
1477     WORD  cbOffset;     /* FileStructW's offset from the beginning of the SHITMEID. 
1478                          * SHITEMID->cb == uOffset + cbLen */
1479 };
1480 #include "poppack.h"
1481
1482 static void test_ITEMIDLIST_format(void) {
1483     WCHAR wszPersonal[MAX_PATH];
1484     LPSHELLFOLDER psfDesktop, psfPersonal;
1485     LPITEMIDLIST pidlPersonal, pidlFile;
1486     HANDLE hFile;
1487     HRESULT hr;
1488     BOOL bResult;
1489     WCHAR wszFile[3][17] = { { 'e','v','e','n','_',0 }, { 'o','d','d','_',0 },
1490         { 'l','o','n','g','e','r','_','t','h','a','n','.','8','_','3',0 } };
1491     int i;
1492
1493     if (!pSHGetSpecialFolderPathW) return;
1494
1495     bResult = pSHGetSpecialFolderPathW(NULL, wszPersonal, CSIDL_PERSONAL, FALSE);
1496     ok(bResult, "SHGetSpecialFolderPathW failed! Last error: %u\n", GetLastError());
1497     if (!bResult) return;
1498
1499     SetLastError(0xdeadbeef);
1500     bResult = SetCurrentDirectoryW(wszPersonal);
1501     if (!bResult && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) {
1502         win_skip("Most W-calls are not implemented\n");
1503         return;
1504     }
1505     ok(bResult, "SetCurrentDirectory failed! Last error: %u\n", GetLastError());
1506     if (!bResult) return;
1507
1508     hr = SHGetDesktopFolder(&psfDesktop);
1509     ok(hr == S_OK, "SHGetDesktopFolder failed! hr: %08x\n", hr);
1510     if (hr != S_OK) return;
1511
1512     hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszPersonal, NULL, &pidlPersonal, NULL);
1513     ok(hr == S_OK, "psfDesktop->ParseDisplayName failed! hr = %08x\n", hr);
1514     if (hr != S_OK) {
1515         IShellFolder_Release(psfDesktop);
1516         return;
1517     }
1518
1519     hr = IShellFolder_BindToObject(psfDesktop, pidlPersonal, NULL, &IID_IShellFolder,
1520         (LPVOID*)&psfPersonal);
1521     IShellFolder_Release(psfDesktop);
1522     pILFree(pidlPersonal);
1523     ok(hr == S_OK, "psfDesktop->BindToObject failed! hr = %08x\n", hr);
1524     if (hr != S_OK) return;
1525
1526     for (i=0; i<3; i++) {
1527         CHAR szFile[MAX_PATH];
1528         struct FileStructA *pFileStructA;
1529         WORD cbOffset;
1530
1531         WideCharToMultiByte(CP_ACP, 0, wszFile[i], -1, szFile, MAX_PATH, NULL, NULL);
1532
1533         hFile = CreateFileW(wszFile[i], GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_FLAG_WRITE_THROUGH, NULL);
1534         ok(hFile != INVALID_HANDLE_VALUE, "CreateFile failed! (%u)\n", GetLastError());
1535         if (hFile == INVALID_HANDLE_VALUE) {
1536             IShellFolder_Release(psfPersonal);
1537             return;
1538         }
1539         CloseHandle(hFile);
1540
1541         hr = IShellFolder_ParseDisplayName(psfPersonal, NULL, NULL, wszFile[i], NULL, &pidlFile, NULL);
1542         DeleteFileW(wszFile[i]);
1543         ok(hr == S_OK, "psfPersonal->ParseDisplayName failed! hr: %08x\n", hr);
1544         if (hr != S_OK) {
1545             IShellFolder_Release(psfPersonal);
1546             return;
1547         }
1548
1549         pFileStructA = (struct FileStructA *)pidlFile->mkid.abID;
1550         ok(pFileStructA->type == 0x32, "PIDLTYPE should be 0x32!\n");
1551         ok(pFileStructA->dummy == 0x00, "Dummy Byte should be 0x00!\n");
1552         ok(pFileStructA->dwFileSize == 0, "Filesize should be zero!\n");
1553
1554         if (i < 2) /* First two file names are already in valid 8.3 format */
1555             ok(!strcmp(szFile, (CHAR*)&pidlFile->mkid.abID[12]), "Wrong file name!\n");
1556         else
1557             /* WinXP stores a derived 8.3 dos name (LONGER~1.8_3) here. We probably
1558              * can't implement this correctly, since unix filesystems don't support
1559              * this nasty short/long filename stuff. So we'll probably stay with our
1560              * current habbit of storing the long filename here, which seems to work
1561              * just fine. */
1562             todo_wine
1563             ok(pidlFile->mkid.abID[18] == '~' ||
1564                broken(pidlFile->mkid.abID[34] == '~'),  /* Win2k */
1565                "Should be derived 8.3 name!\n");
1566
1567         if (i == 0) /* First file name has an even number of chars. No need for alignment. */
1568             ok(pidlFile->mkid.abID[12 + strlen(szFile) + 1] != '\0' ||
1569                broken(pidlFile->mkid.cb == 2 + 12 + strlen(szFile) + 1 + 1),    /* Win2k */
1570                 "Alignment byte, where there shouldn't be!\n");
1571
1572         if (i == 1) /* Second file name has an uneven number of chars => alignment byte */
1573             ok(pidlFile->mkid.abID[12 + strlen(szFile) + 1] == '\0',
1574                 "There should be an alignment byte, but isn't!\n");
1575
1576         /* The offset of the FileStructW member is stored as a WORD at the end of the pidl. */
1577         cbOffset = *(WORD*)(((LPBYTE)pidlFile)+pidlFile->mkid.cb-sizeof(WORD));
1578         ok ((cbOffset >= sizeof(struct FileStructA) &&
1579             cbOffset <= pidlFile->mkid.cb - sizeof(struct FileStructW)) ||
1580             broken(pidlFile->mkid.cb == 2 + 12 + strlen(szFile) + 1 + 1) ||     /* Win2k on short names */
1581             broken(pidlFile->mkid.cb == 2 + 12 + strlen(szFile) + 1 + 12 + 1),  /* Win2k on long names */
1582             "Wrong offset value (%d) stored at the end of the PIDL\n", cbOffset);
1583
1584         if (cbOffset >= sizeof(struct FileStructA) &&
1585             cbOffset <= pidlFile->mkid.cb - sizeof(struct FileStructW))
1586         {
1587             struct FileStructW *pFileStructW = (struct FileStructW *)(((LPBYTE)pidlFile)+cbOffset);
1588
1589             ok(pidlFile->mkid.cb == cbOffset + pFileStructW->cbLen,
1590                 "FileStructW's offset and length should add up to the PIDL's length!\n");
1591
1592             if (pidlFile->mkid.cb == cbOffset + pFileStructW->cbLen) {
1593                 /* Since we just created the file, time of creation,
1594                  * time of last access and time of last write access just be the same.
1595                  * These tests seem to fail sometimes (on WinXP), if the test is run again shortly
1596                  * after the first run. I do remember something with NTFS keeping the creation time
1597                  * if a file is deleted and then created again within a couple of seconds or so.
1598                  * Might be the reason. */
1599                 ok (pFileStructA->uFileDate == pFileStructW->uDate &&
1600                     pFileStructA->uFileTime == pFileStructW->uTime,
1601                     "Last write time should match creation time!\n");
1602
1603                 /* On FAT filesystems the last access time is midnight
1604                    local time, so the values of uDate2 and uTime2 will
1605                    depend on the local timezone.  If the times are exactly
1606                    equal then the dates should be identical for both FAT
1607                    and NTFS as no timezone is more than 1 day away from UTC.
1608                 */
1609                 if (pFileStructA->uFileTime == pFileStructW->uTime2)
1610                 {
1611                     ok (pFileStructA->uFileDate == pFileStructW->uDate2,
1612                         "Last write date and time should match last access date and time!\n");
1613                 }
1614                 else
1615                 {
1616                     /* Filesystem may be FAT. Check date within 1 day
1617                        and seconds are zero. */
1618                     trace ("Filesystem may be FAT. Performing less strict atime test.\n");
1619                     ok ((pFileStructW->uTime2 & 0x1F) == 0,
1620                         "Last access time on FAT filesystems should have zero seconds.\n");
1621                     /* TODO: Perform check for date being within one day.*/
1622                 }
1623
1624                 ok (!lstrcmpW(wszFile[i], pFileStructW->wszName) ||
1625                     !lstrcmpW(wszFile[i], (WCHAR *)(pFileStructW->abFooBar2 + 22)) || /* Vista */
1626                     !lstrcmpW(wszFile[i], (WCHAR *)(pFileStructW->abFooBar2 + 26)), /* Win7 */
1627                     "The filename should be stored in unicode at this position!\n");
1628             }
1629         }
1630
1631         pILFree(pidlFile);
1632     }
1633
1634     IShellFolder_Release(psfPersonal);
1635 }
1636
1637 static void test_SHGetFolderPathAndSubDirA(void)
1638 {
1639     HRESULT ret;
1640     BOOL delret;
1641     DWORD dwret;
1642     int i;
1643     static char wine[] = "wine";
1644     static char winetemp[] = "wine\\temp";
1645     static char appdata[MAX_PATH];
1646     static char testpath[MAX_PATH];
1647     static char toolongpath[MAX_PATH+1];
1648
1649     if(!pSHGetFolderPathAndSubDirA)
1650     {
1651         win_skip("SHGetFolderPathAndSubDirA not present!\n");
1652         return;
1653     }
1654
1655     if(!pSHGetFolderPathA) {
1656         win_skip("SHGetFolderPathA not present!\n");
1657         return;
1658     }
1659     if(FAILED(pSHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, appdata)))
1660     {
1661         win_skip("SHGetFolderPathA failed for CSIDL_LOCAL_APPDATA!\n");
1662         return;
1663     }
1664
1665     sprintf(testpath, "%s\\%s", appdata, winetemp);
1666     delret = RemoveDirectoryA(testpath);
1667     if(!delret && (ERROR_PATH_NOT_FOUND != GetLastError()) ) {
1668         win_skip("RemoveDirectoryA(%s) failed with error %u\n", testpath, GetLastError());
1669         return;
1670     }
1671
1672     sprintf(testpath, "%s\\%s", appdata, wine);
1673     delret = RemoveDirectoryA(testpath);
1674     if(!delret && (ERROR_PATH_NOT_FOUND != GetLastError()) && (ERROR_FILE_NOT_FOUND != GetLastError())) {
1675         win_skip("RemoveDirectoryA(%s) failed with error %u\n", testpath, GetLastError());
1676         return;
1677     }
1678
1679     /* test invalid second parameter */
1680     ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | 0xff, NULL, SHGFP_TYPE_CURRENT, wine, testpath);
1681     ok(E_INVALIDARG == ret, "expected E_INVALIDARG, got  %x\n", ret);
1682
1683     /* test fourth parameter */
1684     ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, 2, winetemp, testpath);
1685     switch(ret) {
1686         case S_OK: /* winvista */
1687             ok(!strncmp(appdata, testpath, strlen(appdata)),
1688                 "expected %s to start with %s\n", testpath, appdata);
1689             ok(!lstrcmpA(&testpath[1 + strlen(appdata)], winetemp),
1690                 "expected %s to end with %s\n", testpath, winetemp);
1691             break;
1692         case E_INVALIDARG: /* winxp, win2k3 */
1693             break;
1694         default:
1695             ok(0, "expected S_OK or E_INVALIDARG, got  %x\n", ret);
1696     }
1697
1698     /* test fifth parameter */
1699     testpath[0] = '\0';
1700     ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, NULL, testpath);
1701     ok(S_OK == ret, "expected S_OK, got %x\n", ret);
1702     ok(!lstrcmpA(appdata, testpath), "expected %s, got %s\n", appdata, testpath);
1703
1704     testpath[0] = '\0';
1705     ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, "", testpath);
1706     ok(S_OK == ret, "expected S_OK, got %x\n", ret);
1707     ok(!lstrcmpA(appdata, testpath), "expected %s, got %s\n", appdata, testpath);
1708
1709     testpath[0] = '\0';
1710     ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, "\\", testpath);
1711     ok(S_OK == ret, "expected S_OK, got %x\n", ret);
1712     ok(!lstrcmpA(appdata, testpath), "expected %s, got %s\n", appdata, testpath);
1713
1714     for(i=0; i< MAX_PATH; i++)
1715         toolongpath[i] = '0' + i % 10;
1716     toolongpath[MAX_PATH] = '\0';
1717     ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, toolongpath, testpath);
1718     ok(HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE) == ret,
1719         "expected %x, got %x\n", HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE), ret);
1720
1721     testpath[0] = '\0';
1722     ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, wine, NULL);
1723     ok((S_OK == ret) || (E_INVALIDARG == ret), "expected S_OK or E_INVALIDARG, got %x\n", ret);
1724
1725     /* test a not existing path */
1726     testpath[0] = '\0';
1727     ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, winetemp, testpath);
1728     ok(HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == ret,
1729         "expected %x, got %x\n", HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND), ret);
1730
1731     /* create a directory inside a not existing directory */
1732     testpath[0] = '\0';
1733     ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_CREATE | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, winetemp, testpath);
1734     ok(S_OK == ret, "expected S_OK, got %x\n", ret);
1735     ok(!strncmp(appdata, testpath, strlen(appdata)),
1736         "expected %s to start with %s\n", testpath, appdata);
1737     ok(!lstrcmpA(&testpath[1 + strlen(appdata)], winetemp),
1738         "expected %s to end with %s\n", testpath, winetemp);
1739     dwret = GetFileAttributes(testpath);
1740     ok(FILE_ATTRIBUTE_DIRECTORY | dwret, "expected %x to contain FILE_ATTRIBUTE_DIRECTORY\n", dwret);
1741
1742     /* cleanup */
1743     sprintf(testpath, "%s\\%s", appdata, winetemp);
1744     RemoveDirectoryA(testpath);
1745     sprintf(testpath, "%s\\%s", appdata, wine);
1746     RemoveDirectoryA(testpath);
1747 }
1748
1749 static void test_LocalizedNames(void)
1750 {
1751     static char cCurrDirA[MAX_PATH];
1752     WCHAR cCurrDirW[MAX_PATH], tempbufW[25];
1753     IShellFolder *IDesktopFolder, *testIShellFolder;
1754     ITEMIDLIST *newPIDL;
1755     int len;
1756     HRESULT hr;
1757     static char resourcefile[MAX_PATH];
1758     DWORD res;
1759     HANDLE file;
1760     STRRET strret;
1761
1762     static const char desktopini_contents1[] =
1763         "[.ShellClassInfo]\r\n"
1764         "LocalizedResourceName=@";
1765     static const char desktopini_contents2[] =
1766         ",-1\r\n";
1767     static WCHAR foldernameW[] = {'t','e','s','t','f','o','l','d','e','r',0};
1768     static const WCHAR folderdisplayW[] = {'F','o','l','d','e','r',' ','N','a','m','e',' ','R','e','s','o','u','r','c','e',0};
1769
1770     /* create folder with desktop.ini and localized name in GetModuleFileNameA(NULL) */
1771     CreateDirectoryA(".\\testfolder", NULL);
1772
1773     SetFileAttributesA(".\\testfolder", GetFileAttributesA(".\\testfolder")|FILE_ATTRIBUTE_SYSTEM);
1774
1775     GetModuleFileNameA(NULL, resourcefile, MAX_PATH);
1776
1777     file = CreateFileA(".\\testfolder\\desktop.ini", GENERIC_WRITE, 0, NULL,
1778                          CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
1779     ok(file != INVALID_HANDLE_VALUE, "CreateFileA failed %i\n", GetLastError());
1780     ok(WriteFile(file, desktopini_contents1, strlen(desktopini_contents1), &res, NULL) &&
1781        WriteFile(file, resourcefile, strlen(resourcefile), &res, NULL) &&
1782        WriteFile(file, desktopini_contents2, strlen(desktopini_contents2), &res, NULL),
1783        "WriteFile failed %i\n", GetLastError());
1784     CloseHandle(file);
1785
1786     /* get IShellFolder for parent */
1787     GetCurrentDirectoryA(MAX_PATH, cCurrDirA);
1788     len = lstrlenA(cCurrDirA);
1789
1790     if (len == 0) {
1791         win_skip("GetCurrentDirectoryA returned empty string. Skipping test_LocalizedNames\n");
1792         goto cleanup;
1793     }
1794     if(cCurrDirA[len-1] == '\\')
1795         cCurrDirA[len-1] = 0;
1796
1797     MultiByteToWideChar(CP_ACP, 0, cCurrDirA, -1, cCurrDirW, MAX_PATH);
1798
1799     hr = SHGetDesktopFolder(&IDesktopFolder);
1800     ok(hr == S_OK, "SHGetDesktopfolder failed %08x\n", hr);
1801
1802     hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cCurrDirW, NULL, &newPIDL, 0);
1803     ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
1804
1805     hr = IShellFolder_BindToObject(IDesktopFolder, newPIDL, NULL, (REFIID)&IID_IShellFolder, (LPVOID *)&testIShellFolder);
1806     ok(hr == S_OK, "BindToObject failed %08x\n", hr);
1807
1808     IMalloc_Free(ppM, newPIDL);
1809
1810     /* windows reads the display name from the resource */
1811     hr = IShellFolder_ParseDisplayName(testIShellFolder, NULL, NULL, foldernameW, NULL, &newPIDL, 0);
1812     ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
1813
1814     hr = IShellFolder_GetDisplayNameOf(testIShellFolder, newPIDL, SHGDN_INFOLDER, &strret);
1815     ok(hr == S_OK, "GetDisplayNameOf failed %08x\n", hr);
1816
1817     if (hr == S_OK && pStrRetToBufW)
1818     {
1819         hr = pStrRetToBufW(&strret, newPIDL, tempbufW, sizeof(tempbufW)/sizeof(WCHAR));
1820         ok (hr == S_OK, "StrRetToBufW failed! hr = %08x\n", hr);
1821         todo_wine
1822         ok (!lstrcmpiW(tempbufW, folderdisplayW) ||
1823             broken(!lstrcmpiW(tempbufW, foldernameW)), /* W2K */
1824             "GetDisplayNameOf returned %s\n", wine_dbgstr_w(tempbufW));
1825     }
1826
1827     /* editing name is also read from the resource */
1828     hr = IShellFolder_GetDisplayNameOf(testIShellFolder, newPIDL, SHGDN_INFOLDER|SHGDN_FOREDITING, &strret);
1829     ok(hr == S_OK, "GetDisplayNameOf failed %08x\n", hr);
1830
1831     if (hr == S_OK && pStrRetToBufW)
1832     {
1833         hr = pStrRetToBufW(&strret, newPIDL, tempbufW, sizeof(tempbufW)/sizeof(WCHAR));
1834         ok (hr == S_OK, "StrRetToBufW failed! hr = %08x\n", hr);
1835         todo_wine
1836         ok (!lstrcmpiW(tempbufW, folderdisplayW) ||
1837             broken(!lstrcmpiW(tempbufW, foldernameW)), /* W2K */
1838             "GetDisplayNameOf returned %s\n", wine_dbgstr_w(tempbufW));
1839     }
1840
1841     /* parsing name is unchanged */
1842     hr = IShellFolder_GetDisplayNameOf(testIShellFolder, newPIDL, SHGDN_INFOLDER|SHGDN_FORPARSING, &strret);
1843     ok(hr == S_OK, "GetDisplayNameOf failed %08x\n", hr);
1844
1845     if (hr == S_OK && pStrRetToBufW)
1846     {
1847         hr = pStrRetToBufW(&strret, newPIDL, tempbufW, sizeof(tempbufW)/sizeof(WCHAR));
1848         ok (hr == S_OK, "StrRetToBufW failed! hr = %08x\n", hr);
1849         ok (!lstrcmpiW(tempbufW, foldernameW), "GetDisplayNameOf returned %s\n", wine_dbgstr_w(tempbufW));
1850     }
1851
1852     IShellFolder_Release(IDesktopFolder);
1853     IShellFolder_Release(testIShellFolder);
1854
1855     IMalloc_Free(ppM, newPIDL);
1856
1857 cleanup:
1858     DeleteFileA(".\\testfolder\\desktop.ini");
1859     SetFileAttributesA(".\\testfolder", GetFileAttributesA(".\\testfolder")&~FILE_ATTRIBUTE_SYSTEM);
1860     RemoveDirectoryA(".\\testfolder");
1861 }
1862
1863 static void test_SHCreateShellItem(void)
1864 {
1865     IShellItem *shellitem, *shellitem2;
1866     IPersistIDList *persistidl;
1867     LPITEMIDLIST pidl_cwd=NULL, pidl_testfile, pidl_abstestfile, pidl_test;
1868     HRESULT ret;
1869     char curdirA[MAX_PATH];
1870     WCHAR curdirW[MAX_PATH];
1871     IShellFolder *desktopfolder=NULL, *currentfolder=NULL;
1872     static WCHAR testfileW[] = {'t','e','s','t','f','i','l','e',0};
1873
1874     GetCurrentDirectoryA(MAX_PATH, curdirA);
1875
1876     if (!pSHCreateShellItem)
1877     {
1878         win_skip("SHCreateShellItem isn't available\n");
1879         return;
1880     }
1881
1882     if (!lstrlenA(curdirA))
1883     {
1884         win_skip("GetCurrentDirectoryA returned empty string, skipping test_SHCreateShellItem\n");
1885         return;
1886     }
1887
1888     MultiByteToWideChar(CP_ACP, 0, curdirA, -1, curdirW, MAX_PATH);
1889
1890     ret = SHGetDesktopFolder(&desktopfolder);
1891     ok(SUCCEEDED(ret), "SHGetShellFolder returned %x\n", ret);
1892
1893     ret = IShellFolder_ParseDisplayName(desktopfolder, NULL, NULL, curdirW, NULL, &pidl_cwd, NULL);
1894     ok(SUCCEEDED(ret), "ParseDisplayName returned %x\n", ret);
1895
1896     ret = IShellFolder_BindToObject(desktopfolder, pidl_cwd, NULL, &IID_IShellFolder, (void**)&currentfolder);
1897     ok(SUCCEEDED(ret), "BindToObject returned %x\n", ret);
1898
1899     CreateTestFile(".\\testfile");
1900
1901     ret = IShellFolder_ParseDisplayName(currentfolder, NULL, NULL, testfileW, NULL, &pidl_testfile, NULL);
1902     ok(SUCCEEDED(ret), "ParseDisplayName returned %x\n", ret);
1903
1904     pidl_abstestfile = pILCombine(pidl_cwd, pidl_testfile);
1905
1906     ret = pSHCreateShellItem(NULL, NULL, NULL, &shellitem);
1907     ok(ret == E_INVALIDARG, "SHCreateShellItem returned %x\n", ret);
1908
1909     if (0) /* crashes on Windows XP */
1910     {
1911         pSHCreateShellItem(NULL, NULL, pidl_cwd, NULL);
1912         pSHCreateShellItem(pidl_cwd, NULL, NULL, &shellitem);
1913         pSHCreateShellItem(NULL, currentfolder, NULL, &shellitem);
1914         pSHCreateShellItem(pidl_cwd, currentfolder, NULL, &shellitem);
1915     }
1916
1917     ret = pSHCreateShellItem(NULL, NULL, pidl_cwd, &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_cwd, 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     ret = pSHCreateShellItem(pidl_cwd, NULL, pidl_testfile, &shellitem);
1938     ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret);
1939     if (SUCCEEDED(ret))
1940     {
1941         ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
1942         ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
1943         if (SUCCEEDED(ret))
1944         {
1945             ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
1946             ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
1947             if (SUCCEEDED(ret))
1948             {
1949                 ok(ILIsEqual(pidl_abstestfile, pidl_test), "id lists are not equal\n");
1950                 pILFree(pidl_test);
1951             }
1952             IPersistIDList_Release(persistidl);
1953         }
1954
1955         ret = IShellItem_GetParent(shellitem, &shellitem2);
1956         ok(SUCCEEDED(ret), "GetParent returned %x\n", ret);
1957         if (SUCCEEDED(ret))
1958         {
1959             ret = IShellItem_QueryInterface(shellitem2, &IID_IPersistIDList, (void**)&persistidl);
1960             ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
1961             if (SUCCEEDED(ret))
1962             {
1963                 ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
1964                 ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
1965                 if (SUCCEEDED(ret))
1966                 {
1967                     ok(ILIsEqual(pidl_cwd, pidl_test), "id lists are not equal\n");
1968                     pILFree(pidl_test);
1969                 }
1970                 IPersistIDList_Release(persistidl);
1971             }
1972             IShellItem_Release(shellitem2);
1973         }
1974
1975         IShellItem_Release(shellitem);
1976     }
1977
1978     ret = pSHCreateShellItem(NULL, currentfolder, pidl_testfile, &shellitem);
1979     ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret);
1980     if (SUCCEEDED(ret))
1981     {
1982         ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
1983         ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
1984         if (SUCCEEDED(ret))
1985         {
1986             ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
1987             ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
1988             if (SUCCEEDED(ret))
1989             {
1990                 ok(ILIsEqual(pidl_abstestfile, pidl_test), "id lists are not equal\n");
1991                 pILFree(pidl_test);
1992             }
1993             IPersistIDList_Release(persistidl);
1994         }
1995         IShellItem_Release(shellitem);
1996     }
1997
1998     /* if a parent pidl and shellfolder are specified, the shellfolder is ignored */
1999     ret = pSHCreateShellItem(pidl_cwd, desktopfolder, pidl_testfile, &shellitem);
2000     ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret);
2001     if (SUCCEEDED(ret))
2002     {
2003         ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
2004         ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
2005         if (SUCCEEDED(ret))
2006         {
2007             ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
2008             ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
2009             if (SUCCEEDED(ret))
2010             {
2011                 ok(ILIsEqual(pidl_abstestfile, pidl_test), "id lists are not equal\n");
2012                 pILFree(pidl_test);
2013             }
2014             IPersistIDList_Release(persistidl);
2015         }
2016         IShellItem_Release(shellitem);
2017     }
2018
2019     ret = pSHCreateShellItem(NULL, desktopfolder, pidl_testfile, &shellitem);
2020     ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret);
2021     if (SUCCEEDED(ret))
2022     {
2023         ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
2024         ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
2025         if (SUCCEEDED(ret))
2026         {
2027             ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
2028             ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
2029             if (SUCCEEDED(ret))
2030             {
2031                 ok(ILIsEqual(pidl_testfile, pidl_test), "id lists are not equal\n");
2032                 pILFree(pidl_test);
2033             }
2034             IPersistIDList_Release(persistidl);
2035         }
2036         IShellItem_Release(shellitem);
2037     }
2038
2039     DeleteFileA(".\\testfile");
2040     pILFree(pidl_abstestfile);
2041     pILFree(pidl_testfile);
2042     pILFree(pidl_cwd);
2043     IShellFolder_Release(currentfolder);
2044     IShellFolder_Release(desktopfolder);
2045 }
2046
2047 static void test_SHParseDisplayName(void)
2048 {
2049     LPITEMIDLIST pidl1, pidl2;
2050     IShellFolder *desktop;
2051     WCHAR dirW[MAX_PATH];
2052     WCHAR nameW[10];
2053     HRESULT hr;
2054     BOOL ret;
2055
2056     if (!pSHParseDisplayName)
2057     {
2058         win_skip("SHParseDisplayName isn't available\n");
2059         return;
2060     }
2061
2062 if (0)
2063 {
2064     /* crashes on native */
2065     hr = pSHParseDisplayName(NULL, NULL, NULL, 0, NULL);
2066     nameW[0] = 0;
2067     hr = pSHParseDisplayName(nameW, NULL, NULL, 0, NULL);
2068 }
2069
2070     pidl1 = (LPITEMIDLIST)0xdeadbeef;
2071     hr = pSHParseDisplayName(NULL, NULL, &pidl1, 0, NULL);
2072     ok(broken(hr == E_OUTOFMEMORY) /* < Vista */ ||
2073        hr == E_INVALIDARG, "failed %08x\n", hr);
2074     ok(pidl1 == 0, "expected null ptr, got %p\n", pidl1);
2075
2076     /* dummy name */
2077     nameW[0] = 0;
2078     hr = pSHParseDisplayName(nameW, NULL, &pidl1, 0, NULL);
2079     ok(hr == S_OK, "failed %08x\n", hr);
2080     hr = SHGetDesktopFolder(&desktop);
2081     ok(hr == S_OK, "failed %08x\n", hr);
2082     hr = IShellFolder_ParseDisplayName(desktop, NULL, NULL, nameW, NULL, &pidl2, NULL);
2083     ok(hr == S_OK, "failed %08x\n", hr);
2084     ret = pILIsEqual(pidl1, pidl2);
2085     ok(ret == TRUE, "expected equal idls\n");
2086     pILFree(pidl1);
2087     pILFree(pidl2);
2088
2089     /* with path */
2090     GetWindowsDirectoryW( dirW, MAX_PATH );
2091
2092     hr = pSHParseDisplayName(dirW, NULL, &pidl1, 0, NULL);
2093     ok(hr == S_OK, "failed %08x\n", hr);
2094     hr = IShellFolder_ParseDisplayName(desktop, NULL, NULL, dirW, NULL, &pidl2, NULL);
2095     ok(hr == S_OK, "failed %08x\n", hr);
2096
2097     ret = pILIsEqual(pidl1, pidl2);
2098     ok(ret == TRUE, "expected equal idls\n");
2099     pILFree(pidl1);
2100     pILFree(pidl2);
2101
2102     IShellFolder_Release(desktop);
2103 }
2104
2105 static void test_desktop_IPersist(void)
2106 {
2107     IShellFolder *desktop;
2108     IPersist *persist;
2109     IPersistFolder2 *ppf2;
2110     CLSID clsid;
2111     HRESULT hr;
2112
2113     hr = SHGetDesktopFolder(&desktop);
2114     ok(hr == S_OK, "failed %08x\n", hr);
2115
2116     hr = IShellFolder_QueryInterface(desktop, &IID_IPersist, (void**)&persist);
2117     ok(hr == S_OK || broken(hr == E_NOINTERFACE) /* NT4, W9X */, "failed %08x\n", hr);
2118
2119     if (hr == S_OK)
2120     {
2121     if (0)
2122     {
2123         /* crashes on native */
2124         hr = IPersist_GetClassID(persist, NULL);
2125     }
2126         memset(&clsid, 0, sizeof(clsid));
2127         hr = IPersist_GetClassID(persist, &clsid);
2128         ok(hr == S_OK, "failed %08x\n", hr);
2129         ok(IsEqualIID(&CLSID_ShellDesktop, &clsid), "Expected CLSID_ShellDesktop\n");
2130         IPersist_Release(persist);
2131     }
2132
2133     hr = IShellFolder_QueryInterface(desktop, &IID_IPersistFolder2, (void**)&ppf2);
2134     ok(hr == S_OK || broken(hr == E_NOINTERFACE) /* pre-Vista */, "failed %08x\n", hr);
2135     if(SUCCEEDED(hr))
2136     {
2137         IPersistFolder *ppf;
2138         LPITEMIDLIST pidl;
2139         hr = IShellFolder_QueryInterface(desktop, &IID_IPersistFolder, (void**)&ppf);
2140         ok(hr == S_OK, "IID_IPersistFolder2 without IID_IPersistFolder.\n");
2141         if(SUCCEEDED(hr))
2142             IPersistFolder_Release(ppf);
2143
2144         todo_wine {
2145             hr = IPersistFolder2_Initialize(ppf2, NULL);
2146             ok(hr == S_OK, "got %08x\n", hr);
2147         }
2148
2149         pidl = NULL;
2150         hr = IPersistFolder2_GetCurFolder(ppf2, &pidl);
2151         ok(hr == S_OK, "got %08x\n", hr);
2152         ok(pidl != NULL, "pidl was NULL.\n");
2153         if(SUCCEEDED(hr)) pILFree(pidl);
2154
2155         IPersistFolder2_Release(ppf2);
2156     }
2157
2158     IShellFolder_Release(desktop);
2159 }
2160
2161 static void test_GetUIObject(void)
2162 {
2163     IShellFolder *psf_desktop;
2164     IContextMenu *pcm;
2165     LPITEMIDLIST pidl;
2166     HRESULT hr;
2167     WCHAR path[MAX_PATH];
2168     const WCHAR filename[] =
2169         {'\\','t','e','s','t','d','i','r','\\','t','e','s','t','1','.','t','x','t',0};
2170
2171     if(!pSHBindToParent)
2172     {
2173         win_skip("SHBindToParent missing.\n");
2174         return;
2175     }
2176
2177     GetCurrentDirectoryW(MAX_PATH, path);
2178     if(!lstrlenW(path))
2179     {
2180         skip("GetCurrentDirectoryW returned an empty string.\n");
2181         return;
2182     }
2183     lstrcatW(path, filename);
2184     SHGetDesktopFolder(&psf_desktop);
2185
2186     CreateFilesFolders();
2187
2188     hr = IShellFolder_ParseDisplayName(psf_desktop, NULL, NULL, path, NULL, &pidl, 0);
2189     ok(hr == S_OK, "Got 0x%08x\n", hr);
2190     if(SUCCEEDED(hr))
2191     {
2192         IShellFolder *psf;
2193         LPCITEMIDLIST pidl_child;
2194         hr = pSHBindToParent(pidl, &IID_IShellFolder, (void**)&psf, &pidl_child);
2195         ok(hr == S_OK, "Got 0x%08x\n", hr);
2196         if(SUCCEEDED(hr))
2197         {
2198             hr = IShellFolder_GetUIObjectOf(psf, NULL, 1, (LPCITEMIDLIST*)&pidl_child,
2199                                             &IID_IContextMenu, NULL, (void**)&pcm);
2200             ok(hr == S_OK, "Got 0x%08x\n", hr);
2201             if(SUCCEEDED(hr))
2202             {
2203                 HMENU hmenu = CreatePopupMenu();
2204                 INT max_id, max_id_check;
2205                 UINT count, i;
2206                 const int id_upper_limit = 32767;
2207                 hr = IContextMenu_QueryContextMenu(pcm, hmenu, 0, 0, id_upper_limit, CMF_NORMAL);
2208                 ok(SUCCEEDED(hr), "Got 0x%08x\n", hr);
2209                 max_id = HRESULT_CODE(hr) - 1; /* returns max_id + 1 */
2210                 ok(max_id <= id_upper_limit, "Got %d\n", max_id);
2211                 count = GetMenuItemCount(hmenu);
2212                 ok(count, "Got %d\n", count);
2213
2214                 max_id_check = 0;
2215                 for(i = 0; i < count; i++)
2216                 {
2217                     MENUITEMINFOA mii;
2218                     INT res;
2219                     ZeroMemory(&mii, sizeof(MENUITEMINFOA));
2220                     mii.cbSize = sizeof(MENUITEMINFOA);
2221                     mii.fMask = MIIM_ID | MIIM_FTYPE;
2222
2223                     SetLastError(0);
2224                     res = GetMenuItemInfoA(hmenu, i, TRUE, &mii);
2225                     ok(res, "Failed (last error: %d).\n", GetLastError());
2226
2227                     ok( (mii.wID <= id_upper_limit) || (mii.fType & MFT_SEPARATOR),
2228                         "Got non-separator ID out of range: %d (type: %x) \n", mii.wID, mii.fType);
2229                     if(!(mii.fType & MFT_SEPARATOR))
2230                         max_id_check = (mii.wID>max_id_check)?mii.wID:max_id_check;
2231                 }
2232                 ok((max_id_check == max_id) ||
2233                    (max_id_check == max_id-1 /* Win 7 */),
2234                    "Not equal (or near equal), got %d and %d\n", max_id_check, max_id);
2235
2236 #define is_win2k() (pSHGetFolderPathA && !pSHGetFolderPathAndSubDirA)
2237
2238                 if(count && !is_win2k())   /* Test is interactive on w2k, so skip */
2239                 {
2240                     CMINVOKECOMMANDINFO cmi;
2241                     ZeroMemory(&cmi, sizeof(CMINVOKECOMMANDINFO));
2242                     cmi.cbSize = sizeof(CMINVOKECOMMANDINFO);
2243
2244                     /* Attempt to execute non-existing command */
2245                     cmi.lpVerb = MAKEINTRESOURCEA(9999);
2246                     hr = IContextMenu_InvokeCommand(pcm, &cmi);
2247                     ok(hr == E_INVALIDARG, "Got 0x%08x\n", hr);
2248
2249                     cmi.lpVerb = "foobar_wine_test";
2250                     hr = IContextMenu_InvokeCommand(pcm, &cmi);
2251                     ok( (hr == E_INVALIDARG) || (hr == E_FAIL /* Win7 */) ||
2252                         (hr == HRESULT_FROM_WIN32(ERROR_NO_ASSOCIATION) /* Vista */),
2253                         "Got 0x%08x\n", hr);
2254                 }
2255 #undef is_win2k
2256
2257                 DestroyMenu(hmenu);
2258                 IContextMenu_Release(pcm);
2259             }
2260             IShellFolder_Release(psf);
2261         }
2262         if(pILFree) pILFree(pidl);
2263     }
2264
2265     IShellFolder_Release(psf_desktop);
2266     Cleanup();
2267 }
2268
2269 #define verify_pidl(i,p) r_verify_pidl(__LINE__, i, p)
2270 static void r_verify_pidl(unsigned l, LPCITEMIDLIST pidl, const WCHAR *path)
2271 {
2272     LPCITEMIDLIST child;
2273     IShellFolder *parent;
2274     STRRET filename;
2275     HRESULT hr;
2276
2277     if(!pSHBindToParent){
2278         win_skip("SHBindToParent is not available, not performing full PIDL verification\n");
2279         if(path)
2280             ok_(__FILE__,l)(pidl != NULL, "Expected PIDL to be non-NULL\n");
2281         else
2282             ok_(__FILE__,l)(pidl == NULL, "Expected PIDL to be NULL\n");
2283         return;
2284     }
2285
2286     if(path){
2287         if(!pidl){
2288             ok_(__FILE__,l)(0, "didn't get expected path (%s), instead: NULL\n", wine_dbgstr_w(path));
2289             return;
2290         }
2291
2292         hr = pSHBindToParent(pidl, &IID_IShellFolder, (LPVOID*)&parent, &child);
2293         ok_(__FILE__,l)(hr == S_OK, "SHBindToParent failed: 0x%08x\n", hr);
2294         if(FAILED(hr))
2295             return;
2296
2297         hr = IShellFolder_GetDisplayNameOf(parent, child, SHGDN_FORPARSING, &filename);
2298         ok_(__FILE__,l)(hr == S_OK, "GetDisplayNameOf failed: 0x%08x\n", hr);
2299         if(FAILED(hr)){
2300             IShellFolder_Release(parent);
2301             return;
2302         }
2303
2304         ok_(__FILE__,l)(filename.uType == STRRET_WSTR, "Got unexpected string type: %d\n", filename.uType);
2305         ok_(__FILE__,l)(lstrcmpW(path, filename.pOleStr) == 0,
2306                 "didn't get expected path (%s), instead: %s\n",
2307                  wine_dbgstr_w(path), wine_dbgstr_w(filename.pOleStr));
2308
2309         IShellFolder_Release(parent);
2310     }else
2311         ok_(__FILE__,l)(pidl == NULL, "Expected PIDL to be NULL\n");
2312 }
2313
2314 static void test_SHSimpleIDListFromPath(void)
2315 {
2316     const WCHAR adirW[] = {'C',':','\\','s','i','d','l','f','p','d','i','r',0};
2317     const CHAR adirA[] = "C:\\sidlfpdir";
2318     BOOL br, is_unicode = !(GetVersion() & 0x80000000);
2319
2320     LPITEMIDLIST pidl = NULL;
2321
2322     if(!pSHSimpleIDListFromPathAW){
2323         win_skip("SHSimpleIDListFromPathAW not available\n");
2324         return;
2325     }
2326
2327     br = CreateDirectoryA(adirA, NULL);
2328     ok(br == TRUE, "CreateDirectory failed: %d\n", GetLastError());
2329
2330     if(is_unicode)
2331         pidl = pSHSimpleIDListFromPathAW(adirW);
2332     else
2333         pidl = pSHSimpleIDListFromPathAW(adirA);
2334     verify_pidl(pidl, adirW);
2335     pILFree(pidl);
2336
2337     br = RemoveDirectoryA(adirA);
2338     ok(br == TRUE, "RemoveDirectory failed: %d\n", GetLastError());
2339
2340     if(is_unicode)
2341         pidl = pSHSimpleIDListFromPathAW(adirW);
2342     else
2343         pidl = pSHSimpleIDListFromPathAW(adirA);
2344     verify_pidl(pidl, adirW);
2345     pILFree(pidl);
2346 }
2347
2348 /* IFileSystemBindData impl */
2349 static HRESULT WINAPI fsbd_QueryInterface(IFileSystemBindData *fsbd,
2350         REFIID riid, void **ppv)
2351 {
2352     if(IsEqualIID(riid, &IID_IFileSystemBindData) ||
2353             IsEqualIID(riid, &IID_IUnknown)){
2354         *ppv = fsbd;
2355         return S_OK;
2356     }
2357     return E_NOINTERFACE;
2358 }
2359
2360 static ULONG WINAPI fsbd_AddRef(IFileSystemBindData *fsbd)
2361 {
2362     return 2;
2363 }
2364
2365 static ULONG WINAPI fsbd_Release(IFileSystemBindData *fsbd)
2366 {
2367     return 1;
2368 }
2369
2370 static HRESULT WINAPI fsbd_SetFindData(IFileSystemBindData *fsbd,
2371         const WIN32_FIND_DATAW *pfd)
2372 {
2373     ok(0, "SetFindData called\n");
2374     return E_NOTIMPL;
2375 }
2376
2377 static HRESULT WINAPI fsbd_GetFindData_nul(IFileSystemBindData *fsbd,
2378         WIN32_FIND_DATAW *pfd)
2379 {
2380     memset(pfd, 0, sizeof(WIN32_FIND_DATAW));
2381     return S_OK;
2382 }
2383
2384 static HRESULT WINAPI fsbd_GetFindData_junk(IFileSystemBindData *fsbd,
2385         WIN32_FIND_DATAW *pfd)
2386 {
2387     memset(pfd, 0xdeadbeef, sizeof(WIN32_FIND_DATAW));
2388     return S_OK;
2389 }
2390
2391 static HRESULT WINAPI fsbd_GetFindData_invalid(IFileSystemBindData *fsbd,
2392         WIN32_FIND_DATAW *pfd)
2393 {
2394     memset(pfd, 0, sizeof(WIN32_FIND_DATAW));
2395     *pfd->cFileName = 'a';
2396     *pfd->cAlternateFileName = 'a';
2397     return S_OK;
2398 }
2399
2400 static HRESULT WINAPI fsbd_GetFindData_valid(IFileSystemBindData *fsbd,
2401         WIN32_FIND_DATAW *pfd)
2402 {
2403     static const WCHAR adirW[] = {'C',':','\\','f','s','b','d','d','i','r',0};
2404     HANDLE handle = FindFirstFileW(adirW, pfd);
2405     FindClose(handle);
2406     return S_OK;
2407 }
2408
2409 static HRESULT WINAPI fsbd_GetFindData_fail(IFileSystemBindData *fsbd,
2410         WIN32_FIND_DATAW *pfd)
2411 {
2412     return E_FAIL;
2413 }
2414
2415 static IFileSystemBindDataVtbl fsbdVtbl = {
2416     fsbd_QueryInterface,
2417     fsbd_AddRef,
2418     fsbd_Release,
2419     fsbd_SetFindData,
2420     NULL
2421 };
2422
2423 static IFileSystemBindData fsbd = { &fsbdVtbl };
2424
2425 static void test_ParseDisplayNamePBC(void)
2426 {
2427     WCHAR wFileSystemBindData[] =
2428         {'F','i','l','e',' ','S','y','s','t','e','m',' ','B','i','n','d',' ','D','a','t','a',0};
2429     WCHAR adirW[] = {'C',':','\\','f','s','b','d','d','i','r',0};
2430     const HRESULT exp_err = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
2431
2432     IShellFolder *psf;
2433     IBindCtx *pbc;
2434     HRESULT hres;
2435     ITEMIDLIST *pidl;
2436
2437     /* Check if we support WCHAR functions */
2438     SetLastError(0xdeadbeef);
2439     lstrcmpiW(adirW, adirW);
2440     if(GetLastError() == ERROR_CALL_NOT_IMPLEMENTED){
2441         win_skip("Most W-calls are not implemented\n");
2442         return;
2443     }
2444
2445     hres = SHGetDesktopFolder(&psf);
2446     ok(hres == S_OK, "SHGetDesktopFolder failed: 0x%08x\n", hres);
2447     if(FAILED(hres)){
2448         win_skip("Failed to get IShellFolder, can't run tests\n");
2449         return;
2450     }
2451
2452     /* fails on unknown dir with no IBindCtx */
2453     hres = IShellFolder_ParseDisplayName(psf, NULL, NULL, adirW, NULL, &pidl, NULL);
2454     ok(hres == exp_err || broken(hres == E_FAIL) /* NT4 */,
2455             "ParseDisplayName failed with wrong error: 0x%08x\n", hres);
2456
2457     /* fails on unknown dir with IBindCtx with no IFileSystemBindData */
2458     hres = CreateBindCtx(0, &pbc);
2459     ok(hres == S_OK, "CreateBindCtx failed: 0x%08x\n", hres);
2460
2461     hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, adirW, NULL, &pidl, NULL);
2462     ok(hres == exp_err || broken(hres == E_FAIL) /* NT4 */,
2463             "ParseDisplayName failed with wrong error: 0x%08x\n", hres);
2464
2465     /* unknown dir with IBindCtx with IFileSystemBindData */
2466     hres = IBindCtx_RegisterObjectParam(pbc, wFileSystemBindData, (IUnknown*)&fsbd);
2467     ok(hres == S_OK, "RegisterObjectParam failed: 0x%08x\n", hres);
2468
2469     /* return E_FAIL from GetFindData */
2470     pidl = (ITEMIDLIST*)0xdeadbeef;
2471     fsbdVtbl.GetFindData = fsbd_GetFindData_fail;
2472     hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, adirW, NULL, &pidl, NULL);
2473     ok(hres == S_OK || broken(hres == E_FAIL) /* NT4 */,
2474             "ParseDisplayName failed: 0x%08x\n", hres);
2475     if(SUCCEEDED(hres)){
2476         verify_pidl(pidl, adirW);
2477         ILFree(pidl);
2478     }
2479
2480     /* set FIND_DATA struct to NULLs */
2481     pidl = (ITEMIDLIST*)0xdeadbeef;
2482     fsbdVtbl.GetFindData = fsbd_GetFindData_nul;
2483     hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, adirW, NULL, &pidl, NULL);
2484     ok(hres == S_OK || broken(hres == E_FAIL) /* NT4 */,
2485             "ParseDisplayName failed: 0x%08x\n", hres);
2486     if(SUCCEEDED(hres)){
2487         verify_pidl(pidl, adirW);
2488         ILFree(pidl);
2489     }
2490
2491     /* set FIND_DATA struct to junk */
2492     pidl = (ITEMIDLIST*)0xdeadbeef;
2493     fsbdVtbl.GetFindData = fsbd_GetFindData_junk;
2494     hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, adirW, NULL, &pidl, NULL);
2495     ok(hres == S_OK || broken(hres == E_FAIL) /* NT4 */,
2496             "ParseDisplayName failed: 0x%08x\n", hres);
2497     if(SUCCEEDED(hres)){
2498         verify_pidl(pidl, adirW);
2499         ILFree(pidl);
2500     }
2501
2502     /* set FIND_DATA struct to invalid data */
2503     pidl = (ITEMIDLIST*)0xdeadbeef;
2504     fsbdVtbl.GetFindData = fsbd_GetFindData_invalid;
2505     hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, adirW, NULL, &pidl, NULL);
2506     ok(hres == S_OK || broken(hres == E_FAIL) /* NT4 */,
2507             "ParseDisplayName failed: 0x%08x\n", hres);
2508     if(SUCCEEDED(hres)){
2509         verify_pidl(pidl, adirW);
2510         ILFree(pidl);
2511     }
2512
2513     /* set FIND_DATA struct to valid data */
2514     pidl = (ITEMIDLIST*)0xdeadbeef;
2515     fsbdVtbl.GetFindData = fsbd_GetFindData_valid;
2516     hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, adirW, NULL, &pidl, NULL);
2517     ok(hres == S_OK || broken(hres == E_FAIL) /* NT4 */,
2518             "ParseDisplayName failed: 0x%08x\n", hres);
2519     if(SUCCEEDED(hres)){
2520         verify_pidl(pidl, adirW);
2521         ILFree(pidl);
2522     }
2523
2524     IBindCtx_Release(pbc);
2525     IShellFolder_Release(psf);
2526 }
2527
2528 START_TEST(shlfolder)
2529 {
2530     init_function_pointers();
2531     /* if OleInitialize doesn't get called, ParseDisplayName returns
2532        CO_E_NOTINITIALIZED for malformed directory names on win2k. */
2533     OleInitialize(NULL);
2534
2535     test_ParseDisplayName();
2536     test_SHParseDisplayName();
2537     test_BindToObject();
2538     test_EnumObjects_and_CompareIDs();
2539     test_GetDisplayName();
2540     test_GetAttributesOf();
2541     test_SHGetPathFromIDList();
2542     test_CallForAttributes();
2543     test_FolderShortcut();
2544     test_ITEMIDLIST_format();
2545     test_SHGetFolderPathAndSubDirA();
2546     test_LocalizedNames();
2547     test_SHCreateShellItem();
2548     test_desktop_IPersist();
2549     test_GetUIObject();
2550     test_SHSimpleIDListFromPath();
2551     test_ParseDisplayNamePBC();
2552
2553     OleUninitialize();
2554 }