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