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