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