1 /*
2  * PROJECT:     shell32
3  * LICENSE:     LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
4  * PURPOSE:     file system folder
5  * COPYRIGHT:   Copyright 1997 Marcus Meissner
6  *              Copyright 1998, 1999, 2002 Juergen Schmied
7  *              Copyright 2019-2024 Katayama Hirofumi MZ
8  *              Copyright 2020 Mark Jansen (mark.jansen@reactos.org)
9  */
10 
11 #include <precomp.h>
12 
13 WINE_DEFAULT_DEBUG_CHANNEL (shell);
14 
15 static HRESULT SHELL32_GetCLSIDForDirectory(LPCWSTR pwszDir, LPCWSTR KeyName, CLSID* pclsidFolder);
16 
17 
18 HKEY OpenKeyFromFileType(LPWSTR pExtension, LPCWSTR KeyName)
19 {
20     HKEY hkey;
21 
22     WCHAR FullName[MAX_PATH];
23     DWORD dwSize = sizeof(FullName);
24     wsprintf(FullName, L"%s\\%s", pExtension, KeyName);
25 
26     LONG res = RegOpenKeyExW(HKEY_CLASSES_ROOT, FullName, 0, KEY_READ, &hkey);
27     if (!res)
28         return hkey;
29 
30     res = RegGetValueW(HKEY_CLASSES_ROOT, pExtension, NULL, RRF_RT_REG_SZ, NULL, FullName, &dwSize);
31     if (res)
32     {
33         WARN("Failed to get progid for extension %S (%x), error %d\n", pExtension, pExtension, res);
34         return NULL;
35     }
36 
37     wcscat(FullName, L"\\");
38     wcscat(FullName, KeyName);
39 
40     hkey = NULL;
41     res = RegOpenKeyExW(HKEY_CLASSES_ROOT, FullName, 0, KEY_READ, &hkey);
42     if (res)
43         WARN("Could not open key %S for extension %S\n", KeyName, pExtension);
44 
45     return hkey;
46 }
47 
48 LPWSTR ExtensionFromPidl(PCUIDLIST_RELATIVE pidl)
49 {
50     if (!_ILIsValue(pidl))
51     {
52         ERR("Invalid pidl!\n");
53         return NULL;
54     }
55 
56     FileStructW* pDataW = _ILGetFileStructW(pidl);
57     if (!pDataW)
58     {
59         ERR("Invalid pidl!\n");
60         return NULL;
61     }
62 
63     LPWSTR pExtension = PathFindExtensionW(pDataW->wszName);
64     if (!pExtension || *pExtension == UNICODE_NULL)
65     {
66         WARN("No extension for %S!\n", pDataW->wszName);
67         return NULL;
68     }
69     return pExtension;
70 }
71 
72 HRESULT GetCLSIDForFileTypeFromExtension(LPWSTR pExtension, LPCWSTR KeyName, CLSID* pclsid)
73 {
74     HKEY hkeyProgId = OpenKeyFromFileType(pExtension, KeyName);
75     if (!hkeyProgId)
76     {
77         WARN("OpenKeyFromFileType failed for key %S\n", KeyName);
78         return S_FALSE;
79     }
80 
81     WCHAR wszCLSIDValue[CHARS_IN_GUID];
82     DWORD dwSize = sizeof(wszCLSIDValue);
83     LONG res = RegGetValueW(hkeyProgId, NULL, NULL, RRF_RT_REG_SZ, NULL, wszCLSIDValue, &dwSize);
84     RegCloseKey(hkeyProgId);
85     if (res)
86     {
87         ERR("OpenKeyFromFileType succeeded but RegGetValueW failed\n");
88         return S_FALSE;
89     }
90 
91 #if 0
92     {
93         res = RegGetValueW(HKEY_LOCAL_MACHINE,
94                            L"Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved",
95                            wszCLSIDValue,
96                            RRF_RT_REG_SZ,
97                            NULL,
98                            NULL,
99                            NULL);
100         if (res != ERROR_SUCCESS)
101         {
102             ERR("DropHandler extension %S not approved\n", wszName);
103             return E_ACCESSDENIED;
104         }
105     }
106 #endif
107 
108     if (RegGetValueW(HKEY_LOCAL_MACHINE,
109                      L"Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Blocked",
110                      wszCLSIDValue,
111                      RRF_RT_REG_SZ,
112                      NULL,
113                      NULL,
114                      NULL) == ERROR_SUCCESS)
115     {
116         ERR("Extension %S  not approved\n", wszCLSIDValue);
117         return E_ACCESSDENIED;
118     }
119 
120     HRESULT hres = CLSIDFromString (wszCLSIDValue, pclsid);
121     if (FAILED_UNEXPECTEDLY(hres))
122         return hres;
123 
124     return S_OK;
125 }
126 
127 HRESULT GetCLSIDForFileType(PCUIDLIST_RELATIVE pidl, LPCWSTR KeyName, CLSID* pclsid)
128 {
129     LPWSTR pExtension = ExtensionFromPidl(pidl);
130     if (!pExtension)
131         return S_FALSE;
132 
133     return GetCLSIDForFileTypeFromExtension(pExtension, KeyName, pclsid);
134 }
135 
136 static HRESULT
137 getDefaultIconLocation(LPWSTR szIconFile, UINT cchMax, int *piIndex, UINT uFlags)
138 {
139     if (!HLM_GetIconW(IDI_SHELL_FOLDER - 1, szIconFile, cchMax, piIndex))
140     {
141         if (!HCR_GetIconW(L"Folder", szIconFile, NULL, cchMax, piIndex))
142         {
143             StringCchCopyW(szIconFile, cchMax, swShell32Name);
144             *piIndex = -IDI_SHELL_FOLDER;
145         }
146     }
147 
148     if (uFlags & GIL_OPENICON)
149     {
150         // next icon
151         if (*piIndex < 0)
152             (*piIndex)--;
153         else
154             (*piIndex)++;
155     }
156 
157     return S_OK;
158 }
159 
160 static BOOL
161 getShellClassInfo(LPCWSTR Entry, LPWSTR pszValue, DWORD cchValueLen, LPCWSTR IniFile)
162 {
163     return GetPrivateProfileStringW(L".ShellClassInfo", Entry, NULL, pszValue, cchValueLen, IniFile);
164 }
165 
166 static HRESULT
167 getIconLocationForFolder(IShellFolder * psf, PCITEMID_CHILD pidl, UINT uFlags,
168                          LPWSTR szIconFile, UINT cchMax, int *piIndex, UINT *pwFlags)
169 {
170     DWORD dwFileAttrs;
171     WCHAR wszPath[MAX_PATH];
172     WCHAR wszIniFullPath[MAX_PATH];
173 
174     if (uFlags & GIL_DEFAULTICON)
175         goto Quit;
176 
177     // get path
178     if (!ILGetDisplayNameExW(psf, pidl, wszPath, 0))
179         goto Quit;
180     if (!PathIsDirectoryW(wszPath))
181         goto Quit;
182 
183     // read-only or system folder?
184     dwFileAttrs = _ILGetFileAttributes(ILFindLastID(pidl), NULL, 0);
185     if ((dwFileAttrs & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)) == 0)
186         goto Quit;
187 
188     // build the full path of ini file
189     StringCchCopyW(wszIniFullPath, _countof(wszIniFullPath), wszPath);
190     PathAppendW(wszIniFullPath, L"desktop.ini");
191 
192     WCHAR wszValue[MAX_PATH], wszTemp[MAX_PATH];
193     if (getShellClassInfo(L"IconFile", wszValue, _countof(wszValue), wszIniFullPath))
194     {
195         // wszValue --> wszTemp
196         ExpandEnvironmentStringsW(wszValue, wszTemp, _countof(wszTemp));
197 
198         // wszPath + wszTemp --> wszPath
199         if (PathIsRelativeW(wszTemp))
200             PathAppendW(wszPath, wszTemp);
201         else
202             StringCchCopyW(wszPath, _countof(wszPath), wszTemp);
203 
204         // wszPath --> szIconFile
205         GetFullPathNameW(wszPath, cchMax, szIconFile, NULL);
206 
207         *piIndex = GetPrivateProfileIntW(L".ShellClassInfo", L"IconIndex", 0, wszIniFullPath);
208         return S_OK;
209     }
210     else if (getShellClassInfo(L"CLSID", wszValue, _countof(wszValue), wszIniFullPath) &&
211              HCR_GetIconW(wszValue, szIconFile, NULL, cchMax, piIndex))
212     {
213         return S_OK;
214     }
215     else if (getShellClassInfo(L"CLSID2", wszValue, _countof(wszValue), wszIniFullPath) &&
216              HCR_GetIconW(wszValue, szIconFile, NULL, cchMax, piIndex))
217     {
218         return S_OK;
219     }
220     else if (getShellClassInfo(L"IconResource", wszValue, _countof(wszValue), wszIniFullPath))
221     {
222         // wszValue --> wszTemp
223         ExpandEnvironmentStringsW(wszValue, wszTemp, _countof(wszTemp));
224 
225         // parse the icon location
226         *piIndex = PathParseIconLocationW(wszTemp);
227 
228         // wszPath + wszTemp --> wszPath
229         if (PathIsRelativeW(wszTemp))
230             PathAppendW(wszPath, wszTemp);
231         else
232             StringCchCopyW(wszPath, _countof(wszPath), wszTemp);
233 
234         // wszPath --> szIconFile
235         GetFullPathNameW(wszPath, cchMax, szIconFile, NULL);
236         return S_OK;
237     }
238 
239 Quit:
240     return getDefaultIconLocation(szIconFile, cchMax, piIndex, uFlags);
241 }
242 
243 HRESULT CFSExtractIcon_CreateInstance(IShellFolder * psf, LPCITEMIDLIST pidl, REFIID iid, LPVOID * ppvOut)
244 {
245     CComPtr<IDefaultExtractIconInit> initIcon;
246     HRESULT hr;
247     int icon_idx = 0;
248     UINT flags = 0; // FIXME: Use it!
249     WCHAR wTemp[MAX_PATH] = L"";
250 
251     hr = SHCreateDefaultExtractIcon(IID_PPV_ARG(IDefaultExtractIconInit,&initIcon));
252     if (FAILED(hr))
253         return hr;
254 
255     if (_ILIsFolder (pidl))
256     {
257         if (SUCCEEDED(getIconLocationForFolder(psf,
258                           pidl, 0, wTemp, _countof(wTemp),
259                           &icon_idx,
260                           &flags)))
261         {
262             initIcon->SetNormalIcon(wTemp, icon_idx);
263             // FIXME: if/when getIconLocationForFolder does something for
264             //        GIL_FORSHORTCUT, code below should be uncommented. and
265             //        the following line removed.
266             initIcon->SetShortcutIcon(wTemp, icon_idx);
267         }
268         if (SUCCEEDED(getIconLocationForFolder(psf,
269                           pidl, GIL_DEFAULTICON, wTemp, _countof(wTemp),
270                           &icon_idx,
271                           &flags)))
272         {
273             initIcon->SetDefaultIcon(wTemp, icon_idx);
274         }
275         // if (SUCCEEDED(getIconLocationForFolder(psf,
276         //                   pidl, GIL_FORSHORTCUT, wTemp, _countof(wTemp),
277         //                   &icon_idx,
278         //                   &flags)))
279         // {
280         //     initIcon->SetShortcutIcon(wTemp, icon_idx);
281         // }
282         if (SUCCEEDED(getIconLocationForFolder(psf,
283                           pidl, GIL_OPENICON, wTemp, _countof(wTemp),
284                           &icon_idx,
285                           &flags)))
286         {
287             initIcon->SetOpenIcon(wTemp, icon_idx);
288         }
289     }
290     else
291     {
292         LPWSTR pExtension = ExtensionFromPidl(pidl);
293         HKEY hkey = pExtension ? OpenKeyFromFileType(pExtension, L"DefaultIcon") : NULL;
294         if (!hkey)
295             WARN("Could not open DefaultIcon key!\n");
296 
297         DWORD dwSize = sizeof(wTemp);
298         if (hkey && !SHQueryValueExW(hkey, NULL, NULL, NULL, wTemp, &dwSize))
299         {
300             WCHAR sNum[5];
301             if (ParseFieldW (wTemp, 2, sNum, 5))
302                 icon_idx = _wtoi(sNum);
303             else
304                 icon_idx = 0; /* sometimes the icon number is missing */
305             ParseFieldW (wTemp, 1, wTemp, MAX_PATH);
306             PathUnquoteSpacesW(wTemp);
307 
308             if (!wcscmp(L"%1", wTemp)) /* icon is in the file */
309             {
310                 ILGetDisplayNameExW(psf, pidl, wTemp, ILGDN_FORPARSING);
311                 icon_idx = 0;
312 
313                 INT ret = PrivateExtractIconsW(wTemp, 0, 0, 0, NULL, NULL, 0, 0);
314                 if (ret <= 0)
315                 {
316                     StringCbCopyW(wTemp, sizeof(wTemp), swShell32Name);
317                     if (lstrcmpiW(pExtension, L".exe") == 0 || lstrcmpiW(pExtension, L".scr") == 0)
318                         icon_idx = -IDI_SHELL_EXE;
319                     else
320                         icon_idx = -IDI_SHELL_DOCUMENT;
321                 }
322             }
323 
324             initIcon->SetNormalIcon(wTemp, icon_idx);
325         }
326         else
327         {
328             initIcon->SetNormalIcon(swShell32Name, 0);
329         }
330 
331         if (hkey)
332             RegCloseKey(hkey);
333     }
334 
335     return initIcon->QueryInterface(iid, ppvOut);
336 }
337 
338 /*
339 CFileSysEnum should do an initial FindFirstFile and do a FindNextFile as each file is
340 returned by Next. When the enumerator is created, it can do numerous additional operations
341 including formatting a drive, reconnecting a network share drive, and requesting a disk
342 be inserted in a removable drive.
343 */
344 
345 
346 class CFileSysEnum :
347     public CEnumIDListBase
348 {
349 private:
350     HRESULT _AddFindResult(LPWSTR sParentDir, const WIN32_FIND_DATAW& FindData, DWORD dwFlags)
351     {
352 #define SUPER_HIDDEN (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)
353 
354         // Does it need special handling because it is hidden?
355         if (FindData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
356         {
357             DWORD dwHidden = FindData.dwFileAttributes & SUPER_HIDDEN;
358 
359             // Is it hidden, but are we not asked to include hidden?
360             if (dwHidden == FILE_ATTRIBUTE_HIDDEN && !(dwFlags & SHCONTF_INCLUDEHIDDEN))
361                 return S_OK;
362 
363             // Is it a system file, but are we not asked to include those?
364             if (dwHidden == SUPER_HIDDEN && !(dwFlags & SHCONTF_INCLUDESUPERHIDDEN))
365                 return S_OK;
366         }
367 
368         BOOL bDirectory = (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
369 
370         HRESULT hr;
371         if (bDirectory)
372         {
373             // Skip the current and parent directory nodes
374             if (!strcmpW(FindData.cFileName, L".") || !strcmpW(FindData.cFileName, L".."))
375                 return S_OK;
376 
377             // Does this directory need special handling?
378             if ((FindData.dwFileAttributes & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)) != 0)
379             {
380                 WCHAR Tmp[MAX_PATH];
381                 CLSID clsidFolder;
382 
383                 PathCombineW(Tmp, sParentDir, FindData.cFileName);
384 
385                 hr = SHELL32_GetCLSIDForDirectory(Tmp, L"CLSID", &clsidFolder);
386                 if (SUCCEEDED(hr))
387                 {
388                     ERR("Got CLSID override for '%S'\n", Tmp);
389                 }
390             }
391         }
392         else
393         {
394             CLSID clsidFile;
395             LPWSTR pExtension = PathFindExtensionW(FindData.cFileName);
396             if (pExtension)
397             {
398                 // FIXME: Cache this?
399                 hr = GetCLSIDForFileTypeFromExtension(pExtension, L"CLSID", &clsidFile);
400                 if (hr == S_OK)
401                 {
402                     HKEY hkey;
403                     hr = SHRegGetCLSIDKeyW(clsidFile, L"ShellFolder", FALSE, FALSE, &hkey);
404                     if (SUCCEEDED(hr))
405                     {
406                         ::RegCloseKey(hkey);
407 
408                         // This should be presented as directory!
409                         bDirectory = TRUE;
410                         TRACE("Treating '%S' as directory!\n", FindData.cFileName);
411                     }
412                 }
413             }
414         }
415 
416         LPITEMIDLIST pidl = NULL;
417         if (bDirectory)
418         {
419             if (dwFlags & SHCONTF_FOLDERS)
420             {
421                 TRACE("(%p)->  (folder=%s)\n", this, debugstr_w(FindData.cFileName));
422                 pidl = _ILCreateFromFindDataW(&FindData);
423             }
424         }
425         else
426         {
427             if (dwFlags & SHCONTF_NONFOLDERS)
428             {
429                 TRACE("(%p)->  (file  =%s)\n", this, debugstr_w(FindData.cFileName));
430                 pidl = _ILCreateFromFindDataW(&FindData);
431             }
432         }
433 
434         if (pidl && !AddToEnumList(pidl))
435         {
436             FAILED_UNEXPECTEDLY(E_FAIL);
437             return E_FAIL;
438         }
439 
440         return S_OK;
441     }
442 
443 public:
444     CFileSysEnum()
445     {
446 
447     }
448 
449     ~CFileSysEnum()
450     {
451     }
452 
453     HRESULT WINAPI Initialize(LPWSTR sPathTarget, DWORD dwFlags)
454     {
455         TRACE("(%p)->(path=%s flags=0x%08x)\n", this, debugstr_w(sPathTarget), dwFlags);
456 
457         if (!sPathTarget || !sPathTarget[0])
458         {
459             WARN("No path for CFileSysEnum, empty result!\n");
460             return S_FALSE;
461         }
462 
463         WCHAR szFindPattern[MAX_PATH];
464         HRESULT hr = StringCchCopyW(szFindPattern, _countof(szFindPattern), sPathTarget);
465         if (FAILED_UNEXPECTEDLY(hr))
466             return hr;
467 
468         /* FIXME: UNSAFE CRAP */
469         PathAddBackslashW(szFindPattern);
470 
471         hr = StringCchCatW(szFindPattern, _countof(szFindPattern), L"*.*");
472         if (FAILED_UNEXPECTEDLY(hr))
473             return hr;
474 
475 
476         WIN32_FIND_DATAW FindData;
477         HANDLE hFind = FindFirstFileW(szFindPattern, &FindData);
478         if (hFind == INVALID_HANDLE_VALUE)
479             return HRESULT_FROM_WIN32(GetLastError());
480 
481         do
482         {
483             hr = _AddFindResult(sPathTarget, FindData, dwFlags);
484 
485             if (FAILED_UNEXPECTEDLY(hr))
486                 break;
487 
488         } while(FindNextFileW(hFind, &FindData));
489 
490         if (SUCCEEDED(hr))
491         {
492             DWORD dwError = GetLastError();
493             if (dwError != ERROR_NO_MORE_FILES)
494             {
495                 hr = HRESULT_FROM_WIN32(dwError);
496                 FAILED_UNEXPECTEDLY(hr);
497             }
498         }
499         TRACE("(%p)->(hr=0x%08x)\n", this, hr);
500         FindClose(hFind);
501         return hr;
502     }
503 
504     BEGIN_COM_MAP(CFileSysEnum)
505         COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList)
506     END_COM_MAP()
507 };
508 
509 
510 /***********************************************************************
511  *   IShellFolder implementation
512  */
513 
514 CFSFolder::CFSFolder()
515 {
516     m_pclsid = &CLSID_ShellFSFolder;
517     m_sPathTarget = NULL;
518     m_pidlRoot = NULL;
519     m_bGroupPolicyActive = 0;
520 }
521 
522 CFSFolder::~CFSFolder()
523 {
524     TRACE("-- destroying IShellFolder(%p)\n", this);
525 
526     SHFree(m_pidlRoot);
527     SHFree(m_sPathTarget);
528 }
529 
530 static const shvheader GenericSFHeader[] = {
531     {IDS_SHV_COLUMN_NAME, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 15},
532     {IDS_SHV_COLUMN_SIZE, SHCOLSTATE_TYPE_INT | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
533     {IDS_SHV_COLUMN_TYPE, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 10},
534     {IDS_SHV_COLUMN_MODIFIED, SHCOLSTATE_TYPE_DATE | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 15},
535     {IDS_SHV_COLUMN_ATTRIBUTES, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 10},
536     {IDS_SHV_COLUMN_COMMENTS, SHCOLSTATE_TYPE_STR | SHCOLSTATE_SLOW, LVCFMT_LEFT, 10},  // We don't currently support comments but CRegFolder does
537 };
538 
539 #define GENERICSHELLVIEWCOLUMNS _countof(GenericSFHeader)
540 
541 HRESULT CFSFolder::GetFSColumnDetails(UINT iColumn, SHELLDETAILS &sd)
542 {
543     if (iColumn >= _countof(GenericSFHeader))
544         return E_INVALIDARG;
545 
546     sd.fmt = GenericSFHeader[iColumn].fmt;
547     sd.cxChar = GenericSFHeader[iColumn].cxChar;
548     return SHSetStrRet(&sd.str, GenericSFHeader[iColumn].colnameid);
549 }
550 
551 HRESULT CFSFolder::GetDefaultFSColumnState(UINT iColumn, SHCOLSTATEF &csFlags)
552 {
553     if (iColumn >= _countof(GenericSFHeader))
554         return E_INVALIDARG;
555 
556     csFlags = GenericSFHeader[iColumn].colstate;
557     return S_OK;
558 }
559 
560 static HRESULT SHELL32_GetCLSIDForDirectory(LPCWSTR pwszDir, LPCWSTR KeyName, CLSID* pclsidFolder)
561 {
562     WCHAR wszCLSIDValue[CHARS_IN_GUID];
563     WCHAR wszDesktopIni[MAX_PATH];
564 
565     StringCchCopyW(wszDesktopIni, MAX_PATH, pwszDir);
566     StringCchCatW(wszDesktopIni, MAX_PATH, L"\\desktop.ini");
567 
568     if (GetPrivateProfileStringW(L".ShellClassInfo",
569                                  KeyName,
570                                  L"",
571                                  wszCLSIDValue,
572                                  CHARS_IN_GUID,
573                                  wszDesktopIni))
574     {
575         return CLSIDFromString(wszCLSIDValue, pclsidFolder);
576     }
577     return E_FAIL;
578 }
579 
580 HRESULT SHELL32_GetFSItemAttributes(IShellFolder * psf, LPCITEMIDLIST pidl, LPDWORD pdwAttributes)
581 {
582     DWORD dwFileAttributes, dwShellAttributes;
583 
584     if (!_ILIsFolder(pidl) && !_ILIsValue(pidl))
585     {
586         ERR("Got wrong type of pidl!\n");
587         *pdwAttributes &= SFGAO_CANLINK;
588         return S_OK;
589     }
590 
591     dwFileAttributes = _ILGetFileAttributes(pidl, NULL, 0);
592 
593     /* Set common attributes */
594     dwShellAttributes = SFGAO_CANCOPY | SFGAO_CANMOVE | SFGAO_CANLINK | SFGAO_CANRENAME | SFGAO_CANDELETE |
595                         SFGAO_HASPROPSHEET | SFGAO_DROPTARGET | SFGAO_FILESYSTEM;
596 
597     BOOL bDirectory = (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
598 
599     if (!bDirectory)
600     {
601         // https://git.reactos.org/?p=reactos.git;a=blob;f=dll/shellext/zipfldr/res/zipfldr.rgs;hb=032b5aacd233cd7b83ab6282aad638c161fdc400#l9
602         WCHAR szFileName[MAX_PATH];
603         LPWSTR pExtension;
604 
605         if (_ILSimpleGetTextW(pidl, szFileName, _countof(szFileName)) && (pExtension = PathFindExtensionW(szFileName)))
606         {
607             CLSID clsidFile;
608             // FIXME: Cache this?
609             HRESULT hr = GetCLSIDForFileTypeFromExtension(pExtension, L"CLSID", &clsidFile);
610             if (hr == S_OK)
611             {
612                 HKEY hkey;
613                 hr = SHRegGetCLSIDKeyW(clsidFile, L"ShellFolder", FALSE, FALSE, &hkey);
614                 if (SUCCEEDED(hr))
615                 {
616                     DWORD dwAttributes = 0;
617                     DWORD dwSize = sizeof(dwAttributes);
618                     LSTATUS Status;
619 
620                     Status = SHRegGetValueW(hkey, NULL, L"Attributes", RRF_RT_REG_DWORD, NULL, &dwAttributes, &dwSize);
621                     if (Status == STATUS_SUCCESS)
622                     {
623                         TRACE("Augmenting '%S' with dwAttributes=0x%x\n", szFileName, dwAttributes);
624                         dwShellAttributes |= dwAttributes;
625                     }
626                     ::RegCloseKey(hkey);
627 
628                     // This should be presented as directory!
629                     bDirectory = TRUE;
630                     TRACE("Treating '%S' as directory!\n", szFileName);
631                 }
632             }
633         }
634     }
635 
636     // This is a directory
637     if (bDirectory)
638     {
639         dwShellAttributes |= (SFGAO_FOLDER | /*SFGAO_HASSUBFOLDER |*/ SFGAO_STORAGE);
640 
641         // Is this a real directory?
642         if (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
643         {
644             dwShellAttributes |= (SFGAO_FILESYSANCESTOR | SFGAO_STORAGEANCESTOR);
645         }
646     }
647     else
648     {
649         dwShellAttributes |= SFGAO_STREAM;
650     }
651 
652     if (dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
653         dwShellAttributes |=  SFGAO_HIDDEN;
654 
655     if (dwFileAttributes & FILE_ATTRIBUTE_READONLY)
656         dwShellAttributes |=  SFGAO_READONLY;
657 
658     if (SFGAO_LINK & *pdwAttributes)
659     {
660         WCHAR ext[MAX_PATH];
661 
662         if (_ILGetExtension(pidl, ext, _countof(ext)) && !lstrcmpiW(ext, L"lnk"))
663             dwShellAttributes |= SFGAO_LINK;
664     }
665 
666     if (SFGAO_HASSUBFOLDER & *pdwAttributes)
667     {
668         CComPtr<IShellFolder> psf2;
669         if (SUCCEEDED(psf->BindToObject(pidl, 0, IID_PPV_ARG(IShellFolder, &psf2))))
670         {
671             CComPtr<IEnumIDList> pEnumIL;
672             if (SUCCEEDED(psf2->EnumObjects(0, SHCONTF_FOLDERS, &pEnumIL)))
673             {
674                 if (pEnumIL->Skip(1) == S_OK)
675                     dwShellAttributes |= SFGAO_HASSUBFOLDER;
676             }
677         }
678     }
679 
680     *pdwAttributes = dwShellAttributes;
681 
682     TRACE ("-- 0x%08x\n", *pdwAttributes);
683     return S_OK;
684 }
685 
686 // This method is typically invoked from SHSimpleIDListFromPathA/W.
687 HRESULT CFSFolder::_ParseSimple(
688     _In_ LPOLESTR lpszDisplayName,
689     _Inout_ WIN32_FIND_DATAW *pFind,
690     _Out_ LPITEMIDLIST *ppidl)
691 {
692     HRESULT hr;
693     LPWSTR pchNext = lpszDisplayName;
694 
695     *ppidl = NULL;
696 
697     const DWORD finalattr = pFind->dwFileAttributes;
698     const DWORD finalsizelo = pFind->nFileSizeLow;
699     LPITEMIDLIST pidl;
700     for (hr = S_OK; SUCCEEDED(hr); hr = SHILAppend(pidl, ppidl))
701     {
702         hr = Shell_NextElement(&pchNext, pFind->cFileName, _countof(pFind->cFileName), FALSE);
703         if (hr != S_OK)
704             break;
705 
706         pFind->dwFileAttributes = pchNext ? FILE_ATTRIBUTE_DIRECTORY : finalattr;
707         pFind->nFileSizeLow = pchNext ? 0 : finalsizelo;
708         pidl = _ILCreateFromFindDataW(pFind);
709         if (!pidl)
710         {
711             hr = E_OUTOFMEMORY;
712             break;
713         }
714     }
715 
716     if (SUCCEEDED(hr))
717         return S_OK;
718 
719     if (*ppidl)
720     {
721         ILFree(*ppidl);
722         *ppidl = NULL;
723     }
724 
725     return hr;
726 }
727 
728 BOOL CFSFolder::_GetFindDataFromName(_In_ LPCWSTR pszName, _Out_ WIN32_FIND_DATAW *pFind)
729 {
730     WCHAR szPath[MAX_PATH];
731     lstrcpynW(szPath, m_sPathTarget, _countof(szPath));
732     PathAppendW(szPath, pszName);
733 
734     HANDLE hFind = ::FindFirstFileW(szPath, pFind);
735     if (hFind == INVALID_HANDLE_VALUE)
736         return FALSE;
737 
738     ::FindClose(hFind);
739     return TRUE;
740 }
741 
742 HRESULT CFSFolder::_CreateIDListFromName(LPCWSTR pszName, DWORD attrs, IBindCtx *pbc, LPITEMIDLIST *ppidl)
743 {
744     *ppidl = NULL;
745 
746     if (PathIsDosDevice(pszName))
747         return HRESULT_FROM_WIN32(ERROR_BAD_DEVICE);
748 
749     WIN32_FIND_DATAW FindData = { 0 };
750 
751     HRESULT hr = S_OK;
752     if (attrs == ULONG_MAX) // Invalid attributes
753     {
754         if (!_GetFindDataFromName(pszName, &FindData))
755             hr = HRESULT_FROM_WIN32(::GetLastError());
756     }
757     else // Pretend as an item of attrs
758     {
759         StringCchCopyW(FindData.cFileName, _countof(FindData.cFileName), pszName);
760         FindData.dwFileAttributes = attrs;
761     }
762 
763     if (FAILED(hr))
764         return hr;
765 
766     *ppidl = _ILCreateFromFindDataW(&FindData);
767     if (!*ppidl)
768         return E_OUTOFMEMORY;
769 
770     return S_OK;
771 }
772 
773 /**************************************************************************
774 * CFSFolder::ParseDisplayName {SHELL32}
775 *
776 * Parse a display name.
777 *
778 * PARAMS
779 *  hwndOwner       [in]  Parent window for any message's
780 *  pbc             [in]  optional FileSystemBindData context
781 *  lpszDisplayName [in]  Unicode displayname.
782 *  pchEaten        [out] (unicode) characters processed
783 *  ppidl           [out] complex pidl to item
784 *  pdwAttributes   [out] items attributes
785 *
786 * NOTES
787 *  Every folder tries to parse only its own (the leftmost) pidl and creates a
788 *  subfolder to evaluate the remaining parts.
789 *  Now we can parse into namespaces implemented by shell extensions
790 *
791 *  Behaviour on win98: lpszDisplayName=NULL -> crash
792 *                      lpszDisplayName="" -> returns mycoputer-pidl
793 *
794 * FIXME
795 *    pdwAttributes is not set
796 *    pchEaten is not set like in windows
797 */
798 HRESULT WINAPI CFSFolder::ParseDisplayName(HWND hwndOwner,
799         LPBC pbc,
800         LPOLESTR lpszDisplayName,
801         DWORD *pchEaten, PIDLIST_RELATIVE *ppidl,
802         DWORD *pdwAttributes)
803 {
804     TRACE ("(%p)->(HWND=%p,%p,%p=%s,%p,pidl=%p,%p)\n",
805            this, hwndOwner, pbc, lpszDisplayName, debugstr_w (lpszDisplayName),
806            pchEaten, ppidl, pdwAttributes);
807 
808     if (!ppidl)
809         return E_INVALIDARG;
810 
811     *ppidl = NULL;
812 
813     if (!lpszDisplayName)
814         return E_INVALIDARG;
815 
816     HRESULT hr;
817     WIN32_FIND_DATAW FindData;
818     if (SHIsFileSysBindCtx(pbc, &FindData) == S_OK)
819     {
820         CComHeapPtr<ITEMIDLIST> pidlTemp;
821         hr = _ParseSimple(lpszDisplayName, &FindData, &pidlTemp);
822         if (SUCCEEDED(hr) && pdwAttributes && *pdwAttributes)
823         {
824             LPCITEMIDLIST pidlLast = ILFindLastID(pidlTemp);
825             GetAttributesOf(1, &pidlLast, pdwAttributes);
826         }
827 
828         if (SUCCEEDED(hr))
829             *ppidl = pidlTemp.Detach();
830     }
831     else
832     {
833         INT cchElement = lstrlenW(lpszDisplayName) + 1;
834         LPWSTR pszElement = (LPWSTR)alloca(cchElement * sizeof(WCHAR));
835         LPWSTR pchNext = lpszDisplayName;
836         hr = Shell_NextElement(&pchNext, pszElement, cchElement, TRUE);
837         if (FAILED(hr))
838             return hr;
839 
840         hr = _CreateIDListFromName(pszElement, ULONG_MAX, pbc, ppidl);
841         if (FAILED(hr))
842         {
843             if (pchNext) // Is there the next element?
844             {
845                 // pszElement seems like a directory
846                 if (_GetFindDataFromName(pszElement, &FindData) &&
847                     (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
848                 {
849                     hr = _CreateIDListFromName(pszElement, FILE_ATTRIBUTE_DIRECTORY, pbc, ppidl);
850                 }
851             }
852             else
853             {
854                 // pszElement seems like a non-directory
855                 if ((hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) ||
856                      hr == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND)) &&
857                     (BindCtx_GetMode(pbc, 0) & STGM_CREATE))
858                 {
859                     // Pretend like a normal file
860                     hr = _CreateIDListFromName(pszElement, FILE_ATTRIBUTE_NORMAL, pbc, ppidl);
861                 }
862             }
863         }
864 
865         if (SUCCEEDED(hr))
866         {
867             if (pchNext) // Is there next?
868             {
869                 CComPtr<IShellFolder> psfChild;
870                 hr = BindToObject(*ppidl, pbc, IID_PPV_ARG(IShellFolder, &psfChild));
871                 if (FAILED(hr))
872                     return hr;
873 
874                 DWORD chEaten;
875                 CComHeapPtr<ITEMIDLIST> pidlChild;
876                 hr = psfChild->ParseDisplayName(hwndOwner, pbc, pchNext, &chEaten, &pidlChild,
877                                                 pdwAttributes);
878 
879                 // Append pidlChild to ppidl
880                 if (SUCCEEDED(hr))
881                     hr = SHILAppend(pidlChild.Detach(), ppidl);
882             }
883             else if (pdwAttributes && *pdwAttributes)
884             {
885                 GetAttributesOf(1, (LPCITEMIDLIST*)ppidl, pdwAttributes);
886             }
887         }
888     }
889 
890     TRACE("(%p)->(-- pidl=%p ret=0x%08x)\n", this, ppidl ? *ppidl : 0, hr);
891 
892     return hr;
893 }
894 
895 /**************************************************************************
896 * CFSFolder::EnumObjects
897 * PARAMETERS
898 *  HWND          hwndOwner,    //[in ] Parent Window
899 *  DWORD         grfFlags,     //[in ] SHCONTF enumeration mask
900 *  LPENUMIDLIST* ppenumIDList  //[out] IEnumIDList interface
901 */
902 HRESULT WINAPI CFSFolder::EnumObjects(
903     HWND hwndOwner,
904     DWORD dwFlags,
905     LPENUMIDLIST *ppEnumIDList)
906 {
907     return ShellObjectCreatorInit<CFileSysEnum>(m_sPathTarget, dwFlags, IID_PPV_ARG(IEnumIDList, ppEnumIDList));
908 }
909 
910 /**************************************************************************
911 * CFSFolder::BindToObject
912 * PARAMETERS
913 *  LPCITEMIDLIST pidl,       //[in ] relative pidl to open
914 *  LPBC          pbc,        //[in ] optional FileSystemBindData context
915 *  REFIID        riid,       //[in ] Initial Interface
916 *  LPVOID*       ppvObject   //[out] Interface*
917 */
918 HRESULT WINAPI CFSFolder::BindToObject(
919     PCUIDLIST_RELATIVE pidl,
920     LPBC pbc,
921     REFIID riid,
922     LPVOID * ppvOut)
923 {
924     TRACE("(%p)->(pidl=%p,%p,%s,%p)\n", this, pidl, pbc,
925           shdebugstr_guid(&riid), ppvOut);
926 
927     CComPtr<IShellFolder> pSF;
928     HRESULT hr;
929 
930     if (!m_pidlRoot || !ppvOut || !pidl || !pidl->mkid.cb)
931     {
932         ERR("CFSFolder::BindToObject: Invalid parameters\n");
933         return E_INVALIDARG;
934     }
935 
936     /* Get the pidl data */
937     FileStruct* pData = &_ILGetDataPointer(pidl)->u.file;
938     FileStructW* pDataW = _ILGetFileStructW(pidl);
939 
940     if (!pDataW)
941     {
942         ERR("CFSFolder::BindToObject: Invalid pidl!\n");
943         return E_INVALIDARG;
944     }
945 
946     *ppvOut = NULL;
947 
948     /* Create the target folder info */
949     PERSIST_FOLDER_TARGET_INFO pfti = {0};
950     pfti.dwAttributes = -1;
951     pfti.csidl = -1;
952     PathCombineW(pfti.szTargetParsingName, m_sPathTarget, pDataW->wszName);
953 
954     /* Get the CLSID to bind to */
955     CLSID clsidFolder;
956     if (_ILIsFolder(pidl))
957     {
958         if ((pData->uFileAttribs & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)) != 0)
959         {
960             hr = SHELL32_GetCLSIDForDirectory(pfti.szTargetParsingName, L"CLSID", &clsidFolder);
961 
962             if (SUCCEEDED(hr))
963             {
964                 /* We got a GUID from a desktop.ini, let's try it */
965                 hr = SHELL32_BindToSF(m_pidlRoot, &pfti, pidl, &clsidFolder, riid, ppvOut);
966                 if (SUCCEEDED(hr))
967                 {
968                     TRACE("-- returning (%p) %08x, (%s)\n", *ppvOut, hr, wine_dbgstr_guid(&clsidFolder));
969                     return hr;
970                 }
971 
972                 /* Something went wrong, re-try it with a normal ShellFSFolder */
973                 ERR("CFSFolder::BindToObject: %s failed to bind, using fallback (0x%08x)\n", wine_dbgstr_guid(&clsidFolder), hr);
974             }
975         }
976         /* No system folder or the custom class failed */
977         clsidFolder = CLSID_ShellFSFolder;
978     }
979     else
980     {
981         hr = GetCLSIDForFileType(pidl, L"CLSID", &clsidFolder);
982         if (hr == S_FALSE)
983             return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
984         if (hr != S_OK)
985             return hr;
986     }
987 
988     hr = SHELL32_BindToSF(m_pidlRoot, &pfti, pidl, &clsidFolder, riid, ppvOut);
989     if (FAILED_UNEXPECTEDLY(hr))
990         return hr;
991 
992     TRACE ("-- returning (%p) %08x\n", *ppvOut, hr);
993 
994     return S_OK;
995 
996 }
997 
998 /**************************************************************************
999 *  CFSFolder::BindToStorage
1000 * PARAMETERS
1001 *  LPCITEMIDLIST pidl,       //[in ] complex pidl to store
1002 *  LPBC          pbc,        //[in ] reserved
1003 *  REFIID        riid,       //[in ] Initial storage interface
1004 *  LPVOID*       ppvObject   //[out] Interface* returned
1005 */
1006 HRESULT WINAPI CFSFolder::BindToStorage(
1007     PCUIDLIST_RELATIVE pidl,
1008     LPBC pbcReserved,
1009     REFIID riid,
1010     LPVOID *ppvOut)
1011 {
1012     FIXME("(%p)->(pidl=%p,%p,%s,%p) stub\n", this, pidl, pbcReserved,
1013           shdebugstr_guid (&riid), ppvOut);
1014 
1015     *ppvOut = NULL;
1016     return E_NOTIMPL;
1017 }
1018 
1019 /**************************************************************************
1020 *  CFSFolder::CompareIDs
1021 */
1022 
1023 HRESULT WINAPI CFSFolder::CompareIDs(LPARAM lParam,
1024                                      PCUIDLIST_RELATIVE pidl1,
1025                                      PCUIDLIST_RELATIVE pidl2)
1026 {
1027     LPPIDLDATA pData1 = _ILGetDataPointer(pidl1);
1028     LPPIDLDATA pData2 = _ILGetDataPointer(pidl2);
1029     FileStructW* pDataW1 = _ILGetFileStructW(pidl1);
1030     FileStructW* pDataW2 = _ILGetFileStructW(pidl2);
1031     BOOL bIsFolder1 = _ILIsFolder(pidl1);
1032     BOOL bIsFolder2 = _ILIsFolder(pidl2);
1033     LPWSTR pExtension1, pExtension2;
1034 
1035     if (!pDataW1 || !pDataW2 || LOWORD(lParam) >= GENERICSHELLVIEWCOLUMNS)
1036         return E_INVALIDARG;
1037 
1038     /* When sorting between a File and a Folder, the Folder gets sorted first */
1039     if (bIsFolder1 != bIsFolder2)
1040     {
1041         return MAKE_COMPARE_HRESULT(bIsFolder1 ? -1 : 1);
1042     }
1043 
1044     int result = 0;
1045     switch (LOWORD(lParam))
1046     {
1047         case SHFSF_COL_NAME:
1048             result = wcsicmp(pDataW1->wszName, pDataW2->wszName);
1049             break;
1050         case SHFSF_COL_SIZE:
1051             if (pData1->u.file.dwFileSize > pData2->u.file.dwFileSize)
1052                 result = 1;
1053             else if (pData1->u.file.dwFileSize < pData2->u.file.dwFileSize)
1054                 result = -1;
1055             else
1056                 result = 0;
1057             break;
1058         case SHFSF_COL_TYPE:
1059             pExtension1 = PathFindExtensionW(pDataW1->wszName);
1060             pExtension2 = PathFindExtensionW(pDataW2->wszName);
1061             result = wcsicmp(pExtension1, pExtension2);
1062             break;
1063         case SHFSF_COL_MDATE:
1064             result = pData1->u.file.uFileDate - pData2->u.file.uFileDate;
1065             if (result == 0)
1066                 result = pData1->u.file.uFileTime - pData2->u.file.uFileTime;
1067             break;
1068         case SHFSF_COL_FATTS:
1069             return SHELL32_CompareDetails(this, lParam, pidl1, pidl2);
1070         case SHFSF_COL_COMMENT:
1071             result = 0;
1072             break;
1073         default:
1074             if (_ILIsPidlSimple(pidl1) || _ILIsPidlSimple(pidl2))
1075                 ERR("Unknown column %u, can't compare\n", LOWORD(lParam));
1076             else
1077                 TRACE("Unknown column %u, deferring to the subfolder\n", LOWORD(lParam));
1078     }
1079 
1080     if (result == 0)
1081         return SHELL32_CompareChildren(this, lParam, pidl1, pidl2);
1082 
1083     return MAKE_COMPARE_HRESULT(result);
1084 }
1085 
1086 /**************************************************************************
1087 * CFSFolder::CreateViewObject
1088 */
1089 HRESULT WINAPI CFSFolder::CreateViewObject(HWND hwndOwner,
1090         REFIID riid, LPVOID * ppvOut)
1091 {
1092     CComPtr<IShellView> pShellView;
1093     HRESULT hr = E_INVALIDARG;
1094 
1095     TRACE ("(%p)->(hwnd=%p,%s,%p)\n", this, hwndOwner, shdebugstr_guid (&riid),
1096            ppvOut);
1097 
1098     if (ppvOut)
1099     {
1100         *ppvOut = NULL;
1101 
1102         BOOL bIsDropTarget = IsEqualIID (riid, IID_IDropTarget);
1103         BOOL bIsShellView = !bIsDropTarget && IsEqualIID (riid, IID_IShellView);
1104 
1105         if (bIsDropTarget || bIsShellView)
1106         {
1107             DWORD dwDirAttributes = _ILGetFileAttributes(ILFindLastID(m_pidlRoot), NULL, 0);
1108 
1109             if ((dwDirAttributes & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)) != 0)
1110             {
1111                 CLSID clsidFolder;
1112                 hr = SHELL32_GetCLSIDForDirectory(m_sPathTarget, L"UICLSID", &clsidFolder);
1113                 if (SUCCEEDED(hr))
1114                 {
1115                     CComPtr<IPersistFolder> spFolder;
1116                     hr = SHCoCreateInstance(NULL, &clsidFolder, NULL, IID_PPV_ARG(IPersistFolder, &spFolder));
1117                     if (!FAILED_UNEXPECTEDLY(hr))
1118                     {
1119                         hr = spFolder->Initialize(m_pidlRoot);
1120 
1121                         if (!FAILED_UNEXPECTEDLY(hr))
1122                         {
1123                             hr = spFolder->QueryInterface(riid, ppvOut);
1124                         }
1125                     }
1126                 }
1127                 else
1128                 {
1129                     // No desktop.ini, or no UICLSID present, continue as if nothing happened
1130                     hr = E_INVALIDARG;
1131                 }
1132             }
1133         }
1134 
1135         if (!SUCCEEDED(hr))
1136         {
1137             // No UICLSID handler found, continue to the default handlers
1138             if (bIsDropTarget)
1139             {
1140                 hr = CFSDropTarget_CreateInstance(m_sPathTarget, riid, ppvOut);
1141             }
1142             else if (IsEqualIID (riid, IID_IContextMenu))
1143             {
1144                 HKEY hKeys[16];
1145                 UINT cKeys = 0;
1146                 AddClassKeyToArray(L"Directory\\Background", hKeys, &cKeys);
1147 
1148                 DEFCONTEXTMENU dcm;
1149                 dcm.hwnd = hwndOwner;
1150                 dcm.pcmcb = this;
1151                 dcm.pidlFolder = m_pidlRoot;
1152                 dcm.psf = this;
1153                 dcm.cidl = 0;
1154                 dcm.apidl = NULL;
1155                 dcm.cKeys = cKeys;
1156                 dcm.aKeys = hKeys;
1157                 dcm.punkAssociationInfo = NULL;
1158                 hr = SHCreateDefaultContextMenu (&dcm, riid, ppvOut);
1159             }
1160             else if (bIsShellView)
1161             {
1162                 SFV_CREATE sfvparams = {sizeof(SFV_CREATE), this, NULL, this};
1163                 hr = SHCreateShellFolderView(&sfvparams, (IShellView**)ppvOut);
1164             }
1165             else
1166             {
1167                 hr = E_INVALIDARG;
1168             }
1169         }
1170     }
1171     TRACE("-- (%p)->(interface=%p)\n", this, ppvOut);
1172     return hr;
1173 }
1174 
1175 /**************************************************************************
1176 *  CFSFolder::GetAttributesOf
1177 *
1178 * PARAMETERS
1179 *  UINT            cidl,     //[in ] num elements in pidl array
1180 *  LPCITEMIDLIST*  apidl,    //[in ] simple pidl array
1181 *  ULONG*          rgfInOut) //[out] result array
1182 *
1183 */
1184 HRESULT WINAPI CFSFolder::GetAttributesOf(UINT cidl,
1185         PCUITEMID_CHILD_ARRAY apidl, DWORD * rgfInOut)
1186 {
1187     HRESULT hr = S_OK;
1188 
1189     if (!rgfInOut)
1190         return E_INVALIDARG;
1191     if (cidl && !apidl)
1192         return E_INVALIDARG;
1193 
1194     if (*rgfInOut == 0)
1195         *rgfInOut = ~0;
1196 
1197     if(cidl == 0)
1198     {
1199         LPCITEMIDLIST rpidl = ILFindLastID(m_pidlRoot);
1200 
1201         if (_ILIsFolder(rpidl) || _ILIsValue(rpidl))
1202         {
1203             SHELL32_GetFSItemAttributes(this, rpidl, rgfInOut);
1204         }
1205         else if (_ILIsDrive(rpidl))
1206         {
1207             IShellFolder *psfParent = NULL;
1208             hr = SHBindToParent(m_pidlRoot, IID_PPV_ARG(IShellFolder, &psfParent), NULL);
1209             if(SUCCEEDED(hr))
1210             {
1211                 hr = psfParent->GetAttributesOf(1, &rpidl, (SFGAOF*)rgfInOut);
1212                 psfParent->Release();
1213             }
1214         }
1215         else
1216         {
1217             ERR("Got and unknown pidl!\n");
1218         }
1219     }
1220     else
1221     {
1222         while (cidl > 0 && *apidl)
1223         {
1224             pdump(*apidl);
1225             if(_ILIsFolder(*apidl) || _ILIsValue(*apidl))
1226                 SHELL32_GetFSItemAttributes(this, *apidl, rgfInOut);
1227             else
1228                 ERR("Got an unknown type of pidl!!!\n");
1229             apidl++;
1230             cidl--;
1231         }
1232     }
1233     /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */
1234     *rgfInOut &= ~SFGAO_VALIDATE;
1235 
1236     TRACE("-- result=0x%08x\n", *rgfInOut);
1237 
1238     return hr;
1239 }
1240 
1241 /**************************************************************************
1242 *  CFSFolder::GetUIObjectOf
1243 *
1244 * PARAMETERS
1245 *  HWND           hwndOwner, //[in ] Parent window for any output
1246 *  UINT           cidl,      //[in ] array size
1247 *  LPCITEMIDLIST* apidl,     //[in ] simple pidl array
1248 *  REFIID         riid,      //[in ] Requested Interface
1249 *  UINT*          prgfInOut, //[   ] reserved
1250 *  LPVOID*        ppvObject) //[out] Resulting Interface
1251 *
1252 * NOTES
1253 *  This function gets asked to return "view objects" for one or more (multiple
1254 *  select) items:
1255 *  The viewobject typically is an COM object with one of the following
1256 *  interfaces:
1257 *  IExtractIcon,IDataObject,IContextMenu
1258 *  In order to support icon positions in the default Listview your DataObject
1259 *  must implement the SetData method (in addition to GetData :) - the shell
1260 *  passes a barely documented "Icon positions" structure to SetData when the
1261 *  drag starts, and GetData's it if the drop is in another explorer window that
1262 *  needs the positions.
1263 */
1264 HRESULT WINAPI CFSFolder::GetUIObjectOf(HWND hwndOwner,
1265                                         UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
1266                                         REFIID riid, UINT * prgfInOut,
1267                                         LPVOID * ppvOut)
1268 {
1269     LPVOID pObj = NULL;
1270     HRESULT hr = E_INVALIDARG;
1271 
1272     TRACE ("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n",
1273            this, hwndOwner, cidl, apidl, shdebugstr_guid (&riid), prgfInOut, ppvOut);
1274 
1275     if (ppvOut)
1276     {
1277         *ppvOut = NULL;
1278 
1279         if (cidl == 1 && _ILIsValue(apidl[0]))
1280         {
1281             hr = _CreateExtensionUIObject(apidl[0], riid, ppvOut);
1282             if(hr != S_FALSE)
1283                 return hr;
1284         }
1285 
1286         if (IsEqualIID(riid, IID_IContextMenu) && (cidl >= 1))
1287         {
1288             HKEY hKeys[16];
1289             UINT cKeys = 0;
1290             AddFSClassKeysToArray(cidl, apidl, hKeys, &cKeys);
1291 
1292             DEFCONTEXTMENU dcm;
1293             dcm.hwnd = hwndOwner;
1294             dcm.pcmcb = this;
1295             dcm.pidlFolder = m_pidlRoot;
1296             dcm.psf = this;
1297             dcm.cidl = cidl;
1298             dcm.apidl = apidl;
1299             dcm.cKeys = cKeys;
1300             dcm.aKeys = hKeys;
1301             dcm.punkAssociationInfo = NULL;
1302             hr = SHCreateDefaultContextMenu (&dcm, riid, &pObj);
1303         }
1304         else if (IsEqualIID (riid, IID_IDataObject))
1305         {
1306             if (cidl >= 1)
1307             {
1308                 hr = IDataObject_Constructor (hwndOwner, m_pidlRoot, apidl, cidl, TRUE, (IDataObject **)&pObj);
1309             }
1310             else
1311             {
1312                 hr = E_INVALIDARG;
1313             }
1314         }
1315         else if ((IsEqualIID (riid, IID_IExtractIconA) || IsEqualIID (riid, IID_IExtractIconW)) && (cidl == 1))
1316         {
1317             if (_ILIsValue(apidl[0]))
1318                 hr = _GetIconHandler(apidl[0], riid, (LPVOID*)&pObj);
1319             if (hr != S_OK)
1320                 hr = CFSExtractIcon_CreateInstance(this, apidl[0], riid, &pObj);
1321         }
1322         else if (IsEqualIID (riid, IID_IDropTarget))
1323         {
1324             /* only interested in attempting to bind to shell folders, not files (except exe), so if we fail, rebind to root */
1325             if (cidl != 1 || FAILED(hr = this->_GetDropTarget(apidl[0], (LPVOID*) &pObj)))
1326             {
1327                 hr = CFSDropTarget_CreateInstance(m_sPathTarget, riid, (LPVOID*) &pObj);
1328             }
1329         }
1330         else
1331             hr = E_NOINTERFACE;
1332 
1333         if (SUCCEEDED(hr) && !pObj)
1334             hr = E_OUTOFMEMORY;
1335 
1336         *ppvOut = pObj;
1337     }
1338     TRACE("(%p)->hr=0x%08x\n", this, hr);
1339     return hr;
1340 }
1341 
1342 /******************************************************************************
1343  * SHELL_FS_HideExtension [Internal]
1344  *
1345  * Query the registry if the filename extension of a given path should be
1346  * hidden.
1347  *
1348  * PARAMS
1349  *  szPath [I] Relative or absolute path of a file
1350  *
1351  * RETURNS
1352  *  TRUE, if the filename's extension should be hidden
1353  *  FALSE, otherwise.
1354  */
1355 BOOL SHELL_FS_HideExtension(LPCWSTR szPath)
1356 {
1357     HKEY hKey;
1358     BOOL doHide = FALSE; /* The default value is FALSE (win98 at least) */
1359     LONG lError;
1360 
1361     doHide = !SHELL_GetSetting(SSF_SHOWEXTENSIONS, fShowExtensions);
1362 
1363     if (!doHide)
1364     {
1365         LPCWSTR DotExt = PathFindExtensionW(szPath);
1366         if (*DotExt != 0)
1367         {
1368             WCHAR classname[MAX_PATH];
1369             LONG classlen = sizeof(classname);
1370             lError = RegQueryValueW(HKEY_CLASSES_ROOT, DotExt, classname, &classlen);
1371             if (lError == ERROR_SUCCESS)
1372             {
1373                 lError = RegOpenKeyW(HKEY_CLASSES_ROOT, classname, &hKey);
1374                 if (lError == ERROR_SUCCESS)
1375                 {
1376                     lError = RegQueryValueExW(hKey, L"NeverShowExt", NULL, NULL, NULL, NULL);
1377                     if (lError == ERROR_SUCCESS)
1378                         doHide = TRUE;
1379 
1380                     RegCloseKey(hKey);
1381                 }
1382             }
1383         }
1384     }
1385 
1386     return doHide;
1387 }
1388 
1389 void SHELL_FS_ProcessDisplayFilename(LPWSTR szPath, DWORD dwFlags)
1390 {
1391     /*FIXME: MSDN also mentions SHGDN_FOREDITING which is not yet handled. */
1392     if (!(dwFlags & SHGDN_FORPARSING) &&
1393         ((dwFlags & SHGDN_INFOLDER) || (dwFlags == SHGDN_NORMAL))) {
1394             if (SHELL_FS_HideExtension(szPath) && szPath[0] != '.')
1395                 PathRemoveExtensionW(szPath);
1396     }
1397 }
1398 
1399 /**************************************************************************
1400 *  CFSFolder::GetDisplayNameOf
1401 *  Retrieves the display name for the specified file object or subfolder
1402 *
1403 * PARAMETERS
1404 *  LPCITEMIDLIST pidl,    //[in ] complex pidl to item
1405 *  DWORD         dwFlags, //[in ] SHGNO formatting flags
1406 *  LPSTRRET      lpName)  //[out] Returned display name
1407 *
1408 * FIXME
1409 *  if the name is in the pidl the ret value should be a STRRET_OFFSET
1410 */
1411 
1412 HRESULT WINAPI CFSFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl,
1413         DWORD dwFlags, LPSTRRET strRet)
1414 {
1415     if (!strRet)
1416         return E_INVALIDARG;
1417 
1418     /* If it is a complex pidl, let the child handle it */
1419     if (!_ILIsPidlSimple (pidl)) /* complex pidl */
1420     {
1421         return SHELL32_GetDisplayNameOfChild(this, pidl, dwFlags, strRet);
1422     }
1423     else if (pidl && !pidl->mkid.cb) /* empty pidl */
1424     {
1425         /* If it is an empty pidl return only the path of the folder */
1426         if ((GET_SHGDN_FOR(dwFlags) & SHGDN_FORPARSING) &&
1427             (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER) &&
1428             m_sPathTarget)
1429         {
1430             return SHSetStrRet(strRet, m_sPathTarget);
1431         }
1432         return E_INVALIDARG;
1433     }
1434 
1435     int len = 0;
1436     LPWSTR pszPath = (LPWSTR)CoTaskMemAlloc((MAX_PATH + 1) * sizeof(WCHAR));
1437     if (!pszPath)
1438         return E_OUTOFMEMORY;
1439 
1440     if ((GET_SHGDN_FOR(dwFlags) & SHGDN_FORPARSING) &&
1441         (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER) &&
1442         m_sPathTarget)
1443     {
1444         lstrcpynW(pszPath, m_sPathTarget, MAX_PATH);
1445         PathAddBackslashW(pszPath);
1446         len = wcslen(pszPath);
1447     }
1448     _ILSimpleGetTextW(pidl, pszPath + len, MAX_PATH + 1 - len);
1449     if (!_ILIsFolder(pidl)) SHELL_FS_ProcessDisplayFilename(pszPath, dwFlags);
1450 
1451     strRet->uType = STRRET_WSTR;
1452     strRet->pOleStr = pszPath;
1453 
1454     TRACE ("-- (%p)->(%s)\n", this, strRet->uType == STRRET_CSTR ? strRet->cStr : debugstr_w(strRet->pOleStr));
1455     return S_OK;
1456 }
1457 
1458 /**************************************************************************
1459 *  CFSFolder::SetNameOf
1460 *  Changes the name of a file object or subfolder, possibly changing its item
1461 *  identifier in the process.
1462 *
1463 * PARAMETERS
1464 *  HWND          hwndOwner,  //[in ] Owner window for output
1465 *  LPCITEMIDLIST pidl,       //[in ] simple pidl of item to change
1466 *  LPCOLESTR     lpszName,   //[in ] the items new display name
1467 *  DWORD         dwFlags,    //[in ] SHGNO formatting flags
1468 *  LPITEMIDLIST* ppidlOut)   //[out] simple pidl returned
1469 */
1470 HRESULT WINAPI CFSFolder::SetNameOf(
1471     HWND hwndOwner,
1472     PCUITEMID_CHILD pidl,
1473     LPCOLESTR lpName,
1474     DWORD dwFlags,
1475     PITEMID_CHILD *pPidlOut)
1476 {
1477     WCHAR szSrc[MAX_PATH + 1], szDest[MAX_PATH + 1];
1478     BOOL bIsFolder = _ILIsFolder (ILFindLastID (pidl));
1479 
1480     TRACE ("(%p)->(%p,pidl=%p,%s,%u,%p)\n", this, hwndOwner, pidl,
1481            debugstr_w (lpName), dwFlags, pPidlOut);
1482 
1483     FileStructW* pDataW = _ILGetFileStructW(pidl);
1484     if (!pDataW)
1485     {
1486         ERR("Got garbage pidl:\n");
1487         pdump_always(pidl);
1488         return E_INVALIDARG;
1489     }
1490 
1491     /* build source path */
1492     PathCombineW(szSrc, m_sPathTarget, pDataW->wszName); // FIXME: PIDLs without wide string
1493 
1494     /* build destination path */
1495     if (dwFlags == SHGDN_NORMAL || dwFlags & SHGDN_INFOLDER)
1496         PathCombineW(szDest, m_sPathTarget, lpName);
1497     else
1498         lstrcpynW(szDest, lpName, MAX_PATH);
1499 
1500     if (!(dwFlags & SHGDN_FORPARSING) && !bIsFolder && SHELL_FS_HideExtension(szSrc))
1501     {
1502         LPCWSTR ext = PathFindExtensionW(szSrc);
1503         if (*ext)
1504             PathAddExtensionW(szDest, ext);
1505     }
1506 
1507     HRESULT hr = S_OK;
1508     TRACE ("src=%s dest=%s\n", debugstr_w(szSrc), debugstr_w(szDest));
1509     if (!wcscmp(szSrc, szDest))
1510     {
1511         /* src and destination is the same */
1512         if (pPidlOut)
1513             hr = SHILClone(pidl, pPidlOut);
1514     }
1515     else if (MoveFileW(szSrc, szDest))
1516     {
1517         if (pPidlOut)
1518             hr = ParseDisplayName(hwndOwner, NULL, PathFindFileNameW(szDest), NULL, pPidlOut, NULL);
1519 
1520         SHChangeNotify(bIsFolder ? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM, SHCNF_PATHW, szSrc, szDest);
1521     }
1522     else
1523     {
1524         hr = HResultFromWin32(GetLastError());
1525     }
1526     return hr;
1527 }
1528 
1529 HRESULT WINAPI CFSFolder::GetDefaultSearchGUID(GUID * pguid)
1530 {
1531     FIXME ("(%p)\n", this);
1532     return E_NOTIMPL;
1533 }
1534 
1535 HRESULT WINAPI CFSFolder::EnumSearches(IEnumExtraSearch ** ppenum)
1536 {
1537     FIXME ("(%p)\n", this);
1538     return E_NOTIMPL;
1539 }
1540 
1541 HRESULT WINAPI CFSFolder::GetDefaultColumn(DWORD dwRes,
1542         ULONG * pSort, ULONG * pDisplay)
1543 {
1544     TRACE ("(%p)\n", this);
1545 
1546     if (pSort)
1547         *pSort = 0;
1548     if (pDisplay)
1549         *pDisplay = 0;
1550 
1551     return S_OK;
1552 }
1553 
1554 HRESULT WINAPI CFSFolder::GetDefaultColumnState(UINT iColumn,
1555         SHCOLSTATEF *pcsFlags)
1556 {
1557     TRACE ("(%p)\n", this);
1558 
1559     if (!pcsFlags)
1560         return E_INVALIDARG;
1561     else
1562         return GetDefaultFSColumnState(iColumn, *pcsFlags);
1563 }
1564 
1565 HRESULT WINAPI CFSFolder::GetDetailsEx(PCUITEMID_CHILD pidl,
1566                                        const SHCOLUMNID * pscid, VARIANT * pv)
1567 {
1568     FIXME ("(%p)\n", this);
1569 
1570     return E_NOTIMPL;
1571 }
1572 
1573 HRESULT WINAPI CFSFolder::GetDetailsOf(PCUITEMID_CHILD pidl,
1574                                        UINT iColumn, SHELLDETAILS * psd)
1575 {
1576     HRESULT hr = E_FAIL;
1577 
1578     TRACE ("(%p)->(%p %i %p)\n", this, pidl, iColumn, psd);
1579 
1580     if (!psd || iColumn >= GENERICSHELLVIEWCOLUMNS)
1581         return E_INVALIDARG;
1582 
1583     if (!pidl)
1584     {
1585         /* the header titles */
1586         return GetFSColumnDetails(iColumn, *psd);
1587     }
1588     else
1589     {
1590         hr = S_OK;
1591         psd->str.uType = STRRET_WSTR;
1592         psd->str.pOleStr = (LPWSTR)CoTaskMemAlloc(MAX_PATH * sizeof(WCHAR));
1593         /* the data from the pidl */
1594         switch (iColumn)
1595         {
1596             case SHFSF_COL_NAME:
1597                 hr = GetDisplayNameOf (pidl, SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str);
1598                 break;
1599             case SHFSF_COL_SIZE:
1600                 _ILGetFileSize(pidl, psd->str.pOleStr, MAX_PATH);
1601                 break;
1602             case SHFSF_COL_TYPE:
1603                 _ILGetFileType(pidl, psd->str.pOleStr, MAX_PATH);
1604                 break;
1605             case SHFSF_COL_MDATE:
1606                 _ILGetFileDate(pidl, psd->str.pOleStr, MAX_PATH);
1607                 break;
1608             case SHFSF_COL_FATTS:
1609                 _ILGetFileAttributes(pidl, psd->str.pOleStr, MAX_PATH);
1610                 break;
1611             case SHFSF_COL_COMMENT:
1612                 psd->str.pOleStr[0] = UNICODE_NULL; // TODO: Extract comment from .lnk files? desktop.ini?
1613                 break;
1614 #if DBG
1615             default:
1616                 ERR("Missing case for column %d\n", iColumn);
1617 #else
1618             DEFAULT_UNREACHABLE;
1619 #endif
1620         }
1621     }
1622 
1623     return hr;
1624 }
1625 
1626 HRESULT WINAPI CFSFolder::MapColumnToSCID (UINT column,
1627         SHCOLUMNID * pscid)
1628 {
1629     FIXME ("(%p)\n", this);
1630     return E_NOTIMPL;
1631 }
1632 
1633 /************************************************************************
1634  * CFSFolder::GetClassID
1635  */
1636 HRESULT WINAPI CFSFolder::GetClassID(CLSID * lpClassId)
1637 {
1638     TRACE ("(%p)\n", this);
1639 
1640     if (!lpClassId)
1641         return E_POINTER;
1642 
1643     *lpClassId = *m_pclsid;
1644 
1645     return S_OK;
1646 }
1647 
1648 /************************************************************************
1649  * CFSFolder::Initialize
1650  *
1651  * NOTES
1652  *  m_sPathTarget is not set. Don't know how to handle in a non rooted environment.
1653  */
1654 HRESULT WINAPI CFSFolder::Initialize(PCIDLIST_ABSOLUTE pidl)
1655 {
1656     WCHAR wszTemp[MAX_PATH];
1657 
1658     TRACE ("(%p)->(%p)\n", this, pidl);
1659 
1660     SHFree(m_pidlRoot);     /* free the old pidl */
1661     m_pidlRoot = ILClone (pidl); /* set my pidl */
1662 
1663     SHFree (m_sPathTarget);
1664     m_sPathTarget = NULL;
1665 
1666     /* set my path */
1667     if (SHGetPathFromIDListW (pidl, wszTemp))
1668     {
1669         int len = wcslen(wszTemp);
1670         m_sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
1671         if (!m_sPathTarget)
1672             return E_OUTOFMEMORY;
1673         memcpy(m_sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR));
1674     }
1675 
1676     TRACE ("--(%p)->(%s)\n", this, debugstr_w(m_sPathTarget));
1677     return S_OK;
1678 }
1679 
1680 /**************************************************************************
1681  * CFSFolder::GetCurFolder
1682  */
1683 HRESULT WINAPI CFSFolder::GetCurFolder(PIDLIST_ABSOLUTE * pidl)
1684 {
1685     TRACE ("(%p)->(%p)\n", this, pidl);
1686 
1687     if (!pidl)
1688         return E_POINTER;
1689 
1690     *pidl = ILClone(m_pidlRoot);
1691     return S_OK;
1692 }
1693 
1694 /**************************************************************************
1695  * CFSFolder::InitializeEx
1696  *
1697  * FIXME: error handling
1698  */
1699 HRESULT WINAPI CFSFolder::InitializeEx(IBindCtx * pbc, LPCITEMIDLIST pidlRootx,
1700                                        const PERSIST_FOLDER_TARGET_INFO * ppfti)
1701 {
1702     WCHAR wszTemp[MAX_PATH];
1703 
1704     TRACE("(%p)->(%p,%p,%p)\n", this, pbc, pidlRootx, ppfti);
1705     if (ppfti)
1706         TRACE("--%p %s %s 0x%08x 0x%08x\n",
1707               ppfti->pidlTargetFolder, debugstr_w (ppfti->szTargetParsingName),
1708               debugstr_w (ppfti->szNetworkProvider), ppfti->dwAttributes,
1709               ppfti->csidl);
1710 
1711     pdump (pidlRootx);
1712     if (ppfti && ppfti->pidlTargetFolder)
1713         pdump(ppfti->pidlTargetFolder);
1714 
1715     if (m_pidlRoot)
1716         __SHFreeAndNil(&m_pidlRoot);    /* free the old */
1717     if (m_sPathTarget)
1718         __SHFreeAndNil(&m_sPathTarget);
1719 
1720     /*
1721      * Root path and pidl
1722      */
1723     m_pidlRoot = ILClone(pidlRootx);
1724 
1725     /*
1726      *  the target folder is spezified in csidl OR pidlTargetFolder OR
1727      *  szTargetParsingName
1728      */
1729     if (ppfti)
1730     {
1731         if (ppfti->csidl != -1)
1732         {
1733             if (SHGetSpecialFolderPathW(0, wszTemp, ppfti->csidl,
1734                                         ppfti->csidl & CSIDL_FLAG_CREATE)) {
1735                 int len = wcslen(wszTemp);
1736                 m_sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
1737                 if (!m_sPathTarget)
1738                     return E_OUTOFMEMORY;
1739                 memcpy(m_sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR));
1740             }
1741         }
1742         else if (ppfti->szTargetParsingName[0])
1743         {
1744             int len = wcslen(ppfti->szTargetParsingName);
1745             m_sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
1746             if (!m_sPathTarget)
1747                 return E_OUTOFMEMORY;
1748             memcpy(m_sPathTarget, ppfti->szTargetParsingName,
1749                    (len + 1) * sizeof(WCHAR));
1750         }
1751         else if (ppfti->pidlTargetFolder)
1752         {
1753             if (SHGetPathFromIDListW(ppfti->pidlTargetFolder, wszTemp))
1754             {
1755                 int len = wcslen(wszTemp);
1756                 m_sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
1757                 if (!m_sPathTarget)
1758                     return E_OUTOFMEMORY;
1759                 memcpy(m_sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR));
1760             }
1761         }
1762     }
1763 
1764     TRACE("--(%p)->(target=%s)\n", this, debugstr_w(m_sPathTarget));
1765     pdump(m_pidlRoot);
1766     return (m_sPathTarget) ? S_OK : E_FAIL;
1767 }
1768 
1769 HRESULT WINAPI CFSFolder::GetFolderTargetInfo(PERSIST_FOLDER_TARGET_INFO * ppfti)
1770 {
1771     FIXME("(%p)->(%p)\n", this, ppfti);
1772     ZeroMemory(ppfti, sizeof (*ppfti));
1773     return E_NOTIMPL;
1774 }
1775 
1776 HRESULT CFSFolder::_CreateExtensionUIObject(PCUIDLIST_RELATIVE pidl, REFIID riid, LPVOID *ppvOut)
1777 {
1778     WCHAR buf[MAX_PATH];
1779 
1780     sprintfW(buf, L"ShellEx\\{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
1781              riid.Data1, riid.Data2, riid.Data3,
1782              riid.Data4[0], riid.Data4[1], riid.Data4[2], riid.Data4[3],
1783              riid.Data4[4], riid.Data4[5], riid.Data4[6], riid.Data4[7]);
1784 
1785     CLSID clsid;
1786     HRESULT hr;
1787 
1788     hr = GetCLSIDForFileType(pidl, buf, &clsid);
1789     if (hr != S_OK)
1790         return hr;
1791 
1792     hr = _CreateShellExtInstance(&clsid, pidl, riid, ppvOut);
1793     if (FAILED_UNEXPECTEDLY(hr))
1794         return hr;
1795 
1796     return S_OK;
1797 }
1798 
1799 HRESULT CFSFolder::_GetDropTarget(LPCITEMIDLIST pidl, LPVOID *ppvOut)
1800 {
1801     HRESULT hr;
1802 
1803     TRACE("CFSFolder::_GetDropTarget entered\n");
1804 
1805     if (_ILIsFolder (pidl))
1806     {
1807         CComPtr<IShellFolder> psfChild;
1808         hr = this->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfChild));
1809         if (FAILED_UNEXPECTEDLY(hr))
1810             return hr;
1811 
1812         return psfChild->CreateViewObject(NULL, IID_IDropTarget, ppvOut);
1813     }
1814 
1815     CLSID clsid;
1816     hr = GetCLSIDForFileType(pidl, L"shellex\\DropHandler", &clsid);
1817     if (hr != S_OK)
1818         return hr;
1819 
1820     hr = _CreateShellExtInstance(&clsid, pidl, IID_IDropTarget, ppvOut);
1821     if (FAILED_UNEXPECTEDLY(hr))
1822         return S_FALSE;
1823 
1824     return S_OK;
1825 }
1826 
1827 HRESULT CFSFolder::_GetIconHandler(LPCITEMIDLIST pidl, REFIID riid, LPVOID *ppvOut)
1828 {
1829     CLSID clsid;
1830     HRESULT hr;
1831 
1832     hr = GetCLSIDForFileType(pidl, L"shellex\\IconHandler", &clsid);
1833     if (hr != S_OK)
1834         return hr;
1835 
1836     hr = _CreateShellExtInstance(&clsid, pidl, riid, ppvOut);
1837     if (FAILED_UNEXPECTEDLY(hr))
1838         return S_FALSE;
1839 
1840     return S_OK;
1841 }
1842 
1843 HRESULT CFSFolder::_CreateShellExtInstance(const CLSID *pclsid, LPCITEMIDLIST pidl, REFIID riid, LPVOID *ppvOut)
1844 {
1845     HRESULT hr;
1846     WCHAR wszPath[MAX_PATH];
1847 
1848     FileStructW* pDataW = _ILGetFileStructW(pidl);
1849     if (!pDataW)
1850     {
1851         ERR("Got garbage pidl\n");
1852         pdump_always(pidl);
1853         return E_INVALIDARG;
1854     }
1855 
1856     PathCombineW(wszPath, m_sPathTarget, pDataW->wszName);
1857 
1858     CComPtr<IPersistFile> pp;
1859     hr = SHCoCreateInstance(NULL, pclsid, NULL, IID_PPV_ARG(IPersistFile, &pp));
1860     if (FAILED_UNEXPECTEDLY(hr))
1861         return hr;
1862 
1863     pp->Load(wszPath, 0);
1864 
1865     hr = pp->QueryInterface(riid, ppvOut);
1866     if (hr != S_OK)
1867     {
1868         ERR("Failed to query for interface IID_IShellExtInit hr %x pclsid %s\n", hr, wine_dbgstr_guid(pclsid));
1869         return hr;
1870     }
1871     return hr;
1872 }
1873 
1874 HRESULT WINAPI CFSFolder::CallBack(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
1875 {
1876     enum { IDC_PROPERTIES };
1877     /* no data object means no selection */
1878     if (!pdtobj)
1879     {
1880         if (uMsg == DFM_INVOKECOMMAND && wParam == IDC_PROPERTIES)
1881         {
1882             // Create an data object
1883             CComHeapPtr<ITEMID_CHILD> pidlChild(ILClone(ILFindLastID(m_pidlRoot)));
1884             CComHeapPtr<ITEMIDLIST> pidlParent(ILClone(m_pidlRoot));
1885             ILRemoveLastID(pidlParent);
1886 
1887             CComPtr<IDataObject> pDataObj;
1888             HRESULT hr = SHCreateDataObject(pidlParent, 1, &pidlChild.m_pData, NULL, IID_PPV_ARG(IDataObject, &pDataObj));
1889             if (!FAILED_UNEXPECTEDLY(hr))
1890             {
1891                 // Ask for a title to display
1892                 CComHeapPtr<WCHAR> wszName;
1893                 if (!FAILED_UNEXPECTEDLY(SHGetNameFromIDList(m_pidlRoot, SIGDN_PARENTRELATIVEPARSING, &wszName)))
1894                 {
1895                     BOOL bSuccess = SH_ShowPropertiesDialog(wszName, pDataObj);
1896                     if (!bSuccess)
1897                         ERR("SH_ShowPropertiesDialog failed\n");
1898                 }
1899             }
1900             return hr;
1901         }
1902         else if (uMsg == DFM_MERGECONTEXTMENU)
1903         {
1904             QCMINFO *pqcminfo = (QCMINFO *)lParam;
1905             HMENU hpopup = CreatePopupMenu();
1906             _InsertMenuItemW(hpopup, 0, TRUE, IDC_PROPERTIES, MFT_STRING, MAKEINTRESOURCEW(IDS_PROPERTIES), MFS_ENABLED);
1907             pqcminfo->idCmdFirst = Shell_MergeMenus(pqcminfo->hmenu, hpopup, pqcminfo->indexMenu, pqcminfo->idCmdFirst, pqcminfo->idCmdLast, MM_ADDSEPARATOR);
1908             DestroyMenu(hpopup);
1909             return S_OK;
1910         }
1911     }
1912     return SHELL32_DefaultContextMenuCallBack(psf, pdtobj, uMsg);
1913 }
1914 
1915 static HBITMAP DoLoadPicture(LPCWSTR pszFileName)
1916 {
1917     // create stream from file
1918     HRESULT hr;
1919     CComPtr<IStream> pStream;
1920     hr = SHCreateStreamOnFileEx(pszFileName, STGM_READ, FILE_ATTRIBUTE_NORMAL,
1921                                 FALSE, NULL, &pStream);
1922     if (FAILED(hr))
1923         return NULL;
1924 
1925     // load the picture
1926     HBITMAP hbm = NULL;
1927     CComPtr<IPicture> pPicture;
1928     OleLoadPicture(pStream, 0, FALSE, IID_IPicture, (LPVOID *)&pPicture);
1929 
1930     // get the bitmap handle
1931     if (pPicture)
1932     {
1933         pPicture->get_Handle((OLE_HANDLE *)&hbm);
1934 
1935         // copy the bitmap handle
1936         hbm = (HBITMAP)CopyImage(hbm, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
1937     }
1938 
1939     return hbm;
1940 }
1941 
1942 HRESULT WINAPI CFSFolder::GetCustomViewInfo(ULONG unknown, SFVM_CUSTOMVIEWINFO_DATA *data)
1943 {
1944     if (data == NULL)
1945     {
1946         return E_POINTER;
1947     }
1948     if (data->cbSize != sizeof(*data))
1949     {
1950         // NOTE: You have to set the cbData member before SFVM_GET_CUSTOMVIEWINFO call.
1951         return E_INVALIDARG;
1952     }
1953 
1954     data->hbmBack = NULL;
1955     data->clrText = CLR_INVALID;
1956     data->clrTextBack = CLR_INVALID;
1957 
1958     WCHAR szPath[MAX_PATH], szIniFile[MAX_PATH];
1959 
1960     // does the folder exists?
1961     if (!SHGetPathFromIDListW(m_pidlRoot, szPath) || !PathIsDirectoryW(szPath))
1962     {
1963         return E_INVALIDARG;
1964     }
1965 
1966     // don't use custom view in network path for security
1967     if (PathIsNetworkPath(szPath))
1968     {
1969         return E_ACCESSDENIED;
1970     }
1971 
1972     // build the ini file path
1973     StringCchCopyW(szIniFile, _countof(szIniFile), szPath);
1974     PathAppend(szIniFile, L"desktop.ini");
1975 
1976     static LPCWSTR TheGUID = L"{BE098140-A513-11D0-A3A4-00C04FD706EC}";
1977     static LPCWSTR Space = L" \t\n\r\f\v";
1978 
1979     // get info from ini file
1980     WCHAR szImage[MAX_PATH], szText[64];
1981 
1982     // load the image
1983     szImage[0] = UNICODE_NULL;
1984     GetPrivateProfileStringW(TheGUID, L"IconArea_Image", L"", szImage, _countof(szImage), szIniFile);
1985     if (szImage[0])
1986     {
1987         StrTrimW(szImage, Space);
1988         if (PathIsRelativeW(szImage))
1989         {
1990             PathAppendW(szPath, szImage);
1991             StringCchCopyW(szImage, _countof(szImage), szPath);
1992         }
1993         data->hbmBack = DoLoadPicture(szImage);
1994     }
1995 
1996     // load the text color
1997     szText[0] = UNICODE_NULL;
1998     GetPrivateProfileStringW(TheGUID, L"IconArea_Text", L"", szText, _countof(szText), szIniFile);
1999     if (szText[0])
2000     {
2001         StrTrimW(szText, Space);
2002 
2003         LPWSTR pchEnd = NULL;
2004         COLORREF cr = (wcstol(szText, &pchEnd, 0) & 0xFFFFFF);
2005 
2006         if (pchEnd && !*pchEnd)
2007             data->clrText = cr;
2008     }
2009 
2010     // load the text background color
2011     szText[0] = UNICODE_NULL;
2012     GetPrivateProfileStringW(TheGUID, L"IconArea_TextBackground", L"", szText, _countof(szText), szIniFile);
2013     if (szText[0])
2014     {
2015         StrTrimW(szText, Space);
2016 
2017         LPWSTR pchEnd = NULL;
2018         COLORREF cr = (wcstol(szText, &pchEnd, 0) & 0xFFFFFF);
2019 
2020         if (pchEnd && !*pchEnd)
2021             data->clrTextBack = cr;
2022     }
2023 
2024     if (data->hbmBack != NULL || data->clrText != CLR_INVALID || data->clrTextBack != CLR_INVALID)
2025         return S_OK;
2026 
2027     return E_FAIL;
2028 }
2029 
2030 HRESULT WINAPI CFSFolder::MessageSFVCB(UINT uMsg, WPARAM wParam, LPARAM lParam)
2031 {
2032     HRESULT hr = E_NOTIMPL;
2033     switch (uMsg)
2034     {
2035     case SFVM_GET_CUSTOMVIEWINFO:
2036         hr = GetCustomViewInfo((ULONG)wParam, (SFVM_CUSTOMVIEWINFO_DATA *)lParam);
2037         break;
2038     }
2039     return hr;
2040 }
2041