rpcrt4: Move the full pointer table init to after a potential call to NdrServerInitia...
[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             "Filename should be stored as wchar-string at this position!\n");
477     }
478     
479     /* It seems as if we cannot bind to regular files on windows, but only directories. 
480      */
481     hr = IShellFolder_BindToObject(psfDesktop, pidlTestFile, NULL, &IID_IUnknown, (VOID**)&psfFile);
482     todo_wine
483     ok (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) ||
484         hr == E_NOTIMPL || /* Vista */
485         broken(SUCCEEDED(hr)), /* Win9x, W2K */
486         "hr = %08x\n", hr);
487     if (SUCCEEDED(hr)) {
488         IShellFolder_Release(psfFile);
489     }
490
491     if (!pSHBindToParent)
492     {
493         win_skip("SHBindToParent is missing\n");
494         DeleteFileA(szTestFile);
495         RemoveDirectoryA(szTestDir);
496         return;
497     }
498   
499     /* Some tests for IShellFolder::SetNameOf */
500     if (pSHGetFolderPathAndSubDirA)
501     {
502         hr = pSHBindToParent(pidlTestFile, &IID_IShellFolder, (VOID**)&psfPersonal, &pidlLast);
503         ok(SUCCEEDED(hr), "SHBindToParent failed! hr = %08x\n", hr);
504         if (SUCCEEDED(hr)) {
505             /* It's ok to use this fixed path. Call will fail anyway. */
506             WCHAR wszAbsoluteFilename[] = { 'C',':','\\','w','i','n','e','t','e','s','t', 0 };
507             LPITEMIDLIST pidlNew;
508
509             /* The pidl returned through the last parameter of SetNameOf is a simple one. */
510             hr = IShellFolder_SetNameOf(psfPersonal, NULL, pidlLast, wszDirName, SHGDN_NORMAL, &pidlNew);
511             ok (SUCCEEDED(hr), "SetNameOf failed! hr = %08x\n", hr);
512             if (hr == S_OK)
513             {
514                 ok (((LPITEMIDLIST)((LPBYTE)pidlNew+pidlNew->mkid.cb))->mkid.cb == 0,
515                     "pidl returned from SetNameOf should be simple!\n");
516
517                 /* Passing an absolute path to SetNameOf fails. The HRESULT code indicates that SetNameOf
518                  * is implemented on top of SHFileOperation in WinXP. */
519                 hr = IShellFolder_SetNameOf(psfPersonal, NULL, pidlNew, wszAbsoluteFilename,
520                         SHGDN_FORPARSING, NULL);
521                 ok (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED), "SetNameOf succeeded! hr = %08x\n", hr);
522
523                 /* Rename the file back to its original name. SetNameOf ignores the fact, that the
524                  * SHGDN flags specify an absolute path. */
525                 hr = IShellFolder_SetNameOf(psfPersonal, NULL, pidlNew, wszFileName, SHGDN_FORPARSING, NULL);
526                 ok (SUCCEEDED(hr), "SetNameOf failed! hr = %08x\n", hr);
527
528                 pILFree(pidlNew);
529             }
530
531             IShellFolder_Release(psfPersonal);
532         }
533     }
534     else
535         win_skip("Avoid needs of interaction on Win2k\n");
536
537     /* Deleting the file and the directory */
538     DeleteFileA(szTestFile);
539     RemoveDirectoryA(szTestDir);
540
541     /* SHGetPathFromIDListW still works, although the file is not present anymore. */
542     if (pSHGetPathFromIDListW)
543     {
544         result = pSHGetPathFromIDListW(pidlTestFile, wszTestFile2);
545         ok (result, "SHGetPathFromIDListW failed! Last error: %u\n", GetLastError());
546         ok (!lstrcmpiW(wszTestFile, wszTestFile2), "SHGetPathFromIDListW returns incorrect path!\n");
547     }
548
549     /* SHBindToParent fails, if called with a NULL PIDL. */
550     hr = pSHBindToParent(NULL, &IID_IShellFolder, (VOID**)&psfPersonal, &pidlLast);
551     ok (FAILED(hr), "SHBindToParent(NULL) should fail!\n");
552
553     /* But it succeeds with an empty PIDL. */
554     hr = pSHBindToParent(pidlEmpty, &IID_IShellFolder, (VOID**)&psfPersonal, &pidlLast);
555     ok (SUCCEEDED(hr), "SHBindToParent(empty PIDL) should succeed! hr = %08x\n", hr);
556     ok (pidlLast == pidlEmpty, "The last element of an empty PIDL should be the PIDL itself!\n");
557     if (SUCCEEDED(hr)) 
558         IShellFolder_Release(psfPersonal);
559     
560     /* Binding to the folder and querying the display name of the file also works. */
561     hr = pSHBindToParent(pidlTestFile, &IID_IShellFolder, (VOID**)&psfPersonal, &pidlLast); 
562     ok (SUCCEEDED(hr), "SHBindToParent failed! hr = %08x\n", hr);
563     if (FAILED(hr)) {
564         IShellFolder_Release(psfDesktop);
565         return;
566     }
567
568     /* This test shows that Windows doesn't allocate a new pidlLast, but returns a pointer into 
569      * pidlTestFile (In accordance with MSDN). */
570     ok (pILFindLastID(pidlTestFile) == pidlLast, 
571                                 "SHBindToParent doesn't return the last id of the pidl param!\n");
572     
573     hr = IShellFolder_GetDisplayNameOf(psfPersonal, pidlLast, SHGDN_FORPARSING, &strret);
574     ok (SUCCEEDED(hr), "Personal->GetDisplayNameOf failed! hr = %08x\n", hr);
575     if (FAILED(hr)) {
576         IShellFolder_Release(psfDesktop);
577         IShellFolder_Release(psfPersonal);
578         return;
579     }
580
581     if (pStrRetToBufW)
582     {
583         hr = pStrRetToBufW(&strret, pidlLast, wszTestFile2, MAX_PATH);
584         ok (SUCCEEDED(hr), "StrRetToBufW failed! hr = %08x\n", hr);
585         ok (!lstrcmpiW(wszTestFile, wszTestFile2), "GetDisplayNameOf returns incorrect path!\n");
586     }
587     
588     IShellFolder_Release(psfDesktop);
589     IShellFolder_Release(psfPersonal);
590 }
591
592 static void test_CallForAttributes(void)
593 {
594     HKEY hKey;
595     LONG lResult;
596     HRESULT hr;
597     DWORD dwSize;
598     LPSHELLFOLDER psfDesktop;
599     LPITEMIDLIST pidlMyDocuments;
600     DWORD dwAttributes, dwCallForAttributes, dwOrigAttributes, dwOrigCallForAttributes;
601     static const WCHAR wszAttributes[] = { 'A','t','t','r','i','b','u','t','e','s',0 };
602     static const WCHAR wszCallForAttributes[] = { 
603         'C','a','l','l','F','o','r','A','t','t','r','i','b','u','t','e','s',0 };
604     static const WCHAR wszMyDocumentsKey[] = {
605         'C','L','S','I','D','\\','{','4','5','0','D','8','F','B','A','-','A','D','2','5','-',
606         '1','1','D','0','-','9','8','A','8','-','0','8','0','0','3','6','1','B','1','1','0','3','}',
607         '\\','S','h','e','l','l','F','o','l','d','e','r',0 };
608     WCHAR wszMyDocuments[] = {
609         ':',':','{','4','5','0','D','8','F','B','A','-','A','D','2','5','-','1','1','D','0','-',
610         '9','8','A','8','-','0','8','0','0','3','6','1','B','1','1','0','3','}',0 };
611     
612     /* For the root of a namespace extension, the attributes are not queried by binding
613      * to the object and calling GetAttributesOf. Instead, the attributes are read from 
614      * the registry value HKCR/CLSID/{...}/ShellFolder/Attributes. This is documented on MSDN.
615      *
616      * The MyDocuments shellfolder on WinXP has a HKCR/CLSID/{...}/ShellFolder/CallForAttributes
617      * value. It seems that if the folder is queried for one of the flags set in CallForAttributes,
618      * the shell does bind to the folder object and calls GetAttributesOf. This is not documented
619      * on MSDN. This test is meant to document the observed behaviour on WinXP SP2.
620      */
621     hr = SHGetDesktopFolder(&psfDesktop);
622     ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08x\n", hr);
623     if (FAILED(hr)) return;
624     
625     hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyDocuments, NULL, 
626                                        &pidlMyDocuments, NULL);
627     ok (SUCCEEDED(hr) ||
628         broken(hr == E_INVALIDARG), /* Win95, NT4 */
629         "Desktop's ParseDisplayName failed to parse MyDocuments's CLSID! hr = %08x\n", hr);
630     if (FAILED(hr)) {
631         IShellFolder_Release(psfDesktop);
632         return;
633     }
634
635     dwAttributes = 0xffffffff;
636     hr = IShellFolder_GetAttributesOf(psfDesktop, 1, 
637                                       (LPCITEMIDLIST*)&pidlMyDocuments, &dwAttributes);
638     ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(MyDocuments) failed! hr = %08x\n", hr);
639
640     /* We need the following setup (as observed on WinXP SP2), for the tests to make sense. */
641     ok (dwAttributes & SFGAO_FILESYSTEM, "SFGAO_FILESYSTEM attribute is not set for MyDocuments!\n");
642     ok (!(dwAttributes & SFGAO_ISSLOW), "SFGAO_ISSLOW attribute is set for MyDocuments!\n");
643     ok (!(dwAttributes & SFGAO_GHOSTED), "SFGAO_GHOSTED attribute is set for MyDocuments!\n");
644
645     /* We don't have the MyDocuments shellfolder in wine yet, and thus we don't have the registry
646      * key. So the test will return at this point, if run on wine. 
647      */
648     lResult = RegOpenKeyExW(HKEY_CLASSES_ROOT, wszMyDocumentsKey, 0, KEY_WRITE|KEY_READ, &hKey);
649     ok (lResult == ERROR_SUCCESS, "RegOpenKeyEx failed! result: %08x\n", lResult);
650     if (lResult != ERROR_SUCCESS) {
651         IMalloc_Free(ppM, pidlMyDocuments);
652         IShellFolder_Release(psfDesktop);
653         return;
654     }
655     
656     /* Query MyDocuments' Attributes value, to be able to restore it later. */
657     dwSize = sizeof(DWORD);
658     lResult = RegQueryValueExW(hKey, wszAttributes, NULL, NULL, (LPBYTE)&dwOrigAttributes, &dwSize);
659     ok (lResult == ERROR_SUCCESS, "RegQueryValueEx failed! result: %08x\n", lResult);
660     if (lResult != ERROR_SUCCESS) {
661         RegCloseKey(hKey);
662         IMalloc_Free(ppM, pidlMyDocuments);
663         IShellFolder_Release(psfDesktop);
664         return;
665     }
666
667     /* Query MyDocuments' CallForAttributes value, to be able to restore it later. */
668     dwSize = sizeof(DWORD);
669     lResult = RegQueryValueExW(hKey, wszCallForAttributes, NULL, NULL, 
670                               (LPBYTE)&dwOrigCallForAttributes, &dwSize);
671     ok (lResult == ERROR_SUCCESS, "RegQueryValueEx failed! result: %08x\n", lResult);
672     if (lResult != ERROR_SUCCESS) {
673         RegCloseKey(hKey);
674         IMalloc_Free(ppM, pidlMyDocuments);
675         IShellFolder_Release(psfDesktop);
676         return;
677     }
678     
679     /* Define via the Attributes value that MyDocuments attributes are SFGAO_ISSLOW and 
680      * SFGAO_GHOSTED and that MyDocuments should be called for the SFGAO_ISSLOW and
681      * SFGAO_FILESYSTEM attributes. */
682     dwAttributes = SFGAO_ISSLOW|SFGAO_GHOSTED;
683     RegSetValueExW(hKey, wszAttributes, 0, REG_DWORD, (LPBYTE)&dwAttributes, sizeof(DWORD));
684     dwCallForAttributes = SFGAO_ISSLOW|SFGAO_FILESYSTEM;
685     RegSetValueExW(hKey, wszCallForAttributes, 0, REG_DWORD, 
686                    (LPBYTE)&dwCallForAttributes, sizeof(DWORD));
687
688     /* Although it is not set in CallForAttributes, the SFGAO_GHOSTED flag is reset by 
689      * GetAttributesOf. It seems that once there is a single attribute queried, for which
690      * CallForAttributes is set, all flags are taken from the GetAttributesOf call and
691      * the flags in Attributes are ignored. 
692      */
693     dwAttributes = SFGAO_ISSLOW|SFGAO_GHOSTED|SFGAO_FILESYSTEM;
694     hr = IShellFolder_GetAttributesOf(psfDesktop, 1, 
695                                       (LPCITEMIDLIST*)&pidlMyDocuments, &dwAttributes);
696     ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(MyDocuments) failed! hr = %08x\n", hr);
697     if (SUCCEEDED(hr)) 
698         ok (dwAttributes == SFGAO_FILESYSTEM, 
699             "Desktop->GetAttributes(MyDocuments) returned unexpected attributes: %08x\n", 
700             dwAttributes);
701
702     /* Restore MyDocuments' original Attributes and CallForAttributes registry values */
703     RegSetValueExW(hKey, wszAttributes, 0, REG_DWORD, (LPBYTE)&dwOrigAttributes, sizeof(DWORD));
704     RegSetValueExW(hKey, wszCallForAttributes, 0, REG_DWORD, 
705                    (LPBYTE)&dwOrigCallForAttributes, sizeof(DWORD));
706     RegCloseKey(hKey);
707     IMalloc_Free(ppM, pidlMyDocuments);
708     IShellFolder_Release(psfDesktop);
709 }
710
711 static void test_GetAttributesOf(void) 
712 {
713     HRESULT hr;
714     LPSHELLFOLDER psfDesktop, psfMyComputer;
715     SHITEMID emptyitem = { 0, { 0 } };
716     LPCITEMIDLIST pidlEmpty = (LPCITEMIDLIST)&emptyitem;
717     LPITEMIDLIST pidlMyComputer;
718     DWORD dwFlags;
719     static const DWORD desktopFlags[] = {
720         /* WinXP */
721         SFGAO_STORAGE | SFGAO_HASPROPSHEET | SFGAO_STORAGEANCESTOR | SFGAO_FILESYSANCESTOR |
722         SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_HASSUBFOLDER,
723         /* Win2k */
724         SFGAO_CANRENAME | SFGAO_HASPROPSHEET | SFGAO_STREAM | SFGAO_FILESYSANCESTOR |
725         SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_HASSUBFOLDER,
726         /* WinMe, Win9x, WinNT*/
727         SFGAO_CANRENAME | SFGAO_HASPROPSHEET | SFGAO_FILESYSANCESTOR |
728         SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_HASSUBFOLDER
729     };
730     static const DWORD myComputerFlags[] = {
731         /* WinXP */
732         SFGAO_CANRENAME | SFGAO_CANDELETE | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET |
733         SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER,
734         /* Win2k */
735         SFGAO_CANRENAME | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET | SFGAO_STREAM |
736         SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER,
737         /* WinMe, Win9x, WinNT */
738         SFGAO_CANRENAME | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET | SFGAO_FILESYSANCESTOR |
739         SFGAO_FOLDER | SFGAO_HASSUBFOLDER,
740         /* Win95, WinNT when queried directly */
741         SFGAO_CANLINK | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET | SFGAO_FILESYSANCESTOR |
742         SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_HASSUBFOLDER
743     };
744     WCHAR wszMyComputer[] = { 
745         ':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
746         'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };
747     char  cCurrDirA [MAX_PATH] = {0};
748     WCHAR cCurrDirW [MAX_PATH];
749     static WCHAR cTestDirW[] = {'t','e','s','t','d','i','r',0};
750     IShellFolder *IDesktopFolder, *testIShellFolder;
751     ITEMIDLIST *newPIDL;
752     int len, i;
753     BOOL foundFlagsMatch;
754
755     hr = SHGetDesktopFolder(&psfDesktop);
756     ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08x\n", hr);
757     if (FAILED(hr)) return;
758
759     /* The Desktop attributes can be queried with a single empty itemidlist, .. */
760     dwFlags = 0xffffffff;
761     hr = IShellFolder_GetAttributesOf(psfDesktop, 1, &pidlEmpty, &dwFlags);
762     ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(empty pidl) failed! hr = %08x\n", hr);
763     for (i = 0, foundFlagsMatch = FALSE; !foundFlagsMatch &&
764          i < sizeof(desktopFlags) / sizeof(desktopFlags[0]); i++)
765     {
766         if (desktopFlags[i] == dwFlags)
767             foundFlagsMatch = TRUE;
768     }
769     ok (foundFlagsMatch, "Wrong Desktop attributes: %08x\n", dwFlags);
770
771     /* .. or with no itemidlist at all. */
772     dwFlags = 0xffffffff;
773     hr = IShellFolder_GetAttributesOf(psfDesktop, 0, NULL, &dwFlags);
774     ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(NULL) failed! hr = %08x\n", hr);
775     for (i = 0, foundFlagsMatch = FALSE; !foundFlagsMatch &&
776          i < sizeof(desktopFlags) / sizeof(desktopFlags[0]); i++)
777     {
778         if (desktopFlags[i] == dwFlags)
779             foundFlagsMatch = TRUE;
780     }
781     ok (foundFlagsMatch, "Wrong Desktop attributes: %08x\n", dwFlags);
782    
783     /* Testing the attributes of the MyComputer shellfolder */
784     hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyComputer, NULL, &pidlMyComputer, NULL);
785     ok (SUCCEEDED(hr), "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08x\n", hr);
786     if (FAILED(hr)) {
787         IShellFolder_Release(psfDesktop);
788         return;
789     }
790
791     /* Windows sets the SFGAO_CANLINK flag, when MyComputer is queried via the Desktop
792      * folder object. It doesn't do this, if MyComputer is queried directly (see below).
793      */
794     dwFlags = 0xffffffff;
795     hr = IShellFolder_GetAttributesOf(psfDesktop, 1, (LPCITEMIDLIST*)&pidlMyComputer, &dwFlags);
796     ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(MyComputer) failed! hr = %08x\n", hr);
797     for (i = 0, foundFlagsMatch = FALSE; !foundFlagsMatch &&
798          i < sizeof(myComputerFlags) / sizeof(myComputerFlags[0]); i++)
799     {
800         if ((myComputerFlags[i] | SFGAO_CANLINK) == dwFlags)
801             foundFlagsMatch = TRUE;
802     }
803     todo_wine
804     ok (foundFlagsMatch, "Wrong MyComputer attributes: %08x\n", dwFlags);
805
806     hr = IShellFolder_BindToObject(psfDesktop, pidlMyComputer, NULL, &IID_IShellFolder, (LPVOID*)&psfMyComputer);
807     ok (SUCCEEDED(hr), "Desktop failed to bind to MyComputer object! hr = %08x\n", hr);
808     IShellFolder_Release(psfDesktop);
809     IMalloc_Free(ppM, pidlMyComputer);
810     if (FAILED(hr)) return;
811
812     hr = IShellFolder_GetAttributesOf(psfMyComputer, 1, &pidlEmpty, &dwFlags);
813     todo_wine
814     ok (hr == E_INVALIDARG ||
815         broken(SUCCEEDED(hr)), /* W2K and earlier */
816         "MyComputer->GetAttributesOf(emtpy pidl) should fail! hr = %08x\n", hr);
817
818     dwFlags = 0xffffffff;
819     hr = IShellFolder_GetAttributesOf(psfMyComputer, 0, NULL, &dwFlags);
820     ok (SUCCEEDED(hr), "MyComputer->GetAttributesOf(NULL) failed! hr = %08x\n", hr); 
821     for (i = 0, foundFlagsMatch = FALSE; !foundFlagsMatch &&
822          i < sizeof(myComputerFlags) / sizeof(myComputerFlags[0]); i++)
823     {
824         if (myComputerFlags[i] == dwFlags)
825             foundFlagsMatch = TRUE;
826     }
827     todo_wine
828     ok (foundFlagsMatch, "Wrong MyComputer attributes: %08x\n", dwFlags);
829
830     IShellFolder_Release(psfMyComputer);
831
832     GetCurrentDirectoryA(MAX_PATH, cCurrDirA);
833     len = lstrlenA(cCurrDirA);
834
835     if (len == 0) {
836         win_skip("GetCurrentDirectoryA returned empty string. Skipping test_GetAttributesOf\n");
837         return;
838     }
839     if (len > 3 && cCurrDirA[len-1] == '\\')
840         cCurrDirA[len-1] = 0;
841
842     /* create test directory */
843     CreateFilesFolders();
844
845     MultiByteToWideChar(CP_ACP, 0, cCurrDirA, -1, cCurrDirW, MAX_PATH);
846  
847     hr = SHGetDesktopFolder(&IDesktopFolder);
848     ok(hr == S_OK, "SHGetDesktopfolder failed %08x\n", hr);
849
850     hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cCurrDirW, NULL, &newPIDL, 0);
851     ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
852
853     hr = IShellFolder_BindToObject(IDesktopFolder, newPIDL, NULL, (REFIID)&IID_IShellFolder, (LPVOID *)&testIShellFolder);
854     ok(hr == S_OK, "BindToObject failed %08x\n", hr);
855
856     IMalloc_Free(ppM, newPIDL);
857
858     /* get relative PIDL */
859     hr = IShellFolder_ParseDisplayName(testIShellFolder, NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
860     ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
861
862     /* test the shell attributes of the test directory using the relative PIDL */
863     dwFlags = SFGAO_FOLDER;
864     hr = IShellFolder_GetAttributesOf(testIShellFolder, 1, (LPCITEMIDLIST*)&newPIDL, &dwFlags);
865     ok (SUCCEEDED(hr), "Desktop->GetAttributesOf() failed! hr = %08x\n", hr);
866     ok ((dwFlags&SFGAO_FOLDER), "Wrong directory attribute for relative PIDL: %08x\n", dwFlags);
867
868     /* free memory */
869     IMalloc_Free(ppM, newPIDL);
870
871     /* append testdirectory name to path */
872     if (cCurrDirA[len-1] == '\\')
873         cCurrDirA[len-1] = 0;
874     lstrcatA(cCurrDirA, "\\testdir");
875     MultiByteToWideChar(CP_ACP, 0, cCurrDirA, -1, cCurrDirW, MAX_PATH);
876
877     hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cCurrDirW, NULL, &newPIDL, 0);
878     ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
879
880     /* test the shell attributes of the test directory using the absolute PIDL */
881     dwFlags = SFGAO_FOLDER;
882     hr = IShellFolder_GetAttributesOf(IDesktopFolder, 1, (LPCITEMIDLIST*)&newPIDL, &dwFlags);
883     ok (SUCCEEDED(hr), "Desktop->GetAttributesOf() failed! hr = %08x\n", hr);
884     ok ((dwFlags&SFGAO_FOLDER), "Wrong directory attribute for absolute PIDL: %08x\n", dwFlags);
885
886     /* free memory */
887     IMalloc_Free(ppM, newPIDL);
888
889     IShellFolder_Release(testIShellFolder);
890
891     Cleanup();
892
893     IShellFolder_Release(IDesktopFolder);
894 }
895
896 static void test_SHGetPathFromIDList(void)
897 {
898     SHITEMID emptyitem = { 0, { 0 } };
899     LPCITEMIDLIST pidlEmpty = (LPCITEMIDLIST)&emptyitem;
900     LPITEMIDLIST pidlMyComputer;
901     WCHAR wszPath[MAX_PATH], wszDesktop[MAX_PATH];
902     BOOL result;
903     HRESULT hr;
904     LPSHELLFOLDER psfDesktop;
905     WCHAR wszMyComputer[] = { 
906         ':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
907         'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };
908     WCHAR wszFileName[MAX_PATH];
909     LPITEMIDLIST pidlTestFile;
910     HANDLE hTestFile;
911     STRRET strret;
912     static WCHAR wszTestFile[] = {
913         'w','i','n','e','t','e','s','t','.','f','o','o',0 };
914         HRESULT (WINAPI *pSHGetSpecialFolderLocation)(HWND, int, LPITEMIDLIST *);
915         HMODULE hShell32;
916         LPITEMIDLIST pidlPrograms;
917
918     if(!pSHGetPathFromIDListW || !pSHGetSpecialFolderPathW)
919     {
920         win_skip("SHGetPathFromIDListW() or SHGetSpecialFolderPathW() is missing\n");
921         return;
922     }
923
924     /* Calling SHGetPathFromIDListW with no pidl should return the empty string */
925     wszPath[0] = 'a';
926     wszPath[1] = '\0';
927     result = pSHGetPathFromIDListW(NULL, wszPath);
928     ok(!result, "Expected failure\n");
929     ok(!wszPath[0], "Expected empty string\n");
930
931     /* Calling SHGetPathFromIDListW with an empty pidl should return the desktop folder's path. */
932     result = pSHGetSpecialFolderPathW(NULL, wszDesktop, CSIDL_DESKTOP, FALSE);
933     ok(result, "SHGetSpecialFolderPathW(CSIDL_DESKTOP) failed! Last error: %u\n", GetLastError());
934     if (!result) return;
935
936     /* Check if we are on Win9x */
937     SetLastError(0xdeadbeef);
938     lstrcmpiW(wszDesktop, wszDesktop);
939     if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
940     {
941         win_skip("Most W-calls are not implemented\n");
942         return;
943     }
944
945     result = pSHGetPathFromIDListW(pidlEmpty, wszPath);
946     ok(result, "SHGetPathFromIDListW failed! Last error: %u\n", GetLastError());
947     if (!result) return;
948     ok(!lstrcmpiW(wszDesktop, wszPath), "SHGetPathFromIDListW didn't return desktop path for empty pidl!\n");
949
950     /* MyComputer does not map to a filesystem path. SHGetPathFromIDListW should fail. */
951     hr = SHGetDesktopFolder(&psfDesktop);
952     ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08x\n", hr);
953     if (FAILED(hr)) return;
954
955     hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyComputer, NULL, &pidlMyComputer, NULL);
956     ok (SUCCEEDED(hr), "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08x\n", hr);
957     if (FAILED(hr)) {
958         IShellFolder_Release(psfDesktop);
959         return;
960     }
961
962     SetLastError(0xdeadbeef);
963     wszPath[0] = 'a';
964     wszPath[1] = '\0';
965     result = pSHGetPathFromIDListW(pidlMyComputer, wszPath);
966     ok (!result, "SHGetPathFromIDListW succeeded where it shouldn't!\n");
967     ok (GetLastError()==0xdeadbeef ||
968         GetLastError()==ERROR_SUCCESS, /* Vista and higher */
969         "Unexpected last error from SHGetPathFromIDListW: %u\n", GetLastError());
970     ok (!wszPath[0], "Expected empty path\n");
971     if (result) {
972         IShellFolder_Release(psfDesktop);
973         return;
974     }
975
976     IMalloc_Free(ppM, pidlMyComputer);
977
978     result = pSHGetSpecialFolderPathW(NULL, wszFileName, CSIDL_DESKTOPDIRECTORY, FALSE);
979     ok(result, "SHGetSpecialFolderPathW failed! Last error: %u\n", GetLastError());
980     if (!result) {
981         IShellFolder_Release(psfDesktop);
982         return;
983     }
984     myPathAddBackslashW(wszFileName);
985     lstrcatW(wszFileName, wszTestFile);
986     hTestFile = CreateFileW(wszFileName, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL);
987     ok(hTestFile != INVALID_HANDLE_VALUE, "CreateFileW failed! Last error: %u\n", GetLastError());
988     if (hTestFile == INVALID_HANDLE_VALUE) {
989         IShellFolder_Release(psfDesktop);
990         return;
991     }
992     CloseHandle(hTestFile);
993
994     hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszTestFile, NULL, &pidlTestFile, NULL);
995     ok (SUCCEEDED(hr), "Desktop's ParseDisplayName failed to parse filename hr = %08x\n", hr);
996     if (FAILED(hr)) {
997         IShellFolder_Release(psfDesktop);
998         DeleteFileW(wszFileName);
999         IMalloc_Free(ppM, pidlTestFile);
1000         return;
1001     }
1002
1003     /* This test is to show that the Desktop shellfolder prepends the CSIDL_DESKTOPDIRECTORY
1004      * path for files placed on the desktop, if called with SHGDN_FORPARSING. */
1005     hr = IShellFolder_GetDisplayNameOf(psfDesktop, pidlTestFile, SHGDN_FORPARSING, &strret);
1006     ok (SUCCEEDED(hr), "Desktop's GetDisplayNamfOf failed! hr = %08x\n", hr);
1007     IShellFolder_Release(psfDesktop);
1008     DeleteFileW(wszFileName);
1009     if (FAILED(hr)) {
1010         IMalloc_Free(ppM, pidlTestFile);
1011         return;
1012     }
1013     if (pStrRetToBufW)
1014     {
1015         pStrRetToBufW(&strret, pidlTestFile, wszPath, MAX_PATH);
1016         ok(0 == lstrcmpW(wszFileName, wszPath), 
1017            "Desktop->GetDisplayNameOf(pidlTestFile, SHGDN_FORPARSING) "
1018            "returned incorrect path for file placed on desktop\n");
1019     }
1020
1021     result = pSHGetPathFromIDListW(pidlTestFile, wszPath);
1022     ok(result, "SHGetPathFromIDListW failed! Last error: %u\n", GetLastError());
1023     IMalloc_Free(ppM, pidlTestFile);
1024     if (!result) return;
1025     ok(0 == lstrcmpW(wszFileName, wszPath), "SHGetPathFromIDListW returned incorrect path for file placed on desktop\n");
1026
1027
1028         /* Test if we can get the path from the start menu "program files" PIDL. */
1029     hShell32 = GetModuleHandleA("shell32");
1030     pSHGetSpecialFolderLocation = (void *)GetProcAddress(hShell32, "SHGetSpecialFolderLocation");
1031
1032     hr = pSHGetSpecialFolderLocation(NULL, CSIDL_PROGRAM_FILES, &pidlPrograms);
1033     ok(SUCCEEDED(hr), "SHGetFolderLocation failed: 0x%08x\n", hr);
1034
1035     SetLastError(0xdeadbeef);
1036     result = pSHGetPathFromIDListW(pidlPrograms, wszPath);
1037         IMalloc_Free(ppM, pidlPrograms);
1038     ok(result, "SHGetPathFromIDListW failed\n");
1039 }
1040
1041 static void test_EnumObjects_and_CompareIDs(void)
1042 {
1043     ITEMIDLIST *newPIDL;
1044     IShellFolder *IDesktopFolder, *testIShellFolder;
1045     char  cCurrDirA [MAX_PATH] = {0};
1046     static const CHAR cTestDirA[] = "\\testdir";
1047     WCHAR cTestDirW[MAX_PATH];
1048     int len;
1049     HRESULT hr;
1050
1051     GetCurrentDirectoryA(MAX_PATH, cCurrDirA);
1052     len = lstrlenA(cCurrDirA);
1053
1054     if(len == 0) {
1055         win_skip("GetCurrentDirectoryA returned empty string. Skipping test_EnumObjects_and_CompareIDs\n");
1056         return;
1057     }
1058     if(cCurrDirA[len-1] == '\\')
1059         cCurrDirA[len-1] = 0;
1060
1061     lstrcatA(cCurrDirA, cTestDirA);
1062     MultiByteToWideChar(CP_ACP, 0, cCurrDirA, -1, cTestDirW, MAX_PATH);
1063
1064     hr = SHGetDesktopFolder(&IDesktopFolder);
1065     ok(hr == S_OK, "SHGetDesktopfolder failed %08x\n", hr);
1066
1067     CreateFilesFolders();
1068
1069     hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
1070     ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
1071
1072     hr = IShellFolder_BindToObject(IDesktopFolder, newPIDL, NULL, (REFIID)&IID_IShellFolder, (LPVOID *)&testIShellFolder);
1073     ok(hr == S_OK, "BindToObject failed %08x\n", hr);
1074
1075     test_EnumObjects(testIShellFolder);
1076
1077     IShellFolder_Release(testIShellFolder);
1078
1079     Cleanup();
1080
1081     IMalloc_Free(ppM, newPIDL);
1082
1083     IShellFolder_Release(IDesktopFolder);
1084 }
1085
1086 /* A simple implementation of an IPropertyBag, which returns fixed values for
1087  * 'Target' and 'Attributes' properties.
1088  */
1089 static HRESULT WINAPI InitPropertyBag_IPropertyBag_QueryInterface(IPropertyBag *iface, REFIID riid,
1090     void **ppvObject) 
1091 {
1092     if (!ppvObject)
1093         return E_INVALIDARG;
1094
1095     if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IPropertyBag, riid)) {
1096         *ppvObject = iface;
1097     } else {
1098         ok (FALSE, "InitPropertyBag asked for unknown interface!\n");
1099         return E_NOINTERFACE;
1100     }
1101
1102     IPropertyBag_AddRef(iface);
1103     return S_OK;
1104 }
1105
1106 static ULONG WINAPI InitPropertyBag_IPropertyBag_AddRef(IPropertyBag *iface) {
1107     return 2;
1108 }
1109
1110 static ULONG WINAPI InitPropertyBag_IPropertyBag_Release(IPropertyBag *iface) {
1111     return 1;
1112 }
1113
1114 static HRESULT WINAPI InitPropertyBag_IPropertyBag_Read(IPropertyBag *iface, LPCOLESTR pszPropName,
1115     VARIANT *pVar, IErrorLog *pErrorLog)
1116 {
1117     static const WCHAR wszTargetSpecialFolder[] = {
1118         'T','a','r','g','e','t','S','p','e','c','i','a','l','F','o','l','d','e','r',0 };
1119     static const WCHAR wszTarget[] = {
1120         'T','a','r','g','e','t',0 };
1121     static const WCHAR wszAttributes[] = {
1122         'A','t','t','r','i','b','u','t','e','s',0 };
1123     static const WCHAR wszResolveLinkFlags[] = {
1124         'R','e','s','o','l','v','e','L','i','n','k','F','l','a','g','s',0 };
1125     static const WCHAR wszTargetKnownFolder[] = {
1126         'T','a','r','g','e','t','K','n','o','w','n','F','o','l','d','e','r',0 };
1127     static const WCHAR wszCLSID[] = {
1128         'C','L','S','I','D',0 };
1129        
1130     if (!lstrcmpW(pszPropName, wszTargetSpecialFolder)) {
1131         ok(V_VT(pVar) == VT_I4 ||
1132            broken(V_VT(pVar) == VT_BSTR),   /* Win2k */
1133            "Wrong variant type for 'TargetSpecialFolder' property!\n");
1134         return E_INVALIDARG;
1135     }
1136     
1137     if (!lstrcmpW(pszPropName, wszResolveLinkFlags)) 
1138     {
1139         ok(V_VT(pVar) == VT_UI4, "Wrong variant type for 'ResolveLinkFlags' property!\n");
1140         return E_INVALIDARG;
1141     }
1142
1143     if (!lstrcmpW(pszPropName, wszTarget)) {
1144         WCHAR wszPath[MAX_PATH];
1145         BOOL result;
1146         
1147         ok(V_VT(pVar) == VT_BSTR ||
1148            broken(V_VT(pVar) == VT_EMPTY),  /* Win2k */
1149            "Wrong variant type for 'Target' property!\n");
1150         if (V_VT(pVar) != VT_BSTR) return E_INVALIDARG;
1151
1152         result = pSHGetSpecialFolderPathW(NULL, wszPath, CSIDL_DESKTOPDIRECTORY, FALSE);
1153         ok(result, "SHGetSpecialFolderPathW(DESKTOPDIRECTORY) failed! %u\n", GetLastError());
1154         if (!result) return E_INVALIDARG;
1155
1156         V_BSTR(pVar) = SysAllocString(wszPath);
1157         return S_OK;
1158     }
1159
1160     if (!lstrcmpW(pszPropName, wszAttributes)) {
1161         ok(V_VT(pVar) == VT_UI4, "Wrong variant type for 'Attributes' property!\n");
1162         if (V_VT(pVar) != VT_UI4) return E_INVALIDARG;
1163         V_UI4(pVar) = SFGAO_FOLDER|SFGAO_HASSUBFOLDER|SFGAO_FILESYSANCESTOR|
1164                       SFGAO_CANRENAME|SFGAO_FILESYSTEM;
1165         return S_OK;
1166     }
1167
1168     if (!lstrcmpW(pszPropName, wszTargetKnownFolder)) {
1169         ok(V_VT(pVar) == VT_BSTR, "Wrong variant type for 'TargetKnownFolder' property!\n");
1170         /* TODO */
1171         return E_INVALIDARG;
1172     }
1173
1174     if (!lstrcmpW(pszPropName, wszCLSID)) {
1175         ok(V_VT(pVar) == VT_EMPTY, "Wrong variant type for 'CLSID' property!\n");
1176         /* TODO */
1177         return E_INVALIDARG;
1178     }
1179
1180     ok(FALSE, "PropertyBag was asked for unknown property %s (vt=%d)!\n", wine_dbgstr_w(pszPropName), V_VT(pVar));
1181     return E_INVALIDARG;
1182 }
1183
1184 static HRESULT WINAPI InitPropertyBag_IPropertyBag_Write(IPropertyBag *iface, LPCOLESTR pszPropName,
1185     VARIANT *pVar)
1186 {
1187     ok(FALSE, "Unexpected call to IPropertyBag_Write\n");
1188     return E_NOTIMPL;
1189 }
1190     
1191 static const IPropertyBagVtbl InitPropertyBag_IPropertyBagVtbl = {
1192     InitPropertyBag_IPropertyBag_QueryInterface,
1193     InitPropertyBag_IPropertyBag_AddRef,
1194     InitPropertyBag_IPropertyBag_Release,
1195     InitPropertyBag_IPropertyBag_Read,
1196     InitPropertyBag_IPropertyBag_Write
1197 };
1198
1199 static struct IPropertyBag InitPropertyBag = {
1200     &InitPropertyBag_IPropertyBagVtbl
1201 };
1202
1203 static void test_FolderShortcut(void) {
1204     IPersistPropertyBag *pPersistPropertyBag;
1205     IShellFolder *pShellFolder, *pDesktopFolder;
1206     IPersistFolder3 *pPersistFolder3;
1207     HRESULT hr;
1208     STRRET strret;
1209     WCHAR wszDesktopPath[MAX_PATH], wszBuffer[MAX_PATH];
1210     BOOL result;
1211     CLSID clsid;
1212     LPITEMIDLIST pidlCurrentFolder, pidlWineTestFolder, pidlSubFolder;
1213     HKEY hShellExtKey;
1214     WCHAR wszWineTestFolder[] = {
1215         ':',':','{','9','B','3','5','2','E','B','F','-','2','7','6','5','-','4','5','C','1','-',
1216         'B','4','C','6','-','8','5','C','C','7','F','7','A','B','C','6','4','}',0 };
1217     WCHAR wszShellExtKey[] = { 'S','o','f','t','w','a','r','e','\\',
1218         'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
1219         'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
1220         'E','x','p','l','o','r','e','r','\\','D','e','s','k','t','o','p','\\',
1221         'N','a','m','e','S','p','a','c','e','\\',
1222         '{','9','b','3','5','2','e','b','f','-','2','7','6','5','-','4','5','c','1','-',
1223         'b','4','c','6','-','8','5','c','c','7','f','7','a','b','c','6','4','}',0 };
1224     
1225     WCHAR wszSomeSubFolder[] = { 'S','u','b','F','o','l','d','e','r', 0};
1226     static const GUID CLSID_UnixDosFolder = 
1227         {0x9d20aae8, 0x0625, 0x44b0, {0x9c, 0xa7, 0x71, 0x88, 0x9c, 0x22, 0x54, 0xd9}};
1228
1229     if (!pSHGetSpecialFolderPathW || !pStrRetToBufW) {
1230         win_skip("SHGetSpecialFolderPathW and/or StrRetToBufW are not available\n");
1231         return;
1232     }
1233
1234     if (!pSHGetFolderPathAndSubDirA)
1235     {
1236         win_skip("FolderShortcut test doesn't work on Win2k\n");
1237         return;
1238     }
1239
1240     /* These tests basically show, that CLSID_FolderShortcuts are initialized
1241      * via their IPersistPropertyBag interface. And that the target folder
1242      * is taken from the IPropertyBag's 'Target' property.
1243      */
1244     hr = CoCreateInstance(&CLSID_FolderShortcut, NULL, CLSCTX_INPROC_SERVER, 
1245                           &IID_IPersistPropertyBag, (LPVOID*)&pPersistPropertyBag);
1246     if (hr == REGDB_E_CLASSNOTREG) {
1247         win_skip("CLSID_FolderShortcut is not implemented\n");
1248         return;
1249     }
1250     ok (SUCCEEDED(hr), "CoCreateInstance failed! hr = 0x%08x\n", hr);
1251     if (FAILED(hr)) return;
1252
1253     hr = IPersistPropertyBag_Load(pPersistPropertyBag, &InitPropertyBag, NULL);
1254     ok(SUCCEEDED(hr), "IPersistPropertyBag_Load failed! hr = %08x\n", hr);
1255     if (FAILED(hr)) {
1256         IPersistPropertyBag_Release(pPersistPropertyBag);
1257         return;
1258     }
1259
1260     hr = IPersistPropertyBag_QueryInterface(pPersistPropertyBag, &IID_IShellFolder, 
1261                                             (LPVOID*)&pShellFolder);
1262     IPersistPropertyBag_Release(pPersistPropertyBag);
1263     ok(SUCCEEDED(hr), "IPersistPropertyBag_QueryInterface(IShellFolder) failed! hr = %08x\n", hr);
1264     if (FAILED(hr)) return;
1265
1266     hr = IShellFolder_GetDisplayNameOf(pShellFolder, NULL, SHGDN_FORPARSING, &strret);
1267     ok(SUCCEEDED(hr), "IShellFolder_GetDisplayNameOf(NULL) failed! hr = %08x\n", hr);
1268     if (FAILED(hr)) {
1269         IShellFolder_Release(pShellFolder);
1270         return;
1271     }
1272
1273     result = pSHGetSpecialFolderPathW(NULL, wszDesktopPath, CSIDL_DESKTOPDIRECTORY, FALSE);
1274     ok(result, "SHGetSpecialFolderPathW(CSIDL_DESKTOPDIRECTORY) failed! %u\n", GetLastError());
1275     if (!result) return;
1276
1277     pStrRetToBufW(&strret, NULL, wszBuffer, MAX_PATH);
1278     ok(!lstrcmpiW(wszDesktopPath, wszBuffer), "FolderShortcut returned incorrect folder!\n");
1279
1280     hr = IShellFolder_QueryInterface(pShellFolder, &IID_IPersistFolder3, (LPVOID*)&pPersistFolder3);
1281     IShellFolder_Release(pShellFolder);
1282     ok(SUCCEEDED(hr), "IShellFolder_QueryInterface(IID_IPersistFolder3 failed! hr = 0x%08x\n", hr);
1283     if (FAILED(hr)) return;
1284
1285     hr = IPersistFolder3_GetClassID(pPersistFolder3, &clsid);
1286     ok(SUCCEEDED(hr), "IPersistFolder3_GetClassID failed! hr=0x%08x\n", hr);
1287     ok(IsEqualCLSID(&clsid, &CLSID_FolderShortcut), "Unexpected CLSID!\n");
1288
1289     hr = IPersistFolder3_GetCurFolder(pPersistFolder3, &pidlCurrentFolder);
1290     ok(SUCCEEDED(hr), "IPersistFolder3_GetCurFolder failed! hr=0x%08x\n", hr);
1291     ok(!pidlCurrentFolder, "IPersistFolder3_GetCurFolder should return a NULL pidl!\n");
1292                     
1293     /* For FolderShortcut objects, the Initialize method initialized the folder's position in the
1294      * shell namespace. The target folder, read from the property bag above, remains untouched. 
1295      * The following tests show this: The itemidlist for some imaginary shellfolder object
1296      * is created and the FolderShortcut is initialized with it. GetCurFolder now returns this
1297      * itemidlist, but GetDisplayNameOf still returns the path from above.
1298      */
1299     hr = SHGetDesktopFolder(&pDesktopFolder);
1300     ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08x\n", hr);
1301     if (FAILED(hr)) return;
1302
1303     /* Temporarily register WineTestFolder as a shell namespace extension at the Desktop. 
1304      * Otherwise ParseDisplayName fails on WinXP with E_INVALIDARG */
1305     RegCreateKeyW(HKEY_CURRENT_USER, wszShellExtKey, &hShellExtKey);
1306     RegCloseKey(hShellExtKey);
1307     hr = IShellFolder_ParseDisplayName(pDesktopFolder, NULL, NULL, wszWineTestFolder, NULL,
1308                                        &pidlWineTestFolder, NULL);
1309     RegDeleteKeyW(HKEY_CURRENT_USER, wszShellExtKey);
1310     IShellFolder_Release(pDesktopFolder);
1311     ok (SUCCEEDED(hr), "IShellFolder::ParseDisplayName failed! hr = %08x\n", hr);
1312     if (FAILED(hr)) return;
1313
1314     hr = IPersistFolder3_Initialize(pPersistFolder3, pidlWineTestFolder);
1315     ok (SUCCEEDED(hr), "IPersistFolder3::Initialize failed! hr = %08x\n", hr);
1316     if (FAILED(hr)) {
1317         IPersistFolder3_Release(pPersistFolder3);
1318         pILFree(pidlWineTestFolder);
1319         return;
1320     }
1321
1322     hr = IPersistFolder3_GetCurFolder(pPersistFolder3, &pidlCurrentFolder);
1323     ok(SUCCEEDED(hr), "IPersistFolder3_GetCurFolder failed! hr=0x%08x\n", hr);
1324     ok(pILIsEqual(pidlCurrentFolder, pidlWineTestFolder),
1325         "IPersistFolder3_GetCurFolder should return pidlWineTestFolder!\n");
1326     pILFree(pidlCurrentFolder);
1327     pILFree(pidlWineTestFolder);
1328
1329     hr = IPersistFolder3_QueryInterface(pPersistFolder3, &IID_IShellFolder, (LPVOID*)&pShellFolder);
1330     IPersistFolder3_Release(pPersistFolder3);
1331     ok(SUCCEEDED(hr), "IPersistFolder3_QueryInterface(IShellFolder) failed! hr = %08x\n", hr);
1332     if (FAILED(hr)) return;
1333
1334     hr = IShellFolder_GetDisplayNameOf(pShellFolder, NULL, SHGDN_FORPARSING, &strret);
1335     ok(SUCCEEDED(hr), "IShellFolder_GetDisplayNameOf(NULL) failed! hr = %08x\n", hr);
1336     if (FAILED(hr)) {
1337         IShellFolder_Release(pShellFolder);
1338         return;
1339     }
1340
1341     pStrRetToBufW(&strret, NULL, wszBuffer, MAX_PATH);
1342     ok(!lstrcmpiW(wszDesktopPath, wszBuffer), "FolderShortcut returned incorrect folder!\n");
1343
1344     /* Next few lines are meant to show that children of FolderShortcuts are not FolderShortcuts,
1345      * but ShellFSFolders. */
1346     myPathAddBackslashW(wszDesktopPath);
1347     lstrcatW(wszDesktopPath, wszSomeSubFolder);
1348     if (!CreateDirectoryW(wszDesktopPath, NULL)) {
1349         IShellFolder_Release(pShellFolder);
1350         return;
1351     }
1352     
1353     hr = IShellFolder_ParseDisplayName(pShellFolder, NULL, NULL, wszSomeSubFolder, NULL, 
1354                                        &pidlSubFolder, NULL);
1355     RemoveDirectoryW(wszDesktopPath);
1356     ok (SUCCEEDED(hr), "IShellFolder::ParseDisplayName failed! hr = %08x\n", hr);
1357     if (FAILED(hr)) {
1358         IShellFolder_Release(pShellFolder);
1359         return;
1360     }
1361
1362     hr = IShellFolder_BindToObject(pShellFolder, pidlSubFolder, NULL, &IID_IPersistFolder3,
1363                                    (LPVOID*)&pPersistFolder3);
1364     IShellFolder_Release(pShellFolder);
1365     pILFree(pidlSubFolder);
1366     ok (SUCCEEDED(hr), "IShellFolder::BindToObject failed! hr = %08x\n", hr);
1367     if (FAILED(hr))
1368         return;
1369
1370     /* On windows, we expect CLSID_ShellFSFolder. On wine we relax this constraint
1371      * a little bit and also allow CLSID_UnixDosFolder. */
1372     hr = IPersistFolder3_GetClassID(pPersistFolder3, &clsid);
1373     ok(SUCCEEDED(hr), "IPersistFolder3_GetClassID failed! hr=0x%08x\n", hr);
1374     ok(IsEqualCLSID(&clsid, &CLSID_ShellFSFolder) || IsEqualCLSID(&clsid, &CLSID_UnixDosFolder),
1375         "IPersistFolder3::GetClassID returned unexpected CLSID!\n");
1376
1377     IPersistFolder3_Release(pPersistFolder3);
1378 }
1379
1380 #include "pshpack1.h"
1381 struct FileStructA {
1382     BYTE  type;
1383     BYTE  dummy;
1384     DWORD dwFileSize;
1385     WORD  uFileDate;    /* In our current implementation this is */
1386     WORD  uFileTime;    /* FileTimeToDosDate(WIN32_FIND_DATA->ftLastWriteTime) */
1387     WORD  uFileAttribs;
1388     CHAR  szName[1];
1389 };
1390
1391 struct FileStructW {
1392     WORD  cbLen;        /* Length of this element. */
1393     BYTE  abFooBar1[6]; /* Beyond any recognition. */
1394     WORD  uDate;        /* FileTimeToDosDate(WIN32_FIND_DATA->ftCreationTime)? */
1395     WORD  uTime;        /* (this is currently speculation) */
1396     WORD  uDate2;       /* FileTimeToDosDate(WIN32_FIND_DATA->ftLastAccessTime)? */
1397     WORD  uTime2;       /* (this is currently speculation) */
1398     BYTE  abFooBar2[4]; /* Beyond any recognition. */
1399     WCHAR wszName[1];   /* The long filename in unicode. */
1400     /* Just for documentation: Right after the unicode string: */
1401     WORD  cbOffset;     /* FileStructW's offset from the beginning of the SHITMEID. 
1402                          * SHITEMID->cb == uOffset + cbLen */
1403 };
1404 #include "poppack.h"
1405
1406 static void test_ITEMIDLIST_format(void) {
1407     WCHAR wszPersonal[MAX_PATH];
1408     LPSHELLFOLDER psfDesktop, psfPersonal;
1409     LPITEMIDLIST pidlPersonal, pidlFile;
1410     HANDLE hFile;
1411     HRESULT hr;
1412     BOOL bResult;
1413     WCHAR wszFile[3][17] = { { 'e','v','e','n','_',0 }, { 'o','d','d','_',0 },
1414         { 'l','o','n','g','e','r','_','t','h','a','n','.','8','_','3',0 } };
1415     int i;
1416
1417     if (!pSHGetSpecialFolderPathW) return;
1418
1419     bResult = pSHGetSpecialFolderPathW(NULL, wszPersonal, CSIDL_PERSONAL, FALSE);
1420     ok(bResult, "SHGetSpecialFolderPathW failed! Last error: %u\n", GetLastError());
1421     if (!bResult) return;
1422
1423     SetLastError(0xdeadbeef);
1424     bResult = SetCurrentDirectoryW(wszPersonal);
1425     if (!bResult && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) {
1426         win_skip("Most W-calls are not implemented\n");
1427         return;
1428     }
1429     ok(bResult, "SetCurrentDirectory failed! Last error: %u\n", GetLastError());
1430     if (!bResult) return;
1431
1432     hr = SHGetDesktopFolder(&psfDesktop);
1433     ok(SUCCEEDED(hr), "SHGetDesktopFolder failed! hr: %08x\n", hr);
1434     if (FAILED(hr)) return;
1435
1436     hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszPersonal, NULL, &pidlPersonal, NULL);
1437     ok(SUCCEEDED(hr), "psfDesktop->ParseDisplayName failed! hr = %08x\n", hr);
1438     if (FAILED(hr)) {
1439         IShellFolder_Release(psfDesktop);
1440         return;
1441     }
1442
1443     hr = IShellFolder_BindToObject(psfDesktop, pidlPersonal, NULL, &IID_IShellFolder,
1444         (LPVOID*)&psfPersonal);
1445     IShellFolder_Release(psfDesktop);
1446     pILFree(pidlPersonal);
1447     ok(SUCCEEDED(hr), "psfDesktop->BindToObject failed! hr = %08x\n", hr);
1448     if (FAILED(hr)) return;
1449
1450     for (i=0; i<3; i++) {
1451         CHAR szFile[MAX_PATH];
1452         struct FileStructA *pFileStructA;
1453         WORD cbOffset;
1454
1455         WideCharToMultiByte(CP_ACP, 0, wszFile[i], -1, szFile, MAX_PATH, NULL, NULL);
1456
1457         hFile = CreateFileW(wszFile[i], GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_FLAG_WRITE_THROUGH, NULL);
1458         ok(hFile != INVALID_HANDLE_VALUE, "CreateFile failed! (%u)\n", GetLastError());
1459         if (hFile == INVALID_HANDLE_VALUE) {
1460             IShellFolder_Release(psfPersonal);
1461             return;
1462         }
1463         CloseHandle(hFile);
1464
1465         hr = IShellFolder_ParseDisplayName(psfPersonal, NULL, NULL, wszFile[i], NULL, &pidlFile, NULL);
1466         DeleteFileW(wszFile[i]);
1467         ok(SUCCEEDED(hr), "psfPersonal->ParseDisplayName failed! hr: %08x\n", hr);
1468         if (FAILED(hr)) {
1469             IShellFolder_Release(psfPersonal);
1470             return;
1471         }
1472
1473         pFileStructA = (struct FileStructA *)pidlFile->mkid.abID;
1474         ok(pFileStructA->type == 0x32, "PIDLTYPE should be 0x32!\n");
1475         ok(pFileStructA->dummy == 0x00, "Dummy Byte should be 0x00!\n");
1476         ok(pFileStructA->dwFileSize == 0, "Filesize should be zero!\n");
1477
1478         if (i < 2) /* First two file names are already in valid 8.3 format */
1479             ok(!strcmp(szFile, (CHAR*)&pidlFile->mkid.abID[12]), "Wrong file name!\n");
1480         else
1481             /* WinXP stores a derived 8.3 dos name (LONGER~1.8_3) here. We probably
1482              * can't implement this correctly, since unix filesystems don't support
1483              * this nasty short/long filename stuff. So we'll probably stay with our
1484              * current habbit of storing the long filename here, which seems to work
1485              * just fine. */
1486             todo_wine
1487             ok(pidlFile->mkid.abID[18] == '~' ||
1488                broken(pidlFile->mkid.abID[34] == '~'),  /* Win2k */
1489                "Should be derived 8.3 name!\n");
1490
1491         if (i == 0) /* First file name has an even number of chars. No need for alignment. */
1492             ok(pidlFile->mkid.abID[12 + strlen(szFile) + 1] != '\0' ||
1493                broken(pidlFile->mkid.cb == 2 + 12 + strlen(szFile) + 1 + 1),    /* Win2k */
1494                 "Alignment byte, where there shouldn't be!\n");
1495
1496         if (i == 1) /* Second file name has an uneven number of chars => alignment byte */
1497             ok(pidlFile->mkid.abID[12 + strlen(szFile) + 1] == '\0',
1498                 "There should be an alignment byte, but isn't!\n");
1499
1500         /* The offset of the FileStructW member is stored as a WORD at the end of the pidl. */
1501         cbOffset = *(WORD*)(((LPBYTE)pidlFile)+pidlFile->mkid.cb-sizeof(WORD));
1502         ok ((cbOffset >= sizeof(struct FileStructA) &&
1503             cbOffset <= pidlFile->mkid.cb - sizeof(struct FileStructW)) ||
1504             broken(pidlFile->mkid.cb == 2 + 12 + strlen(szFile) + 1 + 1) ||     /* Win2k on short names */
1505             broken(pidlFile->mkid.cb == 2 + 12 + strlen(szFile) + 1 + 12 + 1),  /* Win2k on long names */
1506             "Wrong offset value (%d) stored at the end of the PIDL\n", cbOffset);
1507
1508         if (cbOffset >= sizeof(struct FileStructA) &&
1509             cbOffset <= pidlFile->mkid.cb - sizeof(struct FileStructW))
1510         {
1511             struct FileStructW *pFileStructW = (struct FileStructW *)(((LPBYTE)pidlFile)+cbOffset);
1512
1513             ok(pidlFile->mkid.cb == cbOffset + pFileStructW->cbLen,
1514                 "FileStructW's offset and length should add up to the PIDL's length!\n");
1515
1516             if (pidlFile->mkid.cb == cbOffset + pFileStructW->cbLen) {
1517                 /* Since we just created the file, time of creation,
1518                  * time of last access and time of last write access just be the same.
1519                  * These tests seem to fail sometimes (on WinXP), if the test is run again shortly
1520                  * after the first run. I do remember something with NTFS keeping the creation time
1521                  * if a file is deleted and then created again within a couple of seconds or so.
1522                  * Might be the reason. */
1523                 ok (pFileStructA->uFileDate == pFileStructW->uDate &&
1524                     pFileStructA->uFileTime == pFileStructW->uTime,
1525                     "Last write time should match creation time!\n");
1526
1527                 ok (pFileStructA->uFileDate == pFileStructW->uDate2 &&
1528                     pFileStructA->uFileTime == pFileStructW->uTime2,
1529                     "Last write time should match last access time!\n");
1530
1531                 ok (!lstrcmpW(wszFile[i], pFileStructW->wszName) ||
1532                     !lstrcmpW(wszFile[i], (WCHAR *)(pFileStructW->abFooBar2 + 22)), /* Vista */
1533                     "The filename should be stored in unicode at this position!\n");
1534             }
1535         }
1536
1537         pILFree(pidlFile);
1538     }
1539 }
1540
1541 static void testSHGetFolderPathAndSubDirA(void)
1542 {
1543     HRESULT ret;
1544     BOOL delret;
1545     DWORD dwret;
1546     int i;
1547     static char wine[] = "wine";
1548     static char winetemp[] = "wine\\temp";
1549     static char appdata[MAX_PATH];
1550     static char testpath[MAX_PATH];
1551     static char toolongpath[MAX_PATH+1];
1552
1553     if(!pSHGetFolderPathA) {
1554         win_skip("SHGetFolderPathA not present!\n");
1555         return;
1556     }
1557     if(FAILED(pSHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, appdata)))
1558     {
1559         win_skip("SHGetFolderPathA failed for CSIDL_LOCAL_APPDATA!\n");
1560         return;
1561     }
1562
1563     sprintf(testpath, "%s\\%s", appdata, winetemp);
1564     delret = RemoveDirectoryA(testpath);
1565     if(!delret && (ERROR_PATH_NOT_FOUND != GetLastError()) ) {
1566         win_skip("RemoveDirectoryA(%s) failed with error %u\n", testpath, GetLastError());
1567         return;
1568     }
1569
1570     sprintf(testpath, "%s\\%s", appdata, wine);
1571     delret = RemoveDirectoryA(testpath);
1572     if(!delret && (ERROR_PATH_NOT_FOUND != GetLastError()) && (ERROR_FILE_NOT_FOUND != GetLastError())) {
1573         win_skip("RemoveDirectoryA(%s) failed with error %u\n", testpath, GetLastError());
1574         return;
1575     }
1576
1577     /* test invalid second parameter */
1578     ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | 0xff, NULL, SHGFP_TYPE_CURRENT, wine, testpath);
1579     ok(E_INVALIDARG == ret, "expected E_INVALIDARG, got  %x\n", ret);
1580
1581     /* test fourth parameter */
1582     ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, 2, winetemp, testpath);
1583     switch(ret) {
1584         case S_OK: /* winvista */
1585             ok(!strncmp(appdata, testpath, strlen(appdata)),
1586                 "expected %s to start with %s\n", testpath, appdata);
1587             ok(!lstrcmpA(&testpath[1 + strlen(appdata)], winetemp),
1588                 "expected %s to end with %s\n", testpath, winetemp);
1589             break;
1590         case E_INVALIDARG: /* winxp, win2k3 */
1591             break;
1592         default:
1593             ok(0, "expected S_OK or E_INVALIDARG, got  %x\n", ret);
1594     }
1595
1596     /* test fifth parameter */
1597     testpath[0] = '\0';
1598     ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, NULL, testpath);
1599     ok(S_OK == ret, "expected S_OK, got %x\n", ret);
1600     ok(!lstrcmpA(appdata, testpath), "expected %s, got %s\n", appdata, testpath);
1601
1602     testpath[0] = '\0';
1603     ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, "", testpath);
1604     ok(S_OK == ret, "expected S_OK, got %x\n", ret);
1605     ok(!lstrcmpA(appdata, testpath), "expected %s, got %s\n", appdata, testpath);
1606
1607     testpath[0] = '\0';
1608     ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, "\\", testpath);
1609     ok(S_OK == ret, "expected S_OK, got %x\n", ret);
1610     ok(!lstrcmpA(appdata, testpath), "expected %s, got %s\n", appdata, testpath);
1611
1612     for(i=0; i< MAX_PATH; i++)
1613         toolongpath[i] = '0' + i % 10;
1614     toolongpath[MAX_PATH] = '\0';
1615     ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, toolongpath, testpath);
1616     ok(HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE) == ret,
1617         "expected %x, got %x\n", HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE), ret);
1618
1619     testpath[0] = '\0';
1620     ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, wine, NULL);
1621     ok((S_OK == ret) || (E_INVALIDARG == ret), "expected S_OK or E_INVALIDARG, got %x\n", ret);
1622
1623     /* test a not existing path */
1624     testpath[0] = '\0';
1625     ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, winetemp, testpath);
1626     ok(HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == ret,
1627         "expected %x, got %x\n", HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND), ret);
1628
1629     /* create a directory inside a not existing directory */
1630     testpath[0] = '\0';
1631     ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_CREATE | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, winetemp, testpath);
1632     ok(S_OK == ret, "expected S_OK, got %x\n", ret);
1633     ok(!strncmp(appdata, testpath, strlen(appdata)),
1634         "expected %s to start with %s\n", testpath, appdata);
1635     ok(!lstrcmpA(&testpath[1 + strlen(appdata)], winetemp),
1636         "expected %s to end with %s\n", testpath, winetemp);
1637     dwret = GetFileAttributes(testpath);
1638     ok(FILE_ATTRIBUTE_DIRECTORY | dwret, "expected %x to contain FILE_ATTRIBUTE_DIRECTORY\n", dwret);
1639
1640     /* cleanup */
1641     sprintf(testpath, "%s\\%s", appdata, winetemp);
1642     RemoveDirectoryA(testpath);
1643     sprintf(testpath, "%s\\%s", appdata, wine);
1644     RemoveDirectoryA(testpath);
1645 }
1646
1647 static void test_LocalizedNames(void)
1648 {
1649     static char cCurrDirA[MAX_PATH];
1650     WCHAR cCurrDirW[MAX_PATH], tempbufW[25];
1651     IShellFolder *IDesktopFolder, *testIShellFolder;
1652     ITEMIDLIST *newPIDL;
1653     int len;
1654     HRESULT hr;
1655     static char resourcefile[MAX_PATH];
1656     DWORD res;
1657     HANDLE file;
1658     STRRET strret;
1659
1660     static const char desktopini_contents1[] =
1661         "[.ShellClassInfo]\r\n"
1662         "LocalizedResourceName=@";
1663     static const char desktopini_contents2[] =
1664         ",-1\r\n";
1665     static WCHAR foldernameW[] = {'t','e','s','t','f','o','l','d','e','r',0};
1666     static const WCHAR folderdisplayW[] = {'F','o','l','d','e','r',' ','N','a','m','e',' ','R','e','s','o','u','r','c','e',0};
1667
1668     /* create folder with desktop.ini and localized name in GetModuleFileNameA(NULL) */
1669     CreateDirectoryA(".\\testfolder", NULL);
1670
1671     SetFileAttributesA(".\\testfolder", GetFileAttributesA(".\\testfolder")|FILE_ATTRIBUTE_SYSTEM);
1672
1673     GetModuleFileNameA(NULL, resourcefile, MAX_PATH);
1674
1675     file = CreateFileA(".\\testfolder\\desktop.ini", GENERIC_WRITE, 0, NULL,
1676                          CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
1677     ok(file != INVALID_HANDLE_VALUE, "CreateFileA failed %i\n", GetLastError());
1678     ok(WriteFile(file, desktopini_contents1, strlen(desktopini_contents1), &res, NULL) &&
1679        WriteFile(file, resourcefile, strlen(resourcefile), &res, NULL) &&
1680        WriteFile(file, desktopini_contents2, strlen(desktopini_contents2), &res, NULL),
1681        "WriteFile failed %i\n", GetLastError());
1682     CloseHandle(file);
1683
1684     /* get IShellFolder for parent */
1685     GetCurrentDirectoryA(MAX_PATH, cCurrDirA);
1686     len = lstrlenA(cCurrDirA);
1687
1688     if (len == 0) {
1689         win_skip("GetCurrentDirectoryA returned empty string. Skipping test_LocalizedNames\n");
1690         goto cleanup;
1691     }
1692     if(cCurrDirA[len-1] == '\\')
1693         cCurrDirA[len-1] = 0;
1694
1695     MultiByteToWideChar(CP_ACP, 0, cCurrDirA, -1, cCurrDirW, MAX_PATH);
1696
1697     hr = SHGetDesktopFolder(&IDesktopFolder);
1698     ok(hr == S_OK, "SHGetDesktopfolder failed %08x\n", hr);
1699
1700     hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cCurrDirW, NULL, &newPIDL, 0);
1701     ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
1702
1703     hr = IShellFolder_BindToObject(IDesktopFolder, newPIDL, NULL, (REFIID)&IID_IShellFolder, (LPVOID *)&testIShellFolder);
1704     ok(hr == S_OK, "BindToObject failed %08x\n", hr);
1705
1706     IMalloc_Free(ppM, newPIDL);
1707
1708     /* windows reads the display name from the resource */
1709     hr = IShellFolder_ParseDisplayName(testIShellFolder, NULL, NULL, foldernameW, NULL, &newPIDL, 0);
1710     ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
1711
1712     hr = IShellFolder_GetDisplayNameOf(testIShellFolder, newPIDL, SHGDN_INFOLDER, &strret);
1713     ok(hr == S_OK, "GetDisplayNameOf failed %08x\n", hr);
1714
1715     if (SUCCEEDED(hr) && pStrRetToBufW)
1716     {
1717         hr = pStrRetToBufW(&strret, newPIDL, tempbufW, sizeof(tempbufW)/sizeof(WCHAR));
1718         ok (SUCCEEDED(hr), "StrRetToBufW failed! hr = %08x\n", hr);
1719         todo_wine
1720         ok (!lstrcmpiW(tempbufW, folderdisplayW) ||
1721             broken(!lstrcmpiW(tempbufW, foldernameW)), /* W2K */
1722             "GetDisplayNameOf returned %s\n", wine_dbgstr_w(tempbufW));
1723     }
1724
1725     /* editing name is also read from the resource */
1726     hr = IShellFolder_GetDisplayNameOf(testIShellFolder, newPIDL, SHGDN_INFOLDER|SHGDN_FOREDITING, &strret);
1727     ok(hr == S_OK, "GetDisplayNameOf failed %08x\n", hr);
1728
1729     if (SUCCEEDED(hr) && pStrRetToBufW)
1730     {
1731         hr = pStrRetToBufW(&strret, newPIDL, tempbufW, sizeof(tempbufW)/sizeof(WCHAR));
1732         ok (SUCCEEDED(hr), "StrRetToBufW failed! hr = %08x\n", hr);
1733         todo_wine
1734         ok (!lstrcmpiW(tempbufW, folderdisplayW) ||
1735             broken(!lstrcmpiW(tempbufW, foldernameW)), /* W2K */
1736             "GetDisplayNameOf returned %s\n", wine_dbgstr_w(tempbufW));
1737     }
1738
1739     /* parsing name is unchanged */
1740     hr = IShellFolder_GetDisplayNameOf(testIShellFolder, newPIDL, SHGDN_INFOLDER|SHGDN_FORPARSING, &strret);
1741     ok(hr == S_OK, "GetDisplayNameOf failed %08x\n", hr);
1742
1743     if (SUCCEEDED(hr) && pStrRetToBufW)
1744     {
1745         hr = pStrRetToBufW(&strret, newPIDL, tempbufW, sizeof(tempbufW)/sizeof(WCHAR));
1746         ok (SUCCEEDED(hr), "StrRetToBufW failed! hr = %08x\n", hr);
1747         ok (!lstrcmpiW(tempbufW, foldernameW), "GetDisplayNameOf returned %s\n", wine_dbgstr_w(tempbufW));
1748     }
1749
1750     IShellFolder_Release(IDesktopFolder);
1751     IShellFolder_Release(testIShellFolder);
1752
1753     IMalloc_Free(ppM, newPIDL);
1754
1755 cleanup:
1756     DeleteFileA(".\\testfolder\\desktop.ini");
1757     SetFileAttributesA(".\\testfolder", GetFileAttributesA(".\\testfolder")&~FILE_ATTRIBUTE_SYSTEM);
1758     RemoveDirectoryA(".\\testfolder");
1759 }
1760
1761 static void test_SHCreateShellItem(void)
1762 {
1763     IShellItem *shellitem, *shellitem2;
1764     IPersistIDList *persistidl;
1765     LPITEMIDLIST pidl_cwd=NULL, pidl_testfile, pidl_abstestfile, pidl_test;
1766     HRESULT ret;
1767     char curdirA[MAX_PATH];
1768     WCHAR curdirW[MAX_PATH];
1769     IShellFolder *desktopfolder=NULL, *currentfolder=NULL;
1770     static WCHAR testfileW[] = {'t','e','s','t','f','i','l','e',0};
1771
1772     GetCurrentDirectoryA(MAX_PATH, curdirA);
1773
1774     if (!lstrlenA(curdirA))
1775     {
1776         win_skip("GetCurrentDirectoryA returned empty string, skipping test_SHCreateShellItem\n");
1777         return;
1778     }
1779
1780     MultiByteToWideChar(CP_ACP, 0, curdirA, -1, curdirW, MAX_PATH);
1781
1782     ret = SHGetDesktopFolder(&desktopfolder);
1783     ok(SUCCEEDED(ret), "SHGetShellFolder returned %x\n", ret);
1784
1785     ret = IShellFolder_ParseDisplayName(desktopfolder, NULL, NULL, curdirW, NULL, &pidl_cwd, NULL);
1786     ok(SUCCEEDED(ret), "ParseDisplayName returned %x\n", ret);
1787
1788     ret = IShellFolder_BindToObject(desktopfolder, pidl_cwd, NULL, &IID_IShellFolder, (void**)&currentfolder);
1789     ok(SUCCEEDED(ret), "BindToObject returned %x\n", ret);
1790
1791     CreateTestFile(".\\testfile");
1792
1793     ret = IShellFolder_ParseDisplayName(currentfolder, NULL, NULL, testfileW, NULL, &pidl_testfile, NULL);
1794     ok(SUCCEEDED(ret), "ParseDisplayName returned %x\n", ret);
1795
1796     pidl_abstestfile = pILCombine(pidl_cwd, pidl_testfile);
1797
1798     ret = pSHCreateShellItem(NULL, NULL, NULL, &shellitem);
1799     ok(ret == E_INVALIDARG, "SHCreateShellItem returned %x\n", ret);
1800
1801     if (0) /* crashes on Windows XP */
1802     {
1803         pSHCreateShellItem(NULL, NULL, pidl_cwd, NULL);
1804         pSHCreateShellItem(pidl_cwd, NULL, NULL, &shellitem);
1805         pSHCreateShellItem(NULL, currentfolder, NULL, &shellitem);
1806         pSHCreateShellItem(pidl_cwd, currentfolder, NULL, &shellitem);
1807     }
1808
1809     ret = pSHCreateShellItem(NULL, NULL, pidl_cwd, &shellitem);
1810     ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret);
1811     if (SUCCEEDED(ret))
1812     {
1813         ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
1814         ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
1815         if (SUCCEEDED(ret))
1816         {
1817             ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
1818             ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
1819             if (SUCCEEDED(ret))
1820             {
1821                 ok(ILIsEqual(pidl_cwd, pidl_test), "id lists are not equal\n");
1822                 pILFree(pidl_test);
1823             }
1824             IPersistIDList_Release(persistidl);
1825         }
1826         IShellItem_Release(shellitem);
1827     }
1828
1829     ret = pSHCreateShellItem(pidl_cwd, NULL, pidl_testfile, &shellitem);
1830     ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret);
1831     if (SUCCEEDED(ret))
1832     {
1833         ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
1834         ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
1835         if (SUCCEEDED(ret))
1836         {
1837             ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
1838             ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
1839             if (SUCCEEDED(ret))
1840             {
1841                 ok(ILIsEqual(pidl_abstestfile, pidl_test), "id lists are not equal\n");
1842                 pILFree(pidl_test);
1843             }
1844             IPersistIDList_Release(persistidl);
1845         }
1846
1847         ret = IShellItem_GetParent(shellitem, &shellitem2);
1848         ok(SUCCEEDED(ret), "GetParent returned %x\n", ret);
1849         if (SUCCEEDED(ret))
1850         {
1851             ret = IShellItem_QueryInterface(shellitem2, &IID_IPersistIDList, (void**)&persistidl);
1852             ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
1853             if (SUCCEEDED(ret))
1854             {
1855                 ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
1856                 ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
1857                 if (SUCCEEDED(ret))
1858                 {
1859                     ok(ILIsEqual(pidl_cwd, pidl_test), "id lists are not equal\n");
1860                     pILFree(pidl_test);
1861                 }
1862                 IPersistIDList_Release(persistidl);
1863             }
1864             IShellItem_Release(shellitem2);
1865         }
1866
1867         IShellItem_Release(shellitem);
1868     }
1869
1870     ret = pSHCreateShellItem(NULL, currentfolder, pidl_testfile, &shellitem);
1871     ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret);
1872     if (SUCCEEDED(ret))
1873     {
1874         ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
1875         ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
1876         if (SUCCEEDED(ret))
1877         {
1878             ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
1879             ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
1880             if (SUCCEEDED(ret))
1881             {
1882                 ok(ILIsEqual(pidl_abstestfile, pidl_test), "id lists are not equal\n");
1883                 pILFree(pidl_test);
1884             }
1885             IPersistIDList_Release(persistidl);
1886         }
1887         IShellItem_Release(shellitem);
1888     }
1889
1890     /* if a parent pidl and shellfolder are specified, the shellfolder is ignored */
1891     ret = pSHCreateShellItem(pidl_cwd, desktopfolder, pidl_testfile, &shellitem);
1892     ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret);
1893     if (SUCCEEDED(ret))
1894     {
1895         ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
1896         ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
1897         if (SUCCEEDED(ret))
1898         {
1899             ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
1900             ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
1901             if (SUCCEEDED(ret))
1902             {
1903                 ok(ILIsEqual(pidl_abstestfile, pidl_test), "id lists are not equal\n");
1904                 pILFree(pidl_test);
1905             }
1906             IPersistIDList_Release(persistidl);
1907         }
1908         IShellItem_Release(shellitem);
1909     }
1910
1911     DeleteFileA(".\\testfile");
1912     pILFree(pidl_abstestfile);
1913     pILFree(pidl_testfile);
1914     pILFree(pidl_cwd);
1915     IShellFolder_Release(currentfolder);
1916     IShellFolder_Release(desktopfolder);
1917 }
1918
1919 START_TEST(shlfolder)
1920 {
1921     init_function_pointers();
1922     /* if OleInitialize doesn't get called, ParseDisplayName returns
1923        CO_E_NOTINITIALIZED for malformed directory names on win2k. */
1924     OleInitialize(NULL);
1925
1926     test_ParseDisplayName();
1927     test_BindToObject();
1928     test_EnumObjects_and_CompareIDs();
1929     test_GetDisplayName();
1930     test_GetAttributesOf();
1931     test_SHGetPathFromIDList();
1932     test_CallForAttributes();
1933     test_FolderShortcut();
1934     test_ITEMIDLIST_format();
1935     if(pSHGetFolderPathAndSubDirA)
1936         testSHGetFolderPathAndSubDirA();
1937     else
1938         win_skip("SHGetFolderPathAndSubDirA not present\n");
1939     test_LocalizedNames();
1940     if(pSHCreateShellItem)
1941         test_SHCreateShellItem();
1942     else
1943         win_skip("SHCreateShellItem not present\n");
1944
1945     OleUninitialize();
1946 }