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