shell32: Implement SHGetItemFromDataObject.
[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 HRESULT (WINAPI *pSHGetSpecialFolderLocation)(HWND, int, LPITEMIDLIST *);
50 static BOOL (WINAPI *pSHGetSpecialFolderPathA)(HWND, LPSTR, int, BOOL);
51 static BOOL (WINAPI *pSHGetSpecialFolderPathW)(HWND, LPWSTR, int, BOOL);
52 static HRESULT (WINAPI *pStrRetToBufW)(STRRET*,LPCITEMIDLIST,LPWSTR,UINT);
53 static LPITEMIDLIST (WINAPI *pILFindLastID)(LPCITEMIDLIST);
54 static void (WINAPI *pILFree)(LPITEMIDLIST);
55 static BOOL (WINAPI *pILIsEqual)(LPCITEMIDLIST, LPCITEMIDLIST);
56 static HRESULT (WINAPI *pSHCreateItemFromIDList)(PCIDLIST_ABSOLUTE pidl, REFIID riid, void **ppv);
57 static HRESULT (WINAPI *pSHCreateItemFromParsingName)(PCWSTR,IBindCtx*,REFIID,void**);
58 static HRESULT (WINAPI *pSHCreateShellItem)(LPCITEMIDLIST,IShellFolder*,LPCITEMIDLIST,IShellItem**);
59 static LPITEMIDLIST (WINAPI *pILCombine)(LPCITEMIDLIST,LPCITEMIDLIST);
60 static HRESULT (WINAPI *pSHParseDisplayName)(LPCWSTR,IBindCtx*,LPITEMIDLIST*,SFGAOF,SFGAOF*);
61 static LPITEMIDLIST (WINAPI *pSHSimpleIDListFromPathAW)(LPCVOID);
62 static HRESULT (WINAPI *pSHGetNameFromIDList)(PCIDLIST_ABSOLUTE,SIGDN,PWSTR*);
63 static HRESULT (WINAPI *pSHGetItemFromDataObject)(IDataObject*,DATAOBJ_GET_ITEM_FLAGS,REFIID,void**);
64
65 static void init_function_pointers(void)
66 {
67     HMODULE hmod;
68     HRESULT hr;
69     void *ptr;
70
71     hmod = GetModuleHandleA("shell32.dll");
72
73 #define MAKEFUNC(f) (p##f = (void*)GetProcAddress(hmod, #f))
74     MAKEFUNC(SHBindToParent);
75     MAKEFUNC(SHCreateItemFromIDList);
76     MAKEFUNC(SHCreateItemFromParsingName);
77     MAKEFUNC(SHCreateShellItem);
78     MAKEFUNC(SHGetFolderPathA);
79     MAKEFUNC(SHGetFolderPathAndSubDirA);
80     MAKEFUNC(SHGetPathFromIDListW);
81     MAKEFUNC(SHGetSpecialFolderPathA);
82     MAKEFUNC(SHGetSpecialFolderPathW);
83     MAKEFUNC(SHGetSpecialFolderLocation);
84     MAKEFUNC(SHParseDisplayName);
85     MAKEFUNC(SHGetNameFromIDList);
86     MAKEFUNC(SHGetItemFromDataObject);
87 #undef MAKEFUNC
88
89 #define MAKEFUNC_ORD(f, ord) (p##f = (void*)GetProcAddress(hmod, (LPSTR)(ord)))
90     MAKEFUNC_ORD(ILFindLastID, 16);
91     MAKEFUNC_ORD(ILIsEqual, 21);
92     MAKEFUNC_ORD(ILCombine, 25);
93     MAKEFUNC_ORD(ILFree, 155);
94     MAKEFUNC_ORD(SHSimpleIDListFromPathAW, 162);
95 #undef MAKEFUNC_ORD
96
97     /* test named exports */
98     ptr = GetProcAddress(hmod, "ILFree");
99     ok(broken(ptr == 0) || ptr != 0, "expected named export for ILFree\n");
100     if (ptr)
101     {
102 #define TESTNAMED(f) \
103     ptr = (void*)GetProcAddress(hmod, #f); \
104     ok(ptr != 0, "expected named export for " #f "\n");
105
106         TESTNAMED(ILAppendID);
107         TESTNAMED(ILClone);
108         TESTNAMED(ILCloneFirst);
109         TESTNAMED(ILCombine);
110         TESTNAMED(ILCreateFromPath);
111         TESTNAMED(ILCreateFromPathA);
112         TESTNAMED(ILCreateFromPathW);
113         TESTNAMED(ILFindChild);
114         TESTNAMED(ILFindLastID);
115         TESTNAMED(ILGetNext);
116         TESTNAMED(ILGetSize);
117         TESTNAMED(ILIsEqual);
118         TESTNAMED(ILIsParent);
119         TESTNAMED(ILRemoveLastID);
120         TESTNAMED(ILSaveToStream);
121 #undef TESTNAMED
122     }
123
124     hmod = GetModuleHandleA("shlwapi.dll");
125     pStrRetToBufW = (void*)GetProcAddress(hmod, "StrRetToBufW");
126
127     hr = SHGetMalloc(&ppM);
128     ok(hr == S_OK, "SHGetMalloc failed %08x\n", hr);
129 }
130
131 static void test_ParseDisplayName(void)
132 {
133     HRESULT hr;
134     IShellFolder *IDesktopFolder;
135     static const char *cNonExistDir1A = "c:\\nonexist_subdir";
136     static const char *cNonExistDir2A = "c:\\\\nonexist_subdir";
137     static const char *cInetTestA = "http:\\yyy";
138     static const char *cInetTest2A = "xx:yyy";
139     DWORD res;
140     WCHAR cTestDirW [MAX_PATH] = {0};
141     ITEMIDLIST *newPIDL;
142     BOOL bRes;
143
144     hr = SHGetDesktopFolder(&IDesktopFolder);
145     ok(hr == S_OK, "Expected SHGetDesktopFolder to return S_OK, got 0x%08x\n", hr);
146     if(hr != S_OK) return;
147
148     /* Tests crash on W2K and below (SHCreateShellItem available as of XP) */
149     if (pSHCreateShellItem)
150     {
151         /* null name and pidl */
152         hr = IShellFolder_ParseDisplayName(IDesktopFolder,
153             NULL, NULL, NULL, NULL, NULL, 0);
154         ok(hr == E_INVALIDARG, "returned %08x, expected E_INVALIDARG\n", hr);
155
156         /* null name */
157         newPIDL = (ITEMIDLIST*)0xdeadbeef;
158         hr = IShellFolder_ParseDisplayName(IDesktopFolder,
159             NULL, NULL, NULL, NULL, &newPIDL, 0);
160         ok(newPIDL == 0, "expected null, got %p\n", newPIDL);
161         ok(hr == E_INVALIDARG, "returned %08x, expected E_INVALIDARG\n", hr);
162     }
163     else
164         win_skip("Tests would crash on W2K and below\n");
165
166     MultiByteToWideChar(CP_ACP, 0, cInetTestA, -1, cTestDirW, MAX_PATH);
167     hr = IShellFolder_ParseDisplayName(IDesktopFolder,
168         NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
169     todo_wine ok(hr == S_OK || broken(hr == E_FAIL) /* NT4 */,
170         "ParseDisplayName returned %08x, expected SUCCESS or E_FAIL\n", hr);
171     if (hr == S_OK)
172     {
173         ok(pILFindLastID(newPIDL)->mkid.abID[0] == 0x61, "Last pidl should be of type "
174            "PT_IESPECIAL1, but is: %02x\n", pILFindLastID(newPIDL)->mkid.abID[0]);
175         IMalloc_Free(ppM, newPIDL);
176     }
177
178     MultiByteToWideChar(CP_ACP, 0, cInetTest2A, -1, cTestDirW, MAX_PATH);
179     hr = IShellFolder_ParseDisplayName(IDesktopFolder,
180         NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
181     todo_wine ok(hr == S_OK || broken(hr == E_FAIL) /* NT4 */,
182         "ParseDisplayName returned %08x, expected SUCCESS or E_FAIL\n", hr);
183     if (hr == S_OK)
184     {
185         ok(pILFindLastID(newPIDL)->mkid.abID[0] == 0x61, "Last pidl should be of type "
186            "PT_IESPECIAL1, but is: %02x\n", pILFindLastID(newPIDL)->mkid.abID[0]);
187         IMalloc_Free(ppM, newPIDL);
188     }
189
190     res = GetFileAttributesA(cNonExistDir1A);
191     if(res != INVALID_FILE_ATTRIBUTES)
192     {
193         skip("Test directory unexpectedly exists\n");
194         goto finished;
195     }
196
197     MultiByteToWideChar(CP_ACP, 0, cNonExistDir1A, -1, cTestDirW, MAX_PATH);
198     hr = IShellFolder_ParseDisplayName(IDesktopFolder, 
199         NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
200     ok((hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) || (hr == E_FAIL), 
201         "ParseDisplayName returned %08x, expected 80070002 or E_FAIL\n", hr);
202
203     res = GetFileAttributesA(cNonExistDir2A);
204     if(res != INVALID_FILE_ATTRIBUTES)
205     {
206         skip("Test directory unexpectedly exists\n");
207         goto finished;
208     }
209
210     MultiByteToWideChar(CP_ACP, 0, cNonExistDir2A, -1, cTestDirW, MAX_PATH);
211     hr = IShellFolder_ParseDisplayName(IDesktopFolder, 
212         NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
213     ok((hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) || (hr == E_FAIL) || (hr == E_INVALIDARG), 
214         "ParseDisplayName returned %08x, expected 80070002, E_FAIL or E_INVALIDARG\n", hr);
215
216     /* I thought that perhaps the DesktopFolder's ParseDisplayName would recognize the
217      * path corresponding to CSIDL_PERSONAL and return a CLSID_MyDocuments PIDL. Turns
218      * out it doesn't. The magic seems to happen in the file dialogs, then. */
219     if (!pSHGetSpecialFolderPathW || !pILFindLastID)
220     {
221         win_skip("SHGetSpecialFolderPathW and/or ILFindLastID are not available\n");
222         goto finished;
223     }
224
225     bRes = pSHGetSpecialFolderPathW(NULL, cTestDirW, CSIDL_PERSONAL, FALSE);
226     ok(bRes, "SHGetSpecialFolderPath(CSIDL_PERSONAL) failed! %u\n", GetLastError());
227     if (!bRes) goto finished;
228
229     hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
230     ok(hr == S_OK, "DesktopFolder->ParseDisplayName failed. hr = %08x.\n", hr);
231     if (hr != S_OK) goto finished;
232
233     ok(pILFindLastID(newPIDL)->mkid.abID[0] == 0x31 ||
234        pILFindLastID(newPIDL)->mkid.abID[0] == 0xb1, /* Win98 */
235        "Last pidl should be of type PT_FOLDER or PT_IESPECIAL2, but is: %02x\n",
236        pILFindLastID(newPIDL)->mkid.abID[0]);
237     IMalloc_Free(ppM, newPIDL);
238     
239 finished:
240     IShellFolder_Release(IDesktopFolder);
241 }
242
243 /* creates a file with the specified name for tests */
244 static void CreateTestFile(const CHAR *name)
245 {
246     HANDLE file;
247     DWORD written;
248
249     file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
250     if (file != INVALID_HANDLE_VALUE)
251     {
252         WriteFile(file, name, strlen(name), &written, NULL);
253         WriteFile(file, "\n", strlen("\n"), &written, NULL);
254         CloseHandle(file);
255     }
256 }
257
258
259 /* initializes the tests */
260 static void CreateFilesFolders(void)
261 {
262     CreateDirectoryA(".\\testdir", NULL);
263     CreateDirectoryA(".\\testdir\\test.txt", NULL);
264     CreateTestFile  (".\\testdir\\test1.txt ");
265     CreateTestFile  (".\\testdir\\test2.txt ");
266     CreateTestFile  (".\\testdir\\test3.txt ");
267     CreateDirectoryA(".\\testdir\\testdir2 ", NULL);
268     CreateDirectoryA(".\\testdir\\testdir2\\subdir", NULL);
269 }
270
271 /* cleans after tests */
272 static void Cleanup(void)
273 {
274     DeleteFileA(".\\testdir\\test1.txt");
275     DeleteFileA(".\\testdir\\test2.txt");
276     DeleteFileA(".\\testdir\\test3.txt");
277     RemoveDirectoryA(".\\testdir\\test.txt");
278     RemoveDirectoryA(".\\testdir\\testdir2\\subdir");
279     RemoveDirectoryA(".\\testdir\\testdir2");
280     RemoveDirectoryA(".\\testdir");
281 }
282
283
284 /* perform test */
285 static void test_EnumObjects(IShellFolder *iFolder)
286 {
287     IEnumIDList *iEnumList;
288     LPITEMIDLIST newPIDL, idlArr[10];
289     ULONG NumPIDLs;
290     int i=0, j;
291     HRESULT hr;
292
293     static const WORD iResults [5][5] =
294     {
295         { 0,-1,-1,-1,-1},
296         { 1, 0,-1,-1,-1},
297         { 1, 1, 0,-1,-1},
298         { 1, 1, 1, 0,-1},
299         { 1, 1, 1, 1, 0}
300     };
301
302 #define SFGAO_testfor SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR | SFGAO_CAPABILITYMASK
303     /* Don't test for SFGAO_HASSUBFOLDER since we return real state and native cached */
304     static const ULONG attrs[5] =
305     {
306         SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR,
307         SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR,
308         SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM,
309         SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM,
310         SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM,
311     };
312
313     hr = IShellFolder_EnumObjects(iFolder, NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN, &iEnumList);
314     ok(hr == S_OK, "EnumObjects failed %08x\n", hr);
315
316     /* This is to show that, contrary to what is said on MSDN, on IEnumIDList::Next,
317      * the filesystem shellfolders return S_OK even if less than 'celt' items are
318      * returned (in contrast to S_FALSE). We have to do it in a loop since WinXP
319      * only ever returns a single entry per call. */
320     while (IEnumIDList_Next(iEnumList, 10-i, &idlArr[i], &NumPIDLs) == S_OK) 
321         i += NumPIDLs;
322     ok (i == 5, "i: %d\n", i);
323
324     hr = IEnumIDList_Release(iEnumList);
325     ok(hr == S_OK, "IEnumIDList_Release failed %08x\n", hr);
326     
327     /* Sort them first in case of wrong order from system */
328     for (i=0;i<5;i++) for (j=0;j<5;j++)
329         if ((SHORT)IShellFolder_CompareIDs(iFolder, 0, idlArr[i], idlArr[j]) < 0)
330         {
331             newPIDL = idlArr[i];
332             idlArr[i] = idlArr[j];
333             idlArr[j] = newPIDL;
334         }
335             
336     for (i=0;i<5;i++) for (j=0;j<5;j++)
337     {
338         hr = IShellFolder_CompareIDs(iFolder, 0, idlArr[i], idlArr[j]);
339         ok(hr == iResults[i][j], "Got %x expected [%d]-[%d]=%x\n", hr, i, j, iResults[i][j]);
340     }
341
342
343     for (i = 0; i < 5; i++)
344     {
345         SFGAOF flags;
346 #define SFGAO_VISTA SFGAO_DROPTARGET | SFGAO_CANLINK | SFGAO_CANCOPY
347         /* Native returns all flags no matter what we ask for */
348         flags = SFGAO_CANCOPY;
349         hr = IShellFolder_GetAttributesOf(iFolder, 1, (LPCITEMIDLIST*)(idlArr + i), &flags);
350         flags &= SFGAO_testfor;
351         ok(hr == S_OK, "GetAttributesOf returns %08x\n", hr);
352         ok(flags == (attrs[i]) ||
353            flags == (attrs[i] & ~SFGAO_FILESYSANCESTOR) || /* Win9x, NT4 */
354            flags == ((attrs[i] & ~SFGAO_CAPABILITYMASK) | SFGAO_VISTA), /* Vista and higher */
355            "GetAttributesOf[%i] got %08x, expected %08x\n", i, flags, attrs[i]);
356
357         flags = SFGAO_testfor;
358         hr = IShellFolder_GetAttributesOf(iFolder, 1, (LPCITEMIDLIST*)(idlArr + i), &flags);
359         flags &= SFGAO_testfor;
360         ok(hr == S_OK, "GetAttributesOf returns %08x\n", hr);
361         ok(flags == attrs[i] ||
362            flags == (attrs[i] & ~SFGAO_FILESYSANCESTOR), /* Win9x, NT4 */
363            "GetAttributesOf[%i] got %08x, expected %08x\n", i, flags, attrs[i]);
364     }
365
366     for (i=0;i<5;i++)
367         IMalloc_Free(ppM, idlArr[i]);
368 }
369
370 static void test_BindToObject(void)
371 {
372     HRESULT hr;
373     UINT cChars;
374     IShellFolder *psfDesktop, *psfChild, *psfMyComputer, *psfSystemDir;
375     SHITEMID emptyitem = { 0, { 0 } };
376     LPITEMIDLIST pidlMyComputer, pidlSystemDir, pidlEmpty = (LPITEMIDLIST)&emptyitem;
377     WCHAR wszSystemDir[MAX_PATH];
378     char szSystemDir[MAX_PATH];
379     WCHAR wszMyComputer[] = { 
380         ':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
381         'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };
382
383     /* The following tests shows that BindToObject should fail with E_INVALIDARG if called
384      * with an empty pidl. This is tested for Desktop, MyComputer and the FS ShellFolder
385      */
386     hr = SHGetDesktopFolder(&psfDesktop);
387     ok (hr == S_OK, "SHGetDesktopFolder failed! hr = %08x\n", hr);
388     if (hr != S_OK) return;
389     
390     hr = IShellFolder_BindToObject(psfDesktop, pidlEmpty, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
391     ok (hr == E_INVALIDARG, "Desktop's BindToObject should fail, when called with empty pidl! hr = %08x\n", hr);
392
393     hr = IShellFolder_BindToObject(psfDesktop, NULL, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
394     ok (hr == E_INVALIDARG, "Desktop's BindToObject should fail, when called with NULL pidl! hr = %08x\n", hr);
395
396     hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyComputer, NULL, &pidlMyComputer, NULL);
397     ok (hr == S_OK, "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08x\n", hr);
398     if (hr != S_OK) {
399         IShellFolder_Release(psfDesktop);
400         return;
401     }
402     
403     hr = IShellFolder_BindToObject(psfDesktop, pidlMyComputer, NULL, &IID_IShellFolder, (LPVOID*)&psfMyComputer);
404     ok (hr == S_OK, "Desktop failed to bind to MyComputer object! hr = %08x\n", hr);
405     IShellFolder_Release(psfDesktop);
406     IMalloc_Free(ppM, pidlMyComputer);
407     if (hr != S_OK) return;
408
409     hr = IShellFolder_BindToObject(psfMyComputer, pidlEmpty, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
410     ok (hr == E_INVALIDARG, "MyComputers's BindToObject should fail, when called with empty pidl! hr = %08x\n", hr);
411
412 if (0)
413 {
414     /* this call segfaults on 98SE */
415     hr = IShellFolder_BindToObject(psfMyComputer, NULL, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
416     ok (hr == E_INVALIDARG, "MyComputers's BindToObject should fail, when called with NULL pidl! hr = %08x\n", hr);
417 }
418
419     cChars = GetSystemDirectoryA(szSystemDir, MAX_PATH);
420     ok (cChars > 0 && cChars < MAX_PATH, "GetSystemDirectoryA failed! LastError: %u\n", GetLastError());
421     if (cChars == 0 || cChars >= MAX_PATH) {
422         IShellFolder_Release(psfMyComputer);
423         return;
424     }
425     MultiByteToWideChar(CP_ACP, 0, szSystemDir, -1, wszSystemDir, MAX_PATH);
426     
427     hr = IShellFolder_ParseDisplayName(psfMyComputer, NULL, NULL, wszSystemDir, NULL, &pidlSystemDir, NULL);
428     ok (hr == S_OK, "MyComputers's ParseDisplayName failed to parse the SystemDirectory! hr = %08x\n", hr);
429     if (hr != S_OK) {
430         IShellFolder_Release(psfMyComputer);
431         return;
432     }
433
434     hr = IShellFolder_BindToObject(psfMyComputer, pidlSystemDir, NULL, &IID_IShellFolder, (LPVOID*)&psfSystemDir);
435     ok (hr == S_OK, "MyComputer failed to bind to a FileSystem ShellFolder! hr = %08x\n", hr);
436     IShellFolder_Release(psfMyComputer);
437     IMalloc_Free(ppM, pidlSystemDir);
438     if (hr != S_OK) return;
439
440     hr = IShellFolder_BindToObject(psfSystemDir, pidlEmpty, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
441     ok (hr == E_INVALIDARG, 
442         "FileSystem ShellFolder's BindToObject should fail, when called with empty pidl! hr = %08x\n", hr);
443
444 if (0)
445 {
446     /* this call segfaults on 98SE */
447     hr = IShellFolder_BindToObject(psfSystemDir, NULL, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
448     ok (hr == E_INVALIDARG,
449         "FileSystem ShellFolder's BindToObject should fail, when called with NULL pidl! hr = %08x\n", hr);
450 }
451
452     IShellFolder_Release(psfSystemDir);
453 }
454
455 /* Based on PathAddBackslashW from dlls/shlwapi/path.c */
456 static LPWSTR myPathAddBackslashW( LPWSTR lpszPath )
457 {
458   size_t iLen;
459
460   if (!lpszPath || (iLen = lstrlenW(lpszPath)) >= MAX_PATH)
461     return NULL;
462
463   if (iLen)
464   {
465     lpszPath += iLen;
466     if (lpszPath[-1] != '\\')
467     {
468       *lpszPath++ = '\\';
469       *lpszPath = '\0';
470     }
471   }
472   return lpszPath;
473 }
474
475 static void test_GetDisplayName(void)
476 {
477     BOOL result;
478     HRESULT hr;
479     HANDLE hTestFile;
480     WCHAR wszTestFile[MAX_PATH], wszTestFile2[MAX_PATH];
481     char szTestFile[MAX_PATH], szTestDir[MAX_PATH];
482     DWORD attr;
483     STRRET strret;
484     LPSHELLFOLDER psfDesktop, psfPersonal;
485     IUnknown *psfFile;
486     SHITEMID emptyitem = { 0, { 0 } };
487     LPITEMIDLIST pidlTestFile, pidlEmpty = (LPITEMIDLIST)&emptyitem;
488     LPCITEMIDLIST pidlLast;
489     static const CHAR szFileName[] = "winetest.foo";
490     static const WCHAR wszFileName[] = { 'w','i','n','e','t','e','s','t','.','f','o','o',0 };
491     static const WCHAR wszDirName[] = { 'w','i','n','e','t','e','s','t',0 };
492
493     /* I'm trying to figure if there is a functional difference between calling
494      * SHGetPathFromIDListW and calling GetDisplayNameOf(SHGDN_FORPARSING) after
495      * binding to the shellfolder. One thing I thought of was that perhaps 
496      * SHGetPathFromIDListW would be able to get the path to a file, which does
497      * not exist anymore, while the other method wouldn't. It turns out there's
498      * no functional difference in this respect.
499      */
500
501     if(!pSHGetSpecialFolderPathA) {
502         win_skip("SHGetSpecialFolderPathA is not available\n");
503         return;
504     }
505
506     /* First creating a directory in MyDocuments and a file in this directory. */
507     result = pSHGetSpecialFolderPathA(NULL, szTestDir, CSIDL_PERSONAL, FALSE);
508     ok(result, "SHGetSpecialFolderPathA failed! Last error: %u\n", GetLastError());
509     if (!result) return;
510
511     /* Use ANSI file functions so this works on Windows 9x */
512     lstrcatA(szTestDir, "\\winetest");
513     CreateDirectoryA(szTestDir, NULL);
514     attr=GetFileAttributesA(szTestDir);
515     if (attr == INVALID_FILE_ATTRIBUTES || !(attr & FILE_ATTRIBUTE_DIRECTORY))
516     {
517         ok(0, "unable to create the '%s' directory\n", szTestDir);
518         return;
519     }
520
521     lstrcpyA(szTestFile, szTestDir);
522     lstrcatA(szTestFile, "\\");
523     lstrcatA(szTestFile, szFileName);
524     hTestFile = CreateFileA(szTestFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
525     ok((hTestFile != INVALID_HANDLE_VALUE), "CreateFileA failed! Last error: %u\n", GetLastError());
526     if (hTestFile == INVALID_HANDLE_VALUE) return;
527     CloseHandle(hTestFile);
528
529     /* Getting an itemidlist for the file. */
530     hr = SHGetDesktopFolder(&psfDesktop);
531     ok(hr == S_OK, "SHGetDesktopFolder failed! hr = %08x\n", hr);
532     if (hr != S_OK) return;
533
534     MultiByteToWideChar(CP_ACP, 0, szTestFile, -1, wszTestFile, MAX_PATH);
535
536     hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszTestFile, NULL, &pidlTestFile, NULL);
537     ok(hr == S_OK, "Desktop->ParseDisplayName failed! hr = %08x\n", hr);
538     if (hr != S_OK) {
539         IShellFolder_Release(psfDesktop);
540         return;
541     }
542
543     pidlLast = pILFindLastID(pidlTestFile);
544     ok(pidlLast->mkid.cb >=76 ||
545         broken(pidlLast->mkid.cb == 28) || /* W2K */
546         broken(pidlLast->mkid.cb == 40), /* Win9x, WinME */
547         "Expected pidl length of at least 76, got %d.\n", pidlLast->mkid.cb);
548     if (pidlLast->mkid.cb >= 28) {
549         ok(!lstrcmpA((CHAR*)&pidlLast->mkid.abID[12], szFileName),
550             "Filename should be stored as ansi-string at this position!\n");
551     }
552     /* WinXP and up store the filenames as both ANSI and UNICODE in the pidls */
553     if (pidlLast->mkid.cb >= 76) {
554         ok(!lstrcmpW((WCHAR*)&pidlLast->mkid.abID[46], wszFileName) ||
555             (pidlLast->mkid.cb >= 94 && !lstrcmpW((WCHAR*)&pidlLast->mkid.abID[64], wszFileName)) ||  /* Vista */
556             (pidlLast->mkid.cb >= 98 && !lstrcmpW((WCHAR*)&pidlLast->mkid.abID[68], wszFileName)), /* Win7 */
557             "Filename should be stored as wchar-string at this position!\n");
558     }
559     
560     /* It seems as if we cannot bind to regular files on windows, but only directories. 
561      */
562     hr = IShellFolder_BindToObject(psfDesktop, pidlTestFile, NULL, &IID_IUnknown, (VOID**)&psfFile);
563     todo_wine
564     ok (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) ||
565         hr == E_NOTIMPL || /* Vista */
566         broken(hr == S_OK), /* Win9x, W2K */
567         "hr = %08x\n", hr);
568     if (hr == S_OK) {
569         IShellFolder_Release(psfFile);
570     }
571
572     if (!pSHBindToParent)
573     {
574         win_skip("SHBindToParent is missing\n");
575         DeleteFileA(szTestFile);
576         RemoveDirectoryA(szTestDir);
577         return;
578     }
579   
580     /* Some tests for IShellFolder::SetNameOf */
581     if (pSHGetFolderPathAndSubDirA)
582     {
583         hr = pSHBindToParent(pidlTestFile, &IID_IShellFolder, (VOID**)&psfPersonal, &pidlLast);
584         ok(hr == S_OK, "SHBindToParent failed! hr = %08x\n", hr);
585         if (hr == S_OK) {
586             /* It's ok to use this fixed path. Call will fail anyway. */
587             WCHAR wszAbsoluteFilename[] = { 'C',':','\\','w','i','n','e','t','e','s','t', 0 };
588             LPITEMIDLIST pidlNew;
589
590             /* The pidl returned through the last parameter of SetNameOf is a simple one. */
591             hr = IShellFolder_SetNameOf(psfPersonal, NULL, pidlLast, wszDirName, SHGDN_NORMAL, &pidlNew);
592             ok (hr == S_OK, "SetNameOf failed! hr = %08x\n", hr);
593             if (hr == S_OK)
594             {
595                 ok (((LPITEMIDLIST)((LPBYTE)pidlNew+pidlNew->mkid.cb))->mkid.cb == 0,
596                     "pidl returned from SetNameOf should be simple!\n");
597
598                 /* Passing an absolute path to SetNameOf fails. The HRESULT code indicates that SetNameOf
599                  * is implemented on top of SHFileOperation in WinXP. */
600                 hr = IShellFolder_SetNameOf(psfPersonal, NULL, pidlNew, wszAbsoluteFilename,
601                         SHGDN_FORPARSING, NULL);
602                 ok (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED), "SetNameOf succeeded! hr = %08x\n", hr);
603
604                 /* Rename the file back to its original name. SetNameOf ignores the fact, that the
605                  * SHGDN flags specify an absolute path. */
606                 hr = IShellFolder_SetNameOf(psfPersonal, NULL, pidlNew, wszFileName, SHGDN_FORPARSING, NULL);
607                 ok (hr == S_OK, "SetNameOf failed! hr = %08x\n", hr);
608
609                 pILFree(pidlNew);
610             }
611
612             IShellFolder_Release(psfPersonal);
613         }
614     }
615     else
616         win_skip("Avoid needs of interaction on Win2k\n");
617
618     /* Deleting the file and the directory */
619     DeleteFileA(szTestFile);
620     RemoveDirectoryA(szTestDir);
621
622     /* SHGetPathFromIDListW still works, although the file is not present anymore. */
623     if (pSHGetPathFromIDListW)
624     {
625         result = pSHGetPathFromIDListW(pidlTestFile, wszTestFile2);
626         ok (result, "SHGetPathFromIDListW failed! Last error: %u\n", GetLastError());
627         ok (!lstrcmpiW(wszTestFile, wszTestFile2), "SHGetPathFromIDListW returns incorrect path!\n");
628     }
629
630     /* SHBindToParent fails, if called with a NULL PIDL. */
631     hr = pSHBindToParent(NULL, &IID_IShellFolder, (VOID**)&psfPersonal, &pidlLast);
632     ok (hr != S_OK, "SHBindToParent(NULL) should fail!\n");
633
634     /* But it succeeds with an empty PIDL. */
635     hr = pSHBindToParent(pidlEmpty, &IID_IShellFolder, (VOID**)&psfPersonal, &pidlLast);
636     ok (hr == S_OK, "SHBindToParent(empty PIDL) should succeed! hr = %08x\n", hr);
637     ok (pidlLast == pidlEmpty, "The last element of an empty PIDL should be the PIDL itself!\n");
638     if (hr == S_OK)
639         IShellFolder_Release(psfPersonal);
640     
641     /* Binding to the folder and querying the display name of the file also works. */
642     hr = pSHBindToParent(pidlTestFile, &IID_IShellFolder, (VOID**)&psfPersonal, &pidlLast); 
643     ok (hr == S_OK, "SHBindToParent failed! hr = %08x\n", hr);
644     if (hr != S_OK) {
645         IShellFolder_Release(psfDesktop);
646         return;
647     }
648
649     /* This test shows that Windows doesn't allocate a new pidlLast, but returns a pointer into 
650      * pidlTestFile (In accordance with MSDN). */
651     ok (pILFindLastID(pidlTestFile) == pidlLast, 
652                                 "SHBindToParent doesn't return the last id of the pidl param!\n");
653     
654     hr = IShellFolder_GetDisplayNameOf(psfPersonal, pidlLast, SHGDN_FORPARSING, &strret);
655     ok (hr == S_OK, "Personal->GetDisplayNameOf failed! hr = %08x\n", hr);
656     if (hr != S_OK) {
657         IShellFolder_Release(psfDesktop);
658         IShellFolder_Release(psfPersonal);
659         return;
660     }
661
662     if (pStrRetToBufW)
663     {
664         hr = pStrRetToBufW(&strret, pidlLast, wszTestFile2, MAX_PATH);
665         ok (hr == S_OK, "StrRetToBufW failed! hr = %08x\n", hr);
666         ok (!lstrcmpiW(wszTestFile, wszTestFile2), "GetDisplayNameOf returns incorrect path!\n");
667     }
668     
669     ILFree(pidlTestFile);
670     IShellFolder_Release(psfDesktop);
671     IShellFolder_Release(psfPersonal);
672 }
673
674 static void test_CallForAttributes(void)
675 {
676     HKEY hKey;
677     LONG lResult;
678     HRESULT hr;
679     DWORD dwSize;
680     LPSHELLFOLDER psfDesktop;
681     LPITEMIDLIST pidlMyDocuments;
682     DWORD dwAttributes, dwCallForAttributes, dwOrigAttributes, dwOrigCallForAttributes;
683     static const WCHAR wszAttributes[] = { 'A','t','t','r','i','b','u','t','e','s',0 };
684     static const WCHAR wszCallForAttributes[] = { 
685         'C','a','l','l','F','o','r','A','t','t','r','i','b','u','t','e','s',0 };
686     static const WCHAR wszMyDocumentsKey[] = {
687         'C','L','S','I','D','\\','{','4','5','0','D','8','F','B','A','-','A','D','2','5','-',
688         '1','1','D','0','-','9','8','A','8','-','0','8','0','0','3','6','1','B','1','1','0','3','}',
689         '\\','S','h','e','l','l','F','o','l','d','e','r',0 };
690     WCHAR wszMyDocuments[] = {
691         ':',':','{','4','5','0','D','8','F','B','A','-','A','D','2','5','-','1','1','D','0','-',
692         '9','8','A','8','-','0','8','0','0','3','6','1','B','1','1','0','3','}',0 };
693     
694     /* For the root of a namespace extension, the attributes are not queried by binding
695      * to the object and calling GetAttributesOf. Instead, the attributes are read from 
696      * the registry value HKCR/CLSID/{...}/ShellFolder/Attributes. This is documented on MSDN.
697      *
698      * The MyDocuments shellfolder on WinXP has a HKCR/CLSID/{...}/ShellFolder/CallForAttributes
699      * value. It seems that if the folder is queried for one of the flags set in CallForAttributes,
700      * the shell does bind to the folder object and calls GetAttributesOf. This is not documented
701      * on MSDN. This test is meant to document the observed behaviour on WinXP SP2.
702      */
703     hr = SHGetDesktopFolder(&psfDesktop);
704     ok (hr == S_OK, "SHGetDesktopFolder failed! hr = %08x\n", hr);
705     if (hr != S_OK) return;
706     
707     hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyDocuments, NULL, 
708                                        &pidlMyDocuments, NULL);
709     ok (hr == S_OK ||
710         broken(hr == E_INVALIDARG), /* Win95, NT4 */
711         "Desktop's ParseDisplayName failed to parse MyDocuments's CLSID! hr = %08x\n", hr);
712     if (hr != S_OK) {
713         IShellFolder_Release(psfDesktop);
714         return;
715     }
716
717     dwAttributes = 0xffffffff;
718     hr = IShellFolder_GetAttributesOf(psfDesktop, 1, 
719                                       (LPCITEMIDLIST*)&pidlMyDocuments, &dwAttributes);
720     ok (hr == S_OK, "Desktop->GetAttributesOf(MyDocuments) failed! hr = %08x\n", hr);
721
722     /* We need the following setup (as observed on WinXP SP2), for the tests to make sense. */
723     ok (dwAttributes & SFGAO_FILESYSTEM, "SFGAO_FILESYSTEM attribute is not set for MyDocuments!\n");
724     ok (!(dwAttributes & SFGAO_ISSLOW), "SFGAO_ISSLOW attribute is set for MyDocuments!\n");
725     ok (!(dwAttributes & SFGAO_GHOSTED), "SFGAO_GHOSTED attribute is set for MyDocuments!\n");
726
727     /* We don't have the MyDocuments shellfolder in wine yet, and thus we don't have the registry
728      * key. So the test will return at this point, if run on wine. 
729      */
730     lResult = RegOpenKeyExW(HKEY_CLASSES_ROOT, wszMyDocumentsKey, 0, KEY_WRITE|KEY_READ, &hKey);
731     ok (lResult == ERROR_SUCCESS ||
732         lResult == ERROR_ACCESS_DENIED,
733         "RegOpenKeyEx failed! result: %08x\n", lResult);
734     if (lResult != ERROR_SUCCESS) {
735         if (lResult == ERROR_ACCESS_DENIED)
736             skip("Not enough rights to open the registry key\n");
737         IMalloc_Free(ppM, pidlMyDocuments);
738         IShellFolder_Release(psfDesktop);
739         return;
740     }
741     
742     /* Query MyDocuments' Attributes value, to be able to restore it later. */
743     dwSize = sizeof(DWORD);
744     lResult = RegQueryValueExW(hKey, wszAttributes, NULL, NULL, (LPBYTE)&dwOrigAttributes, &dwSize);
745     ok (lResult == ERROR_SUCCESS, "RegQueryValueEx failed! result: %08x\n", lResult);
746     if (lResult != ERROR_SUCCESS) {
747         RegCloseKey(hKey);
748         IMalloc_Free(ppM, pidlMyDocuments);
749         IShellFolder_Release(psfDesktop);
750         return;
751     }
752
753     /* Query MyDocuments' CallForAttributes value, to be able to restore it later. */
754     dwSize = sizeof(DWORD);
755     lResult = RegQueryValueExW(hKey, wszCallForAttributes, NULL, NULL, 
756                               (LPBYTE)&dwOrigCallForAttributes, &dwSize);
757     ok (lResult == ERROR_SUCCESS, "RegQueryValueEx failed! result: %08x\n", lResult);
758     if (lResult != ERROR_SUCCESS) {
759         RegCloseKey(hKey);
760         IMalloc_Free(ppM, pidlMyDocuments);
761         IShellFolder_Release(psfDesktop);
762         return;
763     }
764     
765     /* Define via the Attributes value that MyDocuments attributes are SFGAO_ISSLOW and 
766      * SFGAO_GHOSTED and that MyDocuments should be called for the SFGAO_ISSLOW and
767      * SFGAO_FILESYSTEM attributes. */
768     dwAttributes = SFGAO_ISSLOW|SFGAO_GHOSTED;
769     RegSetValueExW(hKey, wszAttributes, 0, REG_DWORD, (LPBYTE)&dwAttributes, sizeof(DWORD));
770     dwCallForAttributes = SFGAO_ISSLOW|SFGAO_FILESYSTEM;
771     RegSetValueExW(hKey, wszCallForAttributes, 0, REG_DWORD, 
772                    (LPBYTE)&dwCallForAttributes, sizeof(DWORD));
773
774     /* Although it is not set in CallForAttributes, the SFGAO_GHOSTED flag is reset by 
775      * GetAttributesOf. It seems that once there is a single attribute queried, for which
776      * CallForAttributes is set, all flags are taken from the GetAttributesOf call and
777      * the flags in Attributes are ignored. 
778      */
779     dwAttributes = SFGAO_ISSLOW|SFGAO_GHOSTED|SFGAO_FILESYSTEM;
780     hr = IShellFolder_GetAttributesOf(psfDesktop, 1, 
781                                       (LPCITEMIDLIST*)&pidlMyDocuments, &dwAttributes);
782     ok (hr == S_OK, "Desktop->GetAttributesOf(MyDocuments) failed! hr = %08x\n", hr);
783     if (hr == S_OK)
784         ok (dwAttributes == SFGAO_FILESYSTEM, 
785             "Desktop->GetAttributes(MyDocuments) returned unexpected attributes: %08x\n", 
786             dwAttributes);
787
788     /* Restore MyDocuments' original Attributes and CallForAttributes registry values */
789     RegSetValueExW(hKey, wszAttributes, 0, REG_DWORD, (LPBYTE)&dwOrigAttributes, sizeof(DWORD));
790     RegSetValueExW(hKey, wszCallForAttributes, 0, REG_DWORD, 
791                    (LPBYTE)&dwOrigCallForAttributes, sizeof(DWORD));
792     RegCloseKey(hKey);
793     IMalloc_Free(ppM, pidlMyDocuments);
794     IShellFolder_Release(psfDesktop);
795 }
796
797 static void test_GetAttributesOf(void) 
798 {
799     HRESULT hr;
800     LPSHELLFOLDER psfDesktop, psfMyComputer;
801     SHITEMID emptyitem = { 0, { 0 } };
802     LPCITEMIDLIST pidlEmpty = (LPCITEMIDLIST)&emptyitem;
803     LPITEMIDLIST pidlMyComputer;
804     DWORD dwFlags;
805     static const DWORD desktopFlags[] = {
806         /* WinXP */
807         SFGAO_STORAGE | SFGAO_HASPROPSHEET | SFGAO_STORAGEANCESTOR | SFGAO_FILESYSANCESTOR |
808         SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_HASSUBFOLDER,
809         /* Win2k */
810         SFGAO_CANRENAME | SFGAO_HASPROPSHEET | SFGAO_STREAM | SFGAO_FILESYSANCESTOR |
811         SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_HASSUBFOLDER,
812         /* WinMe, Win9x, WinNT*/
813         SFGAO_CANRENAME | SFGAO_HASPROPSHEET | SFGAO_FILESYSANCESTOR |
814         SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_HASSUBFOLDER
815     };
816     static const DWORD myComputerFlags[] = {
817         /* WinXP */
818         SFGAO_CANRENAME | SFGAO_CANDELETE | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET |
819         SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER,
820         /* Win2k */
821         SFGAO_CANRENAME | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET | SFGAO_STREAM |
822         SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER,
823         /* WinMe, Win9x, WinNT */
824         SFGAO_CANRENAME | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET | SFGAO_FILESYSANCESTOR |
825         SFGAO_FOLDER | SFGAO_HASSUBFOLDER,
826         /* Win95, WinNT when queried directly */
827         SFGAO_CANLINK | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET | SFGAO_FILESYSANCESTOR |
828         SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_HASSUBFOLDER
829     };
830     WCHAR wszMyComputer[] = { 
831         ':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
832         'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };
833     char  cCurrDirA [MAX_PATH] = {0};
834     WCHAR cCurrDirW [MAX_PATH];
835     static WCHAR cTestDirW[] = {'t','e','s','t','d','i','r',0};
836     IShellFolder *IDesktopFolder, *testIShellFolder;
837     ITEMIDLIST *newPIDL;
838     int len, i;
839     BOOL foundFlagsMatch;
840
841     hr = SHGetDesktopFolder(&psfDesktop);
842     ok (hr == S_OK, "SHGetDesktopFolder failed! hr = %08x\n", hr);
843     if (hr != S_OK) return;
844
845     /* The Desktop attributes can be queried with a single empty itemidlist, .. */
846     dwFlags = 0xffffffff;
847     hr = IShellFolder_GetAttributesOf(psfDesktop, 1, &pidlEmpty, &dwFlags);
848     ok (hr == S_OK, "Desktop->GetAttributesOf(empty pidl) failed! hr = %08x\n", hr);
849     for (i = 0, foundFlagsMatch = FALSE; !foundFlagsMatch &&
850          i < sizeof(desktopFlags) / sizeof(desktopFlags[0]); i++)
851     {
852         if (desktopFlags[i] == dwFlags)
853             foundFlagsMatch = TRUE;
854     }
855     ok (foundFlagsMatch, "Wrong Desktop attributes: %08x\n", dwFlags);
856
857     /* .. or with no itemidlist at all. */
858     dwFlags = 0xffffffff;
859     hr = IShellFolder_GetAttributesOf(psfDesktop, 0, NULL, &dwFlags);
860     ok (hr == S_OK, "Desktop->GetAttributesOf(NULL) failed! hr = %08x\n", hr);
861     for (i = 0, foundFlagsMatch = FALSE; !foundFlagsMatch &&
862          i < sizeof(desktopFlags) / sizeof(desktopFlags[0]); i++)
863     {
864         if (desktopFlags[i] == dwFlags)
865             foundFlagsMatch = TRUE;
866     }
867     ok (foundFlagsMatch, "Wrong Desktop attributes: %08x\n", dwFlags);
868    
869     /* Testing the attributes of the MyComputer shellfolder */
870     hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyComputer, NULL, &pidlMyComputer, NULL);
871     ok (hr == S_OK, "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08x\n", hr);
872     if (hr != S_OK) {
873         IShellFolder_Release(psfDesktop);
874         return;
875     }
876
877     /* Windows sets the SFGAO_CANLINK flag, when MyComputer is queried via the Desktop
878      * folder object. It doesn't do this, if MyComputer is queried directly (see below).
879      */
880     dwFlags = 0xffffffff;
881     hr = IShellFolder_GetAttributesOf(psfDesktop, 1, (LPCITEMIDLIST*)&pidlMyComputer, &dwFlags);
882     ok (hr == S_OK, "Desktop->GetAttributesOf(MyComputer) failed! hr = %08x\n", hr);
883     for (i = 0, foundFlagsMatch = FALSE; !foundFlagsMatch &&
884          i < sizeof(myComputerFlags) / sizeof(myComputerFlags[0]); i++)
885     {
886         if ((myComputerFlags[i] | SFGAO_CANLINK) == dwFlags)
887             foundFlagsMatch = TRUE;
888     }
889     todo_wine
890     ok (foundFlagsMatch, "Wrong MyComputer attributes: %08x\n", dwFlags);
891
892     hr = IShellFolder_BindToObject(psfDesktop, pidlMyComputer, NULL, &IID_IShellFolder, (LPVOID*)&psfMyComputer);
893     ok (hr == S_OK, "Desktop failed to bind to MyComputer object! hr = %08x\n", hr);
894     IShellFolder_Release(psfDesktop);
895     IMalloc_Free(ppM, pidlMyComputer);
896     if (hr != S_OK) return;
897
898     hr = IShellFolder_GetAttributesOf(psfMyComputer, 1, &pidlEmpty, &dwFlags);
899     todo_wine
900     ok (hr == E_INVALIDARG ||
901         broken(hr == S_OK), /* W2K and earlier */
902         "MyComputer->GetAttributesOf(emtpy pidl) should fail! hr = %08x\n", hr);
903
904     dwFlags = 0xffffffff;
905     hr = IShellFolder_GetAttributesOf(psfMyComputer, 0, NULL, &dwFlags);
906     ok (hr == S_OK, "MyComputer->GetAttributesOf(NULL) failed! hr = %08x\n", hr);
907     for (i = 0, foundFlagsMatch = FALSE; !foundFlagsMatch &&
908          i < sizeof(myComputerFlags) / sizeof(myComputerFlags[0]); i++)
909     {
910         if (myComputerFlags[i] == dwFlags)
911             foundFlagsMatch = TRUE;
912     }
913     todo_wine
914     ok (foundFlagsMatch, "Wrong MyComputer attributes: %08x\n", dwFlags);
915
916     IShellFolder_Release(psfMyComputer);
917
918     GetCurrentDirectoryA(MAX_PATH, cCurrDirA);
919     len = lstrlenA(cCurrDirA);
920
921     if (len == 0) {
922         win_skip("GetCurrentDirectoryA returned empty string. Skipping test_GetAttributesOf\n");
923         return;
924     }
925     if (len > 3 && cCurrDirA[len-1] == '\\')
926         cCurrDirA[len-1] = 0;
927
928     /* create test directory */
929     CreateFilesFolders();
930
931     MultiByteToWideChar(CP_ACP, 0, cCurrDirA, -1, cCurrDirW, MAX_PATH);
932  
933     hr = SHGetDesktopFolder(&IDesktopFolder);
934     ok(hr == S_OK, "SHGetDesktopfolder failed %08x\n", hr);
935
936     hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cCurrDirW, NULL, &newPIDL, 0);
937     ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
938
939     hr = IShellFolder_BindToObject(IDesktopFolder, newPIDL, NULL, (REFIID)&IID_IShellFolder, (LPVOID *)&testIShellFolder);
940     ok(hr == S_OK, "BindToObject failed %08x\n", hr);
941
942     IMalloc_Free(ppM, newPIDL);
943
944     /* get relative PIDL */
945     hr = IShellFolder_ParseDisplayName(testIShellFolder, NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
946     ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
947
948     /* test the shell attributes of the test directory using the relative PIDL */
949     dwFlags = SFGAO_FOLDER;
950     hr = IShellFolder_GetAttributesOf(testIShellFolder, 1, (LPCITEMIDLIST*)&newPIDL, &dwFlags);
951     ok (hr == S_OK, "Desktop->GetAttributesOf() failed! hr = %08x\n", hr);
952     ok ((dwFlags&SFGAO_FOLDER), "Wrong directory attribute for relative PIDL: %08x\n", dwFlags);
953
954     /* free memory */
955     IMalloc_Free(ppM, newPIDL);
956
957     /* append testdirectory name to path */
958     if (cCurrDirA[len-1] == '\\')
959         cCurrDirA[len-1] = 0;
960     lstrcatA(cCurrDirA, "\\testdir");
961     MultiByteToWideChar(CP_ACP, 0, cCurrDirA, -1, cCurrDirW, MAX_PATH);
962
963     hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cCurrDirW, NULL, &newPIDL, 0);
964     ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
965
966     /* test the shell attributes of the test directory using the absolute PIDL */
967     dwFlags = SFGAO_FOLDER;
968     hr = IShellFolder_GetAttributesOf(IDesktopFolder, 1, (LPCITEMIDLIST*)&newPIDL, &dwFlags);
969     ok (hr == S_OK, "Desktop->GetAttributesOf() failed! hr = %08x\n", hr);
970     ok ((dwFlags&SFGAO_FOLDER), "Wrong directory attribute for absolute PIDL: %08x\n", dwFlags);
971
972     /* free memory */
973     IMalloc_Free(ppM, newPIDL);
974
975     IShellFolder_Release(testIShellFolder);
976
977     Cleanup();
978
979     IShellFolder_Release(IDesktopFolder);
980 }
981
982 static void test_SHGetPathFromIDList(void)
983 {
984     SHITEMID emptyitem = { 0, { 0 } };
985     LPCITEMIDLIST pidlEmpty = (LPCITEMIDLIST)&emptyitem;
986     LPITEMIDLIST pidlMyComputer;
987     WCHAR wszPath[MAX_PATH], wszDesktop[MAX_PATH];
988     BOOL result;
989     HRESULT hr;
990     LPSHELLFOLDER psfDesktop;
991     WCHAR wszMyComputer[] = { 
992         ':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
993         'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };
994     WCHAR wszFileName[MAX_PATH];
995     LPITEMIDLIST pidlTestFile;
996     HANDLE hTestFile;
997     STRRET strret;
998     static WCHAR wszTestFile[] = {
999         'w','i','n','e','t','e','s','t','.','f','o','o',0 };
1000     LPITEMIDLIST pidlPrograms;
1001
1002     if(!pSHGetPathFromIDListW || !pSHGetSpecialFolderPathW)
1003     {
1004         win_skip("SHGetPathFromIDListW() or SHGetSpecialFolderPathW() is missing\n");
1005         return;
1006     }
1007
1008     /* Calling SHGetPathFromIDListW with no pidl should return the empty string */
1009     wszPath[0] = 'a';
1010     wszPath[1] = '\0';
1011     result = pSHGetPathFromIDListW(NULL, wszPath);
1012     ok(!result, "Expected failure\n");
1013     ok(!wszPath[0], "Expected empty string\n");
1014
1015     /* Calling SHGetPathFromIDListW with an empty pidl should return the desktop folder's path. */
1016     result = pSHGetSpecialFolderPathW(NULL, wszDesktop, CSIDL_DESKTOP, FALSE);
1017     ok(result, "SHGetSpecialFolderPathW(CSIDL_DESKTOP) failed! Last error: %u\n", GetLastError());
1018     if (!result) return;
1019
1020     /* Check if we are on Win9x */
1021     SetLastError(0xdeadbeef);
1022     lstrcmpiW(wszDesktop, wszDesktop);
1023     if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
1024     {
1025         win_skip("Most W-calls are not implemented\n");
1026         return;
1027     }
1028
1029     result = pSHGetPathFromIDListW(pidlEmpty, wszPath);
1030     ok(result, "SHGetPathFromIDListW failed! Last error: %u\n", GetLastError());
1031     if (!result) return;
1032     ok(!lstrcmpiW(wszDesktop, wszPath), "SHGetPathFromIDListW didn't return desktop path for empty pidl!\n");
1033
1034     /* MyComputer does not map to a filesystem path. SHGetPathFromIDListW should fail. */
1035     hr = SHGetDesktopFolder(&psfDesktop);
1036     ok (hr == S_OK, "SHGetDesktopFolder failed! hr = %08x\n", hr);
1037     if (hr != S_OK) return;
1038
1039     hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyComputer, NULL, &pidlMyComputer, NULL);
1040     ok (hr == S_OK, "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08x\n", hr);
1041     if (hr != S_OK) {
1042         IShellFolder_Release(psfDesktop);
1043         return;
1044     }
1045
1046     SetLastError(0xdeadbeef);
1047     wszPath[0] = 'a';
1048     wszPath[1] = '\0';
1049     result = pSHGetPathFromIDListW(pidlMyComputer, wszPath);
1050     ok (!result, "SHGetPathFromIDListW succeeded where it shouldn't!\n");
1051     ok (GetLastError()==0xdeadbeef ||
1052         GetLastError()==ERROR_SUCCESS, /* Vista and higher */
1053         "Unexpected last error from SHGetPathFromIDListW: %u\n", GetLastError());
1054     ok (!wszPath[0], "Expected empty path\n");
1055     if (result) {
1056         IShellFolder_Release(psfDesktop);
1057         return;
1058     }
1059
1060     IMalloc_Free(ppM, pidlMyComputer);
1061
1062     result = pSHGetSpecialFolderPathW(NULL, wszFileName, CSIDL_DESKTOPDIRECTORY, FALSE);
1063     ok(result, "SHGetSpecialFolderPathW failed! Last error: %u\n", GetLastError());
1064     if (!result) {
1065         IShellFolder_Release(psfDesktop);
1066         return;
1067     }
1068     myPathAddBackslashW(wszFileName);
1069     lstrcatW(wszFileName, wszTestFile);
1070     hTestFile = CreateFileW(wszFileName, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL);
1071     ok(hTestFile != INVALID_HANDLE_VALUE, "CreateFileW failed! Last error: %u\n", GetLastError());
1072     if (hTestFile == INVALID_HANDLE_VALUE) {
1073         IShellFolder_Release(psfDesktop);
1074         return;
1075     }
1076     CloseHandle(hTestFile);
1077
1078     hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszTestFile, NULL, &pidlTestFile, NULL);
1079     ok (hr == S_OK, "Desktop's ParseDisplayName failed to parse filename hr = %08x\n", hr);
1080     if (hr != S_OK) {
1081         IShellFolder_Release(psfDesktop);
1082         DeleteFileW(wszFileName);
1083         IMalloc_Free(ppM, pidlTestFile);
1084         return;
1085     }
1086
1087     /* This test is to show that the Desktop shellfolder prepends the CSIDL_DESKTOPDIRECTORY
1088      * path for files placed on the desktop, if called with SHGDN_FORPARSING. */
1089     hr = IShellFolder_GetDisplayNameOf(psfDesktop, pidlTestFile, SHGDN_FORPARSING, &strret);
1090     ok (hr == S_OK, "Desktop's GetDisplayNamfOf failed! hr = %08x\n", hr);
1091     IShellFolder_Release(psfDesktop);
1092     DeleteFileW(wszFileName);
1093     if (hr != S_OK) {
1094         IMalloc_Free(ppM, pidlTestFile);
1095         return;
1096     }
1097     if (pStrRetToBufW)
1098     {
1099         pStrRetToBufW(&strret, pidlTestFile, wszPath, MAX_PATH);
1100         ok(0 == lstrcmpW(wszFileName, wszPath), 
1101            "Desktop->GetDisplayNameOf(pidlTestFile, SHGDN_FORPARSING) "
1102            "returned incorrect path for file placed on desktop\n");
1103     }
1104
1105     result = pSHGetPathFromIDListW(pidlTestFile, wszPath);
1106     ok(result, "SHGetPathFromIDListW failed! Last error: %u\n", GetLastError());
1107     IMalloc_Free(ppM, pidlTestFile);
1108     if (!result) return;
1109     ok(0 == lstrcmpW(wszFileName, wszPath), "SHGetPathFromIDListW returned incorrect path for file placed on desktop\n");
1110
1111
1112     /* Test if we can get the path from the start menu "program files" PIDL. */
1113     hr = pSHGetSpecialFolderLocation(NULL, CSIDL_PROGRAM_FILES, &pidlPrograms);
1114     ok(hr == S_OK, "SHGetFolderLocation failed: 0x%08x\n", hr);
1115
1116     SetLastError(0xdeadbeef);
1117     result = pSHGetPathFromIDListW(pidlPrograms, wszPath);
1118         IMalloc_Free(ppM, pidlPrograms);
1119     ok(result, "SHGetPathFromIDListW failed\n");
1120 }
1121
1122 static void test_EnumObjects_and_CompareIDs(void)
1123 {
1124     ITEMIDLIST *newPIDL;
1125     IShellFolder *IDesktopFolder, *testIShellFolder;
1126     char  cCurrDirA [MAX_PATH] = {0};
1127     static const CHAR cTestDirA[] = "\\testdir";
1128     WCHAR cTestDirW[MAX_PATH];
1129     int len;
1130     HRESULT hr;
1131
1132     GetCurrentDirectoryA(MAX_PATH, cCurrDirA);
1133     len = lstrlenA(cCurrDirA);
1134
1135     if(len == 0) {
1136         win_skip("GetCurrentDirectoryA returned empty string. Skipping test_EnumObjects_and_CompareIDs\n");
1137         return;
1138     }
1139     if(cCurrDirA[len-1] == '\\')
1140         cCurrDirA[len-1] = 0;
1141
1142     lstrcatA(cCurrDirA, cTestDirA);
1143     MultiByteToWideChar(CP_ACP, 0, cCurrDirA, -1, cTestDirW, MAX_PATH);
1144
1145     hr = SHGetDesktopFolder(&IDesktopFolder);
1146     ok(hr == S_OK, "SHGetDesktopfolder failed %08x\n", hr);
1147
1148     CreateFilesFolders();
1149
1150     hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
1151     ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
1152
1153     hr = IShellFolder_BindToObject(IDesktopFolder, newPIDL, NULL, (REFIID)&IID_IShellFolder, (LPVOID *)&testIShellFolder);
1154     ok(hr == S_OK, "BindToObject failed %08x\n", hr);
1155
1156     test_EnumObjects(testIShellFolder);
1157
1158     IShellFolder_Release(testIShellFolder);
1159
1160     Cleanup();
1161
1162     IMalloc_Free(ppM, newPIDL);
1163
1164     IShellFolder_Release(IDesktopFolder);
1165 }
1166
1167 /* A simple implementation of an IPropertyBag, which returns fixed values for
1168  * 'Target' and 'Attributes' properties.
1169  */
1170 static HRESULT WINAPI InitPropertyBag_IPropertyBag_QueryInterface(IPropertyBag *iface, REFIID riid,
1171     void **ppvObject) 
1172 {
1173     if (!ppvObject)
1174         return E_INVALIDARG;
1175
1176     if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IPropertyBag, riid)) {
1177         *ppvObject = iface;
1178     } else {
1179         ok (FALSE, "InitPropertyBag asked for unknown interface!\n");
1180         return E_NOINTERFACE;
1181     }
1182
1183     IPropertyBag_AddRef(iface);
1184     return S_OK;
1185 }
1186
1187 static ULONG WINAPI InitPropertyBag_IPropertyBag_AddRef(IPropertyBag *iface) {
1188     return 2;
1189 }
1190
1191 static ULONG WINAPI InitPropertyBag_IPropertyBag_Release(IPropertyBag *iface) {
1192     return 1;
1193 }
1194
1195 static HRESULT WINAPI InitPropertyBag_IPropertyBag_Read(IPropertyBag *iface, LPCOLESTR pszPropName,
1196     VARIANT *pVar, IErrorLog *pErrorLog)
1197 {
1198     static const WCHAR wszTargetSpecialFolder[] = {
1199         'T','a','r','g','e','t','S','p','e','c','i','a','l','F','o','l','d','e','r',0 };
1200     static const WCHAR wszTarget[] = {
1201         'T','a','r','g','e','t',0 };
1202     static const WCHAR wszAttributes[] = {
1203         'A','t','t','r','i','b','u','t','e','s',0 };
1204     static const WCHAR wszResolveLinkFlags[] = {
1205         'R','e','s','o','l','v','e','L','i','n','k','F','l','a','g','s',0 };
1206     static const WCHAR wszTargetKnownFolder[] = {
1207         'T','a','r','g','e','t','K','n','o','w','n','F','o','l','d','e','r',0 };
1208     static const WCHAR wszCLSID[] = {
1209         'C','L','S','I','D',0 };
1210        
1211     if (!lstrcmpW(pszPropName, wszTargetSpecialFolder)) {
1212         ok(V_VT(pVar) == VT_I4 ||
1213            broken(V_VT(pVar) == VT_BSTR),   /* Win2k */
1214            "Wrong variant type for 'TargetSpecialFolder' property!\n");
1215         return E_INVALIDARG;
1216     }
1217     
1218     if (!lstrcmpW(pszPropName, wszResolveLinkFlags)) 
1219     {
1220         ok(V_VT(pVar) == VT_UI4, "Wrong variant type for 'ResolveLinkFlags' property!\n");
1221         return E_INVALIDARG;
1222     }
1223
1224     if (!lstrcmpW(pszPropName, wszTarget)) {
1225         WCHAR wszPath[MAX_PATH];
1226         BOOL result;
1227         
1228         ok(V_VT(pVar) == VT_BSTR ||
1229            broken(V_VT(pVar) == VT_EMPTY),  /* Win2k */
1230            "Wrong variant type for 'Target' property!\n");
1231         if (V_VT(pVar) != VT_BSTR) return E_INVALIDARG;
1232
1233         result = pSHGetSpecialFolderPathW(NULL, wszPath, CSIDL_DESKTOPDIRECTORY, FALSE);
1234         ok(result, "SHGetSpecialFolderPathW(DESKTOPDIRECTORY) failed! %u\n", GetLastError());
1235         if (!result) return E_INVALIDARG;
1236
1237         V_BSTR(pVar) = SysAllocString(wszPath);
1238         return S_OK;
1239     }
1240
1241     if (!lstrcmpW(pszPropName, wszAttributes)) {
1242         ok(V_VT(pVar) == VT_UI4, "Wrong variant type for 'Attributes' property!\n");
1243         if (V_VT(pVar) != VT_UI4) return E_INVALIDARG;
1244         V_UI4(pVar) = SFGAO_FOLDER|SFGAO_HASSUBFOLDER|SFGAO_FILESYSANCESTOR|
1245                       SFGAO_CANRENAME|SFGAO_FILESYSTEM;
1246         return S_OK;
1247     }
1248
1249     if (!lstrcmpW(pszPropName, wszTargetKnownFolder)) {
1250         ok(V_VT(pVar) == VT_BSTR, "Wrong variant type for 'TargetKnownFolder' property!\n");
1251         /* TODO */
1252         return E_INVALIDARG;
1253     }
1254
1255     if (!lstrcmpW(pszPropName, wszCLSID)) {
1256         ok(V_VT(pVar) == VT_EMPTY, "Wrong variant type for 'CLSID' property!\n");
1257         /* TODO */
1258         return E_INVALIDARG;
1259     }
1260
1261     ok(FALSE, "PropertyBag was asked for unknown property %s (vt=%d)!\n", wine_dbgstr_w(pszPropName), V_VT(pVar));
1262     return E_INVALIDARG;
1263 }
1264
1265 static HRESULT WINAPI InitPropertyBag_IPropertyBag_Write(IPropertyBag *iface, LPCOLESTR pszPropName,
1266     VARIANT *pVar)
1267 {
1268     ok(FALSE, "Unexpected call to IPropertyBag_Write\n");
1269     return E_NOTIMPL;
1270 }
1271     
1272 static const IPropertyBagVtbl InitPropertyBag_IPropertyBagVtbl = {
1273     InitPropertyBag_IPropertyBag_QueryInterface,
1274     InitPropertyBag_IPropertyBag_AddRef,
1275     InitPropertyBag_IPropertyBag_Release,
1276     InitPropertyBag_IPropertyBag_Read,
1277     InitPropertyBag_IPropertyBag_Write
1278 };
1279
1280 static struct IPropertyBag InitPropertyBag = {
1281     &InitPropertyBag_IPropertyBagVtbl
1282 };
1283
1284 static void test_FolderShortcut(void) {
1285     IPersistPropertyBag *pPersistPropertyBag;
1286     IShellFolder *pShellFolder, *pDesktopFolder;
1287     IPersistFolder3 *pPersistFolder3;
1288     HRESULT hr;
1289     STRRET strret;
1290     WCHAR wszDesktopPath[MAX_PATH], wszBuffer[MAX_PATH];
1291     BOOL result;
1292     CLSID clsid;
1293     LPITEMIDLIST pidlCurrentFolder, pidlWineTestFolder, pidlSubFolder;
1294     HKEY hShellExtKey;
1295     WCHAR wszWineTestFolder[] = {
1296         ':',':','{','9','B','3','5','2','E','B','F','-','2','7','6','5','-','4','5','C','1','-',
1297         'B','4','C','6','-','8','5','C','C','7','F','7','A','B','C','6','4','}',0 };
1298     WCHAR wszShellExtKey[] = { 'S','o','f','t','w','a','r','e','\\',
1299         'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
1300         'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
1301         'E','x','p','l','o','r','e','r','\\','D','e','s','k','t','o','p','\\',
1302         'N','a','m','e','S','p','a','c','e','\\',
1303         '{','9','b','3','5','2','e','b','f','-','2','7','6','5','-','4','5','c','1','-',
1304         'b','4','c','6','-','8','5','c','c','7','f','7','a','b','c','6','4','}',0 };
1305     
1306     WCHAR wszSomeSubFolder[] = { 'S','u','b','F','o','l','d','e','r', 0};
1307     static const GUID CLSID_UnixDosFolder = 
1308         {0x9d20aae8, 0x0625, 0x44b0, {0x9c, 0xa7, 0x71, 0x88, 0x9c, 0x22, 0x54, 0xd9}};
1309
1310     if (!pSHGetSpecialFolderPathW || !pStrRetToBufW) {
1311         win_skip("SHGetSpecialFolderPathW and/or StrRetToBufW are not available\n");
1312         return;
1313     }
1314
1315     if (!pSHGetFolderPathAndSubDirA)
1316     {
1317         win_skip("FolderShortcut test doesn't work on Win2k\n");
1318         return;
1319     }
1320
1321     /* These tests basically show, that CLSID_FolderShortcuts are initialized
1322      * via their IPersistPropertyBag interface. And that the target folder
1323      * is taken from the IPropertyBag's 'Target' property.
1324      */
1325     hr = CoCreateInstance(&CLSID_FolderShortcut, NULL, CLSCTX_INPROC_SERVER, 
1326                           &IID_IPersistPropertyBag, (LPVOID*)&pPersistPropertyBag);
1327     if (hr == REGDB_E_CLASSNOTREG) {
1328         win_skip("CLSID_FolderShortcut is not implemented\n");
1329         return;
1330     }
1331     ok (hr == S_OK, "CoCreateInstance failed! hr = 0x%08x\n", hr);
1332     if (hr != S_OK) return;
1333
1334     hr = IPersistPropertyBag_Load(pPersistPropertyBag, &InitPropertyBag, NULL);
1335     ok(hr == S_OK, "IPersistPropertyBag_Load failed! hr = %08x\n", hr);
1336     if (hr != S_OK) {
1337         IPersistPropertyBag_Release(pPersistPropertyBag);
1338         return;
1339     }
1340
1341     hr = IPersistPropertyBag_QueryInterface(pPersistPropertyBag, &IID_IShellFolder, 
1342                                             (LPVOID*)&pShellFolder);
1343     IPersistPropertyBag_Release(pPersistPropertyBag);
1344     ok(hr == S_OK, "IPersistPropertyBag_QueryInterface(IShellFolder) failed! hr = %08x\n", hr);
1345     if (hr != S_OK) return;
1346
1347     hr = IShellFolder_GetDisplayNameOf(pShellFolder, NULL, SHGDN_FORPARSING, &strret);
1348     ok(hr == S_OK, "IShellFolder_GetDisplayNameOf(NULL) failed! hr = %08x\n", hr);
1349     if (hr != S_OK) {
1350         IShellFolder_Release(pShellFolder);
1351         return;
1352     }
1353
1354     result = pSHGetSpecialFolderPathW(NULL, wszDesktopPath, CSIDL_DESKTOPDIRECTORY, FALSE);
1355     ok(result, "SHGetSpecialFolderPathW(CSIDL_DESKTOPDIRECTORY) failed! %u\n", GetLastError());
1356     if (!result) return;
1357
1358     pStrRetToBufW(&strret, NULL, wszBuffer, MAX_PATH);
1359     ok(!lstrcmpiW(wszDesktopPath, wszBuffer), "FolderShortcut returned incorrect folder!\n");
1360
1361     hr = IShellFolder_QueryInterface(pShellFolder, &IID_IPersistFolder3, (LPVOID*)&pPersistFolder3);
1362     IShellFolder_Release(pShellFolder);
1363     ok(hr == S_OK, "IShellFolder_QueryInterface(IID_IPersistFolder3 failed! hr = 0x%08x\n", hr);
1364     if (hr != S_OK) return;
1365
1366     hr = IPersistFolder3_GetClassID(pPersistFolder3, &clsid);
1367     ok(hr == S_OK, "IPersistFolder3_GetClassID failed! hr=0x%08x\n", hr);
1368     ok(IsEqualCLSID(&clsid, &CLSID_FolderShortcut), "Unexpected CLSID!\n");
1369
1370     hr = IPersistFolder3_GetCurFolder(pPersistFolder3, &pidlCurrentFolder);
1371     todo_wine ok(hr == S_FALSE, "IPersistFolder3_GetCurFolder failed! hr=0x%08x\n", hr);
1372     ok(!pidlCurrentFolder, "IPersistFolder3_GetCurFolder should return a NULL pidl!\n");
1373                     
1374     /* For FolderShortcut objects, the Initialize method initialized the folder's position in the
1375      * shell namespace. The target folder, read from the property bag above, remains untouched. 
1376      * The following tests show this: The itemidlist for some imaginary shellfolder object
1377      * is created and the FolderShortcut is initialized with it. GetCurFolder now returns this
1378      * itemidlist, but GetDisplayNameOf still returns the path from above.
1379      */
1380     hr = SHGetDesktopFolder(&pDesktopFolder);
1381     ok (hr == S_OK, "SHGetDesktopFolder failed! hr = %08x\n", hr);
1382     if (hr != S_OK) return;
1383
1384     /* Temporarily register WineTestFolder as a shell namespace extension at the Desktop. 
1385      * Otherwise ParseDisplayName fails on WinXP with E_INVALIDARG */
1386     RegCreateKeyW(HKEY_CURRENT_USER, wszShellExtKey, &hShellExtKey);
1387     RegCloseKey(hShellExtKey);
1388     hr = IShellFolder_ParseDisplayName(pDesktopFolder, NULL, NULL, wszWineTestFolder, NULL,
1389                                        &pidlWineTestFolder, NULL);
1390     RegDeleteKeyW(HKEY_CURRENT_USER, wszShellExtKey);
1391     IShellFolder_Release(pDesktopFolder);
1392     ok (hr == S_OK, "IShellFolder::ParseDisplayName failed! hr = %08x\n", hr);
1393     if (hr != S_OK) return;
1394
1395     hr = IPersistFolder3_Initialize(pPersistFolder3, pidlWineTestFolder);
1396     ok (hr == S_OK, "IPersistFolder3::Initialize failed! hr = %08x\n", hr);
1397     if (hr != S_OK) {
1398         IPersistFolder3_Release(pPersistFolder3);
1399         pILFree(pidlWineTestFolder);
1400         return;
1401     }
1402
1403     hr = IPersistFolder3_GetCurFolder(pPersistFolder3, &pidlCurrentFolder);
1404     ok(hr == S_OK, "IPersistFolder3_GetCurFolder failed! hr=0x%08x\n", hr);
1405     ok(pILIsEqual(pidlCurrentFolder, pidlWineTestFolder),
1406         "IPersistFolder3_GetCurFolder should return pidlWineTestFolder!\n");
1407     pILFree(pidlCurrentFolder);
1408     pILFree(pidlWineTestFolder);
1409
1410     hr = IPersistFolder3_QueryInterface(pPersistFolder3, &IID_IShellFolder, (LPVOID*)&pShellFolder);
1411     IPersistFolder3_Release(pPersistFolder3);
1412     ok(hr == S_OK, "IPersistFolder3_QueryInterface(IShellFolder) failed! hr = %08x\n", hr);
1413     if (hr != S_OK) return;
1414
1415     hr = IShellFolder_GetDisplayNameOf(pShellFolder, NULL, SHGDN_FORPARSING, &strret);
1416     ok(hr == S_OK, "IShellFolder_GetDisplayNameOf(NULL) failed! hr = %08x\n", hr);
1417     if (hr != S_OK) {
1418         IShellFolder_Release(pShellFolder);
1419         return;
1420     }
1421
1422     pStrRetToBufW(&strret, NULL, wszBuffer, MAX_PATH);
1423     ok(!lstrcmpiW(wszDesktopPath, wszBuffer), "FolderShortcut returned incorrect folder!\n");
1424
1425     /* Next few lines are meant to show that children of FolderShortcuts are not FolderShortcuts,
1426      * but ShellFSFolders. */
1427     myPathAddBackslashW(wszDesktopPath);
1428     lstrcatW(wszDesktopPath, wszSomeSubFolder);
1429     if (!CreateDirectoryW(wszDesktopPath, NULL)) {
1430         IShellFolder_Release(pShellFolder);
1431         return;
1432     }
1433     
1434     hr = IShellFolder_ParseDisplayName(pShellFolder, NULL, NULL, wszSomeSubFolder, NULL, 
1435                                        &pidlSubFolder, NULL);
1436     RemoveDirectoryW(wszDesktopPath);
1437     ok (hr == S_OK, "IShellFolder::ParseDisplayName failed! hr = %08x\n", hr);
1438     if (hr != S_OK) {
1439         IShellFolder_Release(pShellFolder);
1440         return;
1441     }
1442
1443     hr = IShellFolder_BindToObject(pShellFolder, pidlSubFolder, NULL, &IID_IPersistFolder3,
1444                                    (LPVOID*)&pPersistFolder3);
1445     IShellFolder_Release(pShellFolder);
1446     pILFree(pidlSubFolder);
1447     ok (hr == S_OK, "IShellFolder::BindToObject failed! hr = %08x\n", hr);
1448     if (hr != S_OK)
1449         return;
1450
1451     /* On windows, we expect CLSID_ShellFSFolder. On wine we relax this constraint
1452      * a little bit and also allow CLSID_UnixDosFolder. */
1453     hr = IPersistFolder3_GetClassID(pPersistFolder3, &clsid);
1454     ok(hr == S_OK, "IPersistFolder3_GetClassID failed! hr=0x%08x\n", hr);
1455     ok(IsEqualCLSID(&clsid, &CLSID_ShellFSFolder) || IsEqualCLSID(&clsid, &CLSID_UnixDosFolder),
1456         "IPersistFolder3::GetClassID returned unexpected CLSID!\n");
1457
1458     IPersistFolder3_Release(pPersistFolder3);
1459 }
1460
1461 #include "pshpack1.h"
1462 struct FileStructA {
1463     BYTE  type;
1464     BYTE  dummy;
1465     DWORD dwFileSize;
1466     WORD  uFileDate;    /* In our current implementation this is */
1467     WORD  uFileTime;    /* FileTimeToDosDate(WIN32_FIND_DATA->ftLastWriteTime) */
1468     WORD  uFileAttribs;
1469     CHAR  szName[1];
1470 };
1471
1472 struct FileStructW {
1473     WORD  cbLen;        /* Length of this element. */
1474     BYTE  abFooBar1[6]; /* Beyond any recognition. */
1475     WORD  uDate;        /* FileTimeToDosDate(WIN32_FIND_DATA->ftCreationTime)? */
1476     WORD  uTime;        /* (this is currently speculation) */
1477     WORD  uDate2;       /* FileTimeToDosDate(WIN32_FIND_DATA->ftLastAccessTime)? */
1478     WORD  uTime2;       /* (this is currently speculation) */
1479     BYTE  abFooBar2[4]; /* Beyond any recognition. */
1480     WCHAR wszName[1];   /* The long filename in unicode. */
1481     /* Just for documentation: Right after the unicode string: */
1482     WORD  cbOffset;     /* FileStructW's offset from the beginning of the SHITMEID. 
1483                          * SHITEMID->cb == uOffset + cbLen */
1484 };
1485 #include "poppack.h"
1486
1487 static void test_ITEMIDLIST_format(void) {
1488     WCHAR wszPersonal[MAX_PATH];
1489     LPSHELLFOLDER psfDesktop, psfPersonal;
1490     LPITEMIDLIST pidlPersonal, pidlFile;
1491     HANDLE hFile;
1492     HRESULT hr;
1493     BOOL bResult;
1494     WCHAR wszFile[3][17] = { { 'e','v','e','n','_',0 }, { 'o','d','d','_',0 },
1495         { 'l','o','n','g','e','r','_','t','h','a','n','.','8','_','3',0 } };
1496     int i;
1497
1498     if (!pSHGetSpecialFolderPathW) return;
1499
1500     bResult = pSHGetSpecialFolderPathW(NULL, wszPersonal, CSIDL_PERSONAL, FALSE);
1501     ok(bResult, "SHGetSpecialFolderPathW failed! Last error: %u\n", GetLastError());
1502     if (!bResult) return;
1503
1504     SetLastError(0xdeadbeef);
1505     bResult = SetCurrentDirectoryW(wszPersonal);
1506     if (!bResult && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) {
1507         win_skip("Most W-calls are not implemented\n");
1508         return;
1509     }
1510     ok(bResult, "SetCurrentDirectory failed! Last error: %u\n", GetLastError());
1511     if (!bResult) return;
1512
1513     hr = SHGetDesktopFolder(&psfDesktop);
1514     ok(hr == S_OK, "SHGetDesktopFolder failed! hr: %08x\n", hr);
1515     if (hr != S_OK) return;
1516
1517     hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszPersonal, NULL, &pidlPersonal, NULL);
1518     ok(hr == S_OK, "psfDesktop->ParseDisplayName failed! hr = %08x\n", hr);
1519     if (hr != S_OK) {
1520         IShellFolder_Release(psfDesktop);
1521         return;
1522     }
1523
1524     hr = IShellFolder_BindToObject(psfDesktop, pidlPersonal, NULL, &IID_IShellFolder,
1525         (LPVOID*)&psfPersonal);
1526     IShellFolder_Release(psfDesktop);
1527     pILFree(pidlPersonal);
1528     ok(hr == S_OK, "psfDesktop->BindToObject failed! hr = %08x\n", hr);
1529     if (hr != S_OK) return;
1530
1531     for (i=0; i<3; i++) {
1532         CHAR szFile[MAX_PATH];
1533         struct FileStructA *pFileStructA;
1534         WORD cbOffset;
1535
1536         WideCharToMultiByte(CP_ACP, 0, wszFile[i], -1, szFile, MAX_PATH, NULL, NULL);
1537
1538         hFile = CreateFileW(wszFile[i], GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_FLAG_WRITE_THROUGH, NULL);
1539         ok(hFile != INVALID_HANDLE_VALUE, "CreateFile failed! (%u)\n", GetLastError());
1540         if (hFile == INVALID_HANDLE_VALUE) {
1541             IShellFolder_Release(psfPersonal);
1542             return;
1543         }
1544         CloseHandle(hFile);
1545
1546         hr = IShellFolder_ParseDisplayName(psfPersonal, NULL, NULL, wszFile[i], NULL, &pidlFile, NULL);
1547         DeleteFileW(wszFile[i]);
1548         ok(hr == S_OK, "psfPersonal->ParseDisplayName failed! hr: %08x\n", hr);
1549         if (hr != S_OK) {
1550             IShellFolder_Release(psfPersonal);
1551             return;
1552         }
1553
1554         pFileStructA = (struct FileStructA *)pidlFile->mkid.abID;
1555         ok(pFileStructA->type == 0x32, "PIDLTYPE should be 0x32!\n");
1556         ok(pFileStructA->dummy == 0x00, "Dummy Byte should be 0x00!\n");
1557         ok(pFileStructA->dwFileSize == 0, "Filesize should be zero!\n");
1558
1559         if (i < 2) /* First two file names are already in valid 8.3 format */
1560             ok(!strcmp(szFile, (CHAR*)&pidlFile->mkid.abID[12]), "Wrong file name!\n");
1561         else
1562             /* WinXP stores a derived 8.3 dos name (LONGER~1.8_3) here. We probably
1563              * can't implement this correctly, since unix filesystems don't support
1564              * this nasty short/long filename stuff. So we'll probably stay with our
1565              * current habbit of storing the long filename here, which seems to work
1566              * just fine. */
1567             todo_wine
1568             ok(pidlFile->mkid.abID[18] == '~' ||
1569                broken(pidlFile->mkid.abID[34] == '~'),  /* Win2k */
1570                "Should be derived 8.3 name!\n");
1571
1572         if (i == 0) /* First file name has an even number of chars. No need for alignment. */
1573             ok(pidlFile->mkid.abID[12 + strlen(szFile) + 1] != '\0' ||
1574                broken(pidlFile->mkid.cb == 2 + 12 + strlen(szFile) + 1 + 1),    /* Win2k */
1575                 "Alignment byte, where there shouldn't be!\n");
1576
1577         if (i == 1) /* Second file name has an uneven number of chars => alignment byte */
1578             ok(pidlFile->mkid.abID[12 + strlen(szFile) + 1] == '\0',
1579                 "There should be an alignment byte, but isn't!\n");
1580
1581         /* The offset of the FileStructW member is stored as a WORD at the end of the pidl. */
1582         cbOffset = *(WORD*)(((LPBYTE)pidlFile)+pidlFile->mkid.cb-sizeof(WORD));
1583         ok ((cbOffset >= sizeof(struct FileStructA) &&
1584             cbOffset <= pidlFile->mkid.cb - sizeof(struct FileStructW)) ||
1585             broken(pidlFile->mkid.cb == 2 + 12 + strlen(szFile) + 1 + 1) ||     /* Win2k on short names */
1586             broken(pidlFile->mkid.cb == 2 + 12 + strlen(szFile) + 1 + 12 + 1),  /* Win2k on long names */
1587             "Wrong offset value (%d) stored at the end of the PIDL\n", cbOffset);
1588
1589         if (cbOffset >= sizeof(struct FileStructA) &&
1590             cbOffset <= pidlFile->mkid.cb - sizeof(struct FileStructW))
1591         {
1592             struct FileStructW *pFileStructW = (struct FileStructW *)(((LPBYTE)pidlFile)+cbOffset);
1593
1594             ok(pidlFile->mkid.cb == cbOffset + pFileStructW->cbLen,
1595                 "FileStructW's offset and length should add up to the PIDL's length!\n");
1596
1597             if (pidlFile->mkid.cb == cbOffset + pFileStructW->cbLen) {
1598                 /* Since we just created the file, time of creation,
1599                  * time of last access and time of last write access just be the same.
1600                  * These tests seem to fail sometimes (on WinXP), if the test is run again shortly
1601                  * after the first run. I do remember something with NTFS keeping the creation time
1602                  * if a file is deleted and then created again within a couple of seconds or so.
1603                  * Might be the reason. */
1604                 ok (pFileStructA->uFileDate == pFileStructW->uDate &&
1605                     pFileStructA->uFileTime == pFileStructW->uTime,
1606                     "Last write time should match creation time!\n");
1607
1608                 /* On FAT filesystems the last access time is midnight
1609                    local time, so the values of uDate2 and uTime2 will
1610                    depend on the local timezone.  If the times are exactly
1611                    equal then the dates should be identical for both FAT
1612                    and NTFS as no timezone is more than 1 day away from UTC.
1613                 */
1614                 if (pFileStructA->uFileTime == pFileStructW->uTime2)
1615                 {
1616                     ok (pFileStructA->uFileDate == pFileStructW->uDate2,
1617                         "Last write date and time should match last access date and time!\n");
1618                 }
1619                 else
1620                 {
1621                     /* Filesystem may be FAT. Check date within 1 day
1622                        and seconds are zero. */
1623                     trace ("Filesystem may be FAT. Performing less strict atime test.\n");
1624                     ok ((pFileStructW->uTime2 & 0x1F) == 0,
1625                         "Last access time on FAT filesystems should have zero seconds.\n");
1626                     /* TODO: Perform check for date being within one day.*/
1627                 }
1628
1629                 ok (!lstrcmpW(wszFile[i], pFileStructW->wszName) ||
1630                     !lstrcmpW(wszFile[i], (WCHAR *)(pFileStructW->abFooBar2 + 22)) || /* Vista */
1631                     !lstrcmpW(wszFile[i], (WCHAR *)(pFileStructW->abFooBar2 + 26)), /* Win7 */
1632                     "The filename should be stored in unicode at this position!\n");
1633             }
1634         }
1635
1636         pILFree(pidlFile);
1637     }
1638
1639     IShellFolder_Release(psfPersonal);
1640 }
1641
1642 static void test_SHGetFolderPathAndSubDirA(void)
1643 {
1644     HRESULT ret;
1645     BOOL delret;
1646     DWORD dwret;
1647     int i;
1648     static char wine[] = "wine";
1649     static char winetemp[] = "wine\\temp";
1650     static char appdata[MAX_PATH];
1651     static char testpath[MAX_PATH];
1652     static char toolongpath[MAX_PATH+1];
1653
1654     if(!pSHGetFolderPathAndSubDirA)
1655     {
1656         win_skip("SHGetFolderPathAndSubDirA not present!\n");
1657         return;
1658     }
1659
1660     if(!pSHGetFolderPathA) {
1661         win_skip("SHGetFolderPathA not present!\n");
1662         return;
1663     }
1664     if(FAILED(pSHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, appdata)))
1665     {
1666         win_skip("SHGetFolderPathA failed for CSIDL_LOCAL_APPDATA!\n");
1667         return;
1668     }
1669
1670     sprintf(testpath, "%s\\%s", appdata, winetemp);
1671     delret = RemoveDirectoryA(testpath);
1672     if(!delret && (ERROR_PATH_NOT_FOUND != GetLastError()) ) {
1673         win_skip("RemoveDirectoryA(%s) failed with error %u\n", testpath, GetLastError());
1674         return;
1675     }
1676
1677     sprintf(testpath, "%s\\%s", appdata, wine);
1678     delret = RemoveDirectoryA(testpath);
1679     if(!delret && (ERROR_PATH_NOT_FOUND != GetLastError()) && (ERROR_FILE_NOT_FOUND != GetLastError())) {
1680         win_skip("RemoveDirectoryA(%s) failed with error %u\n", testpath, GetLastError());
1681         return;
1682     }
1683
1684     /* test invalid second parameter */
1685     ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | 0xff, NULL, SHGFP_TYPE_CURRENT, wine, testpath);
1686     ok(E_INVALIDARG == ret, "expected E_INVALIDARG, got  %x\n", ret);
1687
1688     /* test fourth parameter */
1689     ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, 2, winetemp, testpath);
1690     switch(ret) {
1691         case S_OK: /* winvista */
1692             ok(!strncmp(appdata, testpath, strlen(appdata)),
1693                 "expected %s to start with %s\n", testpath, appdata);
1694             ok(!lstrcmpA(&testpath[1 + strlen(appdata)], winetemp),
1695                 "expected %s to end with %s\n", testpath, winetemp);
1696             break;
1697         case E_INVALIDARG: /* winxp, win2k3 */
1698             break;
1699         default:
1700             ok(0, "expected S_OK or E_INVALIDARG, got  %x\n", ret);
1701     }
1702
1703     /* test fifth parameter */
1704     testpath[0] = '\0';
1705     ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, NULL, testpath);
1706     ok(S_OK == ret, "expected S_OK, got %x\n", ret);
1707     ok(!lstrcmpA(appdata, testpath), "expected %s, got %s\n", appdata, testpath);
1708
1709     testpath[0] = '\0';
1710     ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, "", testpath);
1711     ok(S_OK == ret, "expected S_OK, got %x\n", ret);
1712     ok(!lstrcmpA(appdata, testpath), "expected %s, got %s\n", appdata, testpath);
1713
1714     testpath[0] = '\0';
1715     ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, "\\", testpath);
1716     ok(S_OK == ret, "expected S_OK, got %x\n", ret);
1717     ok(!lstrcmpA(appdata, testpath), "expected %s, got %s\n", appdata, testpath);
1718
1719     for(i=0; i< MAX_PATH; i++)
1720         toolongpath[i] = '0' + i % 10;
1721     toolongpath[MAX_PATH] = '\0';
1722     ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, toolongpath, testpath);
1723     ok(HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE) == ret,
1724         "expected %x, got %x\n", HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE), ret);
1725
1726     testpath[0] = '\0';
1727     ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, wine, NULL);
1728     ok((S_OK == ret) || (E_INVALIDARG == ret), "expected S_OK or E_INVALIDARG, got %x\n", ret);
1729
1730     /* test a not existing path */
1731     testpath[0] = '\0';
1732     ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, winetemp, testpath);
1733     ok(HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == ret,
1734         "expected %x, got %x\n", HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND), ret);
1735
1736     /* create a directory inside a not existing directory */
1737     testpath[0] = '\0';
1738     ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_CREATE | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, winetemp, testpath);
1739     ok(S_OK == ret, "expected S_OK, got %x\n", ret);
1740     ok(!strncmp(appdata, testpath, strlen(appdata)),
1741         "expected %s to start with %s\n", testpath, appdata);
1742     ok(!lstrcmpA(&testpath[1 + strlen(appdata)], winetemp),
1743         "expected %s to end with %s\n", testpath, winetemp);
1744     dwret = GetFileAttributes(testpath);
1745     ok(FILE_ATTRIBUTE_DIRECTORY | dwret, "expected %x to contain FILE_ATTRIBUTE_DIRECTORY\n", dwret);
1746
1747     /* cleanup */
1748     sprintf(testpath, "%s\\%s", appdata, winetemp);
1749     RemoveDirectoryA(testpath);
1750     sprintf(testpath, "%s\\%s", appdata, wine);
1751     RemoveDirectoryA(testpath);
1752 }
1753
1754 static void test_LocalizedNames(void)
1755 {
1756     static char cCurrDirA[MAX_PATH];
1757     WCHAR cCurrDirW[MAX_PATH], tempbufW[25];
1758     IShellFolder *IDesktopFolder, *testIShellFolder;
1759     ITEMIDLIST *newPIDL;
1760     int len;
1761     HRESULT hr;
1762     static char resourcefile[MAX_PATH];
1763     DWORD res;
1764     HANDLE file;
1765     STRRET strret;
1766
1767     static const char desktopini_contents1[] =
1768         "[.ShellClassInfo]\r\n"
1769         "LocalizedResourceName=@";
1770     static const char desktopini_contents2[] =
1771         ",-1\r\n";
1772     static WCHAR foldernameW[] = {'t','e','s','t','f','o','l','d','e','r',0};
1773     static const WCHAR folderdisplayW[] = {'F','o','l','d','e','r',' ','N','a','m','e',' ','R','e','s','o','u','r','c','e',0};
1774
1775     /* create folder with desktop.ini and localized name in GetModuleFileNameA(NULL) */
1776     CreateDirectoryA(".\\testfolder", NULL);
1777
1778     SetFileAttributesA(".\\testfolder", GetFileAttributesA(".\\testfolder")|FILE_ATTRIBUTE_SYSTEM);
1779
1780     GetModuleFileNameA(NULL, resourcefile, MAX_PATH);
1781
1782     file = CreateFileA(".\\testfolder\\desktop.ini", GENERIC_WRITE, 0, NULL,
1783                          CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
1784     ok(file != INVALID_HANDLE_VALUE, "CreateFileA failed %i\n", GetLastError());
1785     ok(WriteFile(file, desktopini_contents1, strlen(desktopini_contents1), &res, NULL) &&
1786        WriteFile(file, resourcefile, strlen(resourcefile), &res, NULL) &&
1787        WriteFile(file, desktopini_contents2, strlen(desktopini_contents2), &res, NULL),
1788        "WriteFile failed %i\n", GetLastError());
1789     CloseHandle(file);
1790
1791     /* get IShellFolder for parent */
1792     GetCurrentDirectoryA(MAX_PATH, cCurrDirA);
1793     len = lstrlenA(cCurrDirA);
1794
1795     if (len == 0) {
1796         win_skip("GetCurrentDirectoryA returned empty string. Skipping test_LocalizedNames\n");
1797         goto cleanup;
1798     }
1799     if(cCurrDirA[len-1] == '\\')
1800         cCurrDirA[len-1] = 0;
1801
1802     MultiByteToWideChar(CP_ACP, 0, cCurrDirA, -1, cCurrDirW, MAX_PATH);
1803
1804     hr = SHGetDesktopFolder(&IDesktopFolder);
1805     ok(hr == S_OK, "SHGetDesktopfolder failed %08x\n", hr);
1806
1807     hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cCurrDirW, NULL, &newPIDL, 0);
1808     ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
1809
1810     hr = IShellFolder_BindToObject(IDesktopFolder, newPIDL, NULL, (REFIID)&IID_IShellFolder, (LPVOID *)&testIShellFolder);
1811     ok(hr == S_OK, "BindToObject failed %08x\n", hr);
1812
1813     IMalloc_Free(ppM, newPIDL);
1814
1815     /* windows reads the display name from the resource */
1816     hr = IShellFolder_ParseDisplayName(testIShellFolder, NULL, NULL, foldernameW, NULL, &newPIDL, 0);
1817     ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
1818
1819     hr = IShellFolder_GetDisplayNameOf(testIShellFolder, newPIDL, SHGDN_INFOLDER, &strret);
1820     ok(hr == S_OK, "GetDisplayNameOf failed %08x\n", hr);
1821
1822     if (hr == S_OK && pStrRetToBufW)
1823     {
1824         hr = pStrRetToBufW(&strret, newPIDL, tempbufW, sizeof(tempbufW)/sizeof(WCHAR));
1825         ok (hr == S_OK, "StrRetToBufW failed! hr = %08x\n", hr);
1826         todo_wine
1827         ok (!lstrcmpiW(tempbufW, folderdisplayW) ||
1828             broken(!lstrcmpiW(tempbufW, foldernameW)), /* W2K */
1829             "GetDisplayNameOf returned %s\n", wine_dbgstr_w(tempbufW));
1830     }
1831
1832     /* editing name is also read from the resource */
1833     hr = IShellFolder_GetDisplayNameOf(testIShellFolder, newPIDL, SHGDN_INFOLDER|SHGDN_FOREDITING, &strret);
1834     ok(hr == S_OK, "GetDisplayNameOf failed %08x\n", hr);
1835
1836     if (hr == S_OK && pStrRetToBufW)
1837     {
1838         hr = pStrRetToBufW(&strret, newPIDL, tempbufW, sizeof(tempbufW)/sizeof(WCHAR));
1839         ok (hr == S_OK, "StrRetToBufW failed! hr = %08x\n", hr);
1840         todo_wine
1841         ok (!lstrcmpiW(tempbufW, folderdisplayW) ||
1842             broken(!lstrcmpiW(tempbufW, foldernameW)), /* W2K */
1843             "GetDisplayNameOf returned %s\n", wine_dbgstr_w(tempbufW));
1844     }
1845
1846     /* parsing name is unchanged */
1847     hr = IShellFolder_GetDisplayNameOf(testIShellFolder, newPIDL, SHGDN_INFOLDER|SHGDN_FORPARSING, &strret);
1848     ok(hr == S_OK, "GetDisplayNameOf failed %08x\n", hr);
1849
1850     if (hr == S_OK && pStrRetToBufW)
1851     {
1852         hr = pStrRetToBufW(&strret, newPIDL, tempbufW, sizeof(tempbufW)/sizeof(WCHAR));
1853         ok (hr == S_OK, "StrRetToBufW failed! hr = %08x\n", hr);
1854         ok (!lstrcmpiW(tempbufW, foldernameW), "GetDisplayNameOf returned %s\n", wine_dbgstr_w(tempbufW));
1855     }
1856
1857     IShellFolder_Release(IDesktopFolder);
1858     IShellFolder_Release(testIShellFolder);
1859
1860     IMalloc_Free(ppM, newPIDL);
1861
1862 cleanup:
1863     DeleteFileA(".\\testfolder\\desktop.ini");
1864     SetFileAttributesA(".\\testfolder", GetFileAttributesA(".\\testfolder")&~FILE_ATTRIBUTE_SYSTEM);
1865     RemoveDirectoryA(".\\testfolder");
1866 }
1867
1868 static void test_SHCreateShellItem(void)
1869 {
1870     IShellItem *shellitem, *shellitem2;
1871     IPersistIDList *persistidl;
1872     LPITEMIDLIST pidl_cwd=NULL, pidl_testfile, pidl_abstestfile, pidl_test;
1873     HRESULT ret;
1874     char curdirA[MAX_PATH];
1875     WCHAR curdirW[MAX_PATH];
1876     WCHAR fnbufW[MAX_PATH];
1877     IShellFolder *desktopfolder=NULL, *currentfolder=NULL;
1878     static WCHAR testfileW[] = {'t','e','s','t','f','i','l','e',0};
1879
1880     GetCurrentDirectoryA(MAX_PATH, curdirA);
1881
1882     if (!pSHCreateShellItem)
1883     {
1884         win_skip("SHCreateShellItem isn't available\n");
1885         return;
1886     }
1887
1888     if (!lstrlenA(curdirA))
1889     {
1890         win_skip("GetCurrentDirectoryA returned empty string, skipping test_SHCreateShellItem\n");
1891         return;
1892     }
1893
1894     MultiByteToWideChar(CP_ACP, 0, curdirA, -1, curdirW, MAX_PATH);
1895
1896     ret = SHGetDesktopFolder(&desktopfolder);
1897     ok(SUCCEEDED(ret), "SHGetShellFolder returned %x\n", ret);
1898
1899     ret = IShellFolder_ParseDisplayName(desktopfolder, NULL, NULL, curdirW, NULL, &pidl_cwd, NULL);
1900     ok(SUCCEEDED(ret), "ParseDisplayName returned %x\n", ret);
1901
1902     ret = IShellFolder_BindToObject(desktopfolder, pidl_cwd, NULL, &IID_IShellFolder, (void**)&currentfolder);
1903     ok(SUCCEEDED(ret), "BindToObject returned %x\n", ret);
1904
1905     CreateTestFile(".\\testfile");
1906
1907     ret = IShellFolder_ParseDisplayName(currentfolder, NULL, NULL, testfileW, NULL, &pidl_testfile, NULL);
1908     ok(SUCCEEDED(ret), "ParseDisplayName returned %x\n", ret);
1909
1910     pidl_abstestfile = pILCombine(pidl_cwd, pidl_testfile);
1911
1912     ret = pSHCreateShellItem(NULL, NULL, NULL, &shellitem);
1913     ok(ret == E_INVALIDARG, "SHCreateShellItem returned %x\n", ret);
1914
1915     if (0) /* crashes on Windows XP */
1916     {
1917         pSHCreateShellItem(NULL, NULL, pidl_cwd, NULL);
1918         pSHCreateShellItem(pidl_cwd, NULL, NULL, &shellitem);
1919         pSHCreateShellItem(NULL, currentfolder, NULL, &shellitem);
1920         pSHCreateShellItem(pidl_cwd, currentfolder, NULL, &shellitem);
1921     }
1922
1923     ret = pSHCreateShellItem(NULL, NULL, pidl_cwd, &shellitem);
1924     ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret);
1925     if (SUCCEEDED(ret))
1926     {
1927         ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
1928         ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
1929         if (SUCCEEDED(ret))
1930         {
1931             ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
1932             ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
1933             if (SUCCEEDED(ret))
1934             {
1935                 ok(ILIsEqual(pidl_cwd, pidl_test), "id lists are not equal\n");
1936                 pILFree(pidl_test);
1937             }
1938             IPersistIDList_Release(persistidl);
1939         }
1940         IShellItem_Release(shellitem);
1941     }
1942
1943     ret = pSHCreateShellItem(pidl_cwd, NULL, pidl_testfile, &shellitem);
1944     ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret);
1945     if (SUCCEEDED(ret))
1946     {
1947         ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
1948         ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
1949         if (SUCCEEDED(ret))
1950         {
1951             ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
1952             ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
1953             if (SUCCEEDED(ret))
1954             {
1955                 ok(ILIsEqual(pidl_abstestfile, pidl_test), "id lists are not equal\n");
1956                 pILFree(pidl_test);
1957             }
1958             IPersistIDList_Release(persistidl);
1959         }
1960
1961         ret = IShellItem_GetParent(shellitem, &shellitem2);
1962         ok(SUCCEEDED(ret), "GetParent returned %x\n", ret);
1963         if (SUCCEEDED(ret))
1964         {
1965             ret = IShellItem_QueryInterface(shellitem2, &IID_IPersistIDList, (void**)&persistidl);
1966             ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
1967             if (SUCCEEDED(ret))
1968             {
1969                 ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
1970                 ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
1971                 if (SUCCEEDED(ret))
1972                 {
1973                     ok(ILIsEqual(pidl_cwd, pidl_test), "id lists are not equal\n");
1974                     pILFree(pidl_test);
1975                 }
1976                 IPersistIDList_Release(persistidl);
1977             }
1978             IShellItem_Release(shellitem2);
1979         }
1980
1981         IShellItem_Release(shellitem);
1982     }
1983
1984     ret = pSHCreateShellItem(NULL, currentfolder, pidl_testfile, &shellitem);
1985     ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret);
1986     if (SUCCEEDED(ret))
1987     {
1988         ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
1989         ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
1990         if (SUCCEEDED(ret))
1991         {
1992             ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
1993             ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
1994             if (SUCCEEDED(ret))
1995             {
1996                 ok(ILIsEqual(pidl_abstestfile, pidl_test), "id lists are not equal\n");
1997                 pILFree(pidl_test);
1998             }
1999             IPersistIDList_Release(persistidl);
2000         }
2001         IShellItem_Release(shellitem);
2002     }
2003
2004     /* if a parent pidl and shellfolder are specified, the shellfolder is ignored */
2005     ret = pSHCreateShellItem(pidl_cwd, desktopfolder, pidl_testfile, &shellitem);
2006     ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret);
2007     if (SUCCEEDED(ret))
2008     {
2009         ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
2010         ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
2011         if (SUCCEEDED(ret))
2012         {
2013             ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
2014             ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
2015             if (SUCCEEDED(ret))
2016             {
2017                 ok(ILIsEqual(pidl_abstestfile, pidl_test), "id lists are not equal\n");
2018                 pILFree(pidl_test);
2019             }
2020             IPersistIDList_Release(persistidl);
2021         }
2022         IShellItem_Release(shellitem);
2023     }
2024
2025     ret = pSHCreateShellItem(NULL, desktopfolder, pidl_testfile, &shellitem);
2026     ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret);
2027     if (SUCCEEDED(ret))
2028     {
2029         ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
2030         ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
2031         if (SUCCEEDED(ret))
2032         {
2033             ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
2034             ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
2035             if (SUCCEEDED(ret))
2036             {
2037                 ok(ILIsEqual(pidl_testfile, pidl_test), "id lists are not equal\n");
2038                 pILFree(pidl_test);
2039             }
2040             IPersistIDList_Release(persistidl);
2041         }
2042         IShellItem_Release(shellitem);
2043     }
2044
2045     /* SHCreateItemFromParsingName */
2046     if(pSHCreateItemFromParsingName)
2047     {
2048         if(0)
2049         {
2050             /* Crashes under windows 7 */
2051             ret = pSHCreateItemFromParsingName(NULL, NULL, &IID_IShellItem, NULL);
2052         }
2053
2054         shellitem = (void*)0xdeadbeef;
2055         ret = pSHCreateItemFromParsingName(NULL, NULL, &IID_IShellItem, (void**)&shellitem);
2056         ok(ret == E_INVALIDARG, "SHCreateItemFromParsingName returned %x\n", ret);
2057         ok(shellitem == NULL, "shellitem was %p.\n", shellitem);
2058
2059         ret = pSHCreateItemFromParsingName(testfileW, NULL, &IID_IShellItem, (void**)&shellitem);
2060         ok(ret == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
2061            "SHCreateItemFromParsingName returned %x\n", ret);
2062         if(SUCCEEDED(ret)) IShellItem_Release(shellitem);
2063
2064         lstrcpyW(fnbufW, curdirW);
2065         myPathAddBackslashW(fnbufW);
2066         lstrcatW(fnbufW, testfileW);
2067
2068         ret = pSHCreateItemFromParsingName(fnbufW, NULL, &IID_IShellItem, (void**)&shellitem);
2069         ok(ret == S_OK, "SHCreateItemFromParsingName returned %x\n", ret);
2070         if(SUCCEEDED(ret))
2071         {
2072             LPWSTR tmp_fname;
2073             ret = IShellItem_GetDisplayName(shellitem, SIGDN_FILESYSPATH, &tmp_fname);
2074             ok(ret == S_OK, "GetDisplayName returned %x\n", ret);
2075             if(SUCCEEDED(ret))
2076             {
2077                 ok(!lstrcmpW(fnbufW, tmp_fname), "strings not equal\n");
2078                 CoTaskMemFree(tmp_fname);
2079             }
2080             IShellItem_Release(shellitem);
2081         }
2082     }
2083     else
2084         win_skip("No SHCreateItemFromParsingName\n");
2085
2086
2087     /* SHCreateItemFromIDList */
2088     if(pSHCreateItemFromIDList)
2089     {
2090         if(0)
2091         {
2092             /* Crashes under win7 */
2093             ret = pSHCreateItemFromIDList(NULL, &IID_IShellItem, NULL);
2094         }
2095
2096         ret = pSHCreateItemFromIDList(NULL, &IID_IShellItem, (void**)&shellitem);
2097         ok(ret == E_INVALIDARG, "SHCreateItemFromIDList returned %x\n", ret);
2098
2099         ret = pSHCreateItemFromIDList(pidl_cwd, &IID_IShellItem, (void**)&shellitem);
2100         ok(ret == S_OK, "SHCreateItemFromIDList returned %x\n", ret);
2101         if (SUCCEEDED(ret))
2102         {
2103             ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
2104             ok(ret == S_OK, "QueryInterface returned %x\n", ret);
2105             if (SUCCEEDED(ret))
2106             {
2107                 ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
2108                 ok(ret == S_OK, "GetIDList returned %x\n", ret);
2109                 if (SUCCEEDED(ret))
2110                 {
2111                     ok(ILIsEqual(pidl_cwd, pidl_test), "id lists are not equal\n");
2112                     pILFree(pidl_test);
2113                 }
2114                 IPersistIDList_Release(persistidl);
2115             }
2116             IShellItem_Release(shellitem);
2117         }
2118
2119         ret = pSHCreateItemFromIDList(pidl_testfile, &IID_IShellItem, (void**)&shellitem);
2120         ok(ret == S_OK, "SHCreateItemFromIDList returned %x\n", ret);
2121         if (SUCCEEDED(ret))
2122         {
2123             ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
2124             ok(ret == S_OK, "QueryInterface returned %x\n", ret);
2125             if (SUCCEEDED(ret))
2126             {
2127                 ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
2128                 ok(ret == S_OK, "GetIDList returned %x\n", ret);
2129                 if (SUCCEEDED(ret))
2130                 {
2131                     ok(ILIsEqual(pidl_testfile, pidl_test), "id lists are not equal\n");
2132                     pILFree(pidl_test);
2133                 }
2134                 IPersistIDList_Release(persistidl);
2135             }
2136             IShellItem_Release(shellitem);
2137         }
2138     }
2139     else
2140         win_skip("No SHCreateItemFromIDList\n");
2141
2142     DeleteFileA(".\\testfile");
2143     pILFree(pidl_abstestfile);
2144     pILFree(pidl_testfile);
2145     pILFree(pidl_cwd);
2146     IShellFolder_Release(currentfolder);
2147     IShellFolder_Release(desktopfolder);
2148 }
2149
2150 static void test_SHGetNameFromIDList(void)
2151 {
2152     IShellItem *shellitem;
2153     LPITEMIDLIST pidl;
2154     LPWSTR name_string;
2155     HRESULT hres;
2156     UINT i;
2157     static const DWORD flags[] = {
2158         SIGDN_NORMALDISPLAY, SIGDN_PARENTRELATIVEPARSING,
2159         SIGDN_DESKTOPABSOLUTEPARSING,SIGDN_PARENTRELATIVEEDITING,
2160         SIGDN_DESKTOPABSOLUTEEDITING, /*SIGDN_FILESYSPATH, SIGDN_URL, */
2161         SIGDN_PARENTRELATIVEFORADDRESSBAR,SIGDN_PARENTRELATIVE, -1234};
2162
2163     if(!pSHGetNameFromIDList)
2164     {
2165         win_skip("SHGetNameFromIDList missing.\n");
2166         return;
2167     }
2168
2169     /* These should be available on any platform that passed the above test. */
2170     ok(pSHCreateShellItem != NULL, "SHCreateShellItem missing.\n");
2171     ok(pSHBindToParent != NULL, "SHBindToParent missing.\n");
2172     ok(pSHGetSpecialFolderLocation != NULL, "SHGetSpecialFolderLocation missing.\n");
2173     ok(pStrRetToBufW != NULL, "StrRetToBufW missing.\n");
2174
2175     if(0)
2176     {
2177         /* Crashes under win7 */
2178         hres = pSHGetNameFromIDList(NULL, 0, NULL);
2179     }
2180
2181     hres = pSHGetNameFromIDList(NULL, 0, &name_string);
2182     ok(hres == E_INVALIDARG, "Got 0x%08x\n", hres);
2183
2184     /* Test the desktop */
2185     hres = pSHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, &pidl);
2186     ok(hres == S_OK, "Got 0x%08x\n", hres);
2187     hres = pSHCreateShellItem(NULL, NULL, pidl, &shellitem);
2188     ok(hres == S_OK, "Got 0x%08x\n", hres);
2189     if(SUCCEEDED(hres))
2190     {
2191         WCHAR *nameSI, *nameSH;
2192         WCHAR buf[MAX_PATH];
2193         HRESULT hrSI, hrSH, hrSF;
2194         STRRET strret;
2195         IShellFolder *psf;
2196         BOOL res;
2197
2198         SHGetDesktopFolder(&psf);
2199         for(i = 0; flags[i] != -1234; i++)
2200         {
2201             hrSI = IShellItem_GetDisplayName(shellitem, flags[i], &nameSI);
2202             ok(hrSI == S_OK, "Got 0x%08x\n", hrSI);
2203             hrSH = pSHGetNameFromIDList(pidl, flags[i], &nameSH);
2204             ok(hrSH == S_OK, "Got 0x%08x\n", hrSH);
2205             hrSF = IShellFolder_GetDisplayNameOf(psf, pidl, flags[i] & 0xffff, &strret);
2206             ok(hrSF == S_OK, "Got 0x%08x\n", hrSF);
2207
2208             if(SUCCEEDED(hrSI) && SUCCEEDED(hrSH))
2209                 ok(!lstrcmpW(nameSI, nameSH), "Strings differ.\n");
2210
2211             if(SUCCEEDED(hrSF))
2212             {
2213                 pStrRetToBufW(&strret, NULL, buf, MAX_PATH);
2214                 if(SUCCEEDED(hrSI))
2215                     ok(!lstrcmpW(nameSI, buf), "Strings differ.\n");
2216                 if(SUCCEEDED(hrSF))
2217                     ok(!lstrcmpW(nameSI, buf), "Strings differ.\n");
2218             }
2219             if(SUCCEEDED(hrSI)) CoTaskMemFree(nameSI);
2220             if(SUCCEEDED(hrSH)) CoTaskMemFree(nameSH);
2221         }
2222         IShellFolder_Release(psf);
2223
2224         hrSI = pSHGetNameFromIDList(pidl, SIGDN_FILESYSPATH, &nameSI);
2225         ok(hrSI == S_OK, "Got 0x%08x\n", hrSI);
2226         res = SHGetPathFromIDListW(pidl, buf);
2227         ok(res == TRUE, "Got %d\n", res);
2228         if(SUCCEEDED(hrSI) && res)
2229             ok(!lstrcmpW(nameSI, buf), "Strings differ.\n");
2230         if(SUCCEEDED(hrSI)) CoTaskMemFree(nameSI);
2231
2232         hres = pSHGetNameFromIDList(pidl, SIGDN_URL, &name_string);
2233         todo_wine ok(hres == S_OK, "Got 0x%08x\n", hres);
2234         if(SUCCEEDED(hres)) CoTaskMemFree(name_string);
2235
2236         IShellItem_Release(shellitem);
2237     }
2238     pILFree(pidl);
2239
2240     /* Test the control panel */
2241     hres = pSHGetSpecialFolderLocation(NULL, CSIDL_CONTROLS, &pidl);
2242     ok(hres == S_OK, "Got 0x%08x\n", hres);
2243     hres = pSHCreateShellItem(NULL, NULL, pidl, &shellitem);
2244     ok(hres == S_OK, "Got 0x%08x\n", hres);
2245     if(SUCCEEDED(hres))
2246     {
2247         WCHAR *nameSI, *nameSH;
2248         WCHAR buf[MAX_PATH];
2249         HRESULT hrSI, hrSH, hrSF;
2250         STRRET strret;
2251         IShellFolder *psf;
2252         BOOL res;
2253
2254         SHGetDesktopFolder(&psf);
2255         for(i = 0; flags[i] != -1234; i++)
2256         {
2257             hrSI = IShellItem_GetDisplayName(shellitem, flags[i], &nameSI);
2258             ok(hrSI == S_OK, "Got 0x%08x\n", hrSI);
2259             hrSH = pSHGetNameFromIDList(pidl, flags[i], &nameSH);
2260             ok(hrSH == S_OK, "Got 0x%08x\n", hrSH);
2261             hrSF = IShellFolder_GetDisplayNameOf(psf, pidl, flags[i] & 0xffff, &strret);
2262             ok(hrSF == S_OK, "Got 0x%08x\n", hrSF);
2263
2264             if(SUCCEEDED(hrSI) && SUCCEEDED(hrSH))
2265                 ok(!lstrcmpW(nameSI, nameSH), "Strings differ.\n");
2266
2267             if(SUCCEEDED(hrSF))
2268             {
2269                 pStrRetToBufW(&strret, NULL, buf, MAX_PATH);
2270                 if(SUCCEEDED(hrSI))
2271                     ok(!lstrcmpW(nameSI, buf), "Strings differ.\n");
2272                 if(SUCCEEDED(hrSF))
2273                     ok(!lstrcmpW(nameSI, buf), "Strings differ.\n");
2274             }
2275             if(SUCCEEDED(hrSI)) CoTaskMemFree(nameSI);
2276             if(SUCCEEDED(hrSH)) CoTaskMemFree(nameSH);
2277         }
2278         IShellFolder_Release(psf);
2279
2280         hrSI = pSHGetNameFromIDList(pidl, SIGDN_FILESYSPATH, &nameSI);
2281         ok(hrSI == E_INVALIDARG, "Got 0x%08x\n", hrSI);
2282         res = SHGetPathFromIDListW(pidl, buf);
2283         ok(res == FALSE, "Got %d\n", res);
2284         if(SUCCEEDED(hrSI) && res)
2285             ok(!lstrcmpW(nameSI, buf), "Strings differ.\n");
2286         if(SUCCEEDED(hrSI)) CoTaskMemFree(nameSI);
2287
2288         hres = pSHGetNameFromIDList(pidl, SIGDN_URL, &name_string);
2289         todo_wine ok(hres == E_NOTIMPL /* Win7 */ || hres == S_OK /* Vista */,
2290                      "Got 0x%08x\n", hres);
2291         if(SUCCEEDED(hres)) CoTaskMemFree(name_string);
2292
2293         IShellItem_Release(shellitem);
2294     }
2295     pILFree(pidl);
2296 }
2297
2298 static void test_SHGetItemFromDataObject(void)
2299 {
2300     IShellFolder *psfdesktop;
2301     IShellItem *psi;
2302     IShellView *psv;
2303     HRESULT hres;
2304
2305     if(!pSHGetItemFromDataObject)
2306     {
2307         win_skip("No SHGetItemFromDataObject.\n");
2308         return;
2309     }
2310
2311     if(0)
2312     {
2313         /* Crashes under win7 */
2314         hres = pSHGetItemFromDataObject(NULL, 0, &IID_IShellItem, NULL);
2315     }
2316
2317     hres = pSHGetItemFromDataObject(NULL, 0, &IID_IShellItem, (void**)&psv);
2318     ok(hres == E_INVALIDARG, "got 0x%08x\n", hres);
2319
2320     SHGetDesktopFolder(&psfdesktop);
2321
2322     hres = IShellFolder_CreateViewObject(psfdesktop, NULL, &IID_IShellView, (void**)&psv);
2323     ok(hres == S_OK, "got 0x%08x\n", hres);
2324     if(SUCCEEDED(hres))
2325     {
2326         IEnumIDList *peidl;
2327         IDataObject *pdo;
2328         SHCONTF enum_flags;
2329
2330         enum_flags = SHCONTF_NONFOLDERS | SHCONTF_FOLDERS | SHCONTF_INCLUDEHIDDEN;
2331         hres = IShellFolder_EnumObjects(psfdesktop, NULL, enum_flags, &peidl);
2332         ok(hres == S_OK, "got 0x%08x\n", hres);
2333         if(SUCCEEDED(hres))
2334         {
2335             LPITEMIDLIST apidl[5];
2336             UINT count = 0, i;
2337
2338             for(count = 0; count < 5; count++)
2339                 if(IEnumIDList_Next(peidl, 1, &apidl[count], NULL) != S_OK)
2340                     break;
2341
2342             if(count)
2343             {
2344                 hres = IShellFolder_GetUIObjectOf(psfdesktop, NULL, 1, (LPCITEMIDLIST*)apidl,
2345                                                   &IID_IDataObject, NULL, (void**)&pdo);
2346                 ok(hres == S_OK, "got 0x%08x\n", hres);
2347                 if(SUCCEEDED(hres))
2348                 {
2349                     hres = pSHGetItemFromDataObject(pdo, DOGIF_DEFAULT, &IID_IShellItem, (void**)&psi);
2350                     ok(hres == S_OK, "got 0x%08x\n", hres);
2351                     if(SUCCEEDED(hres)) IShellItem_Release(psi);
2352                     hres = pSHGetItemFromDataObject(pdo, DOGIF_TRAVERSE_LINK, &IID_IShellItem, (void**)&psi);
2353                     ok(hres == S_OK, "got 0x%08x\n", hres);
2354                     if(SUCCEEDED(hres)) IShellItem_Release(psi);
2355                     hres = pSHGetItemFromDataObject(pdo, DOGIF_NO_HDROP, &IID_IShellItem, (void**)&psi);
2356                     ok(hres == S_OK, "got 0x%08x\n", hres);
2357                     if(SUCCEEDED(hres)) IShellItem_Release(psi);
2358                     hres = pSHGetItemFromDataObject(pdo, DOGIF_NO_URL, &IID_IShellItem, (void**)&psi);
2359                     ok(hres == S_OK, "got 0x%08x\n", hres);
2360                     if(SUCCEEDED(hres)) IShellItem_Release(psi);
2361                     hres = pSHGetItemFromDataObject(pdo, DOGIF_ONLY_IF_ONE, &IID_IShellItem, (void**)&psi);
2362                     ok(hres == S_OK, "got 0x%08x\n", hres);
2363                     if(SUCCEEDED(hres)) IShellItem_Release(psi);
2364
2365                     IDataObject_Release(pdo);
2366                 }
2367             }
2368             else
2369                 skip("No file(s) found - skipping single-file test.\n");
2370
2371             if(count > 1)
2372             {
2373                 hres = IShellFolder_GetUIObjectOf(psfdesktop, NULL, count, (LPCITEMIDLIST*)apidl,
2374                                                   &IID_IDataObject, NULL, (void**)&pdo);
2375                 ok(hres == S_OK, "got 0x%08x\n", hres);
2376                 if(SUCCEEDED(hres))
2377                 {
2378                     hres = pSHGetItemFromDataObject(pdo, DOGIF_DEFAULT, &IID_IShellItem, (void**)&psi);
2379                     ok(hres == S_OK, "got 0x%08x\n", hres);
2380                     if(SUCCEEDED(hres)) IShellItem_Release(psi);
2381                     hres = pSHGetItemFromDataObject(pdo, DOGIF_TRAVERSE_LINK, &IID_IShellItem, (void**)&psi);
2382                     ok(hres == S_OK, "got 0x%08x\n", hres);
2383                     if(SUCCEEDED(hres)) IShellItem_Release(psi);
2384                     hres = pSHGetItemFromDataObject(pdo, DOGIF_NO_HDROP, &IID_IShellItem, (void**)&psi);
2385                     ok(hres == S_OK, "got 0x%08x\n", hres);
2386                     if(SUCCEEDED(hres)) IShellItem_Release(psi);
2387                     hres = pSHGetItemFromDataObject(pdo, DOGIF_NO_URL, &IID_IShellItem, (void**)&psi);
2388                     ok(hres == S_OK, "got 0x%08x\n", hres);
2389                     if(SUCCEEDED(hres)) IShellItem_Release(psi);
2390                     hres = pSHGetItemFromDataObject(pdo, DOGIF_ONLY_IF_ONE, &IID_IShellItem, (void**)&psi);
2391                     ok(hres == E_FAIL, "got 0x%08x\n", hres);
2392                     if(SUCCEEDED(hres)) IShellItem_Release(psi);
2393
2394                     IDataObject_Release(pdo);
2395                 }
2396             }
2397             else
2398                 skip("zero or one file found - skipping multi-file test.\n");
2399
2400             for(i = 0; i < count; i++)
2401                 pILFree(apidl[i]);
2402
2403             IEnumIDList_Release(peidl);
2404         }
2405
2406         IShellView_Release(psv);
2407     }
2408
2409     IShellFolder_Release(psfdesktop);
2410 }
2411
2412 static void test_SHParseDisplayName(void)
2413 {
2414     LPITEMIDLIST pidl1, pidl2;
2415     IShellFolder *desktop;
2416     WCHAR dirW[MAX_PATH];
2417     WCHAR nameW[10];
2418     HRESULT hr;
2419     BOOL ret;
2420
2421     if (!pSHParseDisplayName)
2422     {
2423         win_skip("SHParseDisplayName isn't available\n");
2424         return;
2425     }
2426
2427 if (0)
2428 {
2429     /* crashes on native */
2430     hr = pSHParseDisplayName(NULL, NULL, NULL, 0, NULL);
2431     nameW[0] = 0;
2432     hr = pSHParseDisplayName(nameW, NULL, NULL, 0, NULL);
2433 }
2434
2435     pidl1 = (LPITEMIDLIST)0xdeadbeef;
2436     hr = pSHParseDisplayName(NULL, NULL, &pidl1, 0, NULL);
2437     ok(broken(hr == E_OUTOFMEMORY) /* < Vista */ ||
2438        hr == E_INVALIDARG, "failed %08x\n", hr);
2439     ok(pidl1 == 0, "expected null ptr, got %p\n", pidl1);
2440
2441     /* dummy name */
2442     nameW[0] = 0;
2443     hr = pSHParseDisplayName(nameW, NULL, &pidl1, 0, NULL);
2444     ok(hr == S_OK, "failed %08x\n", hr);
2445     hr = SHGetDesktopFolder(&desktop);
2446     ok(hr == S_OK, "failed %08x\n", hr);
2447     hr = IShellFolder_ParseDisplayName(desktop, NULL, NULL, nameW, NULL, &pidl2, NULL);
2448     ok(hr == S_OK, "failed %08x\n", hr);
2449     ret = pILIsEqual(pidl1, pidl2);
2450     ok(ret == TRUE, "expected equal idls\n");
2451     pILFree(pidl1);
2452     pILFree(pidl2);
2453
2454     /* with path */
2455     GetWindowsDirectoryW( dirW, MAX_PATH );
2456
2457     hr = pSHParseDisplayName(dirW, NULL, &pidl1, 0, NULL);
2458     ok(hr == S_OK, "failed %08x\n", hr);
2459     hr = IShellFolder_ParseDisplayName(desktop, NULL, NULL, dirW, NULL, &pidl2, NULL);
2460     ok(hr == S_OK, "failed %08x\n", hr);
2461
2462     ret = pILIsEqual(pidl1, pidl2);
2463     ok(ret == TRUE, "expected equal idls\n");
2464     pILFree(pidl1);
2465     pILFree(pidl2);
2466
2467     IShellFolder_Release(desktop);
2468 }
2469
2470 static void test_desktop_IPersist(void)
2471 {
2472     IShellFolder *desktop;
2473     IPersist *persist;
2474     IPersistFolder2 *ppf2;
2475     CLSID clsid;
2476     HRESULT hr;
2477
2478     hr = SHGetDesktopFolder(&desktop);
2479     ok(hr == S_OK, "failed %08x\n", hr);
2480
2481     hr = IShellFolder_QueryInterface(desktop, &IID_IPersist, (void**)&persist);
2482     ok(hr == S_OK || broken(hr == E_NOINTERFACE) /* NT4, W9X */, "failed %08x\n", hr);
2483
2484     if (hr == S_OK)
2485     {
2486     if (0)
2487     {
2488         /* crashes on native */
2489         hr = IPersist_GetClassID(persist, NULL);
2490     }
2491         memset(&clsid, 0, sizeof(clsid));
2492         hr = IPersist_GetClassID(persist, &clsid);
2493         ok(hr == S_OK, "failed %08x\n", hr);
2494         ok(IsEqualIID(&CLSID_ShellDesktop, &clsid), "Expected CLSID_ShellDesktop\n");
2495         IPersist_Release(persist);
2496     }
2497
2498     hr = IShellFolder_QueryInterface(desktop, &IID_IPersistFolder2, (void**)&ppf2);
2499     ok(hr == S_OK || broken(hr == E_NOINTERFACE) /* pre-Vista */, "failed %08x\n", hr);
2500     if(SUCCEEDED(hr))
2501     {
2502         IPersistFolder *ppf;
2503         LPITEMIDLIST pidl;
2504         hr = IShellFolder_QueryInterface(desktop, &IID_IPersistFolder, (void**)&ppf);
2505         ok(hr == S_OK, "IID_IPersistFolder2 without IID_IPersistFolder.\n");
2506         if(SUCCEEDED(hr))
2507             IPersistFolder_Release(ppf);
2508
2509         todo_wine {
2510             hr = IPersistFolder2_Initialize(ppf2, NULL);
2511             ok(hr == S_OK, "got %08x\n", hr);
2512         }
2513
2514         pidl = NULL;
2515         hr = IPersistFolder2_GetCurFolder(ppf2, &pidl);
2516         ok(hr == S_OK, "got %08x\n", hr);
2517         ok(pidl != NULL, "pidl was NULL.\n");
2518         if(SUCCEEDED(hr)) pILFree(pidl);
2519
2520         IPersistFolder2_Release(ppf2);
2521     }
2522
2523     IShellFolder_Release(desktop);
2524 }
2525
2526 static void test_GetUIObject(void)
2527 {
2528     IShellFolder *psf_desktop;
2529     IContextMenu *pcm;
2530     LPITEMIDLIST pidl;
2531     HRESULT hr;
2532     WCHAR path[MAX_PATH];
2533     const WCHAR filename[] =
2534         {'\\','t','e','s','t','d','i','r','\\','t','e','s','t','1','.','t','x','t',0};
2535
2536     if(!pSHBindToParent)
2537     {
2538         win_skip("SHBindToParent missing.\n");
2539         return;
2540     }
2541
2542     GetCurrentDirectoryW(MAX_PATH, path);
2543     if(!lstrlenW(path))
2544     {
2545         skip("GetCurrentDirectoryW returned an empty string.\n");
2546         return;
2547     }
2548     lstrcatW(path, filename);
2549     SHGetDesktopFolder(&psf_desktop);
2550
2551     CreateFilesFolders();
2552
2553     hr = IShellFolder_ParseDisplayName(psf_desktop, NULL, NULL, path, NULL, &pidl, 0);
2554     ok(hr == S_OK, "Got 0x%08x\n", hr);
2555     if(SUCCEEDED(hr))
2556     {
2557         IShellFolder *psf;
2558         LPCITEMIDLIST pidl_child;
2559         hr = pSHBindToParent(pidl, &IID_IShellFolder, (void**)&psf, &pidl_child);
2560         ok(hr == S_OK, "Got 0x%08x\n", hr);
2561         if(SUCCEEDED(hr))
2562         {
2563             hr = IShellFolder_GetUIObjectOf(psf, NULL, 1, (LPCITEMIDLIST*)&pidl_child,
2564                                             &IID_IContextMenu, NULL, (void**)&pcm);
2565             ok(hr == S_OK, "Got 0x%08x\n", hr);
2566             if(SUCCEEDED(hr))
2567             {
2568                 HMENU hmenu = CreatePopupMenu();
2569                 INT max_id, max_id_check;
2570                 UINT count, i;
2571                 const int id_upper_limit = 32767;
2572                 hr = IContextMenu_QueryContextMenu(pcm, hmenu, 0, 0, id_upper_limit, CMF_NORMAL);
2573                 ok(SUCCEEDED(hr), "Got 0x%08x\n", hr);
2574                 max_id = HRESULT_CODE(hr) - 1; /* returns max_id + 1 */
2575                 ok(max_id <= id_upper_limit, "Got %d\n", max_id);
2576                 count = GetMenuItemCount(hmenu);
2577                 ok(count, "Got %d\n", count);
2578
2579                 max_id_check = 0;
2580                 for(i = 0; i < count; i++)
2581                 {
2582                     MENUITEMINFOA mii;
2583                     INT res;
2584                     ZeroMemory(&mii, sizeof(MENUITEMINFOA));
2585                     mii.cbSize = sizeof(MENUITEMINFOA);
2586                     mii.fMask = MIIM_ID | MIIM_FTYPE;
2587
2588                     SetLastError(0);
2589                     res = GetMenuItemInfoA(hmenu, i, TRUE, &mii);
2590                     ok(res, "Failed (last error: %d).\n", GetLastError());
2591
2592                     ok( (mii.wID <= id_upper_limit) || (mii.fType & MFT_SEPARATOR),
2593                         "Got non-separator ID out of range: %d (type: %x) \n", mii.wID, mii.fType);
2594                     if(!(mii.fType & MFT_SEPARATOR))
2595                         max_id_check = (mii.wID>max_id_check)?mii.wID:max_id_check;
2596                 }
2597                 ok((max_id_check == max_id) ||
2598                    (max_id_check == max_id-1 /* Win 7 */),
2599                    "Not equal (or near equal), got %d and %d\n", max_id_check, max_id);
2600
2601 #define is_win2k() (pSHGetFolderPathA && !pSHGetFolderPathAndSubDirA)
2602
2603                 if(count && !is_win2k())   /* Test is interactive on w2k, so skip */
2604                 {
2605                     CMINVOKECOMMANDINFO cmi;
2606                     ZeroMemory(&cmi, sizeof(CMINVOKECOMMANDINFO));
2607                     cmi.cbSize = sizeof(CMINVOKECOMMANDINFO);
2608
2609                     /* Attempt to execute non-existing command */
2610                     cmi.lpVerb = MAKEINTRESOURCEA(9999);
2611                     hr = IContextMenu_InvokeCommand(pcm, &cmi);
2612                     ok(hr == E_INVALIDARG, "Got 0x%08x\n", hr);
2613
2614                     cmi.lpVerb = "foobar_wine_test";
2615                     hr = IContextMenu_InvokeCommand(pcm, &cmi);
2616                     ok( (hr == E_INVALIDARG) || (hr == E_FAIL /* Win7 */) ||
2617                         (hr == HRESULT_FROM_WIN32(ERROR_NO_ASSOCIATION) /* Vista */),
2618                         "Got 0x%08x\n", hr);
2619                 }
2620 #undef is_win2k
2621
2622                 DestroyMenu(hmenu);
2623                 IContextMenu_Release(pcm);
2624             }
2625             IShellFolder_Release(psf);
2626         }
2627         if(pILFree) pILFree(pidl);
2628     }
2629
2630     IShellFolder_Release(psf_desktop);
2631     Cleanup();
2632 }
2633
2634 #define verify_pidl(i,p) r_verify_pidl(__LINE__, i, p)
2635 static void r_verify_pidl(unsigned l, LPCITEMIDLIST pidl, const WCHAR *path)
2636 {
2637     LPCITEMIDLIST child;
2638     IShellFolder *parent;
2639     STRRET filename;
2640     HRESULT hr;
2641
2642     if(!pSHBindToParent){
2643         win_skip("SHBindToParent is not available, not performing full PIDL verification\n");
2644         if(path)
2645             ok_(__FILE__,l)(pidl != NULL, "Expected PIDL to be non-NULL\n");
2646         else
2647             ok_(__FILE__,l)(pidl == NULL, "Expected PIDL to be NULL\n");
2648         return;
2649     }
2650
2651     if(path){
2652         if(!pidl){
2653             ok_(__FILE__,l)(0, "didn't get expected path (%s), instead: NULL\n", wine_dbgstr_w(path));
2654             return;
2655         }
2656
2657         hr = pSHBindToParent(pidl, &IID_IShellFolder, (LPVOID*)&parent, &child);
2658         ok_(__FILE__,l)(hr == S_OK, "SHBindToParent failed: 0x%08x\n", hr);
2659         if(FAILED(hr))
2660             return;
2661
2662         hr = IShellFolder_GetDisplayNameOf(parent, child, SHGDN_FORPARSING, &filename);
2663         ok_(__FILE__,l)(hr == S_OK, "GetDisplayNameOf failed: 0x%08x\n", hr);
2664         if(FAILED(hr)){
2665             IShellFolder_Release(parent);
2666             return;
2667         }
2668
2669         ok_(__FILE__,l)(filename.uType == STRRET_WSTR, "Got unexpected string type: %d\n", filename.uType);
2670         ok_(__FILE__,l)(lstrcmpW(path, filename.pOleStr) == 0,
2671                 "didn't get expected path (%s), instead: %s\n",
2672                  wine_dbgstr_w(path), wine_dbgstr_w(filename.pOleStr));
2673
2674         IShellFolder_Release(parent);
2675     }else
2676         ok_(__FILE__,l)(pidl == NULL, "Expected PIDL to be NULL\n");
2677 }
2678
2679 static void test_SHSimpleIDListFromPath(void)
2680 {
2681     const WCHAR adirW[] = {'C',':','\\','s','i','d','l','f','p','d','i','r',0};
2682     const CHAR adirA[] = "C:\\sidlfpdir";
2683     BOOL br, is_unicode = !(GetVersion() & 0x80000000);
2684
2685     LPITEMIDLIST pidl = NULL;
2686
2687     if(!pSHSimpleIDListFromPathAW){
2688         win_skip("SHSimpleIDListFromPathAW not available\n");
2689         return;
2690     }
2691
2692     br = CreateDirectoryA(adirA, NULL);
2693     ok(br == TRUE, "CreateDirectory failed: %d\n", GetLastError());
2694
2695     if(is_unicode)
2696         pidl = pSHSimpleIDListFromPathAW(adirW);
2697     else
2698         pidl = pSHSimpleIDListFromPathAW(adirA);
2699     verify_pidl(pidl, adirW);
2700     pILFree(pidl);
2701
2702     br = RemoveDirectoryA(adirA);
2703     ok(br == TRUE, "RemoveDirectory failed: %d\n", GetLastError());
2704
2705     if(is_unicode)
2706         pidl = pSHSimpleIDListFromPathAW(adirW);
2707     else
2708         pidl = pSHSimpleIDListFromPathAW(adirA);
2709     verify_pidl(pidl, adirW);
2710     pILFree(pidl);
2711 }
2712
2713 /* IFileSystemBindData impl */
2714 static HRESULT WINAPI fsbd_QueryInterface(IFileSystemBindData *fsbd,
2715         REFIID riid, void **ppv)
2716 {
2717     if(IsEqualIID(riid, &IID_IFileSystemBindData) ||
2718             IsEqualIID(riid, &IID_IUnknown)){
2719         *ppv = fsbd;
2720         return S_OK;
2721     }
2722     return E_NOINTERFACE;
2723 }
2724
2725 static ULONG WINAPI fsbd_AddRef(IFileSystemBindData *fsbd)
2726 {
2727     return 2;
2728 }
2729
2730 static ULONG WINAPI fsbd_Release(IFileSystemBindData *fsbd)
2731 {
2732     return 1;
2733 }
2734
2735 static HRESULT WINAPI fsbd_SetFindData(IFileSystemBindData *fsbd,
2736         const WIN32_FIND_DATAW *pfd)
2737 {
2738     ok(0, "SetFindData called\n");
2739     return E_NOTIMPL;
2740 }
2741
2742 static HRESULT WINAPI fsbd_GetFindData_nul(IFileSystemBindData *fsbd,
2743         WIN32_FIND_DATAW *pfd)
2744 {
2745     memset(pfd, 0, sizeof(WIN32_FIND_DATAW));
2746     return S_OK;
2747 }
2748
2749 static HRESULT WINAPI fsbd_GetFindData_junk(IFileSystemBindData *fsbd,
2750         WIN32_FIND_DATAW *pfd)
2751 {
2752     memset(pfd, 0xdeadbeef, sizeof(WIN32_FIND_DATAW));
2753     return S_OK;
2754 }
2755
2756 static HRESULT WINAPI fsbd_GetFindData_invalid(IFileSystemBindData *fsbd,
2757         WIN32_FIND_DATAW *pfd)
2758 {
2759     memset(pfd, 0, sizeof(WIN32_FIND_DATAW));
2760     *pfd->cFileName = 'a';
2761     *pfd->cAlternateFileName = 'a';
2762     return S_OK;
2763 }
2764
2765 static HRESULT WINAPI fsbd_GetFindData_valid(IFileSystemBindData *fsbd,
2766         WIN32_FIND_DATAW *pfd)
2767 {
2768     static const WCHAR adirW[] = {'C',':','\\','f','s','b','d','d','i','r',0};
2769     HANDLE handle = FindFirstFileW(adirW, pfd);
2770     FindClose(handle);
2771     return S_OK;
2772 }
2773
2774 static HRESULT WINAPI fsbd_GetFindData_fail(IFileSystemBindData *fsbd,
2775         WIN32_FIND_DATAW *pfd)
2776 {
2777     return E_FAIL;
2778 }
2779
2780 static IFileSystemBindDataVtbl fsbdVtbl = {
2781     fsbd_QueryInterface,
2782     fsbd_AddRef,
2783     fsbd_Release,
2784     fsbd_SetFindData,
2785     NULL
2786 };
2787
2788 static IFileSystemBindData fsbd = { &fsbdVtbl };
2789
2790 static void test_ParseDisplayNamePBC(void)
2791 {
2792     WCHAR wFileSystemBindData[] =
2793         {'F','i','l','e',' ','S','y','s','t','e','m',' ','B','i','n','d',' ','D','a','t','a',0};
2794     WCHAR adirW[] = {'C',':','\\','f','s','b','d','d','i','r',0};
2795     const HRESULT exp_err = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
2796
2797     IShellFolder *psf;
2798     IBindCtx *pbc;
2799     HRESULT hres;
2800     ITEMIDLIST *pidl;
2801
2802     /* Check if we support WCHAR functions */
2803     SetLastError(0xdeadbeef);
2804     lstrcmpiW(adirW, adirW);
2805     if(GetLastError() == ERROR_CALL_NOT_IMPLEMENTED){
2806         win_skip("Most W-calls are not implemented\n");
2807         return;
2808     }
2809
2810     hres = SHGetDesktopFolder(&psf);
2811     ok(hres == S_OK, "SHGetDesktopFolder failed: 0x%08x\n", hres);
2812     if(FAILED(hres)){
2813         win_skip("Failed to get IShellFolder, can't run tests\n");
2814         return;
2815     }
2816
2817     /* fails on unknown dir with no IBindCtx */
2818     hres = IShellFolder_ParseDisplayName(psf, NULL, NULL, adirW, NULL, &pidl, NULL);
2819     ok(hres == exp_err || broken(hres == E_FAIL) /* NT4 */,
2820             "ParseDisplayName failed with wrong error: 0x%08x\n", hres);
2821
2822     /* fails on unknown dir with IBindCtx with no IFileSystemBindData */
2823     hres = CreateBindCtx(0, &pbc);
2824     ok(hres == S_OK, "CreateBindCtx failed: 0x%08x\n", hres);
2825
2826     hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, adirW, NULL, &pidl, NULL);
2827     ok(hres == exp_err || broken(hres == E_FAIL) /* NT4 */,
2828             "ParseDisplayName failed with wrong error: 0x%08x\n", hres);
2829
2830     /* unknown dir with IBindCtx with IFileSystemBindData */
2831     hres = IBindCtx_RegisterObjectParam(pbc, wFileSystemBindData, (IUnknown*)&fsbd);
2832     ok(hres == S_OK, "RegisterObjectParam failed: 0x%08x\n", hres);
2833
2834     /* return E_FAIL from GetFindData */
2835     pidl = (ITEMIDLIST*)0xdeadbeef;
2836     fsbdVtbl.GetFindData = fsbd_GetFindData_fail;
2837     hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, adirW, NULL, &pidl, NULL);
2838     ok(hres == S_OK || broken(hres == E_FAIL) /* NT4 */,
2839             "ParseDisplayName failed: 0x%08x\n", hres);
2840     if(SUCCEEDED(hres)){
2841         verify_pidl(pidl, adirW);
2842         ILFree(pidl);
2843     }
2844
2845     /* set FIND_DATA struct to NULLs */
2846     pidl = (ITEMIDLIST*)0xdeadbeef;
2847     fsbdVtbl.GetFindData = fsbd_GetFindData_nul;
2848     hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, adirW, NULL, &pidl, NULL);
2849     ok(hres == S_OK || broken(hres == E_FAIL) /* NT4 */,
2850             "ParseDisplayName failed: 0x%08x\n", hres);
2851     if(SUCCEEDED(hres)){
2852         verify_pidl(pidl, adirW);
2853         ILFree(pidl);
2854     }
2855
2856     /* set FIND_DATA struct to junk */
2857     pidl = (ITEMIDLIST*)0xdeadbeef;
2858     fsbdVtbl.GetFindData = fsbd_GetFindData_junk;
2859     hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, adirW, NULL, &pidl, NULL);
2860     ok(hres == S_OK || broken(hres == E_FAIL) /* NT4 */,
2861             "ParseDisplayName failed: 0x%08x\n", hres);
2862     if(SUCCEEDED(hres)){
2863         verify_pidl(pidl, adirW);
2864         ILFree(pidl);
2865     }
2866
2867     /* set FIND_DATA struct to invalid data */
2868     pidl = (ITEMIDLIST*)0xdeadbeef;
2869     fsbdVtbl.GetFindData = fsbd_GetFindData_invalid;
2870     hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, adirW, NULL, &pidl, NULL);
2871     ok(hres == S_OK || broken(hres == E_FAIL) /* NT4 */,
2872             "ParseDisplayName failed: 0x%08x\n", hres);
2873     if(SUCCEEDED(hres)){
2874         verify_pidl(pidl, adirW);
2875         ILFree(pidl);
2876     }
2877
2878     /* set FIND_DATA struct to valid data */
2879     pidl = (ITEMIDLIST*)0xdeadbeef;
2880     fsbdVtbl.GetFindData = fsbd_GetFindData_valid;
2881     hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, adirW, NULL, &pidl, NULL);
2882     ok(hres == S_OK || broken(hres == E_FAIL) /* NT4 */,
2883             "ParseDisplayName failed: 0x%08x\n", hres);
2884     if(SUCCEEDED(hres)){
2885         verify_pidl(pidl, adirW);
2886         ILFree(pidl);
2887     }
2888
2889     IBindCtx_Release(pbc);
2890     IShellFolder_Release(psf);
2891 }
2892
2893 START_TEST(shlfolder)
2894 {
2895     init_function_pointers();
2896     /* if OleInitialize doesn't get called, ParseDisplayName returns
2897        CO_E_NOTINITIALIZED for malformed directory names on win2k. */
2898     OleInitialize(NULL);
2899
2900     test_ParseDisplayName();
2901     test_SHParseDisplayName();
2902     test_BindToObject();
2903     test_EnumObjects_and_CompareIDs();
2904     test_GetDisplayName();
2905     test_GetAttributesOf();
2906     test_SHGetPathFromIDList();
2907     test_CallForAttributes();
2908     test_FolderShortcut();
2909     test_ITEMIDLIST_format();
2910     test_SHGetFolderPathAndSubDirA();
2911     test_LocalizedNames();
2912     test_SHCreateShellItem();
2913     test_desktop_IPersist();
2914     test_GetUIObject();
2915     test_SHSimpleIDListFromPath();
2916     test_ParseDisplayNamePBC();
2917     test_SHGetNameFromIDList();
2918     test_SHGetItemFromDataObject();
2919
2920     OleUninitialize();
2921 }