EnumThemeColors() and EnumThemeSizes() actually do not return a single
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <stdarg.h>
22 #include <stdio.h>
23
24 #define COBJMACROS
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "wtypes.h"
29 #include "shellapi.h"
30
31
32 #include "shlguid.h"
33 #include "shlobj.h"
34 #include "shobjidl.h"
35 #include "shlwapi.h"
36 #include "ocidl.h"
37 #include "oleauto.h"
38
39
40 #include "wine/unicode.h"
41 #include "wine/test.h"
42
43
44 static IMalloc *ppM;
45
46 static HRESULT (WINAPI *pSHBindToParent)(LPCITEMIDLIST, REFIID, LPVOID*, LPCITEMIDLIST*);
47 static BOOL (WINAPI *pSHGetSpecialFolderPathW)(HWND, LPWSTR, int, BOOL);
48 static HRESULT (WINAPI *pStrRetToBufW)(STRRET*,LPCITEMIDLIST,LPWSTR,UINT);
49
50 static void init_function_pointers(void)
51 {
52     HMODULE hmod;
53     HRESULT hr;
54
55     hmod = GetModuleHandleA("shell32.dll");
56     if(hmod)
57     {
58         pSHBindToParent = (void*)GetProcAddress(hmod, "SHBindToParent");
59         pSHGetSpecialFolderPathW = (void*)GetProcAddress(hmod, "SHGetSpecialFolderPathW");
60     }
61
62     hmod = GetModuleHandleA("shlwapi.dll");
63     if(hmod)
64     {
65         pStrRetToBufW = (void*)GetProcAddress(hmod, "StrRetToBufW");
66     }
67
68     hr = SHGetMalloc(&ppM);
69     ok(hr == S_OK, "SHGetMalloc failed %08lx\n", hr);
70 }
71
72 static void test_ParseDisplayName(void)
73 {
74     HRESULT hr;
75     IShellFolder *IDesktopFolder;
76     static const char *cNonExistDir1A = "c:\\nonexist_subdir";
77     static const char *cNonExistDir2A = "c:\\\\nonexist_subdir";
78     DWORD res;
79     WCHAR cTestDirW [MAX_PATH] = {0};
80     ITEMIDLIST *newPIDL;
81
82     hr = SHGetDesktopFolder(&IDesktopFolder);
83     if(hr != S_OK) return;
84
85     res = GetFileAttributesA(cNonExistDir1A);
86     if(res != INVALID_FILE_ATTRIBUTES) return;
87
88     MultiByteToWideChar(CP_ACP, 0, cNonExistDir1A, -1, cTestDirW, MAX_PATH);
89     hr = IShellFolder_ParseDisplayName(IDesktopFolder, 
90         NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
91     ok((hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) || (hr == E_FAIL), 
92         "ParseDisplayName returned %08lx, expected 80070002 or E_FAIL\n", hr);
93
94     res = GetFileAttributesA(cNonExistDir2A);
95     if(res != INVALID_FILE_ATTRIBUTES) return;
96
97     MultiByteToWideChar(CP_ACP, 0, cNonExistDir2A, -1, cTestDirW, MAX_PATH);
98     hr = IShellFolder_ParseDisplayName(IDesktopFolder, 
99         NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
100     ok((hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) || (hr == E_FAIL) || (hr == E_INVALIDARG), 
101         "ParseDisplayName returned %08lx, expected 80070002, E_FAIL or E_INVALIDARG\n", hr);
102 }
103
104 /* creates a file with the specified name for tests */
105 static void CreateTestFile(const CHAR *name)
106 {
107     HANDLE file;
108     DWORD written;
109
110     file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
111     if (file != INVALID_HANDLE_VALUE)
112     {
113         WriteFile(file, name, strlen(name), &written, NULL);
114         WriteFile(file, "\n", strlen("\n"), &written, NULL);
115         CloseHandle(file);
116     }
117 }
118
119
120 /* initializes the tests */
121 static void CreateFilesFolders(void)
122 {
123     CreateDirectoryA(".\\testdir", NULL);
124     CreateDirectoryA(".\\testdir\\test.txt", NULL);
125     CreateTestFile  (".\\testdir\\test1.txt ");
126     CreateTestFile  (".\\testdir\\test2.txt ");
127     CreateTestFile  (".\\testdir\\test3.txt ");
128     CreateDirectoryA(".\\testdir\\testdir2 ", NULL);
129     CreateDirectoryA(".\\testdir\\testdir2\\subdir", NULL);
130 }
131
132 /* cleans after tests */
133 static void Cleanup(void)
134 {
135     DeleteFileA(".\\testdir\\test1.txt");
136     DeleteFileA(".\\testdir\\test2.txt");
137     DeleteFileA(".\\testdir\\test3.txt");
138     RemoveDirectoryA(".\\testdir\\test.txt");
139     RemoveDirectoryA(".\\testdir\\testdir2\\subdir");
140     RemoveDirectoryA(".\\testdir\\testdir2");
141     RemoveDirectoryA(".\\testdir");
142 }
143
144
145 /* perform test */
146 static void test_EnumObjects(IShellFolder *iFolder)
147 {
148     IEnumIDList *iEnumList;
149     LPITEMIDLIST newPIDL, idlArr[10];
150     ULONG NumPIDLs;
151     int i=0, j;
152     HRESULT hr;
153
154     static const WORD iResults [5][5] =
155     {
156         { 0,-1,-1,-1,-1},
157         { 1, 0,-1,-1,-1},
158         { 1, 1, 0,-1,-1},
159         { 1, 1, 1, 0,-1},
160         { 1, 1, 1, 1, 0}
161     };
162
163     /* Just test SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR for now */
164     static const ULONG attrs[5] =
165     {
166         SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR,
167         SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR,
168         SFGAO_FILESYSTEM,
169         SFGAO_FILESYSTEM,
170         SFGAO_FILESYSTEM,
171     };
172
173     hr = IShellFolder_EnumObjects(iFolder, NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN, &iEnumList);
174     ok(hr == S_OK, "EnumObjects failed %08lx\n", hr);
175
176     /* This is to show that, contrary to what is said on MSDN, on IEnumIDList::Next,
177      * the filesystem shellfolders return S_OK even if less than 'celt' items are
178      * returned (in contrast to S_FALSE). We have to do it in a loop since WinXP
179      * only ever returns a single entry per call. */
180     while (IEnumIDList_Next(iEnumList, 10-i, &idlArr[i], &NumPIDLs) == S_OK) 
181         i += NumPIDLs;
182     ok (i == 5, "i: %d\n", i);
183
184     hr = IEnumIDList_Release(iEnumList);
185     ok(hr == S_OK, "IEnumIDList_Release failed %08lx\n", hr);
186     
187     /* Sort them first in case of wrong order from system */
188     for (i=0;i<5;i++) for (j=0;j<5;j++)
189         if ((SHORT)IShellFolder_CompareIDs(iFolder, 0, idlArr[i], idlArr[j]) < 0)
190         {
191             newPIDL = idlArr[i];
192             idlArr[i] = idlArr[j];
193             idlArr[j] = newPIDL;
194         }
195             
196     for (i=0;i<5;i++) for (j=0;j<5;j++)
197     {
198         hr = IShellFolder_CompareIDs(iFolder, 0, idlArr[i], idlArr[j]);
199         ok(hr == iResults[i][j], "Got %lx expected [%d]-[%d]=%x\n", hr, i, j, iResults[i][j]);
200     }
201
202
203     for (i = 0; i < 5; i++)
204     {
205         SFGAOF flags;
206         flags = SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR;
207         hr = IShellFolder_GetAttributesOf(iFolder, 1, (LPCITEMIDLIST*)(idlArr + i), &flags);
208         flags &= SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR;
209         ok(hr == S_OK, "GetAttributesOf returns %08lx\n", hr);
210         ok(flags == attrs[i], "GetAttributesOf gets attrs %08lx, expects %08lx\n", flags, attrs[i]);
211     }
212
213     for (i=0;i<5;i++)
214         IMalloc_Free(ppM, idlArr[i]);
215 }
216
217 static void test_BindToObject(void)
218 {
219     HRESULT hr;
220     UINT cChars;
221     IShellFolder *psfDesktop, *psfChild, *psfMyComputer, *psfSystemDir;
222     SHITEMID emptyitem = { 0, { 0 } };
223     LPITEMIDLIST pidlMyComputer, pidlSystemDir, pidlEmpty = (LPITEMIDLIST)&emptyitem;
224     WCHAR wszSystemDir[MAX_PATH];
225     char szSystemDir[MAX_PATH];
226     WCHAR wszMyComputer[] = { 
227         ':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
228         'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };
229
230     /* The following tests shows that BindToObject should fail with E_INVALIDARG if called
231      * with an empty pidl. This is tested for Desktop, MyComputer and the FS ShellFolder
232      */
233     hr = SHGetDesktopFolder(&psfDesktop);
234     ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08lx\n", hr);
235     if (FAILED(hr)) return;
236     
237     hr = IShellFolder_BindToObject(psfDesktop, pidlEmpty, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
238     ok (hr == E_INVALIDARG, "Desktop's BindToObject should fail, when called with empty pidl! hr = %08lx\n", hr);
239
240     hr = IShellFolder_BindToObject(psfDesktop, NULL, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
241     ok (hr == E_INVALIDARG, "Desktop's BindToObject should fail, when called with NULL pidl! hr = %08lx\n", hr);
242
243     hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyComputer, NULL, &pidlMyComputer, NULL);
244     ok (SUCCEEDED(hr), "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08lx\n", hr);
245     if (FAILED(hr)) {
246         IShellFolder_Release(psfDesktop);
247         return;
248     }
249     
250     hr = IShellFolder_BindToObject(psfDesktop, pidlMyComputer, NULL, &IID_IShellFolder, (LPVOID*)&psfMyComputer);
251     ok (SUCCEEDED(hr), "Desktop failed to bind to MyComputer object! hr = %08lx\n", hr);
252     IShellFolder_Release(psfDesktop);
253     IMalloc_Free(ppM, pidlMyComputer);
254     if (FAILED(hr)) return;
255
256     hr = IShellFolder_BindToObject(psfMyComputer, pidlEmpty, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
257     ok (hr == E_INVALIDARG, "MyComputers's BindToObject should fail, when called with empty pidl! hr = %08lx\n", hr);
258
259     hr = IShellFolder_BindToObject(psfMyComputer, NULL, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
260     ok (hr == E_INVALIDARG, "MyComputers's BindToObject should fail, when called with NULL pidl! hr = %08lx\n", hr);
261
262     cChars = GetSystemDirectoryA(szSystemDir, MAX_PATH);
263     ok (cChars > 0 && cChars < MAX_PATH, "GetSystemDirectoryA failed! LastError: %08lx\n", GetLastError());
264     if (cChars == 0 || cChars >= MAX_PATH) {
265         IShellFolder_Release(psfMyComputer);
266         return;
267     }
268     MultiByteToWideChar(CP_ACP, 0, szSystemDir, -1, wszSystemDir, MAX_PATH);
269     
270     hr = IShellFolder_ParseDisplayName(psfMyComputer, NULL, NULL, wszSystemDir, NULL, &pidlSystemDir, NULL);
271     ok (SUCCEEDED(hr), "MyComputers's ParseDisplayName failed to parse the SystemDirectory! hr = %08lx\n", hr);
272     if (FAILED(hr)) {
273         IShellFolder_Release(psfMyComputer);
274         return;
275     }
276
277     hr = IShellFolder_BindToObject(psfMyComputer, pidlSystemDir, NULL, &IID_IShellFolder, (LPVOID*)&psfSystemDir);
278     ok (SUCCEEDED(hr), "MyComputer failed to bind to a FileSystem ShellFolder! hr = %08lx\n", hr);
279     IShellFolder_Release(psfMyComputer);
280     IMalloc_Free(ppM, pidlSystemDir);
281     if (FAILED(hr)) return;
282
283     hr = IShellFolder_BindToObject(psfSystemDir, pidlEmpty, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
284     ok (hr == E_INVALIDARG, 
285         "FileSystem ShellFolder's BindToObject should fail, when called with empty pidl! hr = %08lx\n", hr);
286     
287     hr = IShellFolder_BindToObject(psfSystemDir, NULL, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
288     ok (hr == E_INVALIDARG, 
289         "FileSystem ShellFolder's BindToObject should fail, when called with NULL pidl! hr = %08lx\n", hr);
290
291     IShellFolder_Release(psfSystemDir);
292 }
293   
294 static void test_GetDisplayName(void)
295 {
296     BOOL result;
297     HRESULT hr;
298     HANDLE hTestFile;
299     WCHAR wszTestFile[MAX_PATH], wszTestFile2[MAX_PATH], wszTestDir[MAX_PATH];
300     char szTestFile[MAX_PATH], szTestDir[MAX_PATH];
301     STRRET strret;
302     LPSHELLFOLDER psfDesktop, psfPersonal;
303     IUnknown *psfFile;
304     LPITEMIDLIST pidlTestFile;
305     LPCITEMIDLIST pidlLast;
306     static const WCHAR wszFileName[] = { 'w','i','n','e','t','e','s','t','.','f','o','o',0 };
307     static const WCHAR wszDirName[] = { 'w','i','n','e','t','e','s','t',0 };
308
309     /* I'm trying to figure if there is a functional difference between calling
310      * SHGetPathFromIDList and calling GetDisplayNameOf(SHGDN_FORPARSING) after
311      * binding to the shellfolder. One thing I thought of was that perhaps 
312      * SHGetPathFromIDList would be able to get the path to a file, which does
313      * not exist anymore, while the other method would'nt. It turns out there's
314      * no functional difference in this respect.
315      */
316
317     if(!pSHGetSpecialFolderPathW) return;
318
319     /* First creating a directory in MyDocuments and a file in this directory. */
320     result = pSHGetSpecialFolderPathW(NULL, wszTestDir, CSIDL_PERSONAL, FALSE);
321     ok(result, "SHGetSpecialFolderPathW failed! Last error: %08lx\n", GetLastError());
322     if (!result) return;
323
324     PathAddBackslashW(wszTestDir);
325     lstrcatW(wszTestDir, wszDirName);
326     WideCharToMultiByte(CP_ACP, 0, wszTestDir, -1, szTestDir, MAX_PATH, 0, 0);
327     result = CreateDirectoryA(szTestDir, NULL);
328     ok(result, "CreateDirectoryA failed! Last error: %08lx\n", GetLastError());
329     if (!result) return;
330
331     lstrcpyW(wszTestFile, wszTestDir);
332     PathAddBackslashW(wszTestFile);
333     lstrcatW(wszTestFile, wszFileName);
334     WideCharToMultiByte(CP_ACP, 0, wszTestFile, -1, szTestFile, MAX_PATH, 0, 0);
335
336     hTestFile = CreateFileA(szTestFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
337     ok((hTestFile != INVALID_HANDLE_VALUE), "CreateFileA failed! Last error: %08lx\n", GetLastError());
338     if (hTestFile == INVALID_HANDLE_VALUE) return;
339     CloseHandle(hTestFile);
340
341     /* Getting an itemidlist for the file. */
342     hr = SHGetDesktopFolder(&psfDesktop);
343     ok(SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08lx\n", hr);
344     if (FAILED(hr)) return;
345
346     hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszTestFile, NULL, &pidlTestFile, NULL);
347     ok(SUCCEEDED(hr), "Desktop->ParseDisplayName failed! hr = %08lx\n", hr);
348     if (FAILED(hr)) {
349         IShellFolder_Release(psfDesktop);
350         return;
351     }
352
353     /* It seems as if we cannot bind to regular files on windows, but only directories. 
354      */
355     hr = IShellFolder_BindToObject(psfDesktop, pidlTestFile, NULL, &IID_IUnknown, (VOID**)&psfFile);
356     todo_wine { ok (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), "hr = %08lx\n", hr); }
357     if (SUCCEEDED(hr)) {
358         IShellFolder_Release(psfFile);
359     }
360     
361     /* Deleting the file and the directory */
362     DeleteFileA(szTestFile);
363     RemoveDirectoryA(szTestDir);
364
365     /* SHGetPathFromIDListW still works, although the file is not present anymore. */
366     result = SHGetPathFromIDListW(pidlTestFile, wszTestFile2);
367     ok (result, "SHGetPathFromIDListW failed! Last error: %08lx\n", GetLastError());
368     ok (!lstrcmpiW(wszTestFile, wszTestFile2), "SHGetPathFromIDListW returns incorrect path!\n");
369
370     if(!pSHBindToParent) return;
371
372     /* Binding to the folder and querying the display name of the file also works. */
373     hr = pSHBindToParent(pidlTestFile, &IID_IShellFolder, (VOID**)&psfPersonal, &pidlLast); 
374     ok (SUCCEEDED(hr), "SHBindToParent failed! hr = %08lx\n", hr);
375     if (FAILED(hr)) {
376         IShellFolder_Release(psfDesktop);
377         return;
378     }
379
380     /* This test shows that Windows doesn't allocate a new pidlLast, but returns a pointer into 
381      * pidlTestFile (In accordance with MSDN). */
382     todo_wine{ok (ILFindLastID(pidlTestFile) == pidlLast, 
383                                 "SHBindToParent doesn't return the last id of the pidl param!\n");}
384     
385     hr = IShellFolder_GetDisplayNameOf(psfPersonal, pidlLast, SHGDN_FORPARSING, &strret);
386     ok (SUCCEEDED(hr), "Personal->GetDisplayNameOf failed! hr = %08lx\n", hr);
387     if (FAILED(hr)) {
388         IShellFolder_Release(psfDesktop);
389         IShellFolder_Release(psfPersonal);
390         return;
391     }
392
393     if (pStrRetToBufW)
394     {
395         hr = pStrRetToBufW(&strret, pidlLast, wszTestFile2, MAX_PATH);
396         ok (SUCCEEDED(hr), "StrRetToBufW failed! hr = %08lx\n", hr);
397         ok (!lstrcmpiW(wszTestFile, wszTestFile2), "GetDisplayNameOf returns incorrect path!\n");
398     }
399     
400     IShellFolder_Release(psfDesktop);
401     IShellFolder_Release(psfPersonal);
402 }
403
404 static void test_CallForAttributes(void)
405 {
406     HKEY hKey;
407     LONG lResult;
408     HRESULT hr;
409     DWORD dwSize;
410     LPSHELLFOLDER psfDesktop;
411     LPITEMIDLIST pidlMyDocuments;
412     DWORD dwAttributes, dwCallForAttributes, dwOrigAttributes, dwOrigCallForAttributes;
413     static const WCHAR wszAttributes[] = { 'A','t','t','r','i','b','u','t','e','s',0 };
414     static const WCHAR wszCallForAttributes[] = { 
415         'C','a','l','l','F','o','r','A','t','t','r','i','b','u','t','e','s',0 };
416     static const WCHAR wszMyDocumentsKey[] = {
417         'C','L','S','I','D','\\','{','4','5','0','D','8','F','B','A','-','A','D','2','5','-',
418         '1','1','D','0','-','9','8','A','8','-','0','8','0','0','3','6','1','B','1','1','0','3','}',
419         '\\','S','h','e','l','l','F','o','l','d','e','r',0 };
420     WCHAR wszMyDocuments[] = {
421         ':',':','{','4','5','0','D','8','F','B','A','-','A','D','2','5','-','1','1','D','0','-',
422         '9','8','A','8','-','0','8','0','0','3','6','1','B','1','1','0','3','}',0 };
423     
424     /* For the root of a namespace extension, the attributes are not queried by binding
425      * to the object and calling GetAttributesOf. Instead, the attributes are read from 
426      * the registry value HKCR/CLSID/{...}/ShellFolder/Attributes. This is documented on MSDN.
427      *
428      * The MyDocuments shellfolder on WinXP has a HKCR/CLSID/{...}/ShellFolder/CallForAttributes
429      * value. It seems that if the folder is queried for one of the flags set in CallForAttributes,
430      * the shell does bind to the folder object and calls GetAttributesOf. This is not documented
431      * on MSDN. This test is meant to document the observed behaviour on WinXP SP2.
432      */
433     hr = SHGetDesktopFolder(&psfDesktop);
434     ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08lx\n", hr);
435     if (FAILED(hr)) return;
436     
437     hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyDocuments, NULL, 
438                                        &pidlMyDocuments, NULL);
439     ok (SUCCEEDED(hr), 
440         "Desktop's ParseDisplayName failed to parse MyDocuments's CLSID! hr = %08lx\n", hr);
441     if (FAILED(hr)) {
442         IShellFolder_Release(psfDesktop);
443         return;
444     }
445
446     dwAttributes = 0xffffffff;
447     hr = IShellFolder_GetAttributesOf(psfDesktop, 1, 
448                                       (LPCITEMIDLIST*)&pidlMyDocuments, &dwAttributes);
449     ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(MyDocuments) failed! hr = %08lx\n", hr);
450
451     /* We need the following setup (as observed on WinXP SP2), for the tests to make sense. */
452     todo_wine{ ok (dwAttributes & SFGAO_FILESYSTEM, 
453                    "SFGAO_FILESYSTEM attribute is not set for MyDocuments!\n"); }
454     ok (!(dwAttributes & SFGAO_ISSLOW), "SFGAO_ISSLOW attribute is set for MyDocuments!\n");
455     ok (!(dwAttributes & SFGAO_GHOSTED), "SFGAO_GHOSTED attribute is set for MyDocuments!\n");
456
457     /* We don't have the MyDocuments shellfolder in wine yet, and thus we don't have the registry
458      * key. So the test will return at this point, if run on wine. 
459      */
460     lResult = RegOpenKeyExW(HKEY_CLASSES_ROOT, wszMyDocumentsKey, 0, KEY_WRITE|KEY_READ, &hKey);
461     todo_wine { ok (lResult == ERROR_SUCCESS, "RegOpenKeyEx failed! result: %08lx\n", lResult); }
462     if (lResult != ERROR_SUCCESS) {
463         IMalloc_Free(ppM, pidlMyDocuments);
464         IShellFolder_Release(psfDesktop);
465         return;
466     }
467     
468     /* Query MyDocuments' Attributes value, to be able to restore it later. */
469     dwSize = sizeof(DWORD);
470     lResult = RegQueryValueExW(hKey, wszAttributes, NULL, NULL, (LPBYTE)&dwOrigAttributes, &dwSize);
471     ok (lResult == ERROR_SUCCESS, "RegQueryValueEx failed! result: %08lx\n", lResult);
472     if (lResult != ERROR_SUCCESS) {
473         RegCloseKey(hKey);
474         IMalloc_Free(ppM, pidlMyDocuments);
475         IShellFolder_Release(psfDesktop);
476         return;
477     }
478
479     /* Query MyDocuments' CallForAttributes value, to be able to restore it later. */
480     dwSize = sizeof(DWORD);
481     lResult = RegQueryValueExW(hKey, wszCallForAttributes, NULL, NULL, 
482                               (LPBYTE)&dwOrigCallForAttributes, &dwSize);
483     ok (lResult == ERROR_SUCCESS, "RegQueryValueEx failed! result: %08lx\n", lResult);
484     if (lResult != ERROR_SUCCESS) {
485         RegCloseKey(hKey);
486         IMalloc_Free(ppM, pidlMyDocuments);
487         IShellFolder_Release(psfDesktop);
488         return;
489     }
490     
491     /* Define via the Attributes value that MyDocuments attributes are SFGAO_ISSLOW and 
492      * SFGAO_GHOSTED and that MyDocuments should be called for the SFGAO_ISSLOW and
493      * SFGAO_FILESYSTEM attributes. */
494     dwAttributes = SFGAO_ISSLOW|SFGAO_GHOSTED;
495     RegSetValueExW(hKey, wszAttributes, 0, REG_DWORD, (LPBYTE)&dwAttributes, sizeof(DWORD));
496     dwCallForAttributes = SFGAO_ISSLOW|SFGAO_FILESYSTEM;
497     RegSetValueExW(hKey, wszCallForAttributes, 0, REG_DWORD, 
498                    (LPBYTE)&dwCallForAttributes, sizeof(DWORD));
499
500     /* Although it is not set in CallForAttributes, the SFGAO_GHOSTED flag is reset by 
501      * GetAttributesOf. It seems that once there is a single attribute queried, for which
502      * CallForAttributes is set, all flags are taken from the GetAttributesOf call and
503      * the flags in Attributes are ignored. 
504      */
505     dwAttributes = SFGAO_ISSLOW|SFGAO_GHOSTED|SFGAO_FILESYSTEM;
506     hr = IShellFolder_GetAttributesOf(psfDesktop, 1, 
507                                       (LPCITEMIDLIST*)&pidlMyDocuments, &dwAttributes);
508     ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(MyDocuments) failed! hr = %08lx\n", hr);
509     if (SUCCEEDED(hr)) 
510         ok (dwAttributes == SFGAO_FILESYSTEM, 
511             "Desktop->GetAttributes(MyDocuments) returned unexpected attributes: %08lx\n", 
512             dwAttributes);
513
514     /* Restore MyDocuments' original Attributes and CallForAttributes registry values */
515     RegSetValueExW(hKey, wszAttributes, 0, REG_DWORD, (LPBYTE)&dwOrigAttributes, sizeof(DWORD));
516     RegSetValueExW(hKey, wszCallForAttributes, 0, REG_DWORD, 
517                    (LPBYTE)&dwOrigCallForAttributes, sizeof(DWORD));
518     RegCloseKey(hKey);
519     IMalloc_Free(ppM, pidlMyDocuments);
520     IShellFolder_Release(psfDesktop);
521 }
522
523 static void test_GetAttributesOf(void) 
524 {
525     HRESULT hr;
526     LPSHELLFOLDER psfDesktop, psfMyComputer;
527     SHITEMID emptyitem = { 0, { 0 } };
528     LPCITEMIDLIST pidlEmpty = (LPCITEMIDLIST)&emptyitem;
529     LPITEMIDLIST pidlMyComputer;
530     DWORD dwFlags;
531     const static DWORD dwDesktopFlags = /* As observed on WinXP SP2 */
532         SFGAO_STORAGE | SFGAO_HASPROPSHEET | SFGAO_STORAGEANCESTOR |
533         SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_HASSUBFOLDER;
534     const static DWORD dwMyComputerFlags = /* As observed on WinXP SP2 */
535         SFGAO_CANRENAME | SFGAO_CANDELETE | SFGAO_HASPROPSHEET |
536         SFGAO_DROPTARGET | SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER;
537     WCHAR wszMyComputer[] = { 
538         ':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
539         'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };
540
541     hr = SHGetDesktopFolder(&psfDesktop);
542     ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08lx\n", hr);
543     if (FAILED(hr)) return;
544
545     /* The Desktop attributes can be queried with a single empty itemidlist, .. */
546     dwFlags = 0xffffffff;
547     hr = IShellFolder_GetAttributesOf(psfDesktop, 1, &pidlEmpty, &dwFlags);
548     ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(empty pidl) failed! hr = %08lx\n", hr);
549     ok (dwFlags == dwDesktopFlags, "Wrong Desktop attributes: %08lx, expected: %08lx\n", 
550         dwFlags, dwDesktopFlags);
551
552     /* .. or with no itemidlist at all. */
553     dwFlags = 0xffffffff;
554     hr = IShellFolder_GetAttributesOf(psfDesktop, 0, NULL, &dwFlags);
555     ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(NULL) failed! hr = %08lx\n", hr);
556     ok (dwFlags == dwDesktopFlags, "Wrong Desktop attributes: %08lx, expected: %08lx\n", 
557         dwFlags, dwDesktopFlags);
558    
559     /* Testing the attributes of the MyComputer shellfolder */
560     hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyComputer, NULL, &pidlMyComputer, NULL);
561     ok (SUCCEEDED(hr), "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08lx\n", hr);
562     if (FAILED(hr)) {
563         IShellFolder_Release(psfDesktop);
564         return;
565     }
566
567     /* WinXP SP2 sets the SFGAO_CANLINK flag, when MyComputer is queried via the Desktop 
568      * folder object. It doesn't do this, if MyComputer is queried directly (see below).
569      * SFGAO_CANLINK is the same as DROPEFFECT_LINK, which MSDN says means: "Drag source
570      * should create a link to the original data". You can't create links on MyComputer on
571      * Windows, so this flag shouldn't be set. Seems like a bug in Windows. As long as nobody
572      * depends on this bug, we probably shouldn't imitate it.
573      */
574     dwFlags = 0xffffffff;
575     hr = IShellFolder_GetAttributesOf(psfDesktop, 1, (LPCITEMIDLIST*)&pidlMyComputer, &dwFlags);
576     ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(MyComputer) failed! hr = %08lx\n", hr);
577     todo_wine { ok ((dwFlags & ~(DWORD)SFGAO_CANLINK) == dwMyComputerFlags, 
578                     "Wrong MyComputer attributes: %08lx, expected: %08lx\n", dwFlags, dwMyComputerFlags); }
579
580     hr = IShellFolder_BindToObject(psfDesktop, pidlMyComputer, NULL, &IID_IShellFolder, (LPVOID*)&psfMyComputer);
581     ok (SUCCEEDED(hr), "Desktop failed to bind to MyComputer object! hr = %08lx\n", hr);
582     IShellFolder_Release(psfDesktop);
583     IMalloc_Free(ppM, pidlMyComputer);
584     if (FAILED(hr)) return;
585
586     hr = IShellFolder_GetAttributesOf(psfMyComputer, 1, &pidlEmpty, &dwFlags);
587     todo_wine {ok (hr == E_INVALIDARG, "MyComputer->GetAttributesOf(emtpy pidl) should fail! hr = %08lx\n", hr); }
588
589     dwFlags = 0xffffffff;
590     hr = IShellFolder_GetAttributesOf(psfMyComputer, 0, NULL, &dwFlags);
591     ok (SUCCEEDED(hr), "MyComputer->GetAttributesOf(NULL) failed! hr = %08lx\n", hr); 
592     todo_wine { ok (dwFlags == dwMyComputerFlags, 
593                     "Wrong MyComputer attributes: %08lx, expected: %08lx\n", dwFlags, dwMyComputerFlags); }
594
595     IShellFolder_Release(psfMyComputer);
596 }    
597
598 static void test_SHGetPathFromIDList(void)
599 {
600     SHITEMID emptyitem = { 0, { 0 } };
601     LPCITEMIDLIST pidlEmpty = (LPCITEMIDLIST)&emptyitem;
602     LPITEMIDLIST pidlMyComputer;
603     WCHAR wszPath[MAX_PATH], wszDesktop[MAX_PATH];
604     BOOL result;
605     HRESULT hr;
606     LPSHELLFOLDER psfDesktop;
607     WCHAR wszMyComputer[] = { 
608         ':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
609         'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };
610     WCHAR wszFileName[MAX_PATH];
611     LPITEMIDLIST pidlTestFile;
612     HANDLE hTestFile;
613     STRRET strret;
614     static WCHAR wszTestFile[] = {
615         'w','i','n','e','t','e','s','t','.','f','o','o',0 };
616
617     if(!pSHGetSpecialFolderPathW) return;
618
619     /* Calling SHGetPathFromIDList with an empty pidl should return the desktop folder's path. */
620     result = pSHGetSpecialFolderPathW(NULL, wszDesktop, CSIDL_DESKTOP, FALSE);
621     ok(result, "SHGetSpecialFolderPathW(CSIDL_DESKTOP) failed! Last error: %08lx\n", GetLastError());
622     if (!result) return;
623     
624     result = SHGetPathFromIDListW(pidlEmpty, wszPath);
625     ok(result, "SHGetPathFromIDListW failed! Last error: %08lx\n", GetLastError());
626     if (!result) return;
627     ok(!lstrcmpiW(wszDesktop, wszPath), "SHGetPathFromIDList didn't return desktop path for empty pidl!\n");
628
629     /* MyComputer does not map to a filesystem path. SHGetPathFromIDList should fail. */
630     hr = SHGetDesktopFolder(&psfDesktop);
631     ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08lx\n", hr);
632     if (FAILED(hr)) return;
633
634     hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyComputer, NULL, &pidlMyComputer, NULL);
635     ok (SUCCEEDED(hr), "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08lx\n", hr);
636     if (FAILED(hr)) {
637         IShellFolder_Release(psfDesktop);
638         return;
639     }
640
641     SetLastError(0xdeadbeef);
642     result = SHGetPathFromIDListW(pidlMyComputer, wszPath);
643     ok (!result, "SHGetPathFromIDList succeeded where it shouldn't!\n");
644     ok (GetLastError()==0xdeadbeef, "SHGetPathFromIDList shouldn't set last error! Last error: %08lx\n", GetLastError());
645     if (result) {
646         IShellFolder_Release(psfDesktop);
647         return;
648     }
649
650     IMalloc_Free(ppM, pidlMyComputer);
651
652     result = pSHGetSpecialFolderPathW(NULL, wszFileName, CSIDL_DESKTOPDIRECTORY, FALSE);
653     ok(result, "SHGetSpecialFolderPathW failed! Last error: %08lx\n", GetLastError());
654     if (!result) {
655         IShellFolder_Release(psfDesktop);
656         return;
657     }
658     PathAddBackslashW(wszFileName);
659     lstrcatW(wszFileName, wszTestFile);
660     hTestFile = CreateFileW(wszFileName, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL);
661     ok(hTestFile != INVALID_HANDLE_VALUE, "CreateFileW failed! Last error: %08lx\n", GetLastError());
662     if (hTestFile == INVALID_HANDLE_VALUE) {
663         IShellFolder_Release(psfDesktop);
664         return;
665     }
666     CloseHandle(hTestFile);
667
668     hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszTestFile, NULL, &pidlTestFile, NULL);
669     ok (SUCCEEDED(hr), "Desktop's ParseDisplayName failed to parse filename hr = %08lx\n", hr);
670     if (FAILED(hr)) {
671         IShellFolder_Release(psfDesktop);
672         DeleteFileW(wszFileName);
673         IMalloc_Free(ppM, pidlTestFile);
674         return;
675     }
676
677     /* This test is to show that the Desktop shellfolder prepends the CSIDL_DESKTOPDIRECTORY
678      * path for files placed on the desktop, if called with SHGDN_FORPARSING. */
679     hr = IShellFolder_GetDisplayNameOf(psfDesktop, pidlTestFile, SHGDN_FORPARSING, &strret);
680     ok (SUCCEEDED(hr), "Desktop's GetDisplayNamfOf failed! hr = %08lx\n", hr);
681     IShellFolder_Release(psfDesktop);
682     DeleteFileW(wszFileName);
683     if (FAILED(hr)) {
684         IMalloc_Free(ppM, pidlTestFile);
685         return;
686     }
687     if (pStrRetToBufW)
688     {
689         pStrRetToBufW(&strret, pidlTestFile, wszPath, MAX_PATH);
690         ok(0 == lstrcmpW(wszFileName, wszPath), 
691            "Desktop->GetDisplayNameOf(pidlTestFile, SHGDN_FORPARSING) "
692            "returned incorrect path for file placed on desktop\n");
693     }
694
695     result = SHGetPathFromIDListW(pidlTestFile, wszPath);
696     ok(result, "SHGetPathFromIDListW failed! Last error: %08lx\n", GetLastError());
697     IMalloc_Free(ppM, pidlTestFile);
698     if (!result) return;
699     ok(0 == lstrcmpW(wszFileName, wszPath), "SHGetPathFromIDListW returned incorrect path for file placed on desktop\n");
700 }
701
702 static void test_EnumObjects_and_CompareIDs(void)
703 {
704     ITEMIDLIST *newPIDL;
705     IShellFolder *IDesktopFolder, *testIShellFolder;
706     char  cCurrDirA [MAX_PATH] = {0};
707     WCHAR cCurrDirW [MAX_PATH];
708     static const WCHAR cTestDirW[] = {'\\','t','e','s','t','d','i','r',0};
709     int len;
710     HRESULT hr;
711
712     GetCurrentDirectoryA(MAX_PATH, cCurrDirA);
713     len = lstrlenA(cCurrDirA);
714
715     if(len == 0) {
716         trace("GetCurrentDirectoryA returned empty string. Skipping test_EnumObjects_and_CompareIDs\n");
717         return;
718     }
719     if(cCurrDirA[len-1] == '\\')
720         cCurrDirA[len-1] = 0;
721
722     MultiByteToWideChar(CP_ACP, 0, cCurrDirA, -1, cCurrDirW, MAX_PATH);
723     strcatW(cCurrDirW, cTestDirW);
724
725     hr = SHGetDesktopFolder(&IDesktopFolder);
726     ok(hr == S_OK, "SHGetDesktopfolder failed %08lx\n", hr);
727
728     CreateFilesFolders();
729
730     hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cCurrDirW, NULL, &newPIDL, 0);
731     ok(hr == S_OK, "ParseDisplayName failed %08lx\n", hr);
732
733     hr = IShellFolder_BindToObject(IDesktopFolder, newPIDL, NULL, (REFIID)&IID_IShellFolder, (LPVOID *)&testIShellFolder);
734     ok(hr == S_OK, "BindToObject failed %08lx\n", hr);
735
736     test_EnumObjects(testIShellFolder);
737
738     hr = IShellFolder_Release(testIShellFolder);
739     ok(hr == S_OK, "IShellFolder_Release failed %08lx\n", hr);
740
741     Cleanup();
742
743     IMalloc_Free(ppM, newPIDL);
744 }
745
746 /* A simple implementation of an IPropertyBag, which returns fixed values for
747  * 'Target' and 'Attributes' properties.
748  */
749 static HRESULT WINAPI InitPropertyBag_IPropertyBag_QueryInterface(IPropertyBag *iface, REFIID riid,
750     void **ppvObject) 
751 {
752     if (!ppvObject)
753         return E_INVALIDARG;
754
755     if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IPropertyBag, riid)) {
756         *ppvObject = iface;
757     } else {
758         ok (FALSE, "InitPropertyBag asked for unknown interface!\n");
759         return E_NOINTERFACE;
760     }
761
762     IPropertyBag_AddRef(iface);
763     return S_OK;
764 }
765
766 static ULONG WINAPI InitPropertyBag_IPropertyBag_AddRef(IPropertyBag *iface) {
767     return 2;
768 }
769
770 static ULONG WINAPI InitPropertyBag_IPropertyBag_Release(IPropertyBag *iface) {
771     return 1;
772 }
773
774 static HRESULT WINAPI InitPropertyBag_IPropertyBag_Read(IPropertyBag *iface, LPCOLESTR pszPropName,
775     VARIANT *pVar, IErrorLog *pErrorLog)
776 {
777     static const WCHAR wszTargetSpecialFolder[] = {
778         'T','a','r','g','e','t','S','p','e','c','i','a','l','F','o','l','d','e','r',0 };
779     static const WCHAR wszTarget[] = {
780         'T','a','r','g','e','t',0 };
781     static const WCHAR wszAttributes[] = {
782         'A','t','t','r','i','b','u','t','e','s',0 };
783     static const WCHAR wszResolveLinkFlags[] = {
784         'R','e','s','o','l','v','e','L','i','n','k','F','l','a','g','s',0 };
785         
786     if (!lstrcmpW(pszPropName, wszTargetSpecialFolder) || 
787         !lstrcmpW(pszPropName, wszResolveLinkFlags)) 
788     {
789         return E_INVALIDARG;
790     }
791
792     if (!lstrcmpW(pszPropName, wszTarget)) {
793         WCHAR wszPath[MAX_PATH];
794         BOOL result;
795         
796         ok(V_VT(pVar) == VT_BSTR, "Wrong variant type for 'Target' property!\n");
797         if (V_VT(pVar) != VT_BSTR) return E_INVALIDARG;
798
799         result = pSHGetSpecialFolderPathW(NULL, wszPath, CSIDL_DESKTOPDIRECTORY, FALSE);
800         ok(result, "SHGetSpecialFolderPathW(DESKTOPDIRECTORY) failed! x%08lx\n", GetLastError());
801         if (!result) return E_INVALIDARG;
802
803         V_BSTR(pVar) = SysAllocString(wszPath);
804         return S_OK;
805     }
806
807     if (!lstrcmpW(pszPropName, wszAttributes)) {
808         ok(V_VT(pVar) == VT_UI4, "Wrong variant type for 'Attributes' property!\n");
809         if (V_VT(pVar) != VT_UI4) return E_INVALIDARG;
810         V_UI4(pVar) = SFGAO_FOLDER|SFGAO_HASSUBFOLDER|SFGAO_FILESYSANCESTOR|
811                       SFGAO_CANRENAME|SFGAO_FILESYSTEM;
812         return S_OK;
813     }
814
815     ok(FALSE, "PropertyBag was asked for unknown property (vt=%d)!\n", V_VT(pVar));
816     return E_INVALIDARG;
817 }
818
819 static HRESULT WINAPI InitPropertyBag_IPropertyBag_Write(IPropertyBag *iface, LPCOLESTR pszPropName,
820     VARIANT *pVar)
821 {
822     ok(FALSE, "Unexpected call to IPropertyBag_Write\n");
823     return E_NOTIMPL;
824 }
825     
826 static const IPropertyBagVtbl InitPropertyBag_IPropertyBagVtbl = {
827     InitPropertyBag_IPropertyBag_QueryInterface,
828     InitPropertyBag_IPropertyBag_AddRef,
829     InitPropertyBag_IPropertyBag_Release,
830     InitPropertyBag_IPropertyBag_Read,
831     InitPropertyBag_IPropertyBag_Write
832 };
833
834 struct IPropertyBag InitPropertyBag = {
835     &InitPropertyBag_IPropertyBagVtbl
836 };
837
838 void test_FolderShortcut(void) {
839     IPersistPropertyBag *pPersistPropertyBag;
840     IShellFolder *pShellFolder;
841     IPersistFolder3 *pPersistFolder3;
842     HRESULT hr;
843     STRRET strret;
844     WCHAR wszPath[MAX_PATH], wszBuffer[MAX_PATH];
845     BOOL result;
846     CLSID clsid;
847     LPITEMIDLIST pidlCurrentFolder;
848        
849     if (!pSHGetSpecialFolderPathW || !pStrRetToBufW) return;
850    
851     /* These tests basically show, that CLSID_FolderShortcuts are initialized
852      * via their IPersistPropertyBag interface. And that the target folder
853      * is taken from the IPropertyBag's 'Target' property.
854      */
855     hr = CoCreateInstance(&CLSID_FolderShortcut, NULL, CLSCTX_INPROC_SERVER, 
856                           &IID_IPersistPropertyBag, (LPVOID*)&pPersistPropertyBag);
857     ok (SUCCEEDED(hr), "CoCreateInstance failed! hr = 0x%08lx\n", hr);
858     if (FAILED(hr)) return;
859
860     hr = IPersistPropertyBag_Load(pPersistPropertyBag, &InitPropertyBag, NULL);
861     todo_wine { ok(SUCCEEDED(hr), "IPersistPropertyBag_Load failed! hr = %08lx\n", hr); }
862     if (FAILED(hr)) {
863         IPersistPropertyBag_Release(pPersistPropertyBag);
864         return;
865     }
866     
867     hr = IPersistPropertyBag_QueryInterface(pPersistPropertyBag, &IID_IShellFolder, 
868                                             (LPVOID*)&pShellFolder);
869     IPersistPropertyBag_Release(pPersistPropertyBag);
870     ok(SUCCEEDED(hr), "IPersistPropertyBag_QueryInterface(IShellFolder) failed! hr = %08lx\n", hr);
871     if (FAILED(hr)) return;
872
873     hr = IShellFolder_GetDisplayNameOf(pShellFolder, NULL, SHGDN_FORPARSING, &strret);
874     ok(SUCCEEDED(hr), "IShellFolder_GetDisplayNameOf(NULL) failed! hr = %08lx\n", hr);
875     if (FAILED(hr)) {
876         IShellFolder_Release(pShellFolder);
877         return;
878     }
879
880     result = pSHGetSpecialFolderPathW(NULL, wszPath, CSIDL_DESKTOPDIRECTORY, FALSE);
881     ok(result, "SHGetSpecialFolderPathW(CSIDL_MYDOCUMENTS) failed! 0x%08lx\n", GetLastError());
882     if (!result) return;
883
884     pStrRetToBufW(&strret, NULL, wszBuffer, MAX_PATH);
885     ok(!lstrcmpiW(wszPath, wszBuffer), "FolderShortcut returned incorrect folder!\n");
886
887     hr = IShellFolder_QueryInterface(pShellFolder, &IID_IPersistFolder3, (LPVOID*)&pPersistFolder3);
888     IShellFolder_Release(pShellFolder);
889     ok(SUCCEEDED(hr), "IShellFolder_QueryInterface(IID_IPersistFolder3 failed! hr = 0x%08lx\n", hr);
890     if (FAILED(hr)) return;
891
892     hr = IPersistFolder3_GetClassID(pPersistFolder3, &clsid);
893     ok(SUCCEEDED(hr), "IPersistFolder3_GetClassID failed! hr=0x%08lx\n", hr);
894     ok(IsEqualCLSID(&clsid, &CLSID_FolderShortcut), "Unexpected CLSID!\n");
895
896     hr = IPersistFolder3_GetCurFolder(pPersistFolder3, &pidlCurrentFolder);
897     ok(SUCCEEDED(hr), "IPersistFolder3_GetCurFolder failed! hr=0x%08lx\n", hr);
898     ok(!pidlCurrentFolder, "IPersistFolder3_GetCurFolder should return a NULL pidl!\n");
899                     
900     IPersistFolder3_Release(pPersistFolder3);
901 }
902
903 START_TEST(shlfolder)
904 {
905     init_function_pointers();
906     /* if OleInitialize doesn't get called, ParseDisplayName returns
907        CO_E_NOTINITIALIZED for malformed directory names on win2k. */
908     OleInitialize(NULL);
909
910     test_ParseDisplayName();
911     test_BindToObject();
912     test_EnumObjects_and_CompareIDs();
913     test_GetDisplayName();
914     test_GetAttributesOf();
915     test_SHGetPathFromIDList();
916     test_CallForAttributes();
917     test_FolderShortcut();
918
919     OleUninitialize();
920 }