Assorted spelling fixes.
[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 #include <initguid.h>
43 DEFINE_GUID(IID_IParentAndItem, 0xB3A4B685, 0xB685, 0x4805, 0x99,0xD9, 0x5D,0xEA,0xD2,0x87,0x32,0x36);
44
45 static IMalloc *ppM;
46
47 static HRESULT (WINAPI *pSHBindToParent)(LPCITEMIDLIST, REFIID, LPVOID*, LPCITEMIDLIST*);
48 static HRESULT (WINAPI *pSHGetFolderPathA)(HWND, int, HANDLE, DWORD, LPSTR);
49 static HRESULT (WINAPI *pSHGetFolderPathAndSubDirA)(HWND, int, HANDLE, DWORD, LPCSTR, LPSTR);
50 static BOOL (WINAPI *pSHGetPathFromIDListW)(LPCITEMIDLIST,LPWSTR);
51 static HRESULT (WINAPI *pSHGetSpecialFolderLocation)(HWND, int, LPITEMIDLIST *);
52 static BOOL (WINAPI *pSHGetSpecialFolderPathA)(HWND, LPSTR, int, BOOL);
53 static BOOL (WINAPI *pSHGetSpecialFolderPathW)(HWND, LPWSTR, int, BOOL);
54 static HRESULT (WINAPI *pStrRetToBufW)(STRRET*,LPCITEMIDLIST,LPWSTR,UINT);
55 static LPITEMIDLIST (WINAPI *pILFindLastID)(LPCITEMIDLIST);
56 static void (WINAPI *pILFree)(LPITEMIDLIST);
57 static BOOL (WINAPI *pILIsEqual)(LPCITEMIDLIST, LPCITEMIDLIST);
58 static HRESULT (WINAPI *pSHCreateItemFromIDList)(PCIDLIST_ABSOLUTE pidl, REFIID riid, void **ppv);
59 static HRESULT (WINAPI *pSHCreateItemFromParsingName)(PCWSTR,IBindCtx*,REFIID,void**);
60 static HRESULT (WINAPI *pSHCreateShellItem)(LPCITEMIDLIST,IShellFolder*,LPCITEMIDLIST,IShellItem**);
61 static HRESULT (WINAPI *pSHCreateShellItemArray)(LPCITEMIDLIST,IShellFolder*,UINT,LPCITEMIDLIST*,IShellItemArray**);
62 static HRESULT (WINAPI *pSHCreateShellItemArrayFromDataObject)(IDataObject*, REFIID, void **);
63 static HRESULT (WINAPI *pSHCreateShellItemArrayFromShellItem)(IShellItem*, REFIID, void **);
64 static LPITEMIDLIST (WINAPI *pILCombine)(LPCITEMIDLIST,LPCITEMIDLIST);
65 static HRESULT (WINAPI *pSHParseDisplayName)(LPCWSTR,IBindCtx*,LPITEMIDLIST*,SFGAOF,SFGAOF*);
66 static LPITEMIDLIST (WINAPI *pSHSimpleIDListFromPathAW)(LPCVOID);
67 static HRESULT (WINAPI *pSHGetNameFromIDList)(PCIDLIST_ABSOLUTE,SIGDN,PWSTR*);
68 static HRESULT (WINAPI *pSHGetItemFromDataObject)(IDataObject*,DATAOBJ_GET_ITEM_FLAGS,REFIID,void**);
69 static HRESULT (WINAPI *pSHGetIDListFromObject)(IUnknown*, PIDLIST_ABSOLUTE*);
70 static HRESULT (WINAPI *pSHGetItemFromObject)(IUnknown*,REFIID,void**);
71
72 static int strcmp_wa(LPCWSTR strw, const char *stra)
73 {
74     CHAR buf[512];
75     WideCharToMultiByte(CP_ACP, 0, strw, -1, buf, sizeof(buf), NULL, NULL);
76     return lstrcmpA(stra, buf);
77 }
78
79 static void init_function_pointers(void)
80 {
81     HMODULE hmod;
82     HRESULT hr;
83     void *ptr;
84
85     hmod = GetModuleHandleA("shell32.dll");
86
87 #define MAKEFUNC(f) (p##f = (void*)GetProcAddress(hmod, #f))
88     MAKEFUNC(SHBindToParent);
89     MAKEFUNC(SHCreateItemFromIDList);
90     MAKEFUNC(SHCreateItemFromParsingName);
91     MAKEFUNC(SHCreateShellItem);
92     MAKEFUNC(SHCreateShellItemArray);
93     MAKEFUNC(SHCreateShellItemArrayFromDataObject);
94     MAKEFUNC(SHCreateShellItemArrayFromShellItem);
95     MAKEFUNC(SHGetFolderPathA);
96     MAKEFUNC(SHGetFolderPathAndSubDirA);
97     MAKEFUNC(SHGetPathFromIDListW);
98     MAKEFUNC(SHGetSpecialFolderPathA);
99     MAKEFUNC(SHGetSpecialFolderPathW);
100     MAKEFUNC(SHGetSpecialFolderLocation);
101     MAKEFUNC(SHParseDisplayName);
102     MAKEFUNC(SHGetNameFromIDList);
103     MAKEFUNC(SHGetItemFromDataObject);
104     MAKEFUNC(SHGetIDListFromObject);
105     MAKEFUNC(SHGetItemFromObject);
106 #undef MAKEFUNC
107
108 #define MAKEFUNC_ORD(f, ord) (p##f = (void*)GetProcAddress(hmod, (LPSTR)(ord)))
109     MAKEFUNC_ORD(ILFindLastID, 16);
110     MAKEFUNC_ORD(ILIsEqual, 21);
111     MAKEFUNC_ORD(ILCombine, 25);
112     MAKEFUNC_ORD(ILFree, 155);
113     MAKEFUNC_ORD(SHSimpleIDListFromPathAW, 162);
114 #undef MAKEFUNC_ORD
115
116     /* test named exports */
117     ptr = GetProcAddress(hmod, "ILFree");
118     ok(broken(ptr == 0) || ptr != 0, "expected named export for ILFree\n");
119     if (ptr)
120     {
121 #define TESTNAMED(f) \
122     ptr = (void*)GetProcAddress(hmod, #f); \
123     ok(ptr != 0, "expected named export for " #f "\n");
124
125         TESTNAMED(ILAppendID);
126         TESTNAMED(ILClone);
127         TESTNAMED(ILCloneFirst);
128         TESTNAMED(ILCombine);
129         TESTNAMED(ILCreateFromPath);
130         TESTNAMED(ILCreateFromPathA);
131         TESTNAMED(ILCreateFromPathW);
132         TESTNAMED(ILFindChild);
133         TESTNAMED(ILFindLastID);
134         TESTNAMED(ILGetNext);
135         TESTNAMED(ILGetSize);
136         TESTNAMED(ILIsEqual);
137         TESTNAMED(ILIsParent);
138         TESTNAMED(ILRemoveLastID);
139         TESTNAMED(ILSaveToStream);
140 #undef TESTNAMED
141     }
142
143     hmod = GetModuleHandleA("shlwapi.dll");
144     pStrRetToBufW = (void*)GetProcAddress(hmod, "StrRetToBufW");
145
146     hr = SHGetMalloc(&ppM);
147     ok(hr == S_OK, "SHGetMalloc failed %08x\n", hr);
148 }
149
150 static void test_ParseDisplayName(void)
151 {
152     HRESULT hr;
153     IShellFolder *IDesktopFolder;
154     static const char *cNonExistDir1A = "c:\\nonexist_subdir";
155     static const char *cNonExistDir2A = "c:\\\\nonexist_subdir";
156     static const char *cInetTestA = "http:\\yyy";
157     static const char *cInetTest2A = "xx:yyy";
158     DWORD res;
159     WCHAR cTestDirW [MAX_PATH] = {0};
160     ITEMIDLIST *newPIDL;
161     BOOL bRes;
162
163     hr = SHGetDesktopFolder(&IDesktopFolder);
164     ok(hr == S_OK, "Expected SHGetDesktopFolder to return S_OK, got 0x%08x\n", hr);
165     if(hr != S_OK) return;
166
167     /* Tests crash on W2K and below (SHCreateShellItem available as of XP) */
168     if (pSHCreateShellItem)
169     {
170         /* null name and pidl */
171         hr = IShellFolder_ParseDisplayName(IDesktopFolder,
172             NULL, NULL, NULL, NULL, NULL, 0);
173         ok(hr == E_INVALIDARG, "returned %08x, expected E_INVALIDARG\n", hr);
174
175         /* null name */
176         newPIDL = (ITEMIDLIST*)0xdeadbeef;
177         hr = IShellFolder_ParseDisplayName(IDesktopFolder,
178             NULL, NULL, NULL, NULL, &newPIDL, 0);
179         ok(newPIDL == 0, "expected null, got %p\n", newPIDL);
180         ok(hr == E_INVALIDARG, "returned %08x, expected E_INVALIDARG\n", hr);
181     }
182     else
183         win_skip("Tests would crash on W2K and below\n");
184
185     MultiByteToWideChar(CP_ACP, 0, cInetTestA, -1, cTestDirW, MAX_PATH);
186     hr = IShellFolder_ParseDisplayName(IDesktopFolder,
187         NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
188     todo_wine ok(hr == S_OK || broken(hr == E_FAIL) /* NT4 */,
189         "ParseDisplayName returned %08x, expected SUCCESS or E_FAIL\n", hr);
190     if (hr == S_OK)
191     {
192         ok(pILFindLastID(newPIDL)->mkid.abID[0] == 0x61, "Last pidl should be of type "
193            "PT_IESPECIAL1, but is: %02x\n", pILFindLastID(newPIDL)->mkid.abID[0]);
194         IMalloc_Free(ppM, newPIDL);
195     }
196
197     MultiByteToWideChar(CP_ACP, 0, cInetTest2A, -1, cTestDirW, MAX_PATH);
198     hr = IShellFolder_ParseDisplayName(IDesktopFolder,
199         NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
200     todo_wine ok(hr == S_OK || broken(hr == E_FAIL) /* NT4 */,
201         "ParseDisplayName returned %08x, expected SUCCESS or E_FAIL\n", hr);
202     if (hr == S_OK)
203     {
204         ok(pILFindLastID(newPIDL)->mkid.abID[0] == 0x61, "Last pidl should be of type "
205            "PT_IESPECIAL1, but is: %02x\n", pILFindLastID(newPIDL)->mkid.abID[0]);
206         IMalloc_Free(ppM, newPIDL);
207     }
208
209     res = GetFileAttributesA(cNonExistDir1A);
210     if(res != INVALID_FILE_ATTRIBUTES)
211     {
212         skip("Test directory unexpectedly exists\n");
213         goto finished;
214     }
215
216     MultiByteToWideChar(CP_ACP, 0, cNonExistDir1A, -1, cTestDirW, MAX_PATH);
217     hr = IShellFolder_ParseDisplayName(IDesktopFolder, 
218         NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
219     ok((hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) || (hr == E_FAIL), 
220         "ParseDisplayName returned %08x, expected 80070002 or E_FAIL\n", hr);
221
222     res = GetFileAttributesA(cNonExistDir2A);
223     if(res != INVALID_FILE_ATTRIBUTES)
224     {
225         skip("Test directory unexpectedly exists\n");
226         goto finished;
227     }
228
229     MultiByteToWideChar(CP_ACP, 0, cNonExistDir2A, -1, cTestDirW, MAX_PATH);
230     hr = IShellFolder_ParseDisplayName(IDesktopFolder, 
231         NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
232     ok((hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) || (hr == E_FAIL) || (hr == E_INVALIDARG), 
233         "ParseDisplayName returned %08x, expected 80070002, E_FAIL or E_INVALIDARG\n", hr);
234
235     /* I thought that perhaps the DesktopFolder's ParseDisplayName would recognize the
236      * path corresponding to CSIDL_PERSONAL and return a CLSID_MyDocuments PIDL. Turns
237      * out it doesn't. The magic seems to happen in the file dialogs, then. */
238     if (!pSHGetSpecialFolderPathW || !pILFindLastID)
239     {
240         win_skip("SHGetSpecialFolderPathW and/or ILFindLastID are not available\n");
241         goto finished;
242     }
243
244     bRes = pSHGetSpecialFolderPathW(NULL, cTestDirW, CSIDL_PERSONAL, FALSE);
245     ok(bRes, "SHGetSpecialFolderPath(CSIDL_PERSONAL) failed! %u\n", GetLastError());
246     if (!bRes) goto finished;
247
248     hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
249     ok(hr == S_OK, "DesktopFolder->ParseDisplayName failed. hr = %08x.\n", hr);
250     if (hr != S_OK) goto finished;
251
252     ok(pILFindLastID(newPIDL)->mkid.abID[0] == 0x31 ||
253        pILFindLastID(newPIDL)->mkid.abID[0] == 0xb1, /* Win98 */
254        "Last pidl should be of type PT_FOLDER or PT_IESPECIAL2, but is: %02x\n",
255        pILFindLastID(newPIDL)->mkid.abID[0]);
256     IMalloc_Free(ppM, newPIDL);
257     
258 finished:
259     IShellFolder_Release(IDesktopFolder);
260 }
261
262 /* creates a file with the specified name for tests */
263 static void CreateTestFile(const CHAR *name)
264 {
265     HANDLE file;
266     DWORD written;
267
268     file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
269     if (file != INVALID_HANDLE_VALUE)
270     {
271         WriteFile(file, name, strlen(name), &written, NULL);
272         WriteFile(file, "\n", strlen("\n"), &written, NULL);
273         CloseHandle(file);
274     }
275 }
276
277
278 /* initializes the tests */
279 static void CreateFilesFolders(void)
280 {
281     CreateDirectoryA(".\\testdir", NULL);
282     CreateDirectoryA(".\\testdir\\test.txt", NULL);
283     CreateTestFile  (".\\testdir\\test1.txt ");
284     CreateTestFile  (".\\testdir\\test2.txt ");
285     CreateTestFile  (".\\testdir\\test3.txt ");
286     CreateDirectoryA(".\\testdir\\testdir2 ", NULL);
287     CreateDirectoryA(".\\testdir\\testdir2\\subdir", NULL);
288 }
289
290 /* cleans after tests */
291 static void Cleanup(void)
292 {
293     DeleteFileA(".\\testdir\\test1.txt");
294     DeleteFileA(".\\testdir\\test2.txt");
295     DeleteFileA(".\\testdir\\test3.txt");
296     RemoveDirectoryA(".\\testdir\\test.txt");
297     RemoveDirectoryA(".\\testdir\\testdir2\\subdir");
298     RemoveDirectoryA(".\\testdir\\testdir2");
299     RemoveDirectoryA(".\\testdir");
300 }
301
302
303 /* perform test */
304 static void test_EnumObjects(IShellFolder *iFolder)
305 {
306     IEnumIDList *iEnumList;
307     LPITEMIDLIST newPIDL, idlArr[10];
308     ULONG NumPIDLs;
309     int i=0, j;
310     HRESULT hr;
311
312     static const WORD iResults [5][5] =
313     {
314         { 0,-1,-1,-1,-1},
315         { 1, 0,-1,-1,-1},
316         { 1, 1, 0,-1,-1},
317         { 1, 1, 1, 0,-1},
318         { 1, 1, 1, 1, 0}
319     };
320
321 #define SFGAO_testfor SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR | SFGAO_CAPABILITYMASK
322     /* Don't test for SFGAO_HASSUBFOLDER since we return real state and native cached */
323     static const ULONG attrs[5] =
324     {
325         SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR,
326         SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR,
327         SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM,
328         SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM,
329         SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM,
330     };
331
332     hr = IShellFolder_EnumObjects(iFolder, NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN, &iEnumList);
333     ok(hr == S_OK, "EnumObjects failed %08x\n", hr);
334
335     /* This is to show that, contrary to what is said on MSDN, on IEnumIDList::Next,
336      * the filesystem shellfolders return S_OK even if less than 'celt' items are
337      * returned (in contrast to S_FALSE). We have to do it in a loop since WinXP
338      * only ever returns a single entry per call. */
339     while (IEnumIDList_Next(iEnumList, 10-i, &idlArr[i], &NumPIDLs) == S_OK) 
340         i += NumPIDLs;
341     ok (i == 5, "i: %d\n", i);
342
343     hr = IEnumIDList_Release(iEnumList);
344     ok(hr == S_OK, "IEnumIDList_Release failed %08x\n", hr);
345     
346     /* Sort them first in case of wrong order from system */
347     for (i=0;i<5;i++) for (j=0;j<5;j++)
348         if ((SHORT)IShellFolder_CompareIDs(iFolder, 0, idlArr[i], idlArr[j]) < 0)
349         {
350             newPIDL = idlArr[i];
351             idlArr[i] = idlArr[j];
352             idlArr[j] = newPIDL;
353         }
354             
355     for (i=0;i<5;i++) for (j=0;j<5;j++)
356     {
357         hr = IShellFolder_CompareIDs(iFolder, 0, idlArr[i], idlArr[j]);
358         ok(hr == iResults[i][j], "Got %x expected [%d]-[%d]=%x\n", hr, i, j, iResults[i][j]);
359     }
360
361
362     for (i = 0; i < 5; i++)
363     {
364         SFGAOF flags;
365 #define SFGAO_VISTA SFGAO_DROPTARGET | SFGAO_CANLINK | SFGAO_CANCOPY
366         /* Native returns all flags no matter what we ask for */
367         flags = SFGAO_CANCOPY;
368         hr = IShellFolder_GetAttributesOf(iFolder, 1, (LPCITEMIDLIST*)(idlArr + i), &flags);
369         flags &= SFGAO_testfor;
370         ok(hr == S_OK, "GetAttributesOf returns %08x\n", hr);
371         ok(flags == (attrs[i]) ||
372            flags == (attrs[i] & ~SFGAO_FILESYSANCESTOR) || /* Win9x, NT4 */
373            flags == ((attrs[i] & ~SFGAO_CAPABILITYMASK) | SFGAO_VISTA), /* Vista and higher */
374            "GetAttributesOf[%i] got %08x, expected %08x\n", i, flags, attrs[i]);
375
376         flags = SFGAO_testfor;
377         hr = IShellFolder_GetAttributesOf(iFolder, 1, (LPCITEMIDLIST*)(idlArr + i), &flags);
378         flags &= SFGAO_testfor;
379         ok(hr == S_OK, "GetAttributesOf returns %08x\n", hr);
380         ok(flags == attrs[i] ||
381            flags == (attrs[i] & ~SFGAO_FILESYSANCESTOR), /* Win9x, NT4 */
382            "GetAttributesOf[%i] got %08x, expected %08x\n", i, flags, attrs[i]);
383     }
384
385     for (i=0;i<5;i++)
386         IMalloc_Free(ppM, idlArr[i]);
387 }
388
389 static void test_BindToObject(void)
390 {
391     HRESULT hr;
392     UINT cChars;
393     IShellFolder *psfDesktop, *psfChild, *psfMyComputer, *psfSystemDir;
394     SHITEMID emptyitem = { 0, { 0 } };
395     LPITEMIDLIST pidlMyComputer, pidlSystemDir, pidlEmpty = (LPITEMIDLIST)&emptyitem;
396     WCHAR wszSystemDir[MAX_PATH];
397     char szSystemDir[MAX_PATH];
398     WCHAR wszMyComputer[] = { 
399         ':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
400         'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };
401
402     /* The following tests shows that BindToObject should fail with E_INVALIDARG if called
403      * with an empty pidl. This is tested for Desktop, MyComputer and the FS ShellFolder
404      */
405     hr = SHGetDesktopFolder(&psfDesktop);
406     ok (hr == S_OK, "SHGetDesktopFolder failed! hr = %08x\n", hr);
407     if (hr != S_OK) return;
408     
409     hr = IShellFolder_BindToObject(psfDesktop, pidlEmpty, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
410     ok (hr == E_INVALIDARG, "Desktop's BindToObject should fail, when called with empty pidl! hr = %08x\n", hr);
411
412     hr = IShellFolder_BindToObject(psfDesktop, NULL, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
413     ok (hr == E_INVALIDARG, "Desktop's BindToObject should fail, when called with NULL pidl! hr = %08x\n", hr);
414
415     hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyComputer, NULL, &pidlMyComputer, NULL);
416     ok (hr == S_OK, "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08x\n", hr);
417     if (hr != S_OK) {
418         IShellFolder_Release(psfDesktop);
419         return;
420     }
421     
422     hr = IShellFolder_BindToObject(psfDesktop, pidlMyComputer, NULL, &IID_IShellFolder, (LPVOID*)&psfMyComputer);
423     ok (hr == S_OK, "Desktop failed to bind to MyComputer object! hr = %08x\n", hr);
424     IShellFolder_Release(psfDesktop);
425     IMalloc_Free(ppM, pidlMyComputer);
426     if (hr != S_OK) return;
427
428     hr = IShellFolder_BindToObject(psfMyComputer, pidlEmpty, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
429     ok (hr == E_INVALIDARG, "MyComputers's BindToObject should fail, when called with empty pidl! hr = %08x\n", hr);
430
431 if (0)
432 {
433     /* this call segfaults on 98SE */
434     hr = IShellFolder_BindToObject(psfMyComputer, NULL, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
435     ok (hr == E_INVALIDARG, "MyComputers's BindToObject should fail, when called with NULL pidl! hr = %08x\n", hr);
436 }
437
438     cChars = GetSystemDirectoryA(szSystemDir, MAX_PATH);
439     ok (cChars > 0 && cChars < MAX_PATH, "GetSystemDirectoryA failed! LastError: %u\n", GetLastError());
440     if (cChars == 0 || cChars >= MAX_PATH) {
441         IShellFolder_Release(psfMyComputer);
442         return;
443     }
444     MultiByteToWideChar(CP_ACP, 0, szSystemDir, -1, wszSystemDir, MAX_PATH);
445     
446     hr = IShellFolder_ParseDisplayName(psfMyComputer, NULL, NULL, wszSystemDir, NULL, &pidlSystemDir, NULL);
447     ok (hr == S_OK, "MyComputers's ParseDisplayName failed to parse the SystemDirectory! hr = %08x\n", hr);
448     if (hr != S_OK) {
449         IShellFolder_Release(psfMyComputer);
450         return;
451     }
452
453     hr = IShellFolder_BindToObject(psfMyComputer, pidlSystemDir, NULL, &IID_IShellFolder, (LPVOID*)&psfSystemDir);
454     ok (hr == S_OK, "MyComputer failed to bind to a FileSystem ShellFolder! hr = %08x\n", hr);
455     IShellFolder_Release(psfMyComputer);
456     IMalloc_Free(ppM, pidlSystemDir);
457     if (hr != S_OK) return;
458
459     hr = IShellFolder_BindToObject(psfSystemDir, pidlEmpty, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
460     ok (hr == E_INVALIDARG, 
461         "FileSystem ShellFolder's BindToObject should fail, when called with empty pidl! hr = %08x\n", hr);
462
463 if (0)
464 {
465     /* this call segfaults on 98SE */
466     hr = IShellFolder_BindToObject(psfSystemDir, NULL, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
467     ok (hr == E_INVALIDARG,
468         "FileSystem ShellFolder's BindToObject should fail, when called with NULL pidl! hr = %08x\n", hr);
469 }
470
471     IShellFolder_Release(psfSystemDir);
472 }
473
474 /* Based on PathAddBackslashW from dlls/shlwapi/path.c */
475 static LPWSTR myPathAddBackslashW( LPWSTR lpszPath )
476 {
477   size_t iLen;
478
479   if (!lpszPath || (iLen = lstrlenW(lpszPath)) >= MAX_PATH)
480     return NULL;
481
482   if (iLen)
483   {
484     lpszPath += iLen;
485     if (lpszPath[-1] != '\\')
486     {
487       *lpszPath++ = '\\';
488       *lpszPath = '\0';
489     }
490   }
491   return lpszPath;
492 }
493
494 static void test_GetDisplayName(void)
495 {
496     BOOL result;
497     HRESULT hr;
498     HANDLE hTestFile;
499     WCHAR wszTestFile[MAX_PATH], wszTestFile2[MAX_PATH];
500     char szTestFile[MAX_PATH], szTestDir[MAX_PATH];
501     DWORD attr;
502     STRRET strret;
503     LPSHELLFOLDER psfDesktop, psfPersonal;
504     IUnknown *psfFile;
505     SHITEMID emptyitem = { 0, { 0 } };
506     LPITEMIDLIST pidlTestFile, pidlEmpty = (LPITEMIDLIST)&emptyitem;
507     LPCITEMIDLIST pidlLast;
508     static const CHAR szFileName[] = "winetest.foo";
509     static const WCHAR wszFileName[] = { 'w','i','n','e','t','e','s','t','.','f','o','o',0 };
510     static const WCHAR wszDirName[] = { 'w','i','n','e','t','e','s','t',0 };
511
512     /* I'm trying to figure if there is a functional difference between calling
513      * SHGetPathFromIDListW and calling GetDisplayNameOf(SHGDN_FORPARSING) after
514      * binding to the shellfolder. One thing I thought of was that perhaps 
515      * SHGetPathFromIDListW would be able to get the path to a file, which does
516      * not exist anymore, while the other method wouldn't. It turns out there's
517      * no functional difference in this respect.
518      */
519
520     if(!pSHGetSpecialFolderPathA) {
521         win_skip("SHGetSpecialFolderPathA is not available\n");
522         return;
523     }
524
525     /* First creating a directory in MyDocuments and a file in this directory. */
526     result = pSHGetSpecialFolderPathA(NULL, szTestDir, CSIDL_PERSONAL, FALSE);
527     ok(result, "SHGetSpecialFolderPathA failed! Last error: %u\n", GetLastError());
528     if (!result) return;
529
530     /* Use ANSI file functions so this works on Windows 9x */
531     lstrcatA(szTestDir, "\\winetest");
532     CreateDirectoryA(szTestDir, NULL);
533     attr=GetFileAttributesA(szTestDir);
534     if (attr == INVALID_FILE_ATTRIBUTES || !(attr & FILE_ATTRIBUTE_DIRECTORY))
535     {
536         ok(0, "unable to create the '%s' directory\n", szTestDir);
537         return;
538     }
539
540     lstrcpyA(szTestFile, szTestDir);
541     lstrcatA(szTestFile, "\\");
542     lstrcatA(szTestFile, szFileName);
543     hTestFile = CreateFileA(szTestFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
544     ok((hTestFile != INVALID_HANDLE_VALUE), "CreateFileA failed! Last error: %u\n", GetLastError());
545     if (hTestFile == INVALID_HANDLE_VALUE) return;
546     CloseHandle(hTestFile);
547
548     /* Getting an itemidlist for the file. */
549     hr = SHGetDesktopFolder(&psfDesktop);
550     ok(hr == S_OK, "SHGetDesktopFolder failed! hr = %08x\n", hr);
551     if (hr != S_OK) return;
552
553     MultiByteToWideChar(CP_ACP, 0, szTestFile, -1, wszTestFile, MAX_PATH);
554
555     hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszTestFile, NULL, &pidlTestFile, NULL);
556     ok(hr == S_OK, "Desktop->ParseDisplayName failed! hr = %08x\n", hr);
557     if (hr != S_OK) {
558         IShellFolder_Release(psfDesktop);
559         return;
560     }
561
562     pidlLast = pILFindLastID(pidlTestFile);
563     ok(pidlLast->mkid.cb >=76 ||
564         broken(pidlLast->mkid.cb == 28) || /* W2K */
565         broken(pidlLast->mkid.cb == 40), /* Win9x, WinME */
566         "Expected pidl length of at least 76, got %d.\n", pidlLast->mkid.cb);
567     if (pidlLast->mkid.cb >= 28) {
568         ok(!lstrcmpA((CHAR*)&pidlLast->mkid.abID[12], szFileName),
569             "Filename should be stored as ansi-string at this position!\n");
570     }
571     /* WinXP and up store the filenames as both ANSI and UNICODE in the pidls */
572     if (pidlLast->mkid.cb >= 76) {
573         ok(!lstrcmpW((WCHAR*)&pidlLast->mkid.abID[46], wszFileName) ||
574             (pidlLast->mkid.cb >= 94 && !lstrcmpW((WCHAR*)&pidlLast->mkid.abID[64], wszFileName)) ||  /* Vista */
575             (pidlLast->mkid.cb >= 98 && !lstrcmpW((WCHAR*)&pidlLast->mkid.abID[68], wszFileName)), /* Win7 */
576             "Filename should be stored as wchar-string at this position!\n");
577     }
578     
579     /* It seems as if we cannot bind to regular files on windows, but only directories. 
580      */
581     hr = IShellFolder_BindToObject(psfDesktop, pidlTestFile, NULL, &IID_IUnknown, (VOID**)&psfFile);
582     todo_wine
583     ok (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) ||
584         hr == E_NOTIMPL || /* Vista */
585         broken(hr == S_OK), /* Win9x, W2K */
586         "hr = %08x\n", hr);
587     if (hr == S_OK) {
588         IShellFolder_Release(psfFile);
589     }
590
591     if (!pSHBindToParent)
592     {
593         win_skip("SHBindToParent is missing\n");
594         DeleteFileA(szTestFile);
595         RemoveDirectoryA(szTestDir);
596         return;
597     }
598   
599     /* Some tests for IShellFolder::SetNameOf */
600     if (pSHGetFolderPathAndSubDirA)
601     {
602         hr = pSHBindToParent(pidlTestFile, &IID_IShellFolder, (VOID**)&psfPersonal, &pidlLast);
603         ok(hr == S_OK, "SHBindToParent failed! hr = %08x\n", hr);
604         if (hr == S_OK) {
605             /* It's ok to use this fixed path. Call will fail anyway. */
606             WCHAR wszAbsoluteFilename[] = { 'C',':','\\','w','i','n','e','t','e','s','t', 0 };
607             LPITEMIDLIST pidlNew;
608
609             /* The pidl returned through the last parameter of SetNameOf is a simple one. */
610             hr = IShellFolder_SetNameOf(psfPersonal, NULL, pidlLast, wszDirName, SHGDN_NORMAL, &pidlNew);
611             ok (hr == S_OK, "SetNameOf failed! hr = %08x\n", hr);
612             if (hr == S_OK)
613             {
614                 ok (((LPITEMIDLIST)((LPBYTE)pidlNew+pidlNew->mkid.cb))->mkid.cb == 0,
615                     "pidl returned from SetNameOf should be simple!\n");
616
617                 /* Passing an absolute path to SetNameOf fails. The HRESULT code indicates that SetNameOf
618                  * is implemented on top of SHFileOperation in WinXP. */
619                 hr = IShellFolder_SetNameOf(psfPersonal, NULL, pidlNew, wszAbsoluteFilename,
620                         SHGDN_FORPARSING, NULL);
621                 ok (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED), "SetNameOf succeeded! hr = %08x\n", hr);
622
623                 /* Rename the file back to its original name. SetNameOf ignores the fact, that the
624                  * SHGDN flags specify an absolute path. */
625                 hr = IShellFolder_SetNameOf(psfPersonal, NULL, pidlNew, wszFileName, SHGDN_FORPARSING, NULL);
626                 ok (hr == S_OK, "SetNameOf failed! hr = %08x\n", hr);
627
628                 pILFree(pidlNew);
629             }
630
631             IShellFolder_Release(psfPersonal);
632         }
633     }
634     else
635         win_skip("Avoid needs of interaction on Win2k\n");
636
637     /* Deleting the file and the directory */
638     DeleteFileA(szTestFile);
639     RemoveDirectoryA(szTestDir);
640
641     /* SHGetPathFromIDListW still works, although the file is not present anymore. */
642     if (pSHGetPathFromIDListW)
643     {
644         result = pSHGetPathFromIDListW(pidlTestFile, wszTestFile2);
645         ok (result, "SHGetPathFromIDListW failed! Last error: %u\n", GetLastError());
646         ok (!lstrcmpiW(wszTestFile, wszTestFile2), "SHGetPathFromIDListW returns incorrect path!\n");
647     }
648
649     /* SHBindToParent fails, if called with a NULL PIDL. */
650     hr = pSHBindToParent(NULL, &IID_IShellFolder, (VOID**)&psfPersonal, &pidlLast);
651     ok (hr != S_OK, "SHBindToParent(NULL) should fail!\n");
652
653     /* But it succeeds with an empty PIDL. */
654     hr = pSHBindToParent(pidlEmpty, &IID_IShellFolder, (VOID**)&psfPersonal, &pidlLast);
655     ok (hr == S_OK, "SHBindToParent(empty PIDL) should succeed! hr = %08x\n", hr);
656     ok (pidlLast == pidlEmpty, "The last element of an empty PIDL should be the PIDL itself!\n");
657     if (hr == S_OK)
658         IShellFolder_Release(psfPersonal);
659     
660     /* Binding to the folder and querying the display name of the file also works. */
661     hr = pSHBindToParent(pidlTestFile, &IID_IShellFolder, (VOID**)&psfPersonal, &pidlLast); 
662     ok (hr == S_OK, "SHBindToParent failed! hr = %08x\n", hr);
663     if (hr != S_OK) {
664         IShellFolder_Release(psfDesktop);
665         return;
666     }
667
668     /* This test shows that Windows doesn't allocate a new pidlLast, but returns a pointer into 
669      * pidlTestFile (In accordance with MSDN). */
670     ok (pILFindLastID(pidlTestFile) == pidlLast, 
671                                 "SHBindToParent doesn't return the last id of the pidl param!\n");
672     
673     hr = IShellFolder_GetDisplayNameOf(psfPersonal, pidlLast, SHGDN_FORPARSING, &strret);
674     ok (hr == S_OK, "Personal->GetDisplayNameOf failed! hr = %08x\n", hr);
675     if (hr != S_OK) {
676         IShellFolder_Release(psfDesktop);
677         IShellFolder_Release(psfPersonal);
678         return;
679     }
680
681     if (pStrRetToBufW)
682     {
683         hr = pStrRetToBufW(&strret, pidlLast, wszTestFile2, MAX_PATH);
684         ok (hr == S_OK, "StrRetToBufW failed! hr = %08x\n", hr);
685         ok (!lstrcmpiW(wszTestFile, wszTestFile2), "GetDisplayNameOf returns incorrect path!\n");
686     }
687     
688     ILFree(pidlTestFile);
689     IShellFolder_Release(psfDesktop);
690     IShellFolder_Release(psfPersonal);
691 }
692
693 static void test_CallForAttributes(void)
694 {
695     HKEY hKey;
696     LONG lResult;
697     HRESULT hr;
698     DWORD dwSize;
699     LPSHELLFOLDER psfDesktop;
700     LPITEMIDLIST pidlMyDocuments;
701     DWORD dwAttributes, dwCallForAttributes, dwOrigAttributes, dwOrigCallForAttributes;
702     static const WCHAR wszAttributes[] = { 'A','t','t','r','i','b','u','t','e','s',0 };
703     static const WCHAR wszCallForAttributes[] = { 
704         'C','a','l','l','F','o','r','A','t','t','r','i','b','u','t','e','s',0 };
705     static const WCHAR wszMyDocumentsKey[] = {
706         'C','L','S','I','D','\\','{','4','5','0','D','8','F','B','A','-','A','D','2','5','-',
707         '1','1','D','0','-','9','8','A','8','-','0','8','0','0','3','6','1','B','1','1','0','3','}',
708         '\\','S','h','e','l','l','F','o','l','d','e','r',0 };
709     WCHAR wszMyDocuments[] = {
710         ':',':','{','4','5','0','D','8','F','B','A','-','A','D','2','5','-','1','1','D','0','-',
711         '9','8','A','8','-','0','8','0','0','3','6','1','B','1','1','0','3','}',0 };
712     
713     /* For the root of a namespace extension, the attributes are not queried by binding
714      * to the object and calling GetAttributesOf. Instead, the attributes are read from 
715      * the registry value HKCR/CLSID/{...}/ShellFolder/Attributes. This is documented on MSDN.
716      *
717      * The MyDocuments shellfolder on WinXP has a HKCR/CLSID/{...}/ShellFolder/CallForAttributes
718      * value. It seems that if the folder is queried for one of the flags set in CallForAttributes,
719      * the shell does bind to the folder object and calls GetAttributesOf. This is not documented
720      * on MSDN. This test is meant to document the observed behaviour on WinXP SP2.
721      */
722     hr = SHGetDesktopFolder(&psfDesktop);
723     ok (hr == S_OK, "SHGetDesktopFolder failed! hr = %08x\n", hr);
724     if (hr != S_OK) return;
725     
726     hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyDocuments, NULL, 
727                                        &pidlMyDocuments, NULL);
728     ok (hr == S_OK ||
729         broken(hr == E_INVALIDARG), /* Win95, NT4 */
730         "Desktop's ParseDisplayName failed to parse MyDocuments's CLSID! hr = %08x\n", hr);
731     if (hr != S_OK) {
732         IShellFolder_Release(psfDesktop);
733         return;
734     }
735
736     dwAttributes = 0xffffffff;
737     hr = IShellFolder_GetAttributesOf(psfDesktop, 1, 
738                                       (LPCITEMIDLIST*)&pidlMyDocuments, &dwAttributes);
739     ok (hr == S_OK, "Desktop->GetAttributesOf(MyDocuments) failed! hr = %08x\n", hr);
740
741     /* We need the following setup (as observed on WinXP SP2), for the tests to make sense. */
742     ok (dwAttributes & SFGAO_FILESYSTEM, "SFGAO_FILESYSTEM attribute is not set for MyDocuments!\n");
743     ok (!(dwAttributes & SFGAO_ISSLOW), "SFGAO_ISSLOW attribute is set for MyDocuments!\n");
744     ok (!(dwAttributes & SFGAO_GHOSTED), "SFGAO_GHOSTED attribute is set for MyDocuments!\n");
745
746     /* We don't have the MyDocuments shellfolder in wine yet, and thus we don't have the registry
747      * key. So the test will return at this point, if run on wine. 
748      */
749     lResult = RegOpenKeyExW(HKEY_CLASSES_ROOT, wszMyDocumentsKey, 0, KEY_WRITE|KEY_READ, &hKey);
750     ok (lResult == ERROR_SUCCESS ||
751         lResult == ERROR_ACCESS_DENIED,
752         "RegOpenKeyEx failed! result: %08x\n", lResult);
753     if (lResult != ERROR_SUCCESS) {
754         if (lResult == ERROR_ACCESS_DENIED)
755             skip("Not enough rights to open the registry key\n");
756         IMalloc_Free(ppM, pidlMyDocuments);
757         IShellFolder_Release(psfDesktop);
758         return;
759     }
760     
761     /* Query MyDocuments' Attributes value, to be able to restore it later. */
762     dwSize = sizeof(DWORD);
763     lResult = RegQueryValueExW(hKey, wszAttributes, NULL, NULL, (LPBYTE)&dwOrigAttributes, &dwSize);
764     ok (lResult == ERROR_SUCCESS, "RegQueryValueEx failed! result: %08x\n", lResult);
765     if (lResult != ERROR_SUCCESS) {
766         RegCloseKey(hKey);
767         IMalloc_Free(ppM, pidlMyDocuments);
768         IShellFolder_Release(psfDesktop);
769         return;
770     }
771
772     /* Query MyDocuments' CallForAttributes value, to be able to restore it later. */
773     dwSize = sizeof(DWORD);
774     lResult = RegQueryValueExW(hKey, wszCallForAttributes, NULL, NULL, 
775                               (LPBYTE)&dwOrigCallForAttributes, &dwSize);
776     ok (lResult == ERROR_SUCCESS, "RegQueryValueEx failed! result: %08x\n", lResult);
777     if (lResult != ERROR_SUCCESS) {
778         RegCloseKey(hKey);
779         IMalloc_Free(ppM, pidlMyDocuments);
780         IShellFolder_Release(psfDesktop);
781         return;
782     }
783     
784     /* Define via the Attributes value that MyDocuments attributes are SFGAO_ISSLOW and 
785      * SFGAO_GHOSTED and that MyDocuments should be called for the SFGAO_ISSLOW and
786      * SFGAO_FILESYSTEM attributes. */
787     dwAttributes = SFGAO_ISSLOW|SFGAO_GHOSTED;
788     RegSetValueExW(hKey, wszAttributes, 0, REG_DWORD, (LPBYTE)&dwAttributes, sizeof(DWORD));
789     dwCallForAttributes = SFGAO_ISSLOW|SFGAO_FILESYSTEM;
790     RegSetValueExW(hKey, wszCallForAttributes, 0, REG_DWORD, 
791                    (LPBYTE)&dwCallForAttributes, sizeof(DWORD));
792
793     /* Although it is not set in CallForAttributes, the SFGAO_GHOSTED flag is reset by 
794      * GetAttributesOf. It seems that once there is a single attribute queried, for which
795      * CallForAttributes is set, all flags are taken from the GetAttributesOf call and
796      * the flags in Attributes are ignored. 
797      */
798     dwAttributes = SFGAO_ISSLOW|SFGAO_GHOSTED|SFGAO_FILESYSTEM;
799     hr = IShellFolder_GetAttributesOf(psfDesktop, 1, 
800                                       (LPCITEMIDLIST*)&pidlMyDocuments, &dwAttributes);
801     ok (hr == S_OK, "Desktop->GetAttributesOf(MyDocuments) failed! hr = %08x\n", hr);
802     if (hr == S_OK)
803         ok (dwAttributes == SFGAO_FILESYSTEM, 
804             "Desktop->GetAttributes(MyDocuments) returned unexpected attributes: %08x\n", 
805             dwAttributes);
806
807     /* Restore MyDocuments' original Attributes and CallForAttributes registry values */
808     RegSetValueExW(hKey, wszAttributes, 0, REG_DWORD, (LPBYTE)&dwOrigAttributes, sizeof(DWORD));
809     RegSetValueExW(hKey, wszCallForAttributes, 0, REG_DWORD, 
810                    (LPBYTE)&dwOrigCallForAttributes, sizeof(DWORD));
811     RegCloseKey(hKey);
812     IMalloc_Free(ppM, pidlMyDocuments);
813     IShellFolder_Release(psfDesktop);
814 }
815
816 static void test_GetAttributesOf(void) 
817 {
818     HRESULT hr;
819     LPSHELLFOLDER psfDesktop, psfMyComputer;
820     SHITEMID emptyitem = { 0, { 0 } };
821     LPCITEMIDLIST pidlEmpty = (LPCITEMIDLIST)&emptyitem;
822     LPITEMIDLIST pidlMyComputer;
823     DWORD dwFlags;
824     static const DWORD desktopFlags[] = {
825         /* WinXP */
826         SFGAO_STORAGE | SFGAO_HASPROPSHEET | SFGAO_STORAGEANCESTOR | SFGAO_FILESYSANCESTOR |
827         SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_HASSUBFOLDER,
828         /* Win2k */
829         SFGAO_CANRENAME | SFGAO_HASPROPSHEET | SFGAO_STREAM | SFGAO_FILESYSANCESTOR |
830         SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_HASSUBFOLDER,
831         /* WinMe, Win9x, WinNT*/
832         SFGAO_CANRENAME | SFGAO_HASPROPSHEET | SFGAO_FILESYSANCESTOR |
833         SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_HASSUBFOLDER
834     };
835     static const DWORD myComputerFlags[] = {
836         /* WinXP */
837         SFGAO_CANRENAME | SFGAO_CANDELETE | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET |
838         SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER,
839         /* Win2k */
840         SFGAO_CANRENAME | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET | SFGAO_STREAM |
841         SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER,
842         /* WinMe, Win9x, WinNT */
843         SFGAO_CANRENAME | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET | SFGAO_FILESYSANCESTOR |
844         SFGAO_FOLDER | SFGAO_HASSUBFOLDER,
845         /* Win95, WinNT when queried directly */
846         SFGAO_CANLINK | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET | SFGAO_FILESYSANCESTOR |
847         SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_HASSUBFOLDER
848     };
849     WCHAR wszMyComputer[] = { 
850         ':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
851         'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };
852     char  cCurrDirA [MAX_PATH] = {0};
853     WCHAR cCurrDirW [MAX_PATH];
854     static WCHAR cTestDirW[] = {'t','e','s','t','d','i','r',0};
855     IShellFolder *IDesktopFolder, *testIShellFolder;
856     ITEMIDLIST *newPIDL;
857     int len, i;
858     BOOL foundFlagsMatch;
859
860     hr = SHGetDesktopFolder(&psfDesktop);
861     ok (hr == S_OK, "SHGetDesktopFolder failed! hr = %08x\n", hr);
862     if (hr != S_OK) return;
863
864     /* The Desktop attributes can be queried with a single empty itemidlist, .. */
865     dwFlags = 0xffffffff;
866     hr = IShellFolder_GetAttributesOf(psfDesktop, 1, &pidlEmpty, &dwFlags);
867     ok (hr == S_OK, "Desktop->GetAttributesOf(empty pidl) failed! hr = %08x\n", hr);
868     for (i = 0, foundFlagsMatch = FALSE; !foundFlagsMatch &&
869          i < sizeof(desktopFlags) / sizeof(desktopFlags[0]); i++)
870     {
871         if (desktopFlags[i] == dwFlags)
872             foundFlagsMatch = TRUE;
873     }
874     ok (foundFlagsMatch, "Wrong Desktop attributes: %08x\n", dwFlags);
875
876     /* .. or with no itemidlist at all. */
877     dwFlags = 0xffffffff;
878     hr = IShellFolder_GetAttributesOf(psfDesktop, 0, NULL, &dwFlags);
879     ok (hr == S_OK, "Desktop->GetAttributesOf(NULL) failed! hr = %08x\n", hr);
880     for (i = 0, foundFlagsMatch = FALSE; !foundFlagsMatch &&
881          i < sizeof(desktopFlags) / sizeof(desktopFlags[0]); i++)
882     {
883         if (desktopFlags[i] == dwFlags)
884             foundFlagsMatch = TRUE;
885     }
886     ok (foundFlagsMatch, "Wrong Desktop attributes: %08x\n", dwFlags);
887    
888     /* Testing the attributes of the MyComputer shellfolder */
889     hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyComputer, NULL, &pidlMyComputer, NULL);
890     ok (hr == S_OK, "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08x\n", hr);
891     if (hr != S_OK) {
892         IShellFolder_Release(psfDesktop);
893         return;
894     }
895
896     /* Windows sets the SFGAO_CANLINK flag, when MyComputer is queried via the Desktop
897      * folder object. It doesn't do this, if MyComputer is queried directly (see below).
898      */
899     dwFlags = 0xffffffff;
900     hr = IShellFolder_GetAttributesOf(psfDesktop, 1, (LPCITEMIDLIST*)&pidlMyComputer, &dwFlags);
901     ok (hr == S_OK, "Desktop->GetAttributesOf(MyComputer) failed! hr = %08x\n", hr);
902     for (i = 0, foundFlagsMatch = FALSE; !foundFlagsMatch &&
903          i < sizeof(myComputerFlags) / sizeof(myComputerFlags[0]); i++)
904     {
905         if ((myComputerFlags[i] | SFGAO_CANLINK) == dwFlags)
906             foundFlagsMatch = TRUE;
907     }
908     todo_wine
909     ok (foundFlagsMatch, "Wrong MyComputer attributes: %08x\n", dwFlags);
910
911     hr = IShellFolder_BindToObject(psfDesktop, pidlMyComputer, NULL, &IID_IShellFolder, (LPVOID*)&psfMyComputer);
912     ok (hr == S_OK, "Desktop failed to bind to MyComputer object! hr = %08x\n", hr);
913     IShellFolder_Release(psfDesktop);
914     IMalloc_Free(ppM, pidlMyComputer);
915     if (hr != S_OK) return;
916
917     hr = IShellFolder_GetAttributesOf(psfMyComputer, 1, &pidlEmpty, &dwFlags);
918     todo_wine
919     ok (hr == E_INVALIDARG ||
920         broken(hr == S_OK), /* W2K and earlier */
921         "MyComputer->GetAttributesOf(emtpy pidl) should fail! hr = %08x\n", hr);
922
923     dwFlags = 0xffffffff;
924     hr = IShellFolder_GetAttributesOf(psfMyComputer, 0, NULL, &dwFlags);
925     ok (hr == S_OK, "MyComputer->GetAttributesOf(NULL) failed! hr = %08x\n", hr);
926     for (i = 0, foundFlagsMatch = FALSE; !foundFlagsMatch &&
927          i < sizeof(myComputerFlags) / sizeof(myComputerFlags[0]); i++)
928     {
929         if (myComputerFlags[i] == dwFlags)
930             foundFlagsMatch = TRUE;
931     }
932     todo_wine
933     ok (foundFlagsMatch, "Wrong MyComputer attributes: %08x\n", dwFlags);
934
935     IShellFolder_Release(psfMyComputer);
936
937     GetCurrentDirectoryA(MAX_PATH, cCurrDirA);
938     len = lstrlenA(cCurrDirA);
939
940     if (len == 0) {
941         win_skip("GetCurrentDirectoryA returned empty string. Skipping test_GetAttributesOf\n");
942         return;
943     }
944     if (len > 3 && cCurrDirA[len-1] == '\\')
945         cCurrDirA[len-1] = 0;
946
947     /* create test directory */
948     CreateFilesFolders();
949
950     MultiByteToWideChar(CP_ACP, 0, cCurrDirA, -1, cCurrDirW, MAX_PATH);
951  
952     hr = SHGetDesktopFolder(&IDesktopFolder);
953     ok(hr == S_OK, "SHGetDesktopfolder failed %08x\n", hr);
954
955     hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cCurrDirW, NULL, &newPIDL, 0);
956     ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
957
958     hr = IShellFolder_BindToObject(IDesktopFolder, newPIDL, NULL, (REFIID)&IID_IShellFolder, (LPVOID *)&testIShellFolder);
959     ok(hr == S_OK, "BindToObject failed %08x\n", hr);
960
961     IMalloc_Free(ppM, newPIDL);
962
963     /* get relative PIDL */
964     hr = IShellFolder_ParseDisplayName(testIShellFolder, NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
965     ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
966
967     /* test the shell attributes of the test directory using the relative PIDL */
968     dwFlags = SFGAO_FOLDER;
969     hr = IShellFolder_GetAttributesOf(testIShellFolder, 1, (LPCITEMIDLIST*)&newPIDL, &dwFlags);
970     ok (hr == S_OK, "Desktop->GetAttributesOf() failed! hr = %08x\n", hr);
971     ok ((dwFlags&SFGAO_FOLDER), "Wrong directory attribute for relative PIDL: %08x\n", dwFlags);
972
973     /* free memory */
974     IMalloc_Free(ppM, newPIDL);
975
976     /* append testdirectory name to path */
977     if (cCurrDirA[len-1] == '\\')
978         cCurrDirA[len-1] = 0;
979     lstrcatA(cCurrDirA, "\\testdir");
980     MultiByteToWideChar(CP_ACP, 0, cCurrDirA, -1, cCurrDirW, MAX_PATH);
981
982     hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cCurrDirW, NULL, &newPIDL, 0);
983     ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
984
985     /* test the shell attributes of the test directory using the absolute PIDL */
986     dwFlags = SFGAO_FOLDER;
987     hr = IShellFolder_GetAttributesOf(IDesktopFolder, 1, (LPCITEMIDLIST*)&newPIDL, &dwFlags);
988     ok (hr == S_OK, "Desktop->GetAttributesOf() failed! hr = %08x\n", hr);
989     ok ((dwFlags&SFGAO_FOLDER), "Wrong directory attribute for absolute PIDL: %08x\n", dwFlags);
990
991     /* free memory */
992     IMalloc_Free(ppM, newPIDL);
993
994     IShellFolder_Release(testIShellFolder);
995
996     Cleanup();
997
998     IShellFolder_Release(IDesktopFolder);
999 }
1000
1001 static void test_SHGetPathFromIDList(void)
1002 {
1003     SHITEMID emptyitem = { 0, { 0 } };
1004     LPCITEMIDLIST pidlEmpty = (LPCITEMIDLIST)&emptyitem;
1005     LPITEMIDLIST pidlMyComputer;
1006     WCHAR wszPath[MAX_PATH], wszDesktop[MAX_PATH];
1007     BOOL result;
1008     HRESULT hr;
1009     LPSHELLFOLDER psfDesktop;
1010     WCHAR wszMyComputer[] = { 
1011         ':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
1012         'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };
1013     WCHAR wszFileName[MAX_PATH];
1014     LPITEMIDLIST pidlTestFile;
1015     HANDLE hTestFile;
1016     STRRET strret;
1017     static WCHAR wszTestFile[] = {
1018         'w','i','n','e','t','e','s','t','.','f','o','o',0 };
1019     LPITEMIDLIST pidlPrograms;
1020
1021     if(!pSHGetPathFromIDListW || !pSHGetSpecialFolderPathW)
1022     {
1023         win_skip("SHGetPathFromIDListW() or SHGetSpecialFolderPathW() is missing\n");
1024         return;
1025     }
1026
1027     /* Calling SHGetPathFromIDListW with no pidl should return the empty string */
1028     wszPath[0] = 'a';
1029     wszPath[1] = '\0';
1030     result = pSHGetPathFromIDListW(NULL, wszPath);
1031     ok(!result, "Expected failure\n");
1032     ok(!wszPath[0], "Expected empty string\n");
1033
1034     /* Calling SHGetPathFromIDListW with an empty pidl should return the desktop folder's path. */
1035     result = pSHGetSpecialFolderPathW(NULL, wszDesktop, CSIDL_DESKTOP, FALSE);
1036     ok(result, "SHGetSpecialFolderPathW(CSIDL_DESKTOP) failed! Last error: %u\n", GetLastError());
1037     if (!result) return;
1038
1039     /* Check if we are on Win9x */
1040     SetLastError(0xdeadbeef);
1041     lstrcmpiW(wszDesktop, wszDesktop);
1042     if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
1043     {
1044         win_skip("Most W-calls are not implemented\n");
1045         return;
1046     }
1047
1048     result = pSHGetPathFromIDListW(pidlEmpty, wszPath);
1049     ok(result, "SHGetPathFromIDListW failed! Last error: %u\n", GetLastError());
1050     if (!result) return;
1051     ok(!lstrcmpiW(wszDesktop, wszPath), "SHGetPathFromIDListW didn't return desktop path for empty pidl!\n");
1052
1053     /* MyComputer does not map to a filesystem path. SHGetPathFromIDListW should fail. */
1054     hr = SHGetDesktopFolder(&psfDesktop);
1055     ok (hr == S_OK, "SHGetDesktopFolder failed! hr = %08x\n", hr);
1056     if (hr != S_OK) return;
1057
1058     hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyComputer, NULL, &pidlMyComputer, NULL);
1059     ok (hr == S_OK, "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08x\n", hr);
1060     if (hr != S_OK) {
1061         IShellFolder_Release(psfDesktop);
1062         return;
1063     }
1064
1065     SetLastError(0xdeadbeef);
1066     wszPath[0] = 'a';
1067     wszPath[1] = '\0';
1068     result = pSHGetPathFromIDListW(pidlMyComputer, wszPath);
1069     ok (!result, "SHGetPathFromIDListW succeeded where it shouldn't!\n");
1070     ok (GetLastError()==0xdeadbeef ||
1071         GetLastError()==ERROR_SUCCESS, /* Vista and higher */
1072         "Unexpected last error from SHGetPathFromIDListW: %u\n", GetLastError());
1073     ok (!wszPath[0], "Expected empty path\n");
1074     if (result) {
1075         IShellFolder_Release(psfDesktop);
1076         return;
1077     }
1078
1079     IMalloc_Free(ppM, pidlMyComputer);
1080
1081     result = pSHGetSpecialFolderPathW(NULL, wszFileName, CSIDL_DESKTOPDIRECTORY, FALSE);
1082     ok(result, "SHGetSpecialFolderPathW failed! Last error: %u\n", GetLastError());
1083     if (!result) {
1084         IShellFolder_Release(psfDesktop);
1085         return;
1086     }
1087     myPathAddBackslashW(wszFileName);
1088     lstrcatW(wszFileName, wszTestFile);
1089     hTestFile = CreateFileW(wszFileName, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL);
1090     ok(hTestFile != INVALID_HANDLE_VALUE, "CreateFileW failed! Last error: %u\n", GetLastError());
1091     if (hTestFile == INVALID_HANDLE_VALUE) {
1092         IShellFolder_Release(psfDesktop);
1093         return;
1094     }
1095     CloseHandle(hTestFile);
1096
1097     hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszTestFile, NULL, &pidlTestFile, NULL);
1098     ok (hr == S_OK, "Desktop's ParseDisplayName failed to parse filename hr = %08x\n", hr);
1099     if (hr != S_OK) {
1100         IShellFolder_Release(psfDesktop);
1101         DeleteFileW(wszFileName);
1102         IMalloc_Free(ppM, pidlTestFile);
1103         return;
1104     }
1105
1106     /* This test is to show that the Desktop shellfolder prepends the CSIDL_DESKTOPDIRECTORY
1107      * path for files placed on the desktop, if called with SHGDN_FORPARSING. */
1108     hr = IShellFolder_GetDisplayNameOf(psfDesktop, pidlTestFile, SHGDN_FORPARSING, &strret);
1109     ok (hr == S_OK, "Desktop's GetDisplayNamfOf failed! hr = %08x\n", hr);
1110     IShellFolder_Release(psfDesktop);
1111     DeleteFileW(wszFileName);
1112     if (hr != S_OK) {
1113         IMalloc_Free(ppM, pidlTestFile);
1114         return;
1115     }
1116     if (pStrRetToBufW)
1117     {
1118         pStrRetToBufW(&strret, pidlTestFile, wszPath, MAX_PATH);
1119         ok(0 == lstrcmpW(wszFileName, wszPath), 
1120            "Desktop->GetDisplayNameOf(pidlTestFile, SHGDN_FORPARSING) "
1121            "returned incorrect path for file placed on desktop\n");
1122     }
1123
1124     result = pSHGetPathFromIDListW(pidlTestFile, wszPath);
1125     ok(result, "SHGetPathFromIDListW failed! Last error: %u\n", GetLastError());
1126     IMalloc_Free(ppM, pidlTestFile);
1127     if (!result) return;
1128     ok(0 == lstrcmpW(wszFileName, wszPath), "SHGetPathFromIDListW returned incorrect path for file placed on desktop\n");
1129
1130
1131     /* Test if we can get the path from the start menu "program files" PIDL. */
1132     hr = pSHGetSpecialFolderLocation(NULL, CSIDL_PROGRAM_FILES, &pidlPrograms);
1133     ok(hr == S_OK, "SHGetFolderLocation failed: 0x%08x\n", hr);
1134
1135     SetLastError(0xdeadbeef);
1136     result = pSHGetPathFromIDListW(pidlPrograms, wszPath);
1137         IMalloc_Free(ppM, pidlPrograms);
1138     ok(result, "SHGetPathFromIDListW failed\n");
1139 }
1140
1141 static void test_EnumObjects_and_CompareIDs(void)
1142 {
1143     ITEMIDLIST *newPIDL;
1144     IShellFolder *IDesktopFolder, *testIShellFolder;
1145     char  cCurrDirA [MAX_PATH] = {0};
1146     static const CHAR cTestDirA[] = "\\testdir";
1147     WCHAR cTestDirW[MAX_PATH];
1148     int len;
1149     HRESULT hr;
1150
1151     GetCurrentDirectoryA(MAX_PATH, cCurrDirA);
1152     len = lstrlenA(cCurrDirA);
1153
1154     if(len == 0) {
1155         win_skip("GetCurrentDirectoryA returned empty string. Skipping test_EnumObjects_and_CompareIDs\n");
1156         return;
1157     }
1158     if(cCurrDirA[len-1] == '\\')
1159         cCurrDirA[len-1] = 0;
1160
1161     lstrcatA(cCurrDirA, cTestDirA);
1162     MultiByteToWideChar(CP_ACP, 0, cCurrDirA, -1, cTestDirW, MAX_PATH);
1163
1164     hr = SHGetDesktopFolder(&IDesktopFolder);
1165     ok(hr == S_OK, "SHGetDesktopfolder failed %08x\n", hr);
1166
1167     CreateFilesFolders();
1168
1169     hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
1170     ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
1171
1172     hr = IShellFolder_BindToObject(IDesktopFolder, newPIDL, NULL, (REFIID)&IID_IShellFolder, (LPVOID *)&testIShellFolder);
1173     ok(hr == S_OK, "BindToObject failed %08x\n", hr);
1174
1175     test_EnumObjects(testIShellFolder);
1176
1177     IShellFolder_Release(testIShellFolder);
1178
1179     Cleanup();
1180
1181     IMalloc_Free(ppM, newPIDL);
1182
1183     IShellFolder_Release(IDesktopFolder);
1184 }
1185
1186 /* A simple implementation of an IPropertyBag, which returns fixed values for
1187  * 'Target' and 'Attributes' properties.
1188  */
1189 static HRESULT WINAPI InitPropertyBag_IPropertyBag_QueryInterface(IPropertyBag *iface, REFIID riid,
1190     void **ppvObject) 
1191 {
1192     if (!ppvObject)
1193         return E_INVALIDARG;
1194
1195     if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IPropertyBag, riid)) {
1196         *ppvObject = iface;
1197     } else {
1198         ok (FALSE, "InitPropertyBag asked for unknown interface!\n");
1199         return E_NOINTERFACE;
1200     }
1201
1202     IPropertyBag_AddRef(iface);
1203     return S_OK;
1204 }
1205
1206 static ULONG WINAPI InitPropertyBag_IPropertyBag_AddRef(IPropertyBag *iface) {
1207     return 2;
1208 }
1209
1210 static ULONG WINAPI InitPropertyBag_IPropertyBag_Release(IPropertyBag *iface) {
1211     return 1;
1212 }
1213
1214 static HRESULT WINAPI InitPropertyBag_IPropertyBag_Read(IPropertyBag *iface, LPCOLESTR pszPropName,
1215     VARIANT *pVar, IErrorLog *pErrorLog)
1216 {
1217     static const WCHAR wszTargetSpecialFolder[] = {
1218         'T','a','r','g','e','t','S','p','e','c','i','a','l','F','o','l','d','e','r',0 };
1219     static const WCHAR wszTarget[] = {
1220         'T','a','r','g','e','t',0 };
1221     static const WCHAR wszAttributes[] = {
1222         'A','t','t','r','i','b','u','t','e','s',0 };
1223     static const WCHAR wszResolveLinkFlags[] = {
1224         'R','e','s','o','l','v','e','L','i','n','k','F','l','a','g','s',0 };
1225     static const WCHAR wszTargetKnownFolder[] = {
1226         'T','a','r','g','e','t','K','n','o','w','n','F','o','l','d','e','r',0 };
1227     static const WCHAR wszCLSID[] = {
1228         'C','L','S','I','D',0 };
1229        
1230     if (!lstrcmpW(pszPropName, wszTargetSpecialFolder)) {
1231         ok(V_VT(pVar) == VT_I4 ||
1232            broken(V_VT(pVar) == VT_BSTR),   /* Win2k */
1233            "Wrong variant type for 'TargetSpecialFolder' property!\n");
1234         return E_INVALIDARG;
1235     }
1236     
1237     if (!lstrcmpW(pszPropName, wszResolveLinkFlags)) 
1238     {
1239         ok(V_VT(pVar) == VT_UI4, "Wrong variant type for 'ResolveLinkFlags' property!\n");
1240         return E_INVALIDARG;
1241     }
1242
1243     if (!lstrcmpW(pszPropName, wszTarget)) {
1244         WCHAR wszPath[MAX_PATH];
1245         BOOL result;
1246         
1247         ok(V_VT(pVar) == VT_BSTR ||
1248            broken(V_VT(pVar) == VT_EMPTY),  /* Win2k */
1249            "Wrong variant type for 'Target' property!\n");
1250         if (V_VT(pVar) != VT_BSTR) return E_INVALIDARG;
1251
1252         result = pSHGetSpecialFolderPathW(NULL, wszPath, CSIDL_DESKTOPDIRECTORY, FALSE);
1253         ok(result, "SHGetSpecialFolderPathW(DESKTOPDIRECTORY) failed! %u\n", GetLastError());
1254         if (!result) return E_INVALIDARG;
1255
1256         V_BSTR(pVar) = SysAllocString(wszPath);
1257         return S_OK;
1258     }
1259
1260     if (!lstrcmpW(pszPropName, wszAttributes)) {
1261         ok(V_VT(pVar) == VT_UI4, "Wrong variant type for 'Attributes' property!\n");
1262         if (V_VT(pVar) != VT_UI4) return E_INVALIDARG;
1263         V_UI4(pVar) = SFGAO_FOLDER|SFGAO_HASSUBFOLDER|SFGAO_FILESYSANCESTOR|
1264                       SFGAO_CANRENAME|SFGAO_FILESYSTEM;
1265         return S_OK;
1266     }
1267
1268     if (!lstrcmpW(pszPropName, wszTargetKnownFolder)) {
1269         ok(V_VT(pVar) == VT_BSTR, "Wrong variant type for 'TargetKnownFolder' property!\n");
1270         /* TODO */
1271         return E_INVALIDARG;
1272     }
1273
1274     if (!lstrcmpW(pszPropName, wszCLSID)) {
1275         ok(V_VT(pVar) == VT_EMPTY, "Wrong variant type for 'CLSID' property!\n");
1276         /* TODO */
1277         return E_INVALIDARG;
1278     }
1279
1280     ok(FALSE, "PropertyBag was asked for unknown property %s (vt=%d)!\n", wine_dbgstr_w(pszPropName), V_VT(pVar));
1281     return E_INVALIDARG;
1282 }
1283
1284 static HRESULT WINAPI InitPropertyBag_IPropertyBag_Write(IPropertyBag *iface, LPCOLESTR pszPropName,
1285     VARIANT *pVar)
1286 {
1287     ok(FALSE, "Unexpected call to IPropertyBag_Write\n");
1288     return E_NOTIMPL;
1289 }
1290     
1291 static const IPropertyBagVtbl InitPropertyBag_IPropertyBagVtbl = {
1292     InitPropertyBag_IPropertyBag_QueryInterface,
1293     InitPropertyBag_IPropertyBag_AddRef,
1294     InitPropertyBag_IPropertyBag_Release,
1295     InitPropertyBag_IPropertyBag_Read,
1296     InitPropertyBag_IPropertyBag_Write
1297 };
1298
1299 static struct IPropertyBag InitPropertyBag = {
1300     &InitPropertyBag_IPropertyBagVtbl
1301 };
1302
1303 static void test_FolderShortcut(void) {
1304     IPersistPropertyBag *pPersistPropertyBag;
1305     IShellFolder *pShellFolder, *pDesktopFolder;
1306     IPersistFolder3 *pPersistFolder3;
1307     HRESULT hr;
1308     STRRET strret;
1309     WCHAR wszDesktopPath[MAX_PATH], wszBuffer[MAX_PATH];
1310     BOOL result;
1311     CLSID clsid;
1312     LPITEMIDLIST pidlCurrentFolder, pidlWineTestFolder, pidlSubFolder;
1313     HKEY hShellExtKey;
1314     WCHAR wszWineTestFolder[] = {
1315         ':',':','{','9','B','3','5','2','E','B','F','-','2','7','6','5','-','4','5','C','1','-',
1316         'B','4','C','6','-','8','5','C','C','7','F','7','A','B','C','6','4','}',0 };
1317     WCHAR wszShellExtKey[] = { 'S','o','f','t','w','a','r','e','\\',
1318         'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
1319         'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
1320         'E','x','p','l','o','r','e','r','\\','D','e','s','k','t','o','p','\\',
1321         'N','a','m','e','S','p','a','c','e','\\',
1322         '{','9','b','3','5','2','e','b','f','-','2','7','6','5','-','4','5','c','1','-',
1323         'b','4','c','6','-','8','5','c','c','7','f','7','a','b','c','6','4','}',0 };
1324     
1325     WCHAR wszSomeSubFolder[] = { 'S','u','b','F','o','l','d','e','r', 0};
1326     static const GUID CLSID_UnixDosFolder = 
1327         {0x9d20aae8, 0x0625, 0x44b0, {0x9c, 0xa7, 0x71, 0x88, 0x9c, 0x22, 0x54, 0xd9}};
1328
1329     if (!pSHGetSpecialFolderPathW || !pStrRetToBufW) {
1330         win_skip("SHGetSpecialFolderPathW and/or StrRetToBufW are not available\n");
1331         return;
1332     }
1333
1334     if (!pSHGetFolderPathAndSubDirA)
1335     {
1336         win_skip("FolderShortcut test doesn't work on Win2k\n");
1337         return;
1338     }
1339
1340     /* These tests basically show, that CLSID_FolderShortcuts are initialized
1341      * via their IPersistPropertyBag interface. And that the target folder
1342      * is taken from the IPropertyBag's 'Target' property.
1343      */
1344     hr = CoCreateInstance(&CLSID_FolderShortcut, NULL, CLSCTX_INPROC_SERVER, 
1345                           &IID_IPersistPropertyBag, (LPVOID*)&pPersistPropertyBag);
1346     if (hr == REGDB_E_CLASSNOTREG) {
1347         win_skip("CLSID_FolderShortcut is not implemented\n");
1348         return;
1349     }
1350     ok (hr == S_OK, "CoCreateInstance failed! hr = 0x%08x\n", hr);
1351     if (hr != S_OK) return;
1352
1353     hr = IPersistPropertyBag_Load(pPersistPropertyBag, &InitPropertyBag, NULL);
1354     ok(hr == S_OK, "IPersistPropertyBag_Load failed! hr = %08x\n", hr);
1355     if (hr != S_OK) {
1356         IPersistPropertyBag_Release(pPersistPropertyBag);
1357         return;
1358     }
1359
1360     hr = IPersistPropertyBag_QueryInterface(pPersistPropertyBag, &IID_IShellFolder, 
1361                                             (LPVOID*)&pShellFolder);
1362     IPersistPropertyBag_Release(pPersistPropertyBag);
1363     ok(hr == S_OK, "IPersistPropertyBag_QueryInterface(IShellFolder) failed! hr = %08x\n", hr);
1364     if (hr != S_OK) return;
1365
1366     hr = IShellFolder_GetDisplayNameOf(pShellFolder, NULL, SHGDN_FORPARSING, &strret);
1367     ok(hr == S_OK, "IShellFolder_GetDisplayNameOf(NULL) failed! hr = %08x\n", hr);
1368     if (hr != S_OK) {
1369         IShellFolder_Release(pShellFolder);
1370         return;
1371     }
1372
1373     result = pSHGetSpecialFolderPathW(NULL, wszDesktopPath, CSIDL_DESKTOPDIRECTORY, FALSE);
1374     ok(result, "SHGetSpecialFolderPathW(CSIDL_DESKTOPDIRECTORY) failed! %u\n", GetLastError());
1375     if (!result) return;
1376
1377     pStrRetToBufW(&strret, NULL, wszBuffer, MAX_PATH);
1378     ok(!lstrcmpiW(wszDesktopPath, wszBuffer), "FolderShortcut returned incorrect folder!\n");
1379
1380     hr = IShellFolder_QueryInterface(pShellFolder, &IID_IPersistFolder3, (LPVOID*)&pPersistFolder3);
1381     IShellFolder_Release(pShellFolder);
1382     ok(hr == S_OK, "IShellFolder_QueryInterface(IID_IPersistFolder3 failed! hr = 0x%08x\n", hr);
1383     if (hr != S_OK) return;
1384
1385     hr = IPersistFolder3_GetClassID(pPersistFolder3, &clsid);
1386     ok(hr == S_OK, "IPersistFolder3_GetClassID failed! hr=0x%08x\n", hr);
1387     ok(IsEqualCLSID(&clsid, &CLSID_FolderShortcut), "Unexpected CLSID!\n");
1388
1389     hr = IPersistFolder3_GetCurFolder(pPersistFolder3, &pidlCurrentFolder);
1390     todo_wine ok(hr == S_FALSE, "IPersistFolder3_GetCurFolder failed! hr=0x%08x\n", hr);
1391     ok(!pidlCurrentFolder, "IPersistFolder3_GetCurFolder should return a NULL pidl!\n");
1392                     
1393     /* For FolderShortcut objects, the Initialize method initialized the folder's position in the
1394      * shell namespace. The target folder, read from the property bag above, remains untouched. 
1395      * The following tests show this: The itemidlist for some imaginary shellfolder object
1396      * is created and the FolderShortcut is initialized with it. GetCurFolder now returns this
1397      * itemidlist, but GetDisplayNameOf still returns the path from above.
1398      */
1399     hr = SHGetDesktopFolder(&pDesktopFolder);
1400     ok (hr == S_OK, "SHGetDesktopFolder failed! hr = %08x\n", hr);
1401     if (hr != S_OK) return;
1402
1403     /* Temporarily register WineTestFolder as a shell namespace extension at the Desktop. 
1404      * Otherwise ParseDisplayName fails on WinXP with E_INVALIDARG */
1405     RegCreateKeyW(HKEY_CURRENT_USER, wszShellExtKey, &hShellExtKey);
1406     RegCloseKey(hShellExtKey);
1407     hr = IShellFolder_ParseDisplayName(pDesktopFolder, NULL, NULL, wszWineTestFolder, NULL,
1408                                        &pidlWineTestFolder, NULL);
1409     RegDeleteKeyW(HKEY_CURRENT_USER, wszShellExtKey);
1410     IShellFolder_Release(pDesktopFolder);
1411     ok (hr == S_OK, "IShellFolder::ParseDisplayName failed! hr = %08x\n", hr);
1412     if (hr != S_OK) return;
1413
1414     hr = IPersistFolder3_Initialize(pPersistFolder3, pidlWineTestFolder);
1415     ok (hr == S_OK, "IPersistFolder3::Initialize failed! hr = %08x\n", hr);
1416     if (hr != S_OK) {
1417         IPersistFolder3_Release(pPersistFolder3);
1418         pILFree(pidlWineTestFolder);
1419         return;
1420     }
1421
1422     hr = IPersistFolder3_GetCurFolder(pPersistFolder3, &pidlCurrentFolder);
1423     ok(hr == S_OK, "IPersistFolder3_GetCurFolder failed! hr=0x%08x\n", hr);
1424     ok(pILIsEqual(pidlCurrentFolder, pidlWineTestFolder),
1425         "IPersistFolder3_GetCurFolder should return pidlWineTestFolder!\n");
1426     pILFree(pidlCurrentFolder);
1427     pILFree(pidlWineTestFolder);
1428
1429     hr = IPersistFolder3_QueryInterface(pPersistFolder3, &IID_IShellFolder, (LPVOID*)&pShellFolder);
1430     IPersistFolder3_Release(pPersistFolder3);
1431     ok(hr == S_OK, "IPersistFolder3_QueryInterface(IShellFolder) failed! hr = %08x\n", hr);
1432     if (hr != S_OK) return;
1433
1434     hr = IShellFolder_GetDisplayNameOf(pShellFolder, NULL, SHGDN_FORPARSING, &strret);
1435     ok(hr == S_OK, "IShellFolder_GetDisplayNameOf(NULL) failed! hr = %08x\n", hr);
1436     if (hr != S_OK) {
1437         IShellFolder_Release(pShellFolder);
1438         return;
1439     }
1440
1441     pStrRetToBufW(&strret, NULL, wszBuffer, MAX_PATH);
1442     ok(!lstrcmpiW(wszDesktopPath, wszBuffer), "FolderShortcut returned incorrect folder!\n");
1443
1444     /* Next few lines are meant to show that children of FolderShortcuts are not FolderShortcuts,
1445      * but ShellFSFolders. */
1446     myPathAddBackslashW(wszDesktopPath);
1447     lstrcatW(wszDesktopPath, wszSomeSubFolder);
1448     if (!CreateDirectoryW(wszDesktopPath, NULL)) {
1449         IShellFolder_Release(pShellFolder);
1450         return;
1451     }
1452     
1453     hr = IShellFolder_ParseDisplayName(pShellFolder, NULL, NULL, wszSomeSubFolder, NULL, 
1454                                        &pidlSubFolder, NULL);
1455     RemoveDirectoryW(wszDesktopPath);
1456     ok (hr == S_OK, "IShellFolder::ParseDisplayName failed! hr = %08x\n", hr);
1457     if (hr != S_OK) {
1458         IShellFolder_Release(pShellFolder);
1459         return;
1460     }
1461
1462     hr = IShellFolder_BindToObject(pShellFolder, pidlSubFolder, NULL, &IID_IPersistFolder3,
1463                                    (LPVOID*)&pPersistFolder3);
1464     IShellFolder_Release(pShellFolder);
1465     pILFree(pidlSubFolder);
1466     ok (hr == S_OK, "IShellFolder::BindToObject failed! hr = %08x\n", hr);
1467     if (hr != S_OK)
1468         return;
1469
1470     /* On windows, we expect CLSID_ShellFSFolder. On wine we relax this constraint
1471      * a little bit and also allow CLSID_UnixDosFolder. */
1472     hr = IPersistFolder3_GetClassID(pPersistFolder3, &clsid);
1473     ok(hr == S_OK, "IPersistFolder3_GetClassID failed! hr=0x%08x\n", hr);
1474     ok(IsEqualCLSID(&clsid, &CLSID_ShellFSFolder) || IsEqualCLSID(&clsid, &CLSID_UnixDosFolder),
1475         "IPersistFolder3::GetClassID returned unexpected CLSID!\n");
1476
1477     IPersistFolder3_Release(pPersistFolder3);
1478 }
1479
1480 #include "pshpack1.h"
1481 struct FileStructA {
1482     BYTE  type;
1483     BYTE  dummy;
1484     DWORD dwFileSize;
1485     WORD  uFileDate;    /* In our current implementation this is */
1486     WORD  uFileTime;    /* FileTimeToDosDate(WIN32_FIND_DATA->ftLastWriteTime) */
1487     WORD  uFileAttribs;
1488     CHAR  szName[1];
1489 };
1490
1491 struct FileStructW {
1492     WORD  cbLen;        /* Length of this element. */
1493     BYTE  abFooBar1[6]; /* Beyond any recognition. */
1494     WORD  uDate;        /* FileTimeToDosDate(WIN32_FIND_DATA->ftCreationTime)? */
1495     WORD  uTime;        /* (this is currently speculation) */
1496     WORD  uDate2;       /* FileTimeToDosDate(WIN32_FIND_DATA->ftLastAccessTime)? */
1497     WORD  uTime2;       /* (this is currently speculation) */
1498     BYTE  abFooBar2[4]; /* Beyond any recognition. */
1499     WCHAR wszName[1];   /* The long filename in unicode. */
1500     /* Just for documentation: Right after the unicode string: */
1501     WORD  cbOffset;     /* FileStructW's offset from the beginning of the SHITMEID. 
1502                          * SHITEMID->cb == uOffset + cbLen */
1503 };
1504 #include "poppack.h"
1505
1506 static void test_ITEMIDLIST_format(void) {
1507     WCHAR wszPersonal[MAX_PATH];
1508     LPSHELLFOLDER psfDesktop, psfPersonal;
1509     LPITEMIDLIST pidlPersonal, pidlFile;
1510     HANDLE hFile;
1511     HRESULT hr;
1512     BOOL bResult;
1513     WCHAR wszFile[3][17] = { { 'e','v','e','n','_',0 }, { 'o','d','d','_',0 },
1514         { 'l','o','n','g','e','r','_','t','h','a','n','.','8','_','3',0 } };
1515     int i;
1516
1517     if (!pSHGetSpecialFolderPathW) return;
1518
1519     bResult = pSHGetSpecialFolderPathW(NULL, wszPersonal, CSIDL_PERSONAL, FALSE);
1520     ok(bResult, "SHGetSpecialFolderPathW failed! Last error: %u\n", GetLastError());
1521     if (!bResult) return;
1522
1523     SetLastError(0xdeadbeef);
1524     bResult = SetCurrentDirectoryW(wszPersonal);
1525     if (!bResult && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) {
1526         win_skip("Most W-calls are not implemented\n");
1527         return;
1528     }
1529     ok(bResult, "SetCurrentDirectory failed! Last error: %u\n", GetLastError());
1530     if (!bResult) return;
1531
1532     hr = SHGetDesktopFolder(&psfDesktop);
1533     ok(hr == S_OK, "SHGetDesktopFolder failed! hr: %08x\n", hr);
1534     if (hr != S_OK) return;
1535
1536     hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszPersonal, NULL, &pidlPersonal, NULL);
1537     ok(hr == S_OK, "psfDesktop->ParseDisplayName failed! hr = %08x\n", hr);
1538     if (hr != S_OK) {
1539         IShellFolder_Release(psfDesktop);
1540         return;
1541     }
1542
1543     hr = IShellFolder_BindToObject(psfDesktop, pidlPersonal, NULL, &IID_IShellFolder,
1544         (LPVOID*)&psfPersonal);
1545     IShellFolder_Release(psfDesktop);
1546     pILFree(pidlPersonal);
1547     ok(hr == S_OK, "psfDesktop->BindToObject failed! hr = %08x\n", hr);
1548     if (hr != S_OK) return;
1549
1550     for (i=0; i<3; i++) {
1551         CHAR szFile[MAX_PATH];
1552         struct FileStructA *pFileStructA;
1553         WORD cbOffset;
1554
1555         WideCharToMultiByte(CP_ACP, 0, wszFile[i], -1, szFile, MAX_PATH, NULL, NULL);
1556
1557         hFile = CreateFileW(wszFile[i], GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_FLAG_WRITE_THROUGH, NULL);
1558         ok(hFile != INVALID_HANDLE_VALUE, "CreateFile failed! (%u)\n", GetLastError());
1559         if (hFile == INVALID_HANDLE_VALUE) {
1560             IShellFolder_Release(psfPersonal);
1561             return;
1562         }
1563         CloseHandle(hFile);
1564
1565         hr = IShellFolder_ParseDisplayName(psfPersonal, NULL, NULL, wszFile[i], NULL, &pidlFile, NULL);
1566         DeleteFileW(wszFile[i]);
1567         ok(hr == S_OK, "psfPersonal->ParseDisplayName failed! hr: %08x\n", hr);
1568         if (hr != S_OK) {
1569             IShellFolder_Release(psfPersonal);
1570             return;
1571         }
1572
1573         pFileStructA = (struct FileStructA *)pidlFile->mkid.abID;
1574         ok(pFileStructA->type == 0x32, "PIDLTYPE should be 0x32!\n");
1575         ok(pFileStructA->dummy == 0x00, "Dummy Byte should be 0x00!\n");
1576         ok(pFileStructA->dwFileSize == 0, "Filesize should be zero!\n");
1577
1578         if (i < 2) /* First two file names are already in valid 8.3 format */
1579             ok(!strcmp(szFile, (CHAR*)&pidlFile->mkid.abID[12]), "Wrong file name!\n");
1580         else
1581             /* WinXP stores a derived 8.3 dos name (LONGER~1.8_3) here. We probably
1582              * can't implement this correctly, since unix filesystems don't support
1583              * this nasty short/long filename stuff. So we'll probably stay with our
1584              * current habbit of storing the long filename here, which seems to work
1585              * just fine. */
1586             todo_wine
1587             ok(pidlFile->mkid.abID[18] == '~' ||
1588                broken(pidlFile->mkid.abID[34] == '~'),  /* Win2k */
1589                "Should be derived 8.3 name!\n");
1590
1591         if (i == 0) /* First file name has an even number of chars. No need for alignment. */
1592             ok(pidlFile->mkid.abID[12 + strlen(szFile) + 1] != '\0' ||
1593                broken(pidlFile->mkid.cb == 2 + 12 + strlen(szFile) + 1 + 1),    /* Win2k */
1594                 "Alignment byte, where there shouldn't be!\n");
1595
1596         if (i == 1) /* Second file name has an uneven number of chars => alignment byte */
1597             ok(pidlFile->mkid.abID[12 + strlen(szFile) + 1] == '\0',
1598                 "There should be an alignment byte, but isn't!\n");
1599
1600         /* The offset of the FileStructW member is stored as a WORD at the end of the pidl. */
1601         cbOffset = *(WORD*)(((LPBYTE)pidlFile)+pidlFile->mkid.cb-sizeof(WORD));
1602         ok ((cbOffset >= sizeof(struct FileStructA) &&
1603             cbOffset <= pidlFile->mkid.cb - sizeof(struct FileStructW)) ||
1604             broken(pidlFile->mkid.cb == 2 + 12 + strlen(szFile) + 1 + 1) ||     /* Win2k on short names */
1605             broken(pidlFile->mkid.cb == 2 + 12 + strlen(szFile) + 1 + 12 + 1),  /* Win2k on long names */
1606             "Wrong offset value (%d) stored at the end of the PIDL\n", cbOffset);
1607
1608         if (cbOffset >= sizeof(struct FileStructA) &&
1609             cbOffset <= pidlFile->mkid.cb - sizeof(struct FileStructW))
1610         {
1611             struct FileStructW *pFileStructW = (struct FileStructW *)(((LPBYTE)pidlFile)+cbOffset);
1612
1613             ok(pidlFile->mkid.cb == cbOffset + pFileStructW->cbLen,
1614                 "FileStructW's offset and length should add up to the PIDL's length!\n");
1615
1616             if (pidlFile->mkid.cb == cbOffset + pFileStructW->cbLen) {
1617                 /* Since we just created the file, time of creation,
1618                  * time of last access and time of last write access just be the same.
1619                  * These tests seem to fail sometimes (on WinXP), if the test is run again shortly
1620                  * after the first run. I do remember something with NTFS keeping the creation time
1621                  * if a file is deleted and then created again within a couple of seconds or so.
1622                  * Might be the reason. */
1623                 ok (pFileStructA->uFileDate == pFileStructW->uDate &&
1624                     pFileStructA->uFileTime == pFileStructW->uTime,
1625                     "Last write time should match creation time!\n");
1626
1627                 /* On FAT filesystems the last access time is midnight
1628                    local time, so the values of uDate2 and uTime2 will
1629                    depend on the local timezone.  If the times are exactly
1630                    equal then the dates should be identical for both FAT
1631                    and NTFS as no timezone is more than 1 day away from UTC.
1632                 */
1633                 if (pFileStructA->uFileTime == pFileStructW->uTime2)
1634                 {
1635                     ok (pFileStructA->uFileDate == pFileStructW->uDate2,
1636                         "Last write date and time should match last access date and time!\n");
1637                 }
1638                 else
1639                 {
1640                     /* Filesystem may be FAT. Check date within 1 day
1641                        and seconds are zero. */
1642                     trace ("Filesystem may be FAT. Performing less strict atime test.\n");
1643                     ok ((pFileStructW->uTime2 & 0x1F) == 0,
1644                         "Last access time on FAT filesystems should have zero seconds.\n");
1645                     /* TODO: Perform check for date being within one day.*/
1646                 }
1647
1648                 ok (!lstrcmpW(wszFile[i], pFileStructW->wszName) ||
1649                     !lstrcmpW(wszFile[i], (WCHAR *)(pFileStructW->abFooBar2 + 22)) || /* Vista */
1650                     !lstrcmpW(wszFile[i], (WCHAR *)(pFileStructW->abFooBar2 + 26)), /* Win7 */
1651                     "The filename should be stored in unicode at this position!\n");
1652             }
1653         }
1654
1655         pILFree(pidlFile);
1656     }
1657
1658     IShellFolder_Release(psfPersonal);
1659 }
1660
1661 static void test_SHGetFolderPathAndSubDirA(void)
1662 {
1663     HRESULT ret;
1664     BOOL delret;
1665     DWORD dwret;
1666     int i;
1667     static char wine[] = "wine";
1668     static char winetemp[] = "wine\\temp";
1669     static char appdata[MAX_PATH];
1670     static char testpath[MAX_PATH];
1671     static char toolongpath[MAX_PATH+1];
1672
1673     if(!pSHGetFolderPathAndSubDirA)
1674     {
1675         win_skip("SHGetFolderPathAndSubDirA not present!\n");
1676         return;
1677     }
1678
1679     if(!pSHGetFolderPathA) {
1680         win_skip("SHGetFolderPathA not present!\n");
1681         return;
1682     }
1683     if(FAILED(pSHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, appdata)))
1684     {
1685         win_skip("SHGetFolderPathA failed for CSIDL_LOCAL_APPDATA!\n");
1686         return;
1687     }
1688
1689     sprintf(testpath, "%s\\%s", appdata, winetemp);
1690     delret = RemoveDirectoryA(testpath);
1691     if(!delret && (ERROR_PATH_NOT_FOUND != GetLastError()) ) {
1692         win_skip("RemoveDirectoryA(%s) failed with error %u\n", testpath, GetLastError());
1693         return;
1694     }
1695
1696     sprintf(testpath, "%s\\%s", appdata, wine);
1697     delret = RemoveDirectoryA(testpath);
1698     if(!delret && (ERROR_PATH_NOT_FOUND != GetLastError()) && (ERROR_FILE_NOT_FOUND != GetLastError())) {
1699         win_skip("RemoveDirectoryA(%s) failed with error %u\n", testpath, GetLastError());
1700         return;
1701     }
1702
1703     /* test invalid second parameter */
1704     ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | 0xff, NULL, SHGFP_TYPE_CURRENT, wine, testpath);
1705     ok(E_INVALIDARG == ret, "expected E_INVALIDARG, got  %x\n", ret);
1706
1707     /* test fourth parameter */
1708     ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, 2, winetemp, testpath);
1709     switch(ret) {
1710         case S_OK: /* winvista */
1711             ok(!strncmp(appdata, testpath, strlen(appdata)),
1712                 "expected %s to start with %s\n", testpath, appdata);
1713             ok(!lstrcmpA(&testpath[1 + strlen(appdata)], winetemp),
1714                 "expected %s to end with %s\n", testpath, winetemp);
1715             break;
1716         case E_INVALIDARG: /* winxp, win2k3 */
1717             break;
1718         default:
1719             ok(0, "expected S_OK or E_INVALIDARG, got  %x\n", ret);
1720     }
1721
1722     /* test fifth parameter */
1723     testpath[0] = '\0';
1724     ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, NULL, testpath);
1725     ok(S_OK == ret, "expected S_OK, got %x\n", ret);
1726     ok(!lstrcmpA(appdata, testpath), "expected %s, got %s\n", appdata, testpath);
1727
1728     testpath[0] = '\0';
1729     ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, "", testpath);
1730     ok(S_OK == ret, "expected S_OK, got %x\n", ret);
1731     ok(!lstrcmpA(appdata, testpath), "expected %s, got %s\n", appdata, testpath);
1732
1733     testpath[0] = '\0';
1734     ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, "\\", testpath);
1735     ok(S_OK == ret, "expected S_OK, got %x\n", ret);
1736     ok(!lstrcmpA(appdata, testpath), "expected %s, got %s\n", appdata, testpath);
1737
1738     for(i=0; i< MAX_PATH; i++)
1739         toolongpath[i] = '0' + i % 10;
1740     toolongpath[MAX_PATH] = '\0';
1741     ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, toolongpath, testpath);
1742     ok(HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE) == ret,
1743         "expected %x, got %x\n", HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE), ret);
1744
1745     testpath[0] = '\0';
1746     ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, wine, NULL);
1747     ok((S_OK == ret) || (E_INVALIDARG == ret), "expected S_OK or E_INVALIDARG, got %x\n", ret);
1748
1749     /* test a not existing path */
1750     testpath[0] = '\0';
1751     ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, winetemp, testpath);
1752     ok(HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == ret,
1753         "expected %x, got %x\n", HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND), ret);
1754
1755     /* create a directory inside a not existing directory */
1756     testpath[0] = '\0';
1757     ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_CREATE | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, winetemp, testpath);
1758     ok(S_OK == ret, "expected S_OK, got %x\n", ret);
1759     ok(!strncmp(appdata, testpath, strlen(appdata)),
1760         "expected %s to start with %s\n", testpath, appdata);
1761     ok(!lstrcmpA(&testpath[1 + strlen(appdata)], winetemp),
1762         "expected %s to end with %s\n", testpath, winetemp);
1763     dwret = GetFileAttributes(testpath);
1764     ok(FILE_ATTRIBUTE_DIRECTORY | dwret, "expected %x to contain FILE_ATTRIBUTE_DIRECTORY\n", dwret);
1765
1766     /* cleanup */
1767     sprintf(testpath, "%s\\%s", appdata, winetemp);
1768     RemoveDirectoryA(testpath);
1769     sprintf(testpath, "%s\\%s", appdata, wine);
1770     RemoveDirectoryA(testpath);
1771 }
1772
1773 static void test_LocalizedNames(void)
1774 {
1775     static char cCurrDirA[MAX_PATH];
1776     WCHAR cCurrDirW[MAX_PATH], tempbufW[25];
1777     IShellFolder *IDesktopFolder, *testIShellFolder;
1778     ITEMIDLIST *newPIDL;
1779     int len;
1780     HRESULT hr;
1781     static char resourcefile[MAX_PATH];
1782     DWORD res;
1783     HANDLE file;
1784     STRRET strret;
1785
1786     static const char desktopini_contents1[] =
1787         "[.ShellClassInfo]\r\n"
1788         "LocalizedResourceName=@";
1789     static const char desktopini_contents2[] =
1790         ",-1\r\n";
1791     static WCHAR foldernameW[] = {'t','e','s','t','f','o','l','d','e','r',0};
1792     static const WCHAR folderdisplayW[] = {'F','o','l','d','e','r',' ','N','a','m','e',' ','R','e','s','o','u','r','c','e',0};
1793
1794     /* create folder with desktop.ini and localized name in GetModuleFileNameA(NULL) */
1795     CreateDirectoryA(".\\testfolder", NULL);
1796
1797     SetFileAttributesA(".\\testfolder", GetFileAttributesA(".\\testfolder")|FILE_ATTRIBUTE_SYSTEM);
1798
1799     GetModuleFileNameA(NULL, resourcefile, MAX_PATH);
1800
1801     file = CreateFileA(".\\testfolder\\desktop.ini", GENERIC_WRITE, 0, NULL,
1802                          CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
1803     ok(file != INVALID_HANDLE_VALUE, "CreateFileA failed %i\n", GetLastError());
1804     ok(WriteFile(file, desktopini_contents1, strlen(desktopini_contents1), &res, NULL) &&
1805        WriteFile(file, resourcefile, strlen(resourcefile), &res, NULL) &&
1806        WriteFile(file, desktopini_contents2, strlen(desktopini_contents2), &res, NULL),
1807        "WriteFile failed %i\n", GetLastError());
1808     CloseHandle(file);
1809
1810     /* get IShellFolder for parent */
1811     GetCurrentDirectoryA(MAX_PATH, cCurrDirA);
1812     len = lstrlenA(cCurrDirA);
1813
1814     if (len == 0) {
1815         win_skip("GetCurrentDirectoryA returned empty string. Skipping test_LocalizedNames\n");
1816         goto cleanup;
1817     }
1818     if(cCurrDirA[len-1] == '\\')
1819         cCurrDirA[len-1] = 0;
1820
1821     MultiByteToWideChar(CP_ACP, 0, cCurrDirA, -1, cCurrDirW, MAX_PATH);
1822
1823     hr = SHGetDesktopFolder(&IDesktopFolder);
1824     ok(hr == S_OK, "SHGetDesktopfolder failed %08x\n", hr);
1825
1826     hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cCurrDirW, NULL, &newPIDL, 0);
1827     ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
1828
1829     hr = IShellFolder_BindToObject(IDesktopFolder, newPIDL, NULL, (REFIID)&IID_IShellFolder, (LPVOID *)&testIShellFolder);
1830     ok(hr == S_OK, "BindToObject failed %08x\n", hr);
1831
1832     IMalloc_Free(ppM, newPIDL);
1833
1834     /* windows reads the display name from the resource */
1835     hr = IShellFolder_ParseDisplayName(testIShellFolder, NULL, NULL, foldernameW, NULL, &newPIDL, 0);
1836     ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
1837
1838     hr = IShellFolder_GetDisplayNameOf(testIShellFolder, newPIDL, SHGDN_INFOLDER, &strret);
1839     ok(hr == S_OK, "GetDisplayNameOf failed %08x\n", hr);
1840
1841     if (hr == S_OK && pStrRetToBufW)
1842     {
1843         hr = pStrRetToBufW(&strret, newPIDL, tempbufW, sizeof(tempbufW)/sizeof(WCHAR));
1844         ok (hr == S_OK, "StrRetToBufW failed! hr = %08x\n", hr);
1845         todo_wine
1846         ok (!lstrcmpiW(tempbufW, folderdisplayW) ||
1847             broken(!lstrcmpiW(tempbufW, foldernameW)), /* W2K */
1848             "GetDisplayNameOf returned %s\n", wine_dbgstr_w(tempbufW));
1849     }
1850
1851     /* editing name is also read from the resource */
1852     hr = IShellFolder_GetDisplayNameOf(testIShellFolder, newPIDL, SHGDN_INFOLDER|SHGDN_FOREDITING, &strret);
1853     ok(hr == S_OK, "GetDisplayNameOf failed %08x\n", hr);
1854
1855     if (hr == S_OK && pStrRetToBufW)
1856     {
1857         hr = pStrRetToBufW(&strret, newPIDL, tempbufW, sizeof(tempbufW)/sizeof(WCHAR));
1858         ok (hr == S_OK, "StrRetToBufW failed! hr = %08x\n", hr);
1859         todo_wine
1860         ok (!lstrcmpiW(tempbufW, folderdisplayW) ||
1861             broken(!lstrcmpiW(tempbufW, foldernameW)), /* W2K */
1862             "GetDisplayNameOf returned %s\n", wine_dbgstr_w(tempbufW));
1863     }
1864
1865     /* parsing name is unchanged */
1866     hr = IShellFolder_GetDisplayNameOf(testIShellFolder, newPIDL, SHGDN_INFOLDER|SHGDN_FORPARSING, &strret);
1867     ok(hr == S_OK, "GetDisplayNameOf failed %08x\n", hr);
1868
1869     if (hr == S_OK && pStrRetToBufW)
1870     {
1871         hr = pStrRetToBufW(&strret, newPIDL, tempbufW, sizeof(tempbufW)/sizeof(WCHAR));
1872         ok (hr == S_OK, "StrRetToBufW failed! hr = %08x\n", hr);
1873         ok (!lstrcmpiW(tempbufW, foldernameW), "GetDisplayNameOf returned %s\n", wine_dbgstr_w(tempbufW));
1874     }
1875
1876     IShellFolder_Release(IDesktopFolder);
1877     IShellFolder_Release(testIShellFolder);
1878
1879     IMalloc_Free(ppM, newPIDL);
1880
1881 cleanup:
1882     DeleteFileA(".\\testfolder\\desktop.ini");
1883     SetFileAttributesA(".\\testfolder", GetFileAttributesA(".\\testfolder")&~FILE_ATTRIBUTE_SYSTEM);
1884     RemoveDirectoryA(".\\testfolder");
1885 }
1886
1887 static void test_SHCreateShellItem(void)
1888 {
1889     IShellItem *shellitem, *shellitem2;
1890     IPersistIDList *persistidl;
1891     LPITEMIDLIST pidl_cwd=NULL, pidl_testfile, pidl_abstestfile, pidl_test;
1892     HRESULT ret;
1893     char curdirA[MAX_PATH];
1894     WCHAR curdirW[MAX_PATH];
1895     WCHAR fnbufW[MAX_PATH];
1896     IShellFolder *desktopfolder=NULL, *currentfolder=NULL;
1897     static WCHAR testfileW[] = {'t','e','s','t','f','i','l','e',0};
1898
1899     GetCurrentDirectoryA(MAX_PATH, curdirA);
1900
1901     if (!pSHCreateShellItem)
1902     {
1903         win_skip("SHCreateShellItem isn't available\n");
1904         return;
1905     }
1906
1907     if (!lstrlenA(curdirA))
1908     {
1909         win_skip("GetCurrentDirectoryA returned empty string, skipping test_SHCreateShellItem\n");
1910         return;
1911     }
1912
1913     MultiByteToWideChar(CP_ACP, 0, curdirA, -1, curdirW, MAX_PATH);
1914
1915     ret = SHGetDesktopFolder(&desktopfolder);
1916     ok(SUCCEEDED(ret), "SHGetShellFolder returned %x\n", ret);
1917
1918     ret = IShellFolder_ParseDisplayName(desktopfolder, NULL, NULL, curdirW, NULL, &pidl_cwd, NULL);
1919     ok(SUCCEEDED(ret), "ParseDisplayName returned %x\n", ret);
1920
1921     ret = IShellFolder_BindToObject(desktopfolder, pidl_cwd, NULL, &IID_IShellFolder, (void**)&currentfolder);
1922     ok(SUCCEEDED(ret), "BindToObject returned %x\n", ret);
1923
1924     CreateTestFile(".\\testfile");
1925
1926     ret = IShellFolder_ParseDisplayName(currentfolder, NULL, NULL, testfileW, NULL, &pidl_testfile, NULL);
1927     ok(SUCCEEDED(ret), "ParseDisplayName returned %x\n", ret);
1928
1929     pidl_abstestfile = pILCombine(pidl_cwd, pidl_testfile);
1930
1931     ret = pSHCreateShellItem(NULL, NULL, NULL, &shellitem);
1932     ok(ret == E_INVALIDARG, "SHCreateShellItem returned %x\n", ret);
1933
1934     if (0) /* crashes on Windows XP */
1935     {
1936         pSHCreateShellItem(NULL, NULL, pidl_cwd, NULL);
1937         pSHCreateShellItem(pidl_cwd, NULL, NULL, &shellitem);
1938         pSHCreateShellItem(NULL, currentfolder, NULL, &shellitem);
1939         pSHCreateShellItem(pidl_cwd, currentfolder, NULL, &shellitem);
1940     }
1941
1942     ret = pSHCreateShellItem(NULL, NULL, pidl_cwd, &shellitem);
1943     ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret);
1944     if (SUCCEEDED(ret))
1945     {
1946         ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
1947         ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
1948         if (SUCCEEDED(ret))
1949         {
1950             ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
1951             ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
1952             if (SUCCEEDED(ret))
1953             {
1954                 ok(ILIsEqual(pidl_cwd, pidl_test), "id lists are not equal\n");
1955                 pILFree(pidl_test);
1956             }
1957             IPersistIDList_Release(persistidl);
1958         }
1959         IShellItem_Release(shellitem);
1960     }
1961
1962     ret = pSHCreateShellItem(pidl_cwd, NULL, pidl_testfile, &shellitem);
1963     ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret);
1964     if (SUCCEEDED(ret))
1965     {
1966         ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
1967         ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
1968         if (SUCCEEDED(ret))
1969         {
1970             ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
1971             ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
1972             if (SUCCEEDED(ret))
1973             {
1974                 ok(ILIsEqual(pidl_abstestfile, pidl_test), "id lists are not equal\n");
1975                 pILFree(pidl_test);
1976             }
1977             IPersistIDList_Release(persistidl);
1978         }
1979
1980         ret = IShellItem_GetParent(shellitem, &shellitem2);
1981         ok(SUCCEEDED(ret), "GetParent returned %x\n", ret);
1982         if (SUCCEEDED(ret))
1983         {
1984             ret = IShellItem_QueryInterface(shellitem2, &IID_IPersistIDList, (void**)&persistidl);
1985             ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
1986             if (SUCCEEDED(ret))
1987             {
1988                 ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
1989                 ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
1990                 if (SUCCEEDED(ret))
1991                 {
1992                     ok(ILIsEqual(pidl_cwd, pidl_test), "id lists are not equal\n");
1993                     pILFree(pidl_test);
1994                 }
1995                 IPersistIDList_Release(persistidl);
1996             }
1997             IShellItem_Release(shellitem2);
1998         }
1999
2000         IShellItem_Release(shellitem);
2001     }
2002
2003     ret = pSHCreateShellItem(NULL, currentfolder, pidl_testfile, &shellitem);
2004     ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret);
2005     if (SUCCEEDED(ret))
2006     {
2007         ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
2008         ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
2009         if (SUCCEEDED(ret))
2010         {
2011             ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
2012             ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
2013             if (SUCCEEDED(ret))
2014             {
2015                 ok(ILIsEqual(pidl_abstestfile, pidl_test), "id lists are not equal\n");
2016                 pILFree(pidl_test);
2017             }
2018             IPersistIDList_Release(persistidl);
2019         }
2020         IShellItem_Release(shellitem);
2021     }
2022
2023     /* if a parent pidl and shellfolder are specified, the shellfolder is ignored */
2024     ret = pSHCreateShellItem(pidl_cwd, desktopfolder, pidl_testfile, &shellitem);
2025     ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret);
2026     if (SUCCEEDED(ret))
2027     {
2028         ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
2029         ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
2030         if (SUCCEEDED(ret))
2031         {
2032             ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
2033             ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
2034             if (SUCCEEDED(ret))
2035             {
2036                 ok(ILIsEqual(pidl_abstestfile, pidl_test), "id lists are not equal\n");
2037                 pILFree(pidl_test);
2038             }
2039             IPersistIDList_Release(persistidl);
2040         }
2041         IShellItem_Release(shellitem);
2042     }
2043
2044     ret = pSHCreateShellItem(NULL, desktopfolder, pidl_testfile, &shellitem);
2045     ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret);
2046     if (SUCCEEDED(ret))
2047     {
2048         ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
2049         ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret);
2050         if (SUCCEEDED(ret))
2051         {
2052             ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
2053             ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret);
2054             if (SUCCEEDED(ret))
2055             {
2056                 ok(ILIsEqual(pidl_testfile, pidl_test), "id lists are not equal\n");
2057                 pILFree(pidl_test);
2058             }
2059             IPersistIDList_Release(persistidl);
2060         }
2061         IShellItem_Release(shellitem);
2062     }
2063
2064     /* SHCreateItemFromParsingName */
2065     if(pSHCreateItemFromParsingName)
2066     {
2067         if(0)
2068         {
2069             /* Crashes under windows 7 */
2070             ret = pSHCreateItemFromParsingName(NULL, NULL, &IID_IShellItem, NULL);
2071         }
2072
2073         shellitem = (void*)0xdeadbeef;
2074         ret = pSHCreateItemFromParsingName(NULL, NULL, &IID_IShellItem, (void**)&shellitem);
2075         ok(ret == E_INVALIDARG, "SHCreateItemFromParsingName returned %x\n", ret);
2076         ok(shellitem == NULL, "shellitem was %p.\n", shellitem);
2077
2078         ret = pSHCreateItemFromParsingName(testfileW, NULL, &IID_IShellItem, (void**)&shellitem);
2079         ok(ret == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
2080            "SHCreateItemFromParsingName returned %x\n", ret);
2081         if(SUCCEEDED(ret)) IShellItem_Release(shellitem);
2082
2083         lstrcpyW(fnbufW, curdirW);
2084         myPathAddBackslashW(fnbufW);
2085         lstrcatW(fnbufW, testfileW);
2086
2087         ret = pSHCreateItemFromParsingName(fnbufW, NULL, &IID_IShellItem, (void**)&shellitem);
2088         ok(ret == S_OK, "SHCreateItemFromParsingName returned %x\n", ret);
2089         if(SUCCEEDED(ret))
2090         {
2091             LPWSTR tmp_fname;
2092             ret = IShellItem_GetDisplayName(shellitem, SIGDN_FILESYSPATH, &tmp_fname);
2093             ok(ret == S_OK, "GetDisplayName returned %x\n", ret);
2094             if(SUCCEEDED(ret))
2095             {
2096                 ok(!lstrcmpW(fnbufW, tmp_fname), "strings not equal\n");
2097                 CoTaskMemFree(tmp_fname);
2098             }
2099             IShellItem_Release(shellitem);
2100         }
2101     }
2102     else
2103         win_skip("No SHCreateItemFromParsingName\n");
2104
2105
2106     /* SHCreateItemFromIDList */
2107     if(pSHCreateItemFromIDList)
2108     {
2109         if(0)
2110         {
2111             /* Crashes under win7 */
2112             ret = pSHCreateItemFromIDList(NULL, &IID_IShellItem, NULL);
2113         }
2114
2115         ret = pSHCreateItemFromIDList(NULL, &IID_IShellItem, (void**)&shellitem);
2116         ok(ret == E_INVALIDARG, "SHCreateItemFromIDList returned %x\n", ret);
2117
2118         ret = pSHCreateItemFromIDList(pidl_cwd, &IID_IShellItem, (void**)&shellitem);
2119         ok(ret == S_OK, "SHCreateItemFromIDList returned %x\n", ret);
2120         if (SUCCEEDED(ret))
2121         {
2122             ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
2123             ok(ret == S_OK, "QueryInterface returned %x\n", ret);
2124             if (SUCCEEDED(ret))
2125             {
2126                 ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
2127                 ok(ret == S_OK, "GetIDList returned %x\n", ret);
2128                 if (SUCCEEDED(ret))
2129                 {
2130                     ok(ILIsEqual(pidl_cwd, pidl_test), "id lists are not equal\n");
2131                     pILFree(pidl_test);
2132                 }
2133                 IPersistIDList_Release(persistidl);
2134             }
2135             IShellItem_Release(shellitem);
2136         }
2137
2138         ret = pSHCreateItemFromIDList(pidl_testfile, &IID_IShellItem, (void**)&shellitem);
2139         ok(ret == S_OK, "SHCreateItemFromIDList returned %x\n", ret);
2140         if (SUCCEEDED(ret))
2141         {
2142             ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl);
2143             ok(ret == S_OK, "QueryInterface returned %x\n", ret);
2144             if (SUCCEEDED(ret))
2145             {
2146                 ret = IPersistIDList_GetIDList(persistidl, &pidl_test);
2147                 ok(ret == S_OK, "GetIDList returned %x\n", ret);
2148                 if (SUCCEEDED(ret))
2149                 {
2150                     ok(ILIsEqual(pidl_testfile, pidl_test), "id lists are not equal\n");
2151                     pILFree(pidl_test);
2152                 }
2153                 IPersistIDList_Release(persistidl);
2154             }
2155             IShellItem_Release(shellitem);
2156         }
2157     }
2158     else
2159         win_skip("No SHCreateItemFromIDList\n");
2160
2161     DeleteFileA(".\\testfile");
2162     pILFree(pidl_abstestfile);
2163     pILFree(pidl_testfile);
2164     pILFree(pidl_cwd);
2165     IShellFolder_Release(currentfolder);
2166     IShellFolder_Release(desktopfolder);
2167 }
2168
2169 static void test_SHGetNameFromIDList(void)
2170 {
2171     IShellItem *shellitem;
2172     LPITEMIDLIST pidl;
2173     LPWSTR name_string;
2174     HRESULT hres;
2175     UINT i;
2176     static const DWORD flags[] = {
2177         SIGDN_NORMALDISPLAY, SIGDN_PARENTRELATIVEPARSING,
2178         SIGDN_DESKTOPABSOLUTEPARSING,SIGDN_PARENTRELATIVEEDITING,
2179         SIGDN_DESKTOPABSOLUTEEDITING, /*SIGDN_FILESYSPATH, SIGDN_URL, */
2180         SIGDN_PARENTRELATIVEFORADDRESSBAR,SIGDN_PARENTRELATIVE, -1234};
2181
2182     if(!pSHGetNameFromIDList)
2183     {
2184         win_skip("SHGetNameFromIDList missing.\n");
2185         return;
2186     }
2187
2188     /* These should be available on any platform that passed the above test. */
2189     ok(pSHCreateShellItem != NULL, "SHCreateShellItem missing.\n");
2190     ok(pSHBindToParent != NULL, "SHBindToParent missing.\n");
2191     ok(pSHGetSpecialFolderLocation != NULL, "SHGetSpecialFolderLocation missing.\n");
2192     ok(pStrRetToBufW != NULL, "StrRetToBufW missing.\n");
2193
2194     if(0)
2195     {
2196         /* Crashes under win7 */
2197         hres = pSHGetNameFromIDList(NULL, 0, NULL);
2198     }
2199
2200     hres = pSHGetNameFromIDList(NULL, 0, &name_string);
2201     ok(hres == E_INVALIDARG, "Got 0x%08x\n", hres);
2202
2203     /* Test the desktop */
2204     hres = pSHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, &pidl);
2205     ok(hres == S_OK, "Got 0x%08x\n", hres);
2206     hres = pSHCreateShellItem(NULL, NULL, pidl, &shellitem);
2207     ok(hres == S_OK, "Got 0x%08x\n", hres);
2208     if(SUCCEEDED(hres))
2209     {
2210         WCHAR *nameSI, *nameSH;
2211         WCHAR buf[MAX_PATH];
2212         HRESULT hrSI, hrSH, hrSF;
2213         STRRET strret;
2214         IShellFolder *psf;
2215         BOOL res;
2216
2217         SHGetDesktopFolder(&psf);
2218         for(i = 0; flags[i] != -1234; i++)
2219         {
2220             hrSI = IShellItem_GetDisplayName(shellitem, flags[i], &nameSI);
2221             ok(hrSI == S_OK, "Got 0x%08x\n", hrSI);
2222             hrSH = pSHGetNameFromIDList(pidl, flags[i], &nameSH);
2223             ok(hrSH == S_OK, "Got 0x%08x\n", hrSH);
2224             hrSF = IShellFolder_GetDisplayNameOf(psf, pidl, flags[i] & 0xffff, &strret);
2225             ok(hrSF == S_OK, "Got 0x%08x\n", hrSF);
2226
2227             if(SUCCEEDED(hrSI) && SUCCEEDED(hrSH))
2228                 ok(!lstrcmpW(nameSI, nameSH), "Strings differ.\n");
2229
2230             if(SUCCEEDED(hrSF))
2231             {
2232                 pStrRetToBufW(&strret, NULL, buf, MAX_PATH);
2233                 if(SUCCEEDED(hrSI))
2234                     ok(!lstrcmpW(nameSI, buf), "Strings differ.\n");
2235                 if(SUCCEEDED(hrSF))
2236                     ok(!lstrcmpW(nameSI, buf), "Strings differ.\n");
2237             }
2238             if(SUCCEEDED(hrSI)) CoTaskMemFree(nameSI);
2239             if(SUCCEEDED(hrSH)) CoTaskMemFree(nameSH);
2240         }
2241         IShellFolder_Release(psf);
2242
2243         if(pSHGetPathFromIDListW){
2244             hrSI = pSHGetNameFromIDList(pidl, SIGDN_FILESYSPATH, &nameSI);
2245             ok(hrSI == S_OK, "Got 0x%08x\n", hrSI);
2246             res = pSHGetPathFromIDListW(pidl, buf);
2247             ok(res == TRUE, "Got %d\n", res);
2248             if(SUCCEEDED(hrSI) && res)
2249                 ok(!lstrcmpW(nameSI, buf), "Strings differ.\n");
2250             if(SUCCEEDED(hrSI)) CoTaskMemFree(nameSI);
2251         }else
2252             win_skip("pSHGetPathFromIDListW not available\n");
2253
2254         hres = pSHGetNameFromIDList(pidl, SIGDN_URL, &name_string);
2255         todo_wine ok(hres == S_OK, "Got 0x%08x\n", hres);
2256         if(SUCCEEDED(hres)) CoTaskMemFree(name_string);
2257
2258         IShellItem_Release(shellitem);
2259     }
2260     pILFree(pidl);
2261
2262     /* Test the control panel */
2263     hres = pSHGetSpecialFolderLocation(NULL, CSIDL_CONTROLS, &pidl);
2264     ok(hres == S_OK, "Got 0x%08x\n", hres);
2265     hres = pSHCreateShellItem(NULL, NULL, pidl, &shellitem);
2266     ok(hres == S_OK, "Got 0x%08x\n", hres);
2267     if(SUCCEEDED(hres))
2268     {
2269         WCHAR *nameSI, *nameSH;
2270         WCHAR buf[MAX_PATH];
2271         HRESULT hrSI, hrSH, hrSF;
2272         STRRET strret;
2273         IShellFolder *psf;
2274         BOOL res;
2275
2276         SHGetDesktopFolder(&psf);
2277         for(i = 0; flags[i] != -1234; i++)
2278         {
2279             hrSI = IShellItem_GetDisplayName(shellitem, flags[i], &nameSI);
2280             ok(hrSI == S_OK, "Got 0x%08x\n", hrSI);
2281             hrSH = pSHGetNameFromIDList(pidl, flags[i], &nameSH);
2282             ok(hrSH == S_OK, "Got 0x%08x\n", hrSH);
2283             hrSF = IShellFolder_GetDisplayNameOf(psf, pidl, flags[i] & 0xffff, &strret);
2284             ok(hrSF == S_OK, "Got 0x%08x\n", hrSF);
2285
2286             if(SUCCEEDED(hrSI) && SUCCEEDED(hrSH))
2287                 ok(!lstrcmpW(nameSI, nameSH), "Strings differ.\n");
2288
2289             if(SUCCEEDED(hrSF))
2290             {
2291                 pStrRetToBufW(&strret, NULL, buf, MAX_PATH);
2292                 if(SUCCEEDED(hrSI))
2293                     ok(!lstrcmpW(nameSI, buf), "Strings differ.\n");
2294                 if(SUCCEEDED(hrSF))
2295                     ok(!lstrcmpW(nameSI, buf), "Strings differ.\n");
2296             }
2297             if(SUCCEEDED(hrSI)) CoTaskMemFree(nameSI);
2298             if(SUCCEEDED(hrSH)) CoTaskMemFree(nameSH);
2299         }
2300         IShellFolder_Release(psf);
2301
2302         if(pSHGetPathFromIDListW){
2303             hrSI = pSHGetNameFromIDList(pidl, SIGDN_FILESYSPATH, &nameSI);
2304             ok(hrSI == E_INVALIDARG, "Got 0x%08x\n", hrSI);
2305             res = pSHGetPathFromIDListW(pidl, buf);
2306             ok(res == FALSE, "Got %d\n", res);
2307             if(SUCCEEDED(hrSI) && res)
2308                 ok(!lstrcmpW(nameSI, buf), "Strings differ.\n");
2309             if(SUCCEEDED(hrSI)) CoTaskMemFree(nameSI);
2310         }else
2311             win_skip("pSHGetPathFromIDListW not available\n");
2312
2313         hres = pSHGetNameFromIDList(pidl, SIGDN_URL, &name_string);
2314         todo_wine ok(hres == E_NOTIMPL /* Win7 */ || hres == S_OK /* Vista */,
2315                      "Got 0x%08x\n", hres);
2316         if(SUCCEEDED(hres)) CoTaskMemFree(name_string);
2317
2318         IShellItem_Release(shellitem);
2319     }
2320     pILFree(pidl);
2321 }
2322
2323 static void test_SHGetItemFromDataObject(void)
2324 {
2325     IShellFolder *psfdesktop;
2326     IShellItem *psi;
2327     IShellView *psv;
2328     HRESULT hres;
2329
2330     if(!pSHGetItemFromDataObject)
2331     {
2332         win_skip("No SHGetItemFromDataObject.\n");
2333         return;
2334     }
2335
2336     if(0)
2337     {
2338         /* Crashes under win7 */
2339         hres = pSHGetItemFromDataObject(NULL, 0, &IID_IShellItem, NULL);
2340     }
2341
2342     hres = pSHGetItemFromDataObject(NULL, 0, &IID_IShellItem, (void**)&psv);
2343     ok(hres == E_INVALIDARG, "got 0x%08x\n", hres);
2344
2345     SHGetDesktopFolder(&psfdesktop);
2346
2347     hres = IShellFolder_CreateViewObject(psfdesktop, NULL, &IID_IShellView, (void**)&psv);
2348     ok(hres == S_OK, "got 0x%08x\n", hres);
2349     if(SUCCEEDED(hres))
2350     {
2351         IEnumIDList *peidl;
2352         IDataObject *pdo;
2353         SHCONTF enum_flags;
2354
2355         enum_flags = SHCONTF_NONFOLDERS | SHCONTF_FOLDERS | SHCONTF_INCLUDEHIDDEN;
2356         hres = IShellFolder_EnumObjects(psfdesktop, NULL, enum_flags, &peidl);
2357         ok(hres == S_OK, "got 0x%08x\n", hres);
2358         if(SUCCEEDED(hres))
2359         {
2360             LPITEMIDLIST apidl[5];
2361             UINT count = 0, i;
2362
2363             for(count = 0; count < 5; count++)
2364                 if(IEnumIDList_Next(peidl, 1, &apidl[count], NULL) != S_OK)
2365                     break;
2366
2367             if(count)
2368             {
2369                 hres = IShellFolder_GetUIObjectOf(psfdesktop, NULL, 1, (LPCITEMIDLIST*)apidl,
2370                                                   &IID_IDataObject, NULL, (void**)&pdo);
2371                 ok(hres == S_OK, "got 0x%08x\n", hres);
2372                 if(SUCCEEDED(hres))
2373                 {
2374                     hres = pSHGetItemFromDataObject(pdo, DOGIF_DEFAULT, &IID_IShellItem, (void**)&psi);
2375                     ok(hres == S_OK, "got 0x%08x\n", hres);
2376                     if(SUCCEEDED(hres)) IShellItem_Release(psi);
2377                     hres = pSHGetItemFromDataObject(pdo, DOGIF_TRAVERSE_LINK, &IID_IShellItem, (void**)&psi);
2378                     ok(hres == S_OK, "got 0x%08x\n", hres);
2379                     if(SUCCEEDED(hres)) IShellItem_Release(psi);
2380                     hres = pSHGetItemFromDataObject(pdo, DOGIF_NO_HDROP, &IID_IShellItem, (void**)&psi);
2381                     ok(hres == S_OK, "got 0x%08x\n", hres);
2382                     if(SUCCEEDED(hres)) IShellItem_Release(psi);
2383                     hres = pSHGetItemFromDataObject(pdo, DOGIF_NO_URL, &IID_IShellItem, (void**)&psi);
2384                     ok(hres == S_OK, "got 0x%08x\n", hres);
2385                     if(SUCCEEDED(hres)) IShellItem_Release(psi);
2386                     hres = pSHGetItemFromDataObject(pdo, DOGIF_ONLY_IF_ONE, &IID_IShellItem, (void**)&psi);
2387                     ok(hres == S_OK, "got 0x%08x\n", hres);
2388                     if(SUCCEEDED(hres)) IShellItem_Release(psi);
2389
2390                     IDataObject_Release(pdo);
2391                 }
2392             }
2393             else
2394                 skip("No file(s) found - skipping single-file test.\n");
2395
2396             if(count > 1)
2397             {
2398                 hres = IShellFolder_GetUIObjectOf(psfdesktop, NULL, count, (LPCITEMIDLIST*)apidl,
2399                                                   &IID_IDataObject, NULL, (void**)&pdo);
2400                 ok(hres == S_OK, "got 0x%08x\n", hres);
2401                 if(SUCCEEDED(hres))
2402                 {
2403                     hres = pSHGetItemFromDataObject(pdo, DOGIF_DEFAULT, &IID_IShellItem, (void**)&psi);
2404                     ok(hres == S_OK, "got 0x%08x\n", hres);
2405                     if(SUCCEEDED(hres)) IShellItem_Release(psi);
2406                     hres = pSHGetItemFromDataObject(pdo, DOGIF_TRAVERSE_LINK, &IID_IShellItem, (void**)&psi);
2407                     ok(hres == S_OK, "got 0x%08x\n", hres);
2408                     if(SUCCEEDED(hres)) IShellItem_Release(psi);
2409                     hres = pSHGetItemFromDataObject(pdo, DOGIF_NO_HDROP, &IID_IShellItem, (void**)&psi);
2410                     ok(hres == S_OK, "got 0x%08x\n", hres);
2411                     if(SUCCEEDED(hres)) IShellItem_Release(psi);
2412                     hres = pSHGetItemFromDataObject(pdo, DOGIF_NO_URL, &IID_IShellItem, (void**)&psi);
2413                     ok(hres == S_OK, "got 0x%08x\n", hres);
2414                     if(SUCCEEDED(hres)) IShellItem_Release(psi);
2415                     hres = pSHGetItemFromDataObject(pdo, DOGIF_ONLY_IF_ONE, &IID_IShellItem, (void**)&psi);
2416                     ok(hres == E_FAIL, "got 0x%08x\n", hres);
2417                     if(SUCCEEDED(hres)) IShellItem_Release(psi);
2418                     IDataObject_Release(pdo);
2419                 }
2420             }
2421             else
2422                 skip("zero or one file found - skipping multi-file test.\n");
2423
2424             for(i = 0; i < count; i++)
2425                 pILFree(apidl[i]);
2426
2427             IEnumIDList_Release(peidl);
2428         }
2429
2430         IShellView_Release(psv);
2431     }
2432
2433     IShellFolder_Release(psfdesktop);
2434 }
2435
2436 static void test_ShellItemCompare(void)
2437 {
2438     IShellItem *psi[9]; /* a\a, a\b, a\c, b\a, .. */
2439     IShellItem *psi_a, *psi_b, *psi_c;
2440     IShellFolder *psf_desktop, *psf_current;
2441     LPITEMIDLIST pidl_cwd;
2442     WCHAR curdirW[MAX_PATH];
2443     BOOL failed;
2444     HRESULT hr;
2445     static const WCHAR filesW[][9] = {
2446         {'a','\\','a',0}, {'a','\\','b',0}, {'a','\\','c',0},
2447         {'b','\\','a',0}, {'b','\\','b',0}, {'b','\\','c',0},
2448         {'c','\\','a',0}, {'c','\\','b',0}, {'c','\\','c',0} };
2449     int order;
2450     UINT i;
2451
2452     if(!pSHCreateShellItem)
2453     {
2454         win_skip("SHCreateShellItem missing.\n");
2455         return;
2456     }
2457
2458     GetCurrentDirectoryW(MAX_PATH, curdirW);
2459     if(!lstrlenW(curdirW))
2460     {
2461         skip("Failed to get current directory, skipping.\n");
2462         return;
2463     }
2464
2465     CreateDirectoryA(".\\a", NULL);
2466     CreateDirectoryA(".\\b", NULL);
2467     CreateDirectoryA(".\\c", NULL);
2468     CreateTestFile(".\\a\\a");
2469     CreateTestFile(".\\a\\b");
2470     CreateTestFile(".\\a\\c");
2471     CreateTestFile(".\\b\\a");
2472     CreateTestFile(".\\b\\b");
2473     CreateTestFile(".\\b\\c");
2474     CreateTestFile(".\\c\\a");
2475     CreateTestFile(".\\c\\b");
2476     CreateTestFile(".\\c\\c");
2477
2478     SHGetDesktopFolder(&psf_desktop);
2479     hr = IShellFolder_ParseDisplayName(psf_desktop, NULL, NULL, curdirW, NULL, &pidl_cwd, NULL);
2480     ok(SUCCEEDED(hr), "ParseDisplayName returned %x\n", hr);
2481     hr = IShellFolder_BindToObject(psf_desktop, pidl_cwd, NULL, &IID_IShellFolder, (void**)&psf_current);
2482     ok(SUCCEEDED(hr), "BindToObject returned %x\n", hr);
2483     IShellFolder_Release(psf_desktop);
2484
2485     /* Generate ShellItems for the files */
2486     ZeroMemory(&psi, sizeof(IShellItem*)*9);
2487     failed = FALSE;
2488     for(i = 0; i < 9; i++)
2489     {
2490         LPITEMIDLIST pidl_testfile = NULL;
2491
2492         hr = IShellFolder_ParseDisplayName(psf_current, NULL, NULL, (LPWSTR)filesW[i],
2493                                            NULL, &pidl_testfile, NULL);
2494         ok(SUCCEEDED(hr), "ParseDisplayName returned %x\n", hr);
2495         if(SUCCEEDED(hr))
2496         {
2497             hr = pSHCreateShellItem(NULL, NULL, pidl_testfile, &psi[i]);
2498             ok(hr == S_OK, "Got 0x%08x\n", hr);
2499             pILFree(pidl_testfile);
2500         }
2501         if(FAILED(hr)) failed = TRUE;
2502     }
2503     if(failed)
2504     {
2505         skip("Failed to create all shellitems. \n");
2506         goto cleanup;
2507     }
2508
2509     /* Generate ShellItems for the folders */
2510     psi_a = psi_b = psi_c = NULL;
2511     hr = IShellItem_GetParent(psi[0], &psi_a);
2512     ok(hr == S_OK, "Got 0x%08x\n", hr);
2513     if(FAILED(hr)) failed = TRUE;
2514     hr = IShellItem_GetParent(psi[3], &psi_b);
2515     ok(hr == S_OK, "Got 0x%08x\n", hr);
2516     if(FAILED(hr)) failed = TRUE;
2517     hr = IShellItem_GetParent(psi[6], &psi_c);
2518     ok(hr == S_OK, "Got 0x%08x\n", hr);
2519     if(FAILED(hr)) failed = TRUE;
2520
2521     if(failed)
2522     {
2523         skip("Failed to create shellitems. \n");
2524         goto cleanup;
2525     }
2526
2527     if(0)
2528     {
2529         /* Crashes on native (win7, winxp) */
2530         hr = IShellItem_Compare(psi_a, NULL, 0, NULL);
2531         hr = IShellItem_Compare(psi_a, psi_b, 0, NULL);
2532         hr = IShellItem_Compare(psi_a, NULL, 0, &order);
2533     }
2534
2535     /* Basics */
2536     for(i = 0; i < 9; i++)
2537     {
2538         hr = IShellItem_Compare(psi[i], psi[i], SICHINT_DISPLAY, &order);
2539         ok(hr == S_OK, "Got 0x%08x\n", hr);
2540         ok(order == 0, "Got order %d\n", order);
2541         hr = IShellItem_Compare(psi[i], psi[i], SICHINT_CANONICAL, &order);
2542         ok(hr == S_OK, "Got 0x%08x\n", hr);
2543         ok(order == 0, "Got order %d\n", order);
2544         hr = IShellItem_Compare(psi[i], psi[i], SICHINT_ALLFIELDS, &order);
2545         ok(hr == S_OK, "Got 0x%08x\n", hr);
2546         ok(order == 0, "Got order %d\n", order);
2547     }
2548
2549     /* Order */
2550     /* a\b:a\a , a\b:a\c, a\b:a\b */
2551     hr = IShellItem_Compare(psi[1], psi[0], SICHINT_DISPLAY, &order);
2552     ok(hr == S_FALSE, "Got 0x%08x\n", hr);
2553     ok(order == 1, "Got order %d\n", order);
2554     hr = IShellItem_Compare(psi[1], psi[2], SICHINT_DISPLAY, &order);
2555     ok(hr == S_FALSE, "Got 0x%08x\n", hr);
2556     ok(order == -1, "Got order %d\n", order);
2557     hr = IShellItem_Compare(psi[1], psi[1], SICHINT_DISPLAY, &order);
2558     ok(hr == S_OK, "Got 0x%08x\n", hr);
2559     ok(order == 0, "Got order %d\n", order);
2560
2561     /* b\b:a\b, b\b:c\b, b\b:c\b */
2562     hr = IShellItem_Compare(psi[4], psi[1], SICHINT_DISPLAY, &order);
2563     ok(hr == S_FALSE, "Got 0x%08x\n", hr);
2564     ok(order == 1, "Got order %d\n", order);
2565     hr = IShellItem_Compare(psi[4], psi[7], SICHINT_DISPLAY, &order);
2566     ok(hr == S_FALSE, "Got 0x%08x\n", hr);
2567     ok(order == -1, "Got order %d\n", order);
2568     hr = IShellItem_Compare(psi[4], psi[4], SICHINT_DISPLAY, &order);
2569     ok(hr == S_OK, "Got 0x%08x\n", hr);
2570     ok(order == 0, "Got order %d\n", order);
2571
2572     /* b:a\a, b:a\c, b:a\b */
2573     hr = IShellItem_Compare(psi_b, psi[0], SICHINT_DISPLAY, &order);
2574     ok(hr == S_FALSE, "Got 0x%08x\n", hr);
2575     todo_wine ok(order == 1, "Got order %d\n", order);
2576     hr = IShellItem_Compare(psi_b, psi[2], SICHINT_DISPLAY, &order);
2577     ok(hr == S_FALSE, "Got 0x%08x\n", hr);
2578     todo_wine ok(order == 1, "Got order %d\n", order);
2579     hr = IShellItem_Compare(psi_b, psi[1], SICHINT_DISPLAY, &order);
2580     ok(hr == S_FALSE, "Got 0x%08x\n", hr);
2581     todo_wine ok(order == 1, "Got order %d\n", order);
2582
2583     /* b:c\a, b:c\c, b:c\b */
2584     hr = IShellItem_Compare(psi_b, psi[6], SICHINT_DISPLAY, &order);
2585     ok(hr == S_FALSE, "Got 0x%08x\n", hr);
2586     ok(order == -1, "Got order %d\n", order);
2587     hr = IShellItem_Compare(psi_b, psi[8], SICHINT_DISPLAY, &order);
2588     ok(hr == S_FALSE, "Got 0x%08x\n", hr);
2589     ok(order == -1, "Got order %d\n", order);
2590     hr = IShellItem_Compare(psi_b, psi[7], SICHINT_DISPLAY, &order);
2591     ok(hr == S_FALSE, "Got 0x%08x\n", hr);
2592     ok(order == -1, "Got order %d\n", order);
2593
2594     /* a\b:a\a , a\b:a\c, a\b:a\b */
2595     hr = IShellItem_Compare(psi[1], psi[0], SICHINT_CANONICAL, &order);
2596     ok(hr == S_FALSE, "Got 0x%08x\n", hr);
2597     ok(order == 1, "Got order %d\n", order);
2598     hr = IShellItem_Compare(psi[1], psi[2], SICHINT_CANONICAL, &order);
2599     ok(hr == S_FALSE, "Got 0x%08x\n", hr);
2600     ok(order == -1, "Got order %d\n", order);
2601     hr = IShellItem_Compare(psi[1], psi[1], SICHINT_CANONICAL, &order);
2602     ok(hr == S_OK, "Got 0x%08x\n", hr);
2603     ok(order == 0, "Got order %d\n", order);
2604
2605     /* b\b:a\b, b\b:c\b, b\b:c\b */
2606     hr = IShellItem_Compare(psi[4], psi[1], SICHINT_CANONICAL, &order);
2607     ok(hr == S_FALSE, "Got 0x%08x\n", hr);
2608     ok(order == 1, "Got order %d\n", order);
2609     hr = IShellItem_Compare(psi[4], psi[7], SICHINT_CANONICAL, &order);
2610     ok(hr == S_FALSE, "Got 0x%08x\n", hr);
2611     ok(order == -1, "Got order %d\n", order);
2612     hr = IShellItem_Compare(psi[4], psi[4], SICHINT_CANONICAL, &order);
2613     ok(hr == S_OK, "Got 0x%08x\n", hr);
2614     ok(order == 0, "Got order %d\n", order);
2615
2616     /* b:a\a, b:a\c, b:a\b */
2617     hr = IShellItem_Compare(psi_b, psi[0], SICHINT_CANONICAL, &order);
2618     ok(hr == S_FALSE, "Got 0x%08x\n", hr);
2619     todo_wine ok(order == 1, "Got order %d\n", order);
2620     hr = IShellItem_Compare(psi_b, psi[2], SICHINT_CANONICAL, &order);
2621     ok(hr == S_FALSE, "Got 0x%08x\n", hr);
2622     todo_wine ok(order == 1, "Got order %d\n", order);
2623     hr = IShellItem_Compare(psi_b, psi[1], SICHINT_CANONICAL, &order);
2624     ok(hr == S_FALSE, "Got 0x%08x\n", hr);
2625     todo_wine ok(order == 1, "Got order %d\n", order);
2626
2627     /* b:c\a, b:c\c, b:c\b */
2628     hr = IShellItem_Compare(psi_b, psi[6], SICHINT_CANONICAL, &order);
2629     ok(hr == S_FALSE, "Got 0x%08x\n", hr);
2630     ok(order == -1, "Got order %d\n", order);
2631     hr = IShellItem_Compare(psi_b, psi[8], SICHINT_CANONICAL, &order);
2632     ok(hr == S_FALSE, "Got 0x%08x\n", hr);
2633     ok(order == -1, "Got order %d\n", order);
2634     hr = IShellItem_Compare(psi_b, psi[7], SICHINT_CANONICAL, &order);
2635     ok(hr == S_FALSE, "Got 0x%08x\n", hr);
2636     ok(order == -1, "Got order %d\n", order);
2637
2638 cleanup:
2639     IShellFolder_Release(psf_current);
2640
2641     DeleteFileA(".\\a\\a");
2642     DeleteFileA(".\\a\\b");
2643     DeleteFileA(".\\a\\c");
2644     DeleteFileA(".\\b\\a");
2645     DeleteFileA(".\\b\\b");
2646     DeleteFileA(".\\b\\c");
2647     DeleteFileA(".\\c\\a");
2648     DeleteFileA(".\\c\\b");
2649     DeleteFileA(".\\c\\c");
2650     RemoveDirectoryA(".\\a");
2651     RemoveDirectoryA(".\\b");
2652     RemoveDirectoryA(".\\c");
2653
2654     if(psi_a) IShellItem_Release(psi_a);
2655     if(psi_b) IShellItem_Release(psi_b);
2656     if(psi_c) IShellItem_Release(psi_c);
2657
2658     for(i = 0; i < 9; i++)
2659         if(psi[i]) IShellItem_Release(psi[i]);
2660 }
2661
2662 /**************************************************************/
2663 /* IUnknown implementation for counting QueryInterface calls. */
2664 typedef struct {
2665     const IUnknownVtbl *lpVtbl;
2666     struct if_count {
2667         REFIID id;
2668         LONG count;
2669     } *ifaces;
2670     LONG unknown;
2671 } IUnknownImpl;
2672
2673 static HRESULT WINAPI unk_fnQueryInterface(IUnknown *iunk, REFIID riid, void** punk)
2674 {
2675     IUnknownImpl *This = (IUnknownImpl*)iunk;
2676     UINT i, found;
2677     for(i = found = 0; This->ifaces[i].id != NULL; i++)
2678     {
2679         if(IsEqualIID(This->ifaces[i].id, riid))
2680         {
2681             This->ifaces[i].count++;
2682             found = 1;
2683             break;
2684         }
2685     }
2686     if(!found)
2687         This->unknown++;
2688     return E_NOINTERFACE;
2689 }
2690
2691 static ULONG WINAPI unk_fnAddRef(IUnknown *iunk)
2692 {
2693     return 2;
2694 }
2695
2696 static ULONG WINAPI unk_fnRelease(IUnknown *iunk)
2697 {
2698     return 1;
2699 }
2700
2701 const IUnknownVtbl vt_IUnknown = {
2702     unk_fnQueryInterface,
2703     unk_fnAddRef,
2704     unk_fnRelease
2705 };
2706
2707 static void test_SHGetIDListFromObject(void)
2708 {
2709     IUnknownImpl *punkimpl;
2710     IShellFolder *psfdesktop;
2711     IShellView *psv;
2712     LPITEMIDLIST pidl, pidl_desktop;
2713     HRESULT hres;
2714     UINT i;
2715     struct if_count ifaces[] =
2716         { {&IID_IPersistIDList, 0},
2717           {&IID_IPersistFolder2, 0},
2718           {&IID_IDataObject, 0},
2719           {&IID_IParentAndItem, 0},
2720           {&IID_IFolderView, 0},
2721           {NULL, 0} };
2722
2723     if(!pSHGetIDListFromObject)
2724     {
2725         win_skip("SHGetIDListFromObject missing.\n");
2726         return;
2727     }
2728
2729     ok(pSHGetSpecialFolderLocation != NULL, "SHGetSpecialFolderLocation missing.\n");
2730
2731     if(0)
2732     {
2733         /* Crashes native */
2734         pSHGetIDListFromObject(NULL, NULL);
2735         pSHGetIDListFromObject((void*)0xDEADBEEF, NULL);
2736     }
2737
2738     hres = pSHGetIDListFromObject(NULL, &pidl);
2739     ok(hres == E_NOINTERFACE, "Got %x\n", hres);
2740
2741     punkimpl = HeapAlloc(GetProcessHeap(), 0, sizeof(IUnknownImpl));
2742     punkimpl->lpVtbl = &vt_IUnknown;
2743     punkimpl->ifaces = ifaces;
2744     punkimpl->unknown = 0;
2745
2746     hres = pSHGetIDListFromObject((IUnknown*)punkimpl, &pidl);
2747     ok(hres == E_NOINTERFACE, "Got %x\n", hres);
2748     ok(ifaces[0].count, "interface not requested.\n");
2749     ok(ifaces[1].count, "interface not requested.\n");
2750     ok(ifaces[2].count, "interface not requested.\n");
2751     todo_wine
2752         ok(ifaces[3].count || broken(!ifaces[3].count /*vista*/),
2753            "interface not requested.\n");
2754     ok(ifaces[4].count || broken(!ifaces[4].count /*vista*/),
2755        "interface not requested.\n");
2756
2757     ok(!punkimpl->unknown, "Got %d unknown.\n", punkimpl->unknown);
2758     HeapFree(GetProcessHeap(), 0, punkimpl);
2759
2760     pidl_desktop = NULL;
2761     pSHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, &pidl_desktop);
2762     ok(pidl_desktop != NULL, "Failed to get desktop pidl.\n");
2763
2764     SHGetDesktopFolder(&psfdesktop);
2765
2766     /* Test IShellItem */
2767     if(pSHCreateShellItem)
2768     {
2769         IShellItem *shellitem;
2770         hres = pSHCreateShellItem(NULL, NULL, pidl_desktop, &shellitem);
2771         ok(hres == S_OK, "got 0x%08x\n", hres);
2772         if(SUCCEEDED(hres))
2773         {
2774             hres = pSHGetIDListFromObject((IUnknown*)shellitem, &pidl);
2775             ok(hres == S_OK, "got 0x%08x\n", hres);
2776             if(SUCCEEDED(hres))
2777             {
2778                 ok(ILIsEqual(pidl_desktop, pidl), "pidl not equal.\n");
2779                 pILFree(pidl);
2780             }
2781             IShellItem_Release(shellitem);
2782         }
2783     }
2784     else
2785         skip("no SHCreateShellItem.\n");
2786
2787     /* Test IShellFolder */
2788     hres = pSHGetIDListFromObject((IUnknown*)psfdesktop, &pidl);
2789     ok(hres == S_OK, "got 0x%08x\n", hres);
2790     if(SUCCEEDED(hres))
2791     {
2792         ok(ILIsEqual(pidl_desktop, pidl), "pidl not equal.\n");
2793         pILFree(pidl);
2794     }
2795
2796     hres = IShellFolder_CreateViewObject(psfdesktop, NULL, &IID_IShellView, (void**)&psv);
2797     ok(hres == S_OK, "got 0x%08x\n", hres);
2798     if(SUCCEEDED(hres))
2799     {
2800         IEnumIDList *peidl;
2801         IDataObject *pdo;
2802         SHCONTF enum_flags;
2803
2804         /* Test IFolderView */
2805         hres = pSHGetIDListFromObject((IUnknown*)psv, &pidl);
2806         ok(hres == S_OK, "got 0x%08x\n", hres);
2807         if(SUCCEEDED(hres))
2808         {
2809             ok(ILIsEqual(pidl_desktop, pidl), "pidl not equal.\n");
2810             pILFree(pidl);
2811         }
2812
2813         /* Test IDataObject */
2814         enum_flags = SHCONTF_NONFOLDERS | SHCONTF_FOLDERS | SHCONTF_INCLUDEHIDDEN;
2815         hres = IShellFolder_EnumObjects(psfdesktop, NULL, enum_flags, &peidl);
2816         ok(hres == S_OK, "got 0x%08x\n", hres);
2817         if(SUCCEEDED(hres))
2818         {
2819             LPITEMIDLIST apidl[5];
2820             UINT count = 0;
2821             for(count = 0; count < 5; count++)
2822                 if(IEnumIDList_Next(peidl, 1, &apidl[count], NULL) != S_OK)
2823                     break;
2824
2825             if(count)
2826             {
2827                 hres = IShellFolder_GetUIObjectOf(psfdesktop, NULL, 1, (LPCITEMIDLIST*)apidl,
2828                                                   &IID_IDataObject, NULL, (void**)&pdo);
2829                 ok(hres == S_OK, "got 0x%08x\n", hres);
2830                 if(SUCCEEDED(hres))
2831                 {
2832                     pidl = (void*)0xDEADBEEF;
2833                     hres = pSHGetIDListFromObject((IUnknown*)pdo, &pidl);
2834                     ok(hres == S_OK, "got 0x%08x\n", hres);
2835                     ok(pidl != NULL, "pidl is NULL.\n");
2836                     ok(ILIsEqual(pidl, apidl[0]), "pidl not equal.\n");
2837                     pILFree(pidl);
2838
2839                     IDataObject_Release(pdo);
2840                 }
2841             }
2842             else
2843                 skip("No files found - skipping single-file test.\n");
2844
2845             if(count > 1)
2846             {
2847                 hres = IShellFolder_GetUIObjectOf(psfdesktop, NULL, count, (LPCITEMIDLIST*)apidl,
2848                                                   &IID_IDataObject, NULL, (void**)&pdo);
2849                 ok(hres == S_OK, "got 0x%08x\n", hres);
2850                 if(SUCCEEDED(hres))
2851                 {
2852                     pidl = (void*)0xDEADBEEF;
2853                     hres = pSHGetIDListFromObject((IUnknown*)pdo, &pidl);
2854                     ok(hres == E_NOINTERFACE || hres == E_FAIL /*Vista*/,
2855                        "got 0x%08x\n", hres);
2856                     ok(pidl == NULL, "pidl is not NULL.\n");
2857
2858                     IDataObject_Release(pdo);
2859                 }
2860             }
2861             else
2862                 skip("zero or one file found - skipping multi-file test.\n");
2863
2864             for(i = 0; i < count; i++)
2865                 pILFree(apidl[i]);
2866
2867             IEnumIDList_Release(peidl);
2868         }
2869
2870         IShellView_Release(psv);
2871     }
2872
2873     IShellFolder_Release(psfdesktop);
2874     pILFree(pidl_desktop);
2875 }
2876
2877 static void test_SHGetItemFromObject(void)
2878 {
2879     IUnknownImpl *punkimpl;
2880     IShellFolder *psfdesktop;
2881     LPITEMIDLIST pidl;
2882     IShellItem *psi;
2883     IUnknown *punk;
2884     HRESULT hres;
2885     struct if_count ifaces[] =
2886         { {&IID_IPersistIDList, 0},
2887           {&IID_IPersistFolder2, 0},
2888           {&IID_IDataObject, 0},
2889           {&IID_IParentAndItem, 0},
2890           {&IID_IFolderView, 0},
2891           {NULL, 0} };
2892
2893     if(!pSHGetItemFromObject)
2894     {
2895         skip("No SHGetItemFromObject.\n");
2896         return;
2897     }
2898
2899     SHGetDesktopFolder(&psfdesktop);
2900
2901     if(0)
2902     {
2903         /* Crashes with Windows 7 */
2904         hres = pSHGetItemFromObject((IUnknown*)psfdesktop, &IID_IUnknown, (void**)NULL);
2905         hres = pSHGetItemFromObject(NULL, &IID_IUnknown, (void**)NULL);
2906         hres = pSHGetItemFromObject((IUnknown*)psfdesktop, NULL, (void**)&punk);
2907     }
2908
2909     hres = pSHGetItemFromObject(NULL, &IID_IUnknown, (void**)&punk);
2910     ok(hres == E_NOINTERFACE, "Got 0x%08x\n", hres);
2911
2912     punkimpl = HeapAlloc(GetProcessHeap(), 0, sizeof(IUnknownImpl));
2913     punkimpl->lpVtbl = &vt_IUnknown;
2914     punkimpl->ifaces = ifaces;
2915     punkimpl->unknown = 0;
2916
2917     /* The same as SHGetIDListFromObject */
2918     hres = pSHGetIDListFromObject((IUnknown*)punkimpl, &pidl);
2919     ok(hres == E_NOINTERFACE, "Got %x\n", hres);
2920     ok(ifaces[0].count, "interface not requested.\n");
2921     ok(ifaces[1].count, "interface not requested.\n");
2922     ok(ifaces[2].count, "interface not requested.\n");
2923     todo_wine
2924         ok(ifaces[3].count || broken(!ifaces[3].count /*vista*/),
2925            "interface not requested.\n");
2926     ok(ifaces[4].count || broken(!ifaces[4].count /*vista*/),
2927        "interface not requested.\n");
2928
2929     ok(!punkimpl->unknown, "Got %d unknown.\n", punkimpl->unknown);
2930     HeapFree(GetProcessHeap(), 0, punkimpl);
2931
2932     /* Test IShellItem */
2933     hres = pSHGetItemFromObject((IUnknown*)psfdesktop, &IID_IShellItem, (void**)&psi);
2934     ok(hres == S_OK, "Got 0x%08x\n", hres);
2935     if(SUCCEEDED(hres))
2936     {
2937         IShellItem *psi2;
2938         hres = pSHGetItemFromObject((IUnknown*)psi, &IID_IShellItem, (void**)&psi2);
2939         ok(hres == S_OK, "Got 0x%08x\n", hres);
2940         if(SUCCEEDED(hres))
2941         {
2942             todo_wine
2943                 ok(psi == psi2, "Different instances (%p != %p).\n", psi, psi2);
2944             IShellItem_Release(psi2);
2945         }
2946         IShellItem_Release(psi);
2947     }
2948
2949     IShellFolder_Release(psfdesktop);
2950 }
2951
2952 static void test_SHCreateShellItemArray(void)
2953 {
2954     IShellFolder *pdesktopsf, *psf;
2955     IShellItemArray *psia;
2956     IEnumIDList *peidl;
2957     HRESULT hr;
2958     WCHAR cTestDirW[MAX_PATH];
2959     LPITEMIDLIST pidl_testdir, pidl;
2960     static const WCHAR testdirW[] = {'t','e','s','t','d','i','r',0};
2961
2962     if(!pSHCreateShellItemArray) {
2963         skip("No pSHCreateShellItemArray!\n");
2964         return;
2965     }
2966
2967     ok(pSHGetSpecialFolderLocation != NULL, "SHGetSpecialFolderLocation missing.\n");
2968
2969     if(0)
2970     {
2971         /* Crashes under native */
2972         pSHCreateShellItemArray(NULL, NULL, 0, NULL, NULL);
2973         pSHCreateShellItemArray(NULL, NULL, 1, NULL, NULL);
2974         pSHCreateShellItemArray(NULL, pdesktopsf, 0, NULL, NULL);
2975         pSHCreateShellItemArray(pidl, NULL, 0, NULL, NULL);
2976     }
2977
2978     hr = pSHCreateShellItemArray(NULL, NULL, 0, NULL, &psia);
2979     ok(hr == E_POINTER, "got 0x%08x\n", hr);
2980
2981     SHGetDesktopFolder(&pdesktopsf);
2982     hr = pSHCreateShellItemArray(NULL, pdesktopsf, 0, NULL, &psia);
2983     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
2984
2985     hr = pSHCreateShellItemArray(NULL, pdesktopsf, 1, NULL, &psia);
2986     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
2987
2988     pSHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, &pidl);
2989     hr = pSHCreateShellItemArray(pidl, NULL, 0, NULL, &psia);
2990     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
2991     pILFree(pidl);
2992
2993     GetCurrentDirectoryW(MAX_PATH, cTestDirW);
2994     myPathAddBackslashW(cTestDirW);
2995     lstrcatW(cTestDirW, testdirW);
2996
2997     CreateFilesFolders();
2998
2999     hr = IShellFolder_ParseDisplayName(pdesktopsf, NULL, NULL, cTestDirW, NULL, &pidl_testdir, 0);
3000     ok(hr == S_OK, "got 0x%08x\n", hr);
3001     if(SUCCEEDED(hr))
3002     {
3003         hr = IShellFolder_BindToObject(pdesktopsf, pidl_testdir, NULL, (REFIID)&IID_IShellFolder,
3004                                        (void**)&psf);
3005         ok(hr == S_OK, "Got 0x%08x\n", hr);
3006     }
3007     IShellFolder_Release(pdesktopsf);
3008
3009     if(FAILED(hr))
3010     {
3011         skip("Failed to set up environment for SHCreateShellItemArray tests.\n");
3012         pILFree(pidl_testdir);
3013         Cleanup();
3014         return;
3015     }
3016
3017     hr = IShellFolder_EnumObjects(psf, NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &peidl);
3018     ok(hr == S_OK, "Got %08x\n", hr);
3019     if(SUCCEEDED(hr))
3020     {
3021         LPITEMIDLIST apidl[5];
3022         UINT done, numitems, i;
3023
3024         for(done = 0; done < 5; done++)
3025             if(IEnumIDList_Next(peidl, 1, &apidl[done], NULL) != S_OK)
3026                 break;
3027         ok(done == 5, "Got %d pidls\n", done);
3028         IEnumIDList_Release(peidl);
3029
3030         /* Create a ShellItemArray */
3031         hr = pSHCreateShellItemArray(NULL, psf, done, (LPCITEMIDLIST*)apidl, &psia);
3032         ok(hr == S_OK, "Got 0x%08x\n", hr);
3033         if(SUCCEEDED(hr))
3034         {
3035             IShellItem *psi;
3036
3037             if(0)
3038             {
3039                 /* Crashes in Windows 7 */
3040                 hr = IShellItemArray_GetCount(psia, NULL);
3041             }
3042
3043             IShellItemArray_GetCount(psia, &numitems);
3044             ok(numitems == done, "Got %d, expected %d\n", numitems, done);
3045
3046             hr = IShellItemArray_GetItemAt(psia, numitems, &psi);
3047             ok(hr == E_FAIL, "Got 0x%08x\n", hr);
3048
3049             /* Compare all the items */
3050             for(i = 0; i < numitems; i++)
3051             {
3052                 LPITEMIDLIST pidl_abs;
3053                 pidl_abs = ILCombine(pidl_testdir, apidl[i]);
3054
3055                 hr = IShellItemArray_GetItemAt(psia, i, &psi);
3056                 ok(hr == S_OK, "(%d) Failed with 0x%08x\n", i, hr);
3057                 if(SUCCEEDED(hr))
3058                 {
3059                     hr = pSHGetIDListFromObject((IUnknown*)psi, &pidl);
3060                     ok(hr == S_OK, "Got 0x%08x\n", hr);
3061                     if(SUCCEEDED(hr))
3062                     {
3063                         ok(ILIsEqual(pidl_abs, pidl), "Pidl not equal.\n");
3064                         pILFree(pidl);
3065                     }
3066                     IShellItem_Release(psi);
3067                 }
3068                 pILFree(pidl_abs);
3069             }
3070             for(i = 0; i < done; i++)
3071                 pILFree(apidl[i]);
3072             IShellItemArray_Release(psia);
3073         }
3074     }
3075
3076     /* SHCreateShellItemArrayFromShellItem */
3077     if(pSHCreateShellItemArrayFromShellItem)
3078     {
3079         IShellItem *psi;
3080
3081         if(0)
3082         {
3083             /* Crashes under Windows 7 */
3084             hr = pSHCreateShellItemArrayFromShellItem(NULL, &IID_IShellItemArray, NULL);
3085             hr = pSHCreateShellItemArrayFromShellItem(NULL, &IID_IShellItemArray, (void**)&psia);
3086             hr = pSHCreateShellItemArrayFromShellItem(psi, &IID_IShellItemArray, NULL);
3087         }
3088
3089         hr = pSHCreateItemFromIDList(pidl_testdir, &IID_IShellItem, (void**)&psi);
3090         ok(hr == S_OK, "Got 0x%08x\n", hr);
3091         if(SUCCEEDED(hr))
3092         {
3093             hr = pSHCreateShellItemArrayFromShellItem(psi, &IID_IShellItemArray, (void**)&psia);
3094             ok(hr == S_OK, "Got 0x%08x\n", hr);
3095             if(SUCCEEDED(hr))
3096             {
3097                 IShellItem *psi2;
3098                 UINT count;
3099                 hr = IShellItemArray_GetCount(psia, &count);
3100                 ok(hr == S_OK, "Got 0x%08x\n", hr);
3101                 ok(count == 1, "Got count %d\n", count);
3102                 hr = IShellItemArray_GetItemAt(psia, 0, &psi2);
3103                 ok(hr == S_OK, "Got 0x%08x\n", hr);
3104                 todo_wine
3105                     ok(psi != psi2, "ShellItems are of the same instance.\n");
3106                 if(SUCCEEDED(hr))
3107                 {
3108                     LPITEMIDLIST pidl1, pidl2;
3109                     hr = pSHGetIDListFromObject((IUnknown*)psi, &pidl1);
3110                     ok(hr == S_OK, "Got 0x%08x\n", hr);
3111                     ok(pidl1 != NULL, "pidl1 was null.\n");
3112                     hr = pSHGetIDListFromObject((IUnknown*)psi2, &pidl2);
3113                     ok(hr == S_OK, "Got 0x%08x\n", hr);
3114                     ok(pidl2 != NULL, "pidl2 was null.\n");
3115                     ok(ILIsEqual(pidl1, pidl2), "pidls not equal.\n");
3116                     pILFree(pidl1);
3117                     pILFree(pidl2);
3118                     IShellItem_Release(psi2);
3119                 }
3120                 hr = IShellItemArray_GetItemAt(psia, 1, &psi2);
3121                 ok(hr == E_FAIL, "Got 0x%08x\n", hr);
3122                 IShellItemArray_Release(psia);
3123             }
3124             IShellItem_Release(psi);
3125         }
3126     }
3127     else
3128         skip("No SHCreateShellItemArrayFromShellItem.\n");
3129
3130     if(pSHCreateShellItemArrayFromDataObject)
3131     {
3132         IShellView *psv;
3133
3134         if(0)
3135         {
3136             /* Crashes under Windows 7 */
3137             hr = pSHCreateShellItemArrayFromDataObject(NULL, &IID_IShellItemArray, NULL);
3138         }
3139         hr = pSHCreateShellItemArrayFromDataObject(NULL, &IID_IShellItemArray, (void**)&psia);
3140         ok(hr == E_INVALIDARG, "Got 0x%08x\n", hr);
3141
3142         hr = IShellFolder_CreateViewObject(psf, NULL, &IID_IShellView, (void**)&psv);
3143         ok(hr == S_OK, "got 0x%08x\n", hr);
3144         if(SUCCEEDED(hr))
3145         {
3146             IEnumIDList *peidl;
3147             IDataObject *pdo;
3148             SHCONTF enum_flags;
3149
3150             enum_flags = SHCONTF_NONFOLDERS | SHCONTF_FOLDERS | SHCONTF_INCLUDEHIDDEN;
3151             hr = IShellFolder_EnumObjects(psf, NULL, enum_flags, &peidl);
3152             ok(hr == S_OK, "got 0x%08x\n", hr);
3153             if(SUCCEEDED(hr))
3154             {
3155                 LPITEMIDLIST apidl[5];
3156                 UINT count, i;
3157
3158                 for(count = 0; count < 5; count++)
3159                     if(IEnumIDList_Next(peidl, 1, &apidl[count], NULL) != S_OK)
3160                         break;
3161                 ok(count == 5, "Got %d\n", count);
3162
3163                 if(count)
3164                 {
3165                     hr = IShellFolder_GetUIObjectOf(psf, NULL, count, (LPCITEMIDLIST*)apidl,
3166                                                     &IID_IDataObject, NULL, (void**)&pdo);
3167                     ok(hr == S_OK, "Got 0x%08x\n", hr);
3168                     if(SUCCEEDED(hr))
3169                     {
3170                         hr = pSHCreateShellItemArrayFromDataObject(pdo, &IID_IShellItemArray,
3171                                                                    (void**)&psia);
3172                         ok(hr == S_OK, "Got 0x%08x\n", hr);
3173                         if(SUCCEEDED(hr))
3174                         {
3175                             UINT count_sia, i;
3176                             hr = IShellItemArray_GetCount(psia, &count_sia);
3177                             ok(count_sia == count, "Counts differ (%d, %d)\n", count, count_sia);
3178                             for(i = 0; i < count_sia; i++)
3179                             {
3180                                 LPITEMIDLIST pidl_abs = ILCombine(pidl_testdir, apidl[i]);
3181                                 IShellItem *psi;
3182                                 hr = IShellItemArray_GetItemAt(psia, i, &psi);
3183                                 ok(hr == S_OK, "Got 0x%08x\n", hr);
3184                                 if(SUCCEEDED(hr))
3185                                 {
3186                                     LPITEMIDLIST pidl;
3187                                     hr = pSHGetIDListFromObject((IUnknown*)psi, &pidl);
3188                                     ok(hr == S_OK, "Got 0x%08x\n", hr);
3189                                     ok(pidl != NULL, "pidl as NULL.\n");
3190                                     ok(ILIsEqual(pidl, pidl_abs), "pidls differ.\n");
3191                                     pILFree(pidl);
3192                                     IShellItem_Release(psi);
3193                                 }
3194                                 pILFree(pidl_abs);
3195                             }
3196
3197                             IShellItemArray_Release(psia);
3198                         }
3199
3200                         IDataObject_Release(pdo);
3201                     }
3202                     for(i = 0; i < count; i++)
3203                         pILFree(apidl[i]);
3204                 }
3205                 else
3206                     skip("No files found - skipping test.\n");
3207
3208                 IEnumIDList_Release(peidl);
3209             }
3210             IShellView_Release(psv);
3211         }
3212     }
3213     else
3214         skip("No SHCreateShellItemArrayFromDataObject.\n");
3215
3216     IShellFolder_Release(psf);
3217     pILFree(pidl_testdir);
3218     Cleanup();
3219 }
3220
3221 static void test_ShellItemBindToHandler(void)
3222 {
3223     IShellItem *psi;
3224     LPITEMIDLIST pidl_desktop;
3225     HRESULT hr;
3226
3227     if(!pSHCreateShellItem)
3228     {
3229         skip("SHCreateShellItem missing.\n");
3230         return;
3231     }
3232
3233     hr = pSHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, &pidl_desktop);
3234     ok(hr == S_OK, "Got 0x%08x\n", hr);
3235     if(SUCCEEDED(hr))
3236     {
3237         hr = pSHCreateShellItem(NULL, NULL, pidl_desktop, &psi);
3238         ok(hr == S_OK, "Got 0x%08x\n", hr);
3239     }
3240     if(SUCCEEDED(hr))
3241     {
3242         IPersistFolder2 *ppf2;
3243         IUnknown *punk;
3244
3245         if(0)
3246         {
3247             /* Crashes under Windows 7 */
3248             hr = IShellItem_BindToHandler(psi, NULL, NULL, NULL, NULL);
3249             hr = IShellItem_BindToHandler(psi, NULL, &IID_IUnknown, &IID_IUnknown, NULL);
3250         }
3251         hr = IShellItem_BindToHandler(psi, NULL, &IID_IUnknown, &IID_IUnknown, (void**)&punk);
3252         ok(hr == MK_E_NOOBJECT, "Got 0x%08x\n", hr);
3253
3254         /* BHID_SFObject */
3255         hr = IShellItem_BindToHandler(psi, NULL, &BHID_SFObject, &IID_IShellFolder, (void**)&punk);
3256         ok(hr == S_OK, "Got 0x%08x\n", hr);
3257         if(SUCCEEDED(hr)) IUnknown_Release(punk);
3258         hr = IShellItem_BindToHandler(psi, NULL, &BHID_SFObject, &IID_IPersistFolder2, (void**)&ppf2);
3259         ok(hr == S_OK, "Got 0x%08x\n", hr);
3260         if(SUCCEEDED(hr))
3261         {
3262             LPITEMIDLIST pidl_tmp;
3263             hr = IPersistFolder2_GetCurFolder(ppf2, &pidl_tmp);
3264             ok(hr == S_OK, "Got 0x%08x\n", hr);
3265             if(SUCCEEDED(hr))
3266             {
3267                 ok(ILIsEqual(pidl_desktop, pidl_tmp), "Pidl not equal (%p, %p)\n", pidl_desktop, pidl_tmp);
3268                 pILFree(pidl_tmp);
3269             }
3270             IPersistFolder2_Release(ppf2);
3271         }
3272
3273         todo_wine
3274         {
3275             /* BHID_SFUIObject */
3276             hr = IShellItem_BindToHandler(psi, NULL, &BHID_SFUIObject, &IID_IDataObject, (void**)&punk);
3277             ok(hr == S_OK || broken(hr == E_NOINTERFACE /* XP */), "Got 0x%08x\n", hr);
3278             if(SUCCEEDED(hr)) IUnknown_Release(punk);
3279             hr = IShellItem_BindToHandler(psi, NULL, &BHID_SFUIObject, &IID_IContextMenu, (void**)&punk);
3280             ok(hr == S_OK || broken(hr == E_NOINTERFACE /* XP */), "Got 0x%08x\n", hr);
3281             if(SUCCEEDED(hr)) IUnknown_Release(punk);
3282
3283             /* BHID_SFViewObject */
3284             hr = IShellItem_BindToHandler(psi, NULL, &BHID_SFViewObject, &IID_IShellView, (void**)&punk);
3285             ok(hr == S_OK, "Got 0x%08x\n", hr);
3286             if(SUCCEEDED(hr)) IUnknown_Release(punk);
3287             hr = IShellItem_BindToHandler(psi, NULL, &BHID_SFViewObject, &IID_IShellFolderView, (void**)&punk);
3288             ok(hr == E_NOINTERFACE, "Got 0x%08x\n", hr);
3289             if(SUCCEEDED(hr)) IUnknown_Release(punk);
3290
3291             /* BHID_Storage */
3292             hr = IShellItem_BindToHandler(psi, NULL, &BHID_Storage, &IID_IStream, (void**)&punk);
3293             ok(hr == E_NOINTERFACE, "Got 0x%08x\n", hr);
3294             if(SUCCEEDED(hr)) IUnknown_Release(punk);
3295             hr = IShellItem_BindToHandler(psi, NULL, &BHID_Storage, &IID_IUnknown, (void**)&punk);
3296             ok(hr == S_OK, "Got 0x%08x\n", hr);
3297             if(SUCCEEDED(hr)) IUnknown_Release(punk);
3298
3299             /* BHID_Stream */
3300             hr = IShellItem_BindToHandler(psi, NULL, &BHID_Stream, &IID_IStream, (void**)&punk);
3301             ok(hr == E_NOINTERFACE, "Got 0x%08x\n", hr);
3302             if(SUCCEEDED(hr)) IUnknown_Release(punk);
3303             hr = IShellItem_BindToHandler(psi, NULL, &BHID_Stream, &IID_IUnknown, (void**)&punk);
3304             ok(hr == S_OK, "Got 0x%08x\n", hr);
3305             if(SUCCEEDED(hr)) IUnknown_Release(punk);
3306
3307             /* BHID_StorageEnum */
3308             hr = IShellItem_BindToHandler(psi, NULL, &BHID_StorageEnum, &IID_IEnumShellItems, (void**)&punk);
3309             ok(hr == S_OK, "Got 0x%08x\n", hr);
3310             if(SUCCEEDED(hr)) IUnknown_Release(punk);
3311
3312             /* BHID_Transfer */
3313             hr = IShellItem_BindToHandler(psi, NULL, &BHID_Transfer, &IID_IUnknown, (void**)&punk);
3314             ok(hr == E_NOINTERFACE || broken(hr == MK_E_NOOBJECT /* XP */), "Got 0x%08x\n", hr);
3315             if(SUCCEEDED(hr)) IUnknown_Release(punk);
3316
3317             /* BHID_EnumItems */
3318             hr = IShellItem_BindToHandler(psi, NULL, &BHID_EnumItems, &IID_IEnumShellItems, (void**)&punk);
3319             ok(hr == S_OK || broken(hr == MK_E_NOOBJECT /* XP */), "Got 0x%08x\n", hr);
3320             if(SUCCEEDED(hr)) IUnknown_Release(punk);
3321
3322             /* BHID_DataObject */
3323             hr = IShellItem_BindToHandler(psi, NULL, &BHID_DataObject, &IID_IDataObject, (void**)&punk);
3324             ok(hr == S_OK || broken(hr == MK_E_NOOBJECT /* XP */), "Got 0x%08x\n", hr);
3325             if(SUCCEEDED(hr)) IUnknown_Release(punk);
3326
3327             /* BHID_Filter */
3328             hr = IShellItem_BindToHandler(psi, NULL, &BHID_Filter, &IID_IUnknown, (void**)&punk);
3329             ok(hr == S_OK || broken(hr == MK_E_NOOBJECT /* XP */), "Got 0x%08x\n", hr);
3330             if(SUCCEEDED(hr)) IUnknown_Release(punk);
3331
3332             /* BHID_LinkTargetItem */
3333             hr = IShellItem_BindToHandler(psi, NULL, &BHID_LinkTargetItem, &IID_IShellItem, (void**)&punk);
3334             ok(hr == E_NOINTERFACE || broken(hr == E_INVALIDARG /* XP */), "Got 0x%08x\n", hr);
3335             if(SUCCEEDED(hr)) IUnknown_Release(punk);
3336             hr = IShellItem_BindToHandler(psi, NULL, &BHID_LinkTargetItem, &IID_IUnknown, (void**)&punk);
3337             ok(hr == E_NOINTERFACE || broken(hr == E_INVALIDARG /* XP */), "Got 0x%08x\n", hr);
3338             if(SUCCEEDED(hr)) IUnknown_Release(punk);
3339
3340             /* BHID_PropertyStore */
3341             hr = IShellItem_BindToHandler(psi, NULL, &BHID_PropertyStore, &IID_IPropertyStore, (void**)&punk);
3342             ok(hr == E_NOINTERFACE || broken(hr == MK_E_NOOBJECT /* XP */), "Got 0x%08x\n", hr);
3343             if(SUCCEEDED(hr)) IUnknown_Release(punk);
3344             hr = IShellItem_BindToHandler(psi, NULL, &BHID_PropertyStore, &IID_IPropertyStoreFactory, (void**)&punk);
3345             ok(hr == E_NOINTERFACE || broken(hr == MK_E_NOOBJECT /* XP */), "Got 0x%08x\n", hr);
3346             if(SUCCEEDED(hr)) IUnknown_Release(punk);
3347
3348             /* BHID_ThumbnailHandler */
3349             hr = IShellItem_BindToHandler(psi, NULL, &BHID_ThumbnailHandler, &IID_IUnknown, (void**)&punk);
3350             ok(hr == E_INVALIDARG || broken(hr == MK_E_NOOBJECT /* XP */), "Got 0x%08x\n", hr);
3351             if(SUCCEEDED(hr)) IUnknown_Release(punk);
3352
3353             /* BHID_AssociationArray */
3354             hr = IShellItem_BindToHandler(psi, NULL, &BHID_AssociationArray, &IID_IQueryAssociations, (void**)&punk);
3355             ok(hr == S_OK || broken(hr == MK_E_NOOBJECT /* XP */), "Got 0x%08x\n", hr);
3356             if(SUCCEEDED(hr)) IUnknown_Release(punk);
3357
3358             /* BHID_EnumAssocHandlers */
3359             hr = IShellItem_BindToHandler(psi, NULL, &BHID_EnumAssocHandlers, &IID_IUnknown, (void**)&punk);
3360             ok(hr == E_NOINTERFACE || broken(hr == MK_E_NOOBJECT /* XP */), "Got 0x%08x\n", hr);
3361             if(SUCCEEDED(hr)) IUnknown_Release(punk);
3362         }
3363
3364         IShellItem_Release(psi);
3365     }
3366     else
3367         skip("Failed to create ShellItem.\n");
3368
3369     pILFree(pidl_desktop);
3370 }
3371
3372 static void test_SHParseDisplayName(void)
3373 {
3374     LPITEMIDLIST pidl1, pidl2;
3375     IShellFolder *desktop;
3376     WCHAR dirW[MAX_PATH];
3377     WCHAR nameW[10];
3378     HRESULT hr;
3379     BOOL ret;
3380
3381     if (!pSHParseDisplayName)
3382     {
3383         win_skip("SHParseDisplayName isn't available\n");
3384         return;
3385     }
3386
3387 if (0)
3388 {
3389     /* crashes on native */
3390     hr = pSHParseDisplayName(NULL, NULL, NULL, 0, NULL);
3391     nameW[0] = 0;
3392     hr = pSHParseDisplayName(nameW, NULL, NULL, 0, NULL);
3393 }
3394
3395     pidl1 = (LPITEMIDLIST)0xdeadbeef;
3396     hr = pSHParseDisplayName(NULL, NULL, &pidl1, 0, NULL);
3397     ok(broken(hr == E_OUTOFMEMORY) /* < Vista */ ||
3398        hr == E_INVALIDARG, "failed %08x\n", hr);
3399     ok(pidl1 == 0, "expected null ptr, got %p\n", pidl1);
3400
3401     /* dummy name */
3402     nameW[0] = 0;
3403     hr = pSHParseDisplayName(nameW, NULL, &pidl1, 0, NULL);
3404     ok(hr == S_OK, "failed %08x\n", hr);
3405     hr = SHGetDesktopFolder(&desktop);
3406     ok(hr == S_OK, "failed %08x\n", hr);
3407     hr = IShellFolder_ParseDisplayName(desktop, NULL, NULL, nameW, NULL, &pidl2, NULL);
3408     ok(hr == S_OK, "failed %08x\n", hr);
3409     ret = pILIsEqual(pidl1, pidl2);
3410     ok(ret == TRUE, "expected equal idls\n");
3411     pILFree(pidl1);
3412     pILFree(pidl2);
3413
3414     /* with path */
3415     GetWindowsDirectoryW( dirW, MAX_PATH );
3416
3417     hr = pSHParseDisplayName(dirW, NULL, &pidl1, 0, NULL);
3418     ok(hr == S_OK, "failed %08x\n", hr);
3419     hr = IShellFolder_ParseDisplayName(desktop, NULL, NULL, dirW, NULL, &pidl2, NULL);
3420     ok(hr == S_OK, "failed %08x\n", hr);
3421
3422     ret = pILIsEqual(pidl1, pidl2);
3423     ok(ret == TRUE, "expected equal idls\n");
3424     pILFree(pidl1);
3425     pILFree(pidl2);
3426
3427     IShellFolder_Release(desktop);
3428 }
3429
3430 static void test_desktop_IPersist(void)
3431 {
3432     IShellFolder *desktop;
3433     IPersist *persist;
3434     IPersistFolder2 *ppf2;
3435     CLSID clsid;
3436     HRESULT hr;
3437
3438     hr = SHGetDesktopFolder(&desktop);
3439     ok(hr == S_OK, "failed %08x\n", hr);
3440
3441     hr = IShellFolder_QueryInterface(desktop, &IID_IPersist, (void**)&persist);
3442     ok(hr == S_OK || broken(hr == E_NOINTERFACE) /* NT4, W9X */, "failed %08x\n", hr);
3443
3444     if (hr == S_OK)
3445     {
3446     if (0)
3447     {
3448         /* crashes on native */
3449         hr = IPersist_GetClassID(persist, NULL);
3450     }
3451         memset(&clsid, 0, sizeof(clsid));
3452         hr = IPersist_GetClassID(persist, &clsid);
3453         ok(hr == S_OK, "failed %08x\n", hr);
3454         ok(IsEqualIID(&CLSID_ShellDesktop, &clsid), "Expected CLSID_ShellDesktop\n");
3455         IPersist_Release(persist);
3456     }
3457
3458     hr = IShellFolder_QueryInterface(desktop, &IID_IPersistFolder2, (void**)&ppf2);
3459     ok(hr == S_OK || broken(hr == E_NOINTERFACE) /* pre-Vista */, "failed %08x\n", hr);
3460     if(SUCCEEDED(hr))
3461     {
3462         IPersistFolder *ppf;
3463         LPITEMIDLIST pidl;
3464         hr = IShellFolder_QueryInterface(desktop, &IID_IPersistFolder, (void**)&ppf);
3465         ok(hr == S_OK, "IID_IPersistFolder2 without IID_IPersistFolder.\n");
3466         if(SUCCEEDED(hr))
3467             IPersistFolder_Release(ppf);
3468
3469         todo_wine {
3470             hr = IPersistFolder2_Initialize(ppf2, NULL);
3471             ok(hr == S_OK, "got %08x\n", hr);
3472         }
3473
3474         pidl = NULL;
3475         hr = IPersistFolder2_GetCurFolder(ppf2, &pidl);
3476         ok(hr == S_OK, "got %08x\n", hr);
3477         ok(pidl != NULL, "pidl was NULL.\n");
3478         if(SUCCEEDED(hr)) pILFree(pidl);
3479
3480         IPersistFolder2_Release(ppf2);
3481     }
3482
3483     IShellFolder_Release(desktop);
3484 }
3485
3486 static void test_GetUIObject(void)
3487 {
3488     IShellFolder *psf_desktop;
3489     IContextMenu *pcm;
3490     LPITEMIDLIST pidl;
3491     HRESULT hr;
3492     WCHAR path[MAX_PATH];
3493     const WCHAR filename[] =
3494         {'\\','t','e','s','t','d','i','r','\\','t','e','s','t','1','.','t','x','t',0};
3495
3496     if(!pSHBindToParent)
3497     {
3498         win_skip("SHBindToParent missing.\n");
3499         return;
3500     }
3501
3502     GetCurrentDirectoryW(MAX_PATH, path);
3503     if(!lstrlenW(path))
3504     {
3505         skip("GetCurrentDirectoryW returned an empty string.\n");
3506         return;
3507     }
3508     lstrcatW(path, filename);
3509     SHGetDesktopFolder(&psf_desktop);
3510
3511     CreateFilesFolders();
3512
3513     hr = IShellFolder_ParseDisplayName(psf_desktop, NULL, NULL, path, NULL, &pidl, 0);
3514     ok(hr == S_OK || broken(hr == E_FAIL) /* WinME */, "Got 0x%08x\n", hr);
3515     if(SUCCEEDED(hr))
3516     {
3517         IShellFolder *psf;
3518         LPCITEMIDLIST pidl_child;
3519         hr = pSHBindToParent(pidl, &IID_IShellFolder, (void**)&psf, &pidl_child);
3520         ok(hr == S_OK, "Got 0x%08x\n", hr);
3521         if(SUCCEEDED(hr))
3522         {
3523             hr = IShellFolder_GetUIObjectOf(psf, NULL, 1, (LPCITEMIDLIST*)&pidl_child,
3524                                             &IID_IContextMenu, NULL, (void**)&pcm);
3525             ok(hr == S_OK, "Got 0x%08x\n", hr);
3526             if(SUCCEEDED(hr))
3527             {
3528                 HMENU hmenu = CreatePopupMenu();
3529                 INT max_id, max_id_check;
3530                 UINT count, i;
3531                 const int id_upper_limit = 32767;
3532                 hr = IContextMenu_QueryContextMenu(pcm, hmenu, 0, 0, id_upper_limit, CMF_NORMAL);
3533                 ok(SUCCEEDED(hr), "Got 0x%08x\n", hr);
3534                 max_id = HRESULT_CODE(hr) - 1; /* returns max_id + 1 */
3535                 ok(max_id <= id_upper_limit, "Got %d\n", max_id);
3536                 count = GetMenuItemCount(hmenu);
3537                 ok(count, "Got %d\n", count);
3538
3539                 max_id_check = 0;
3540                 for(i = 0; i < count; i++)
3541                 {
3542                     MENUITEMINFOA mii;
3543                     INT res;
3544                     ZeroMemory(&mii, sizeof(MENUITEMINFOA));
3545                     mii.cbSize = sizeof(MENUITEMINFOA);
3546                     mii.fMask = MIIM_ID | MIIM_FTYPE;
3547
3548                     SetLastError(0);
3549                     res = GetMenuItemInfoA(hmenu, i, TRUE, &mii);
3550                     ok(res, "Failed (last error: %d).\n", GetLastError());
3551
3552                     ok( (mii.wID <= id_upper_limit) || (mii.fType & MFT_SEPARATOR),
3553                         "Got non-separator ID out of range: %d (type: %x) \n", mii.wID, mii.fType);
3554                     if(!(mii.fType & MFT_SEPARATOR))
3555                         max_id_check = (mii.wID>max_id_check)?mii.wID:max_id_check;
3556                 }
3557                 ok((max_id_check == max_id) ||
3558                    (max_id_check == max_id-1 /* Win 7 */),
3559                    "Not equal (or near equal), got %d and %d\n", max_id_check, max_id);
3560
3561 #define is_win2k() (pSHGetFolderPathA && !pSHGetFolderPathAndSubDirA)
3562
3563                 if(count && !is_win2k())   /* Test is interactive on w2k, so skip */
3564                 {
3565                     CMINVOKECOMMANDINFO cmi;
3566                     ZeroMemory(&cmi, sizeof(CMINVOKECOMMANDINFO));
3567                     cmi.cbSize = sizeof(CMINVOKECOMMANDINFO);
3568
3569                     /* Attempt to execute a nonexistent command */
3570                     cmi.lpVerb = MAKEINTRESOURCEA(9999);
3571                     hr = IContextMenu_InvokeCommand(pcm, &cmi);
3572                     ok(hr == E_INVALIDARG, "Got 0x%08x\n", hr);
3573
3574                     cmi.lpVerb = "foobar_wine_test";
3575                     hr = IContextMenu_InvokeCommand(pcm, &cmi);
3576                     ok( (hr == E_INVALIDARG) || (hr == E_FAIL /* Win7 */) ||
3577                         (hr == HRESULT_FROM_WIN32(ERROR_NO_ASSOCIATION) /* Vista */),
3578                         "Got 0x%08x\n", hr);
3579                 }
3580 #undef is_win2k
3581
3582                 DestroyMenu(hmenu);
3583                 IContextMenu_Release(pcm);
3584             }
3585             IShellFolder_Release(psf);
3586         }
3587         if(pILFree) pILFree(pidl);
3588     }
3589
3590     IShellFolder_Release(psf_desktop);
3591     Cleanup();
3592 }
3593
3594 #define verify_pidl(i,p) r_verify_pidl(__LINE__, i, p)
3595 static void r_verify_pidl(unsigned l, LPCITEMIDLIST pidl, const WCHAR *path)
3596 {
3597     LPCITEMIDLIST child;
3598     IShellFolder *parent;
3599     STRRET filename;
3600     HRESULT hr;
3601
3602     if(!pSHBindToParent){
3603         win_skip("SHBindToParent is not available, not performing full PIDL verification\n");
3604         if(path)
3605             ok_(__FILE__,l)(pidl != NULL, "Expected PIDL to be non-NULL\n");
3606         else
3607             ok_(__FILE__,l)(pidl == NULL, "Expected PIDL to be NULL\n");
3608         return;
3609     }
3610
3611     if(path){
3612         if(!pidl){
3613             ok_(__FILE__,l)(0, "didn't get expected path (%s), instead: NULL\n", wine_dbgstr_w(path));
3614             return;
3615         }
3616
3617         hr = pSHBindToParent(pidl, &IID_IShellFolder, (LPVOID*)&parent, &child);
3618         ok_(__FILE__,l)(hr == S_OK, "SHBindToParent failed: 0x%08x\n", hr);
3619         if(FAILED(hr))
3620             return;
3621
3622         hr = IShellFolder_GetDisplayNameOf(parent, child, SHGDN_FORPARSING, &filename);
3623         ok_(__FILE__,l)(hr == S_OK, "GetDisplayNameOf failed: 0x%08x\n", hr);
3624         if(FAILED(hr)){
3625             IShellFolder_Release(parent);
3626             return;
3627         }
3628
3629         ok_(__FILE__,l)(filename.uType == STRRET_WSTR || filename.uType == STRRET_CSTR,
3630                 "Got unexpected string type: %d\n", filename.uType);
3631         if(filename.uType == STRRET_WSTR){
3632             ok_(__FILE__,l)(lstrcmpW(path, filename.pOleStr) == 0,
3633                     "didn't get expected path (%s), instead: %s\n",
3634                      wine_dbgstr_w(path), wine_dbgstr_w(filename.pOleStr));
3635         }else if(filename.uType == STRRET_CSTR){
3636             ok_(__FILE__,l)(strcmp_wa(path, filename.cStr) == 0,
3637                     "didn't get expected path (%s), instead: %s\n",
3638                      wine_dbgstr_w(path), filename.cStr);
3639         }
3640
3641         IShellFolder_Release(parent);
3642     }else
3643         ok_(__FILE__,l)(pidl == NULL, "Expected PIDL to be NULL\n");
3644 }
3645
3646 static void test_SHSimpleIDListFromPath(void)
3647 {
3648     const WCHAR adirW[] = {'C',':','\\','s','i','d','l','f','p','d','i','r',0};
3649     const CHAR adirA[] = "C:\\sidlfpdir";
3650     BOOL br, is_unicode = !(GetVersion() & 0x80000000);
3651
3652     LPITEMIDLIST pidl = NULL;
3653
3654     if(!pSHSimpleIDListFromPathAW){
3655         win_skip("SHSimpleIDListFromPathAW not available\n");
3656         return;
3657     }
3658
3659     br = CreateDirectoryA(adirA, NULL);
3660     ok(br == TRUE, "CreateDirectory failed: %d\n", GetLastError());
3661
3662     if(is_unicode)
3663         pidl = pSHSimpleIDListFromPathAW(adirW);
3664     else
3665         pidl = pSHSimpleIDListFromPathAW(adirA);
3666     verify_pidl(pidl, adirW);
3667     pILFree(pidl);
3668
3669     br = RemoveDirectoryA(adirA);
3670     ok(br == TRUE, "RemoveDirectory failed: %d\n", GetLastError());
3671
3672     if(is_unicode)
3673         pidl = pSHSimpleIDListFromPathAW(adirW);
3674     else
3675         pidl = pSHSimpleIDListFromPathAW(adirA);
3676     verify_pidl(pidl, adirW);
3677     pILFree(pidl);
3678 }
3679
3680 /* IFileSystemBindData impl */
3681 static HRESULT WINAPI fsbd_QueryInterface(IFileSystemBindData *fsbd,
3682         REFIID riid, void **ppv)
3683 {
3684     if(IsEqualIID(riid, &IID_IFileSystemBindData) ||
3685             IsEqualIID(riid, &IID_IUnknown)){
3686         *ppv = fsbd;
3687         return S_OK;
3688     }
3689     return E_NOINTERFACE;
3690 }
3691
3692 static ULONG WINAPI fsbd_AddRef(IFileSystemBindData *fsbd)
3693 {
3694     return 2;
3695 }
3696
3697 static ULONG WINAPI fsbd_Release(IFileSystemBindData *fsbd)
3698 {
3699     return 1;
3700 }
3701
3702 static HRESULT WINAPI fsbd_SetFindData(IFileSystemBindData *fsbd,
3703         const WIN32_FIND_DATAW *pfd)
3704 {
3705     ok(0, "SetFindData called\n");
3706     return E_NOTIMPL;
3707 }
3708
3709 static HRESULT WINAPI fsbd_GetFindData_nul(IFileSystemBindData *fsbd,
3710         WIN32_FIND_DATAW *pfd)
3711 {
3712     memset(pfd, 0, sizeof(WIN32_FIND_DATAW));
3713     return S_OK;
3714 }
3715
3716 static HRESULT WINAPI fsbd_GetFindData_junk(IFileSystemBindData *fsbd,
3717         WIN32_FIND_DATAW *pfd)
3718 {
3719     memset(pfd, 0xdeadbeef, sizeof(WIN32_FIND_DATAW));
3720     return S_OK;
3721 }
3722
3723 static HRESULT WINAPI fsbd_GetFindData_invalid(IFileSystemBindData *fsbd,
3724         WIN32_FIND_DATAW *pfd)
3725 {
3726     memset(pfd, 0, sizeof(WIN32_FIND_DATAW));
3727     *pfd->cFileName = 'a';
3728     *pfd->cAlternateFileName = 'a';
3729     return S_OK;
3730 }
3731
3732 static HRESULT WINAPI fsbd_GetFindData_valid(IFileSystemBindData *fsbd,
3733         WIN32_FIND_DATAW *pfd)
3734 {
3735     static const WCHAR adirW[] = {'C',':','\\','f','s','b','d','d','i','r',0};
3736     HANDLE handle = FindFirstFileW(adirW, pfd);
3737     FindClose(handle);
3738     return S_OK;
3739 }
3740
3741 static HRESULT WINAPI fsbd_GetFindData_fail(IFileSystemBindData *fsbd,
3742         WIN32_FIND_DATAW *pfd)
3743 {
3744     return E_FAIL;
3745 }
3746
3747 static IFileSystemBindDataVtbl fsbdVtbl = {
3748     fsbd_QueryInterface,
3749     fsbd_AddRef,
3750     fsbd_Release,
3751     fsbd_SetFindData,
3752     NULL
3753 };
3754
3755 static IFileSystemBindData fsbd = { &fsbdVtbl };
3756
3757 static void test_ParseDisplayNamePBC(void)
3758 {
3759     WCHAR wFileSystemBindData[] =
3760         {'F','i','l','e',' ','S','y','s','t','e','m',' ','B','i','n','d',' ','D','a','t','a',0};
3761     WCHAR adirW[] = {'C',':','\\','f','s','b','d','d','i','r',0};
3762     const HRESULT exp_err = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
3763
3764     IShellFolder *psf;
3765     IBindCtx *pbc;
3766     HRESULT hres;
3767     ITEMIDLIST *pidl;
3768
3769     /* Check if we support WCHAR functions */
3770     SetLastError(0xdeadbeef);
3771     lstrcmpiW(adirW, adirW);
3772     if(GetLastError() == ERROR_CALL_NOT_IMPLEMENTED){
3773         win_skip("Most W-calls are not implemented\n");
3774         return;
3775     }
3776
3777     hres = SHGetDesktopFolder(&psf);
3778     ok(hres == S_OK, "SHGetDesktopFolder failed: 0x%08x\n", hres);
3779     if(FAILED(hres)){
3780         win_skip("Failed to get IShellFolder, can't run tests\n");
3781         return;
3782     }
3783
3784     /* fails on unknown dir with no IBindCtx */
3785     hres = IShellFolder_ParseDisplayName(psf, NULL, NULL, adirW, NULL, &pidl, NULL);
3786     ok(hres == exp_err || broken(hres == E_FAIL) /* NT4 */,
3787             "ParseDisplayName failed with wrong error: 0x%08x\n", hres);
3788
3789     /* fails on unknown dir with IBindCtx with no IFileSystemBindData */
3790     hres = CreateBindCtx(0, &pbc);
3791     ok(hres == S_OK, "CreateBindCtx failed: 0x%08x\n", hres);
3792
3793     hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, adirW, NULL, &pidl, NULL);
3794     ok(hres == exp_err || broken(hres == E_FAIL) /* NT4 */,
3795             "ParseDisplayName failed with wrong error: 0x%08x\n", hres);
3796
3797     /* unknown dir with IBindCtx with IFileSystemBindData */
3798     hres = IBindCtx_RegisterObjectParam(pbc, wFileSystemBindData, (IUnknown*)&fsbd);
3799     ok(hres == S_OK, "RegisterObjectParam failed: 0x%08x\n", hres);
3800
3801     /* return E_FAIL from GetFindData */
3802     pidl = (ITEMIDLIST*)0xdeadbeef;
3803     fsbdVtbl.GetFindData = fsbd_GetFindData_fail;
3804     hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, adirW, NULL, &pidl, NULL);
3805     ok(hres == S_OK || broken(hres == E_FAIL) /* NT4 */,
3806             "ParseDisplayName failed: 0x%08x\n", hres);
3807     if(SUCCEEDED(hres)){
3808         verify_pidl(pidl, adirW);
3809         ILFree(pidl);
3810     }
3811
3812     /* set FIND_DATA struct to NULLs */
3813     pidl = (ITEMIDLIST*)0xdeadbeef;
3814     fsbdVtbl.GetFindData = fsbd_GetFindData_nul;
3815     hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, adirW, NULL, &pidl, NULL);
3816     ok(hres == S_OK || broken(hres == E_FAIL) /* NT4 */,
3817             "ParseDisplayName failed: 0x%08x\n", hres);
3818     if(SUCCEEDED(hres)){
3819         verify_pidl(pidl, adirW);
3820         ILFree(pidl);
3821     }
3822
3823     /* set FIND_DATA struct to junk */
3824     pidl = (ITEMIDLIST*)0xdeadbeef;
3825     fsbdVtbl.GetFindData = fsbd_GetFindData_junk;
3826     hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, adirW, NULL, &pidl, NULL);
3827     ok(hres == S_OK || broken(hres == E_FAIL) /* NT4 */,
3828             "ParseDisplayName failed: 0x%08x\n", hres);
3829     if(SUCCEEDED(hres)){
3830         verify_pidl(pidl, adirW);
3831         ILFree(pidl);
3832     }
3833
3834     /* set FIND_DATA struct to invalid data */
3835     pidl = (ITEMIDLIST*)0xdeadbeef;
3836     fsbdVtbl.GetFindData = fsbd_GetFindData_invalid;
3837     hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, adirW, NULL, &pidl, NULL);
3838     ok(hres == S_OK || broken(hres == E_FAIL) /* NT4 */,
3839             "ParseDisplayName failed: 0x%08x\n", hres);
3840     if(SUCCEEDED(hres)){
3841         verify_pidl(pidl, adirW);
3842         ILFree(pidl);
3843     }
3844
3845     /* set FIND_DATA struct to valid data */
3846     pidl = (ITEMIDLIST*)0xdeadbeef;
3847     fsbdVtbl.GetFindData = fsbd_GetFindData_valid;
3848     hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, adirW, NULL, &pidl, NULL);
3849     ok(hres == S_OK || broken(hres == E_FAIL) /* NT4 */,
3850             "ParseDisplayName failed: 0x%08x\n", hres);
3851     if(SUCCEEDED(hres)){
3852         verify_pidl(pidl, adirW);
3853         ILFree(pidl);
3854     }
3855
3856     IBindCtx_Release(pbc);
3857     IShellFolder_Release(psf);
3858 }
3859
3860 static const CHAR testwindow_class[] = "testwindow";
3861 #define WM_USER_NOTIFY (WM_APP+1)
3862
3863 static struct {
3864     const char *id;
3865     BOOL exp_notify;
3866     UINT signal;
3867     const WCHAR *path_1;
3868     const WCHAR *path_2;
3869 } exp_data;
3870
3871 static LRESULT CALLBACK testwindow_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
3872 {
3873     UINT signal = (UINT)lparam;
3874
3875     switch(msg){
3876     case WM_USER_NOTIFY:
3877         if(exp_data.exp_notify){
3878             LPCITEMIDLIST *pidls = (LPCITEMIDLIST*)wparam;
3879
3880             ok(exp_data.signal == signal,
3881                     "%s: expected notification type %x, got: %x\n",
3882                     exp_data.id, exp_data.signal, signal);
3883
3884             trace("verifying pidls for: %s\n", exp_data.id);
3885             verify_pidl(pidls[0], exp_data.path_1);
3886             verify_pidl(pidls[1], exp_data.path_2);
3887
3888             exp_data.exp_notify = FALSE;
3889         }else
3890             ok(exp_data.exp_notify, "Didn't expect a WM_USER_NOTIFY message (event: %x)\n", signal);
3891         return 0;
3892     }
3893     return DefWindowProc(hwnd, msg, wparam, lparam);
3894 }
3895
3896 static void register_testwindow_class(void)
3897 {
3898     WNDCLASSEXA cls;
3899     ATOM ret;
3900
3901     ZeroMemory(&cls, sizeof(cls));
3902     cls.cbSize = sizeof(cls);
3903     cls.style = 0;
3904     cls.lpfnWndProc = testwindow_wndproc;
3905     cls.hInstance = GetModuleHandleA(NULL);
3906     cls.lpszClassName = testwindow_class;
3907
3908     SetLastError(0);
3909     ret = RegisterClassExA(&cls);
3910     ok(ret != 0, "RegisterClassExA failed: %d\n", GetLastError());
3911 }
3912
3913 /* SHCNF_FLUSH doesn't seem to work as advertised for SHCNF_PATHA, so we
3914  * have to poll repeatedly for the message to appear */
3915 static void do_events(void)
3916 {
3917     int c = 0;
3918     while (exp_data.exp_notify && (c++ < 10)){
3919         MSG msg;
3920         while(PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)){
3921             TranslateMessage(&msg);
3922             DispatchMessageA(&msg);
3923         }
3924         if(exp_data.exp_notify)
3925             Sleep(500);
3926     }
3927     trace("%s: took %d tries\n", exp_data.id, c);
3928 }
3929
3930 static void test_SHChangeNotify(void)
3931 {
3932     HWND wnd;
3933     ULONG notifyID;
3934     HRESULT hr;
3935     BOOL br, has_unicode;
3936     SHChangeNotifyEntry entries[1];
3937     const CHAR root_dirA[] = "C:\\shell32_cn_test";
3938     const WCHAR root_dirW[] = {'C',':','\\','s','h','e','l','l','3','2','_','c','n','_','t','e','s','t',0};
3939     const CHAR test_dirA[] = "C:\\shell32_cn_test\\test";
3940     const WCHAR test_dirW[] = {'C',':','\\','s','h','e','l','l','3','2','_','c','n','_','t','e','s','t','\\','t','e','s','t',0};
3941
3942     CreateDirectoryW(NULL, NULL);
3943     has_unicode = !(GetLastError() == ERROR_CALL_NOT_IMPLEMENTED);
3944
3945     /* set up the root directory & window */
3946     br = CreateDirectoryA(root_dirA, NULL);
3947     ok(br == TRUE, "CreateDirectory failed: %d\n", GetLastError());
3948
3949     register_testwindow_class();
3950
3951     wnd = CreateWindowExA(0, testwindow_class, testwindow_class, 0,
3952             CW_USEDEFAULT, CW_USEDEFAULT, 130, 105,
3953             NULL, NULL, GetModuleHandleA(NULL), 0);
3954     ok(wnd != NULL, "Failed to make a window\n");
3955
3956     entries[0].pidl = NULL;
3957     if(has_unicode)
3958         hr = SHILCreateFromPath(root_dirW, (LPITEMIDLIST*)&entries[0].pidl, 0);
3959     else
3960         hr = SHILCreateFromPath((LPCVOID)root_dirA, (LPITEMIDLIST*)&entries[0].pidl, 0);
3961     ok(hr == S_OK, "SHILCreateFromPath failed: 0x%08x\n", hr);
3962     entries[0].fRecursive = TRUE;
3963
3964     notifyID = SHChangeNotifyRegister(wnd, SHCNRF_ShellLevel,
3965             SHCNE_ALLEVENTS, WM_USER_NOTIFY, 1, entries);
3966     ok(notifyID != 0, "Failed to register a window for change notifications\n");
3967
3968     /* MKDIR */
3969     br = CreateDirectoryA(test_dirA, NULL);
3970     ok(br == TRUE, "CreateDirectory failed: %d\n", GetLastError());
3971
3972     if(has_unicode){
3973         exp_data.id = "MKDIR PATHW";
3974         exp_data.signal = SHCNE_MKDIR;
3975         exp_data.exp_notify = TRUE;
3976         exp_data.path_1 = test_dirW;
3977         exp_data.path_2 = NULL;
3978         SHChangeNotify(SHCNE_MKDIR, SHCNF_PATHW | SHCNF_FLUSH, test_dirW, NULL);
3979         do_events();
3980         ok(exp_data.exp_notify == FALSE, "Expected wndproc to be called\n");
3981     }else
3982         win_skip("skipping WCHAR tests\n");
3983
3984     exp_data.id = "MKDIR PATHA";
3985     exp_data.signal = SHCNE_MKDIR;
3986     exp_data.exp_notify = TRUE;
3987     exp_data.path_1 = test_dirW;
3988     exp_data.path_2 = NULL;
3989     SHChangeNotify(SHCNE_MKDIR, SHCNF_PATHA | SHCNF_FLUSH, test_dirA, NULL);
3990     do_events();
3991     ok(exp_data.exp_notify == FALSE, "Expected wndproc to be called\n");
3992
3993     /* RMDIR */
3994     br = RemoveDirectoryA(test_dirA);
3995     ok(br == TRUE, "RemoveDirectory failed: %d\n", GetLastError());
3996
3997     if(has_unicode){
3998         exp_data.id = "RMDIR PATHW";
3999         exp_data.signal = SHCNE_RMDIR;
4000         exp_data.exp_notify = TRUE;
4001         exp_data.path_1 = test_dirW;
4002         exp_data.path_2 = NULL;
4003         SHChangeNotify(SHCNE_RMDIR, SHCNF_PATHW | SHCNF_FLUSH, test_dirW, NULL);
4004         do_events();
4005         ok(exp_data.exp_notify == FALSE, "Expected wndproc to be called\n");
4006     }else
4007         win_skip("skipping WCHAR tests\n");
4008
4009     exp_data.id = "RMDIR PATHA";
4010     exp_data.signal = SHCNE_RMDIR;
4011     exp_data.exp_notify = TRUE;
4012     exp_data.path_1 = test_dirW;
4013     exp_data.path_2 = NULL;
4014     SHChangeNotify(SHCNE_RMDIR, SHCNF_PATHA | SHCNF_FLUSH, test_dirA, NULL);
4015     do_events();
4016     ok(exp_data.exp_notify == FALSE, "Expected wndproc to be called\n");
4017
4018     SHChangeNotifyDeregister(notifyID);
4019     DestroyWindow(wnd);
4020
4021     br = RemoveDirectoryA(root_dirA);
4022     ok(br == TRUE, "RemoveDirectory failed: %d\n", GetLastError());
4023 }
4024
4025 START_TEST(shlfolder)
4026 {
4027     init_function_pointers();
4028     /* if OleInitialize doesn't get called, ParseDisplayName returns
4029        CO_E_NOTINITIALIZED for malformed directory names on win2k. */
4030     OleInitialize(NULL);
4031
4032     test_ParseDisplayName();
4033     test_SHParseDisplayName();
4034     test_BindToObject();
4035     test_EnumObjects_and_CompareIDs();
4036     test_GetDisplayName();
4037     test_GetAttributesOf();
4038     test_SHGetPathFromIDList();
4039     test_CallForAttributes();
4040     test_FolderShortcut();
4041     test_ITEMIDLIST_format();
4042     test_SHGetFolderPathAndSubDirA();
4043     test_LocalizedNames();
4044     test_SHCreateShellItem();
4045     test_SHCreateShellItemArray();
4046     test_desktop_IPersist();
4047     test_GetUIObject();
4048     test_SHSimpleIDListFromPath();
4049     test_ParseDisplayNamePBC();
4050     test_SHGetNameFromIDList();
4051     test_SHGetItemFromDataObject();
4052     test_SHGetIDListFromObject();
4053     test_SHGetItemFromObject();
4054     test_ShellItemCompare();
4055     test_SHChangeNotify();
4056     test_ShellItemBindToHandler();
4057
4058     OleUninitialize();
4059 }