[SHELL32]
[reactos.git] / reactos / dll / win32 / shell32 / shlfolder.cpp
1 /*
2 * Shell Folder stuff
3 *
4 * Copyright 1997 Marcus Meissner
5 * Copyright 1998, 1999, 2002 Juergen Schmied
6 *
7 * IShellFolder2 and related interfaces
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24 #include "precomp.h"
25
26 WINE_DEFAULT_DEBUG_CHANNEL(shell);
27
28 static const WCHAR wszDotShellClassInfo[] = {
29 '.','S','h','e','l','l','C','l','a','s','s','I','n','f','o',0};
30
31 /***************************************************************************
32 * SHELL32_GetCustomFolderAttribute (internal function)
33 *
34 * Gets a value from the folder's desktop.ini file, if one exists.
35 *
36 * PARAMETERS
37 * pidl [I] Folder containing the desktop.ini file.
38 * pwszHeading [I] Heading in .ini file.
39 * pwszAttribute [I] Attribute in .ini file.
40 * pwszValue [O] Buffer to store value into.
41 * cchValue [I] Size in characters including NULL of buffer pointed to
42 * by pwszValue.
43 *
44 * RETURNS
45 * TRUE if returned non-NULL value.
46 * FALSE otherwise.
47 */
48 static BOOL __inline SHELL32_GetCustomFolderAttributeFromPath(
49 LPWSTR pwszFolderPath, LPCWSTR pwszHeading, LPCWSTR pwszAttribute,
50 LPWSTR pwszValue, DWORD cchValue)
51 {
52 static const WCHAR wszDesktopIni[] =
53 {'d','e','s','k','t','o','p','.','i','n','i',0};
54 static const WCHAR wszDefault[] = {0};
55
56 PathAddBackslashW(pwszFolderPath);
57 PathAppendW(pwszFolderPath, wszDesktopIni);
58 return GetPrivateProfileStringW(pwszHeading, pwszAttribute, wszDefault,
59 pwszValue, cchValue, pwszFolderPath);
60 }
61
62 BOOL SHELL32_GetCustomFolderAttribute(
63 LPCITEMIDLIST pidl, LPCWSTR pwszHeading, LPCWSTR pwszAttribute,
64 LPWSTR pwszValue, DWORD cchValue)
65 {
66 DWORD dwAttrib = FILE_ATTRIBUTE_SYSTEM;
67 WCHAR wszFolderPath[MAX_PATH];
68
69 /* Hack around not having system attribute on non-Windows file systems */
70 if (0)
71 dwAttrib = _ILGetFileAttributes(pidl, NULL, 0);
72
73 if (dwAttrib & FILE_ATTRIBUTE_SYSTEM)
74 {
75 if (!SHGetPathFromIDListW(pidl, wszFolderPath))
76 return FALSE;
77
78 return SHELL32_GetCustomFolderAttributeFromPath(wszFolderPath, pwszHeading,
79 pwszAttribute, pwszValue, cchValue);
80 }
81 return FALSE;
82 }
83
84 BOOL SHELL32_GetCustomFolderAttributes(
85 LPCITEMIDLIST pidl, LPCWSTR pwszHeading,
86 LPWSTR pwszValue, DWORD cchValue)
87 {
88 DWORD dwAttrib = FILE_ATTRIBUTE_SYSTEM;
89 WCHAR wszFolderPath[MAX_PATH];
90
91 /* Hack around not having system attribute on non-Windows file systems */
92 if (0)
93 dwAttrib = _ILGetFileAttributes(pidl, NULL, 0);
94
95 if (dwAttrib & FILE_ATTRIBUTE_SYSTEM)
96 {
97 if (!SHGetPathFromIDListW(pidl, wszFolderPath))
98 return FALSE;
99
100 static const WCHAR wszDesktopIni[] =
101 {'d','e','s','k','t','o','p','.','i','n','i',0};
102
103 PathAddBackslashW(wszFolderPath);
104 PathAppendW(wszFolderPath, wszDesktopIni);
105 return GetPrivateProfileSectionW(pwszHeading, pwszValue, cchValue, wszFolderPath);
106 }
107 return FALSE;
108 }
109
110
111 /***************************************************************************
112 * GetNextElement (internal function)
113 *
114 * Gets a part of a string till the first backslash.
115 *
116 * PARAMETERS
117 * pszNext [IN] string to get the element from
118 * pszOut [IN] pointer to buffer which receives string
119 * dwOut [IN] length of pszOut
120 *
121 * RETURNS
122 * LPSTR pointer to first, not yet parsed char
123 */
124
125 LPCWSTR GetNextElementW (LPCWSTR pszNext, LPWSTR pszOut, DWORD dwOut)
126 {
127 LPCWSTR pszTail = pszNext;
128 DWORD dwCopy;
129
130 TRACE ("(%s %p 0x%08x)\n", debugstr_w (pszNext), pszOut, dwOut);
131
132 *pszOut = 0x0000;
133
134 if (!pszNext || !*pszNext)
135 return NULL;
136
137 while (*pszTail && (*pszTail != (WCHAR) '\\'))
138 pszTail++;
139
140 dwCopy = pszTail - pszNext + 1;
141 lstrcpynW (pszOut, pszNext, (dwOut < dwCopy) ? dwOut : dwCopy);
142
143 if (*pszTail)
144 pszTail++;
145 else
146 pszTail = NULL;
147
148 TRACE ("--(%s %s 0x%08x %p)\n", debugstr_w (pszNext), debugstr_w (pszOut), dwOut, pszTail);
149 return pszTail;
150 }
151
152 HRESULT SHELL32_ParseNextElement (IShellFolder2 * psf, HWND hwndOwner, LPBC pbc,
153 LPITEMIDLIST * pidlInOut, LPOLESTR szNext, DWORD * pEaten, DWORD * pdwAttributes)
154 {
155 HRESULT hr = E_INVALIDARG;
156 LPITEMIDLIST pidlIn = pidlInOut ? *pidlInOut : NULL;
157 LPITEMIDLIST pidlOut = NULL;
158 LPITEMIDLIST pidlTemp = NULL;
159 CComPtr<IShellFolder> psfChild;
160
161 TRACE ("(%p, %p, %p, %s)\n", psf, pbc, pidlIn, debugstr_w (szNext));
162
163 /* get the shellfolder for the child pidl and let it analyse further */
164 hr = psf->BindToObject(pidlIn, pbc, IID_PPV_ARG(IShellFolder, &psfChild));
165 if (FAILED(hr))
166 return hr;
167
168 hr = psfChild->ParseDisplayName(hwndOwner, pbc, szNext, pEaten, &pidlOut, pdwAttributes);
169 if (FAILED(hr))
170 return hr;
171
172 pidlTemp = ILCombine (pidlIn, pidlOut);
173 if (!pidlTemp)
174 {
175 hr = E_OUTOFMEMORY;
176 if (pidlOut)
177 ILFree(pidlOut);
178 return hr;
179 }
180
181 if (pidlOut)
182 ILFree (pidlOut);
183
184 if (pidlIn)
185 ILFree (pidlIn);
186
187 *pidlInOut = pidlTemp;
188
189 TRACE ("-- pidl=%p ret=0x%08x\n", pidlInOut ? *pidlInOut : NULL, hr);
190 return S_OK;
191 }
192
193 /***********************************************************************
194 * SHELL32_CoCreateInitSF
195 *
196 * Creates a shell folder and initializes it with a pidl and a root folder
197 * via IPersistFolder3 or IPersistFolder.
198 *
199 * NOTES
200 * pathRoot can be NULL for Folders being a drive.
201 * In this case the absolute path is built from pidlChild (eg. C:)
202 */
203 static HRESULT SHELL32_CoCreateInitSF (LPCITEMIDLIST pidlRoot, LPCWSTR pathRoot,
204 LPCITEMIDLIST pidlChild, REFCLSID clsid, LPVOID * ppvOut)
205 {
206 HRESULT hr;
207 CComPtr<IShellFolder> pShellFolder;
208
209 TRACE ("%p %s %p\n", pidlRoot, debugstr_w(pathRoot), pidlChild);
210
211 hr = SHCoCreateInstance(NULL, &clsid, NULL, IID_PPV_ARG(IShellFolder, &pShellFolder));
212 if (SUCCEEDED (hr))
213 {
214 LPITEMIDLIST pidlAbsolute = ILCombine (pidlRoot, pidlChild);
215 CComPtr<IPersistFolder> ppf;
216 CComPtr<IPersistFolder3> ppf3;
217
218 if (_ILIsFolder(pidlChild) &&
219 SUCCEEDED(pShellFolder->QueryInterface(IID_PPV_ARG(IPersistFolder3, &ppf3))))
220 {
221 PERSIST_FOLDER_TARGET_INFO ppfti;
222
223 ZeroMemory (&ppfti, sizeof (ppfti));
224
225 /* fill the PERSIST_FOLDER_TARGET_INFO */
226 ppfti.dwAttributes = -1;
227 ppfti.csidl = -1;
228
229 /* build path */
230 if (pathRoot)
231 {
232 lstrcpynW (ppfti.szTargetParsingName, pathRoot, MAX_PATH - 1);
233 PathAddBackslashW(ppfti.szTargetParsingName); /* FIXME: why have drives a backslash here ? */
234 }
235
236 if (pidlChild)
237 {
238 int len = wcslen(ppfti.szTargetParsingName);
239
240 if (!_ILSimpleGetTextW(pidlChild, ppfti.szTargetParsingName + len, MAX_PATH - len))
241 hr = E_INVALIDARG;
242 }
243
244 ppf3->InitializeEx(NULL, pidlAbsolute, &ppfti);
245 }
246 else if (SUCCEEDED((hr = pShellFolder->QueryInterface(IID_PPV_ARG(IPersistFolder, &ppf)))))
247 {
248 ppf->Initialize(pidlAbsolute);
249 }
250 ILFree (pidlAbsolute);
251 }
252
253 *ppvOut = pShellFolder.Detach();
254
255 TRACE ("-- (%p) ret=0x%08x\n", *ppvOut, hr);
256
257 return hr;
258 }
259
260 /***********************************************************************
261 * SHELL32_BindToChild [Internal]
262 *
263 * Common code for IShellFolder_BindToObject.
264 *
265 * PARAMS
266 * pidlRoot [I] The parent shell folder's absolute pidl.
267 * pathRoot [I] Absolute dos path of the parent shell folder.
268 * pidlComplete [I] PIDL of the child. Relative to pidlRoot.
269 * riid [I] GUID of the interface, which ppvOut shall be bound to.
270 * ppvOut [O] A reference to the child's interface (riid).
271 *
272 * NOTES
273 * pidlComplete has to contain at least one non empty SHITEMID.
274 * This function makes special assumptions on the shell namespace, which
275 * means you probably can't use it for your IShellFolder implementation.
276 */
277 HRESULT SHELL32_BindToChild (LPCITEMIDLIST pidlRoot,
278 LPCWSTR pathRoot, LPCITEMIDLIST pidlComplete, REFIID riid, LPVOID * ppvOut)
279 {
280 GUID const *clsid;
281 CComPtr<IShellFolder> pSF;
282 HRESULT hr;
283 LPITEMIDLIST pidlChild;
284
285 if (!pidlRoot || !ppvOut || !pidlComplete || !pidlComplete->mkid.cb)
286 return E_INVALIDARG;
287
288 *ppvOut = NULL;
289
290 pidlChild = ILCloneFirst (pidlComplete);
291
292 if ((clsid = _ILGetGUIDPointer (pidlChild))) {
293 /* virtual folder */
294 hr = SHELL32_CoCreateInitSF (pidlRoot, pathRoot, pidlChild, *clsid, (LPVOID *)&pSF);
295 } else {
296 /* file system folder */
297 CLSID clsidFolder = CLSID_ShellFSFolder;
298 static const WCHAR wszCLSID[] = {'C','L','S','I','D',0};
299 WCHAR wszCLSIDValue[CHARS_IN_GUID], wszFolderPath[MAX_PATH], *pwszPathTail = wszFolderPath;
300
301 /* see if folder CLSID should be overridden by desktop.ini file */
302 if (pathRoot) {
303 lstrcpynW(wszFolderPath, pathRoot, MAX_PATH);
304 pwszPathTail = PathAddBackslashW(wszFolderPath);
305 }
306
307 _ILSimpleGetTextW(pidlChild,pwszPathTail,MAX_PATH - (int)(pwszPathTail - wszFolderPath));
308
309 if (SHELL32_GetCustomFolderAttributeFromPath (wszFolderPath,
310 wszDotShellClassInfo, wszCLSID, wszCLSIDValue, CHARS_IN_GUID))
311 CLSIDFromString (wszCLSIDValue, &clsidFolder);
312
313 hr = SHELL32_CoCreateInitSF (pidlRoot, pathRoot, pidlChild,
314 clsidFolder, (LPVOID *)&pSF);
315 }
316 ILFree (pidlChild);
317
318 if (SUCCEEDED (hr)) {
319 if (_ILIsPidlSimple (pidlComplete)) {
320 /* no sub folders */
321 hr = pSF->QueryInterface(riid, ppvOut);
322 } else {
323 /* go deeper */
324 hr = pSF->BindToObject(ILGetNext (pidlComplete), NULL, riid, ppvOut);
325 }
326 }
327
328 TRACE ("-- returning (%p) %08x\n", *ppvOut, hr);
329
330 return hr;
331 }
332
333 HRESULT SHELL32_BindToGuidItem(LPCITEMIDLIST pidlRoot,
334 PCUIDLIST_RELATIVE pidl,
335 LPBC pbcReserved,
336 REFIID riid,
337 LPVOID *ppvOut)
338 {
339 CComPtr<IPersistFolder> pFolder;
340 HRESULT hr;
341
342 GUID *pGUID = _ILGetGUIDPointer(pidl);
343 if (!pGUID)
344 {
345 ERR("SHELL32_BindToGuidItem called for non guid item!\n");
346 return E_FAIL;
347 }
348
349 hr = SHCoCreateInstance(NULL, pGUID, NULL, IID_PPV_ARG(IPersistFolder, &pFolder));
350 if (FAILED(hr))
351 return hr;
352
353 hr = pFolder->Initialize(ILCombine(pidlRoot, pidl));
354 if (FAILED(hr))
355 return hr;
356
357 if (_ILIsPidlSimple (pidl))
358 {
359 return pFolder->QueryInterface(riid, ppvOut);
360 }
361 else
362 {
363 CComPtr<IShellFolder> psf;
364 hr = pFolder->QueryInterface(IID_PPV_ARG(IShellFolder, &psf));
365 if (FAILED(hr))
366 return hr;
367
368 return psf->BindToObject(ILGetNext (pidl), pbcReserved, riid, ppvOut);
369 }
370 }
371
372 /***********************************************************************
373 * SHELL32_GetDisplayNameOfChild
374 *
375 * Retrieves the display name of a child object of a shellfolder.
376 *
377 * For a pidl eg. [subpidl1][subpidl2][subpidl3]:
378 * - it binds to the child shellfolder [subpidl1]
379 * - asks it for the displayname of [subpidl2][subpidl3]
380 *
381 * Is possible the pidl is a simple pidl. In this case it asks the
382 * subfolder for the displayname of an empty pidl. The subfolder
383 * returns the own displayname eg. "::{guid}". This is used for
384 * virtual folders with the registry key WantsFORPARSING set.
385 */
386 HRESULT SHELL32_GetDisplayNameOfChild (IShellFolder2 * psf,
387 LPCITEMIDLIST pidl, DWORD dwFlags, LPWSTR szOut, DWORD dwOutLen)
388 {
389 LPITEMIDLIST pidlFirst;
390 HRESULT hr = E_INVALIDARG;
391
392 TRACE ("(%p)->(pidl=%p 0x%08x %p 0x%08x)\n", psf, pidl, dwFlags, szOut, dwOutLen);
393 pdump (pidl);
394
395 pidlFirst = ILCloneFirst(pidl);
396 if (pidlFirst)
397 {
398 CComPtr<IShellFolder> psfChild;
399
400 hr = psf->BindToObject(pidlFirst, NULL, IID_PPV_ARG(IShellFolder, &psfChild));
401 if (SUCCEEDED (hr))
402 {
403 STRRET strTemp;
404 LPITEMIDLIST pidlNext = ILGetNext (pidl);
405
406 hr = psfChild->GetDisplayNameOf(pidlNext, dwFlags, &strTemp);
407 if (SUCCEEDED (hr))
408 {
409 if(!StrRetToStrNW (szOut, dwOutLen, &strTemp, pidlNext))
410 hr = E_FAIL;
411 }
412 }
413 ILFree (pidlFirst);
414 } else
415 hr = E_OUTOFMEMORY;
416
417 TRACE ("-- ret=0x%08x %s\n", hr, debugstr_w(szOut));
418
419 return hr;
420 }
421
422 HRESULT SHELL32_GetDisplayNameOfGUIDItem(IShellFolder2* psf, LPCWSTR pszFolderPath, PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET strRet)
423 {
424 HRESULT hr = S_OK;
425 GUID const *clsid = _ILGetGUIDPointer (pidl);
426
427 if (!strRet)
428 return E_INVALIDARG;
429
430 LPWSTR pszPath = (LPWSTR)CoTaskMemAlloc((MAX_PATH + 1) * sizeof(WCHAR));
431 if (!pszPath)
432 return E_OUTOFMEMORY;
433
434 if (GET_SHGDN_FOR (dwFlags) == SHGDN_FORPARSING)
435 {
436 int bWantsForParsing;
437
438 /*
439 * We can only get a filesystem path from a shellfolder if the
440 * value WantsFORPARSING in CLSID\\{...}\\shellfolder exists.
441 *
442 * Exception: The MyComputer folder doesn't have this key,
443 * but any other filesystem backed folder it needs it.
444 */
445 if (IsEqualIID (*clsid, CLSID_MyComputer))
446 {
447 bWantsForParsing = TRUE;
448 }
449 else
450 {
451 HKEY hkeyClass;
452 if (HCR_RegOpenClassIDKey(*clsid, &hkeyClass))
453 {
454 LONG res = SHGetValueW(hkeyClass, L"Shellfolder", L"WantsForParsing", NULL, NULL, NULL);
455 bWantsForParsing = (res == ERROR_SUCCESS);
456 RegCloseKey(hkeyClass);
457 }
458 }
459
460 if ((GET_SHGDN_RELATION (dwFlags) == SHGDN_NORMAL) &&
461 bWantsForParsing)
462 {
463 /*
464 * we need the filesystem path to the destination folder.
465 * Only the folder itself can know it
466 */
467 hr = SHELL32_GetDisplayNameOfChild (psf, pidl, dwFlags,
468 pszPath,
469 MAX_PATH);
470 }
471 else
472 {
473 wcscpy(pszPath, pszFolderPath);
474 PWCHAR pItemName = &pszPath[wcslen(pszPath)];
475
476 /* parsing name like ::{...} */
477 pItemName[0] = ':';
478 pItemName[1] = ':';
479 SHELL32_GUIDToStringW (*clsid, &pItemName[2]);
480 }
481 }
482 else
483 {
484 /* user friendly name */
485 HCR_GetClassNameW (*clsid, pszPath, MAX_PATH);
486 }
487
488 if (SUCCEEDED(hr))
489 {
490 strRet->uType = STRRET_WSTR;
491 strRet->pOleStr = pszPath;
492 }
493 else
494 {
495 CoTaskMemFree(pszPath);
496 }
497
498 return hr;
499 }
500
501 /***********************************************************************
502 * SHELL32_GetItemAttributes
503 *
504 * NOTES
505 * Observed values:
506 * folder: 0xE0000177 FILESYSTEM | HASSUBFOLDER | FOLDER
507 * file: 0x40000177 FILESYSTEM
508 * drive: 0xf0000144 FILESYSTEM | HASSUBFOLDER | FOLDER | FILESYSANCESTOR
509 * mycomputer: 0xb0000154 HASSUBFOLDER | FOLDER | FILESYSANCESTOR
510 * (seems to be default for shell extensions if no registry entry exists)
511 *
512 * win2k:
513 * folder: 0xF0400177 FILESYSTEM | HASSUBFOLDER | FOLDER | FILESYSANCESTOR | CANMONIKER
514 * file: 0x40400177 FILESYSTEM | CANMONIKER
515 * drive 0xF0400154 FILESYSTEM | HASSUBFOLDER | FOLDER | FILESYSANCESTOR | CANMONIKER | CANRENAME (LABEL)
516 *
517 * According to the MSDN documentation this function should not set flags. It claims only to reset flags when necessary.
518 * However it turns out the native shell32.dll _sets_ flags in several cases - so do we.
519 */
520
521 static const DWORD dwSupportedAttr=
522 SFGAO_CANCOPY | /*0x00000001 */
523 SFGAO_CANMOVE | /*0x00000002 */
524 SFGAO_CANLINK | /*0x00000004 */
525 SFGAO_CANRENAME | /*0x00000010 */
526 SFGAO_CANDELETE | /*0x00000020 */
527 SFGAO_HASPROPSHEET | /*0x00000040 */
528 SFGAO_DROPTARGET | /*0x00000100 */
529 SFGAO_LINK | /*0x00010000 */
530 SFGAO_READONLY | /*0x00040000 */
531 SFGAO_HIDDEN | /*0x00080000 */
532 SFGAO_FILESYSANCESTOR | /*0x10000000 */
533 SFGAO_FOLDER | /*0x20000000 */
534 SFGAO_FILESYSTEM | /*0x40000000 */
535 SFGAO_HASSUBFOLDER; /*0x80000000 */
536
537 HRESULT SHELL32_GetGuidItemAttributes (IShellFolder * psf, LPCITEMIDLIST pidl, LPDWORD pdwAttributes)
538 {
539 if (!_ILIsSpecialFolder(pidl))
540 {
541 ERR("Got wrong type of pidl!\n");
542 *pdwAttributes &= SFGAO_CANLINK;
543 return S_OK;
544 }
545
546 if (*pdwAttributes & ~dwSupportedAttr)
547 {
548 WARN ("attributes 0x%08x not implemented\n", (*pdwAttributes & ~dwSupportedAttr));
549 *pdwAttributes &= dwSupportedAttr;
550 }
551
552 /* First try to get them from the registry */
553 if (HCR_GetFolderAttributes(pidl, pdwAttributes) && *pdwAttributes)
554 {
555 return S_OK;
556 }
557 else
558 {
559 /* If we can't get it from the registry we have to query the child */
560 CComPtr<IShellFolder> psf2;
561 if (SUCCEEDED(psf->BindToObject(pidl, 0, IID_PPV_ARG(IShellFolder, &psf2))))
562 {
563 return psf2->GetAttributesOf(0, NULL, pdwAttributes);
564 }
565 }
566
567 *pdwAttributes &= SFGAO_CANLINK;
568 return S_OK;
569 }
570
571 HRESULT SHELL32_GetFSItemAttributes(IShellFolder * psf, LPCITEMIDLIST pidl, LPDWORD pdwAttributes)
572 {
573 DWORD dwAttributes;
574
575 if (!_ILIsFolder(pidl) && !_ILIsValue(pidl))
576 {
577 ERR("Got wrong type of pidl!\n");
578 *pdwAttributes &= SFGAO_CANLINK;
579 return S_OK;
580 }
581
582 if (*pdwAttributes & ~dwSupportedAttr)
583 {
584 WARN ("attributes 0x%08x not implemented\n", (*pdwAttributes & ~dwSupportedAttr));
585 *pdwAttributes &= dwSupportedAttr;
586 }
587
588 dwAttributes = _ILGetFileAttributes(pidl, NULL, 0);
589
590 /* Set common attributes */
591 *pdwAttributes |= SFGAO_FILESYSTEM | SFGAO_DROPTARGET | SFGAO_HASPROPSHEET | SFGAO_CANDELETE |
592 SFGAO_CANRENAME | SFGAO_CANLINK | SFGAO_CANMOVE | SFGAO_CANCOPY;
593
594 if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY)
595 {
596 *pdwAttributes |= (SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_FILESYSANCESTOR);
597 }
598 else
599 *pdwAttributes &= ~(SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_FILESYSANCESTOR);
600
601 if (dwAttributes & FILE_ATTRIBUTE_HIDDEN)
602 *pdwAttributes |= SFGAO_HIDDEN;
603 else
604 *pdwAttributes &= ~SFGAO_HIDDEN;
605
606 if (dwAttributes & FILE_ATTRIBUTE_READONLY)
607 *pdwAttributes |= SFGAO_READONLY;
608 else
609 *pdwAttributes &= ~SFGAO_READONLY;
610
611 if (SFGAO_LINK & *pdwAttributes)
612 {
613 char ext[MAX_PATH];
614
615 if (!_ILGetExtension(pidl, ext, MAX_PATH) || lstrcmpiA(ext, "lnk"))
616 *pdwAttributes &= ~SFGAO_LINK;
617 }
618
619 if (SFGAO_HASSUBFOLDER & *pdwAttributes)
620 {
621 CComPtr<IShellFolder> psf2;
622 if (SUCCEEDED(psf->BindToObject(pidl, 0, IID_PPV_ARG(IShellFolder, &psf2))))
623 {
624 CComPtr<IEnumIDList> pEnumIL;
625 if (SUCCEEDED(psf2->EnumObjects(0, SHCONTF_FOLDERS, &pEnumIL)))
626 {
627 if (pEnumIL->Skip(1) != S_OK)
628 *pdwAttributes &= ~SFGAO_HASSUBFOLDER;
629 }
630 }
631 }
632
633 TRACE ("-- 0x%08x\n", *pdwAttributes);
634 return S_OK;
635 }
636
637 HRESULT SHELL32_GetItemAttributes (IShellFolder * psf, LPCITEMIDLIST pidl, LPDWORD pdwAttributes)
638 {
639 TRACE ("0x%08x\n", *pdwAttributes);
640
641 if (*pdwAttributes & ~dwSupportedAttr)
642 {
643 WARN ("attributes 0x%08x not implemented\n", (*pdwAttributes & ~dwSupportedAttr));
644 *pdwAttributes &= dwSupportedAttr;
645 }
646
647 if (_ILIsSpecialFolder(pidl))
648 {
649 return SHELL32_GetGuidItemAttributes(psf, pidl, pdwAttributes);
650 }
651 else if(_ILIsFolder(pidl) || _ILIsValue(pidl))
652 {
653 return SHELL32_GetFSItemAttributes(psf, pidl, pdwAttributes);
654 }
655 else
656 {
657 /* Set common attributes */
658 ERR("We got a pidl that is neither a guid or an FS item!!! Type=0x%x\n",pidl->mkid.abID[0]);
659 *pdwAttributes &= SFGAO_CANLINK;
660 }
661
662 TRACE ("-- 0x%08x\n", *pdwAttributes);
663 return S_OK;
664 }
665
666 /***********************************************************************
667 * SHELL32_CompareIDs
668 */
669 HRESULT SHELL32_CompareIDs(IShellFolder * iface, LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
670 {
671 int type1,
672 type2;
673 char szTemp1[MAX_PATH];
674 char szTemp2[MAX_PATH];
675 HRESULT nReturn;
676 LPITEMIDLIST firstpidl;
677 LPITEMIDLIST nextpidl1;
678 LPITEMIDLIST nextpidl2;
679 CComPtr<IShellFolder> psf;
680
681 /* test for empty pidls */
682 BOOL isEmpty1 = _ILIsDesktop(pidl1);
683 BOOL isEmpty2 = _ILIsDesktop(pidl2);
684
685 if (isEmpty1 && isEmpty2)
686 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 0);
687 if (isEmpty1)
688 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD) -1);
689 if (isEmpty2)
690 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 1);
691
692 /* test for different types. Sort order is the PT_* constant */
693 type1 = _ILGetDataPointer(pidl1)->type;
694 type2 = _ILGetDataPointer(pidl2)->type;
695 if (type1 < type2)
696 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD) -1);
697 else if (type1 > type2)
698 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 1);
699
700 /* test for name of pidl */
701 _ILSimpleGetText(pidl1, szTemp1, MAX_PATH);
702 _ILSimpleGetText(pidl2, szTemp2, MAX_PATH);
703 nReturn = lstrcmpiA(szTemp1, szTemp2);
704 if (nReturn < 0)
705 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD) -1);
706 else if (nReturn > 0)
707 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 1);
708
709 /* test of complex pidls */
710 firstpidl = ILCloneFirst(pidl1);
711 nextpidl1 = ILGetNext(pidl1);
712 nextpidl2 = ILGetNext(pidl2);
713
714 /* optimizing: test special cases and bind not deeper */
715 /* the deeper shellfolder would do the same */
716 isEmpty1 = _ILIsDesktop(nextpidl1);
717 isEmpty2 = _ILIsDesktop(nextpidl2);
718
719 if (isEmpty1 && isEmpty2)
720 {
721 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 0);
722 }
723 else if (isEmpty1)
724 {
725 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD) -1);
726 }
727 else if (isEmpty2)
728 {
729 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 1);
730 /* optimizing end */
731 }
732 else if (SUCCEEDED(iface->BindToObject(firstpidl, NULL, IID_PPV_ARG(IShellFolder, &psf)))) {
733 nReturn = psf->CompareIDs(lParam, nextpidl1, nextpidl2);
734 }
735 ILFree(firstpidl);
736 return nReturn;
737 }
738
739 /***********************************************************************
740 * SHCreateLinks
741 *
742 * Undocumented.
743 */
744 HRESULT WINAPI SHCreateLinks( HWND hWnd, LPCSTR lpszDir, IDataObject * lpDataObject,
745 UINT uFlags, LPITEMIDLIST *lppidlLinks)
746 {
747 FIXME("%p %s %p %08x %p\n", hWnd, lpszDir, lpDataObject, uFlags, lppidlLinks);
748 return E_NOTIMPL;
749 }
750
751 /***********************************************************************
752 * SHOpenFolderAndSelectItems
753 *
754 * Unimplemented.
755 */
756 EXTERN_C HRESULT
757 WINAPI
758 SHOpenFolderAndSelectItems(LPITEMIDLIST pidlFolder,
759 UINT cidl,
760 PCUITEMID_CHILD_ARRAY apidl,
761 DWORD dwFlags)
762 {
763 FIXME("SHOpenFolderAndSelectItems() stub\n");
764 return E_NOTIMPL;
765 }