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