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