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