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