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("SHOULD DO SOMETHING WITH CLSID?\n");
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                         ERR("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                     ERR("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         clsidFolder = CLSID_ShellFSFolder;
887 
888         if ((pData->uFileAttribs & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)) != 0)
889             SHELL32_GetCLSIDForDirectory(pfti.szTargetParsingName, L"CLSID", &clsidFolder);
890     }
891     else
892     {
893         hr = GetCLSIDForFileType(pidl, L"CLSID", &clsidFolder);
894         if (hr == S_FALSE)
895             return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
896         if (hr != S_OK)
897             return hr;
898     }
899 
900     hr = SHELL32_BindToSF(m_pidlRoot, &pfti, pidl, &clsidFolder, riid, ppvOut);
901     if (FAILED_UNEXPECTEDLY(hr))
902         return hr;
903 
904     TRACE ("-- returning (%p) %08x\n", *ppvOut, hr);
905 
906     return S_OK;
907 
908 }
909 
910 /**************************************************************************
911 *  CFSFolder::BindToStorage
912 * PARAMETERS
913 *  LPCITEMIDLIST pidl,       //[in ] complex pidl to store
914 *  LPBC          pbc,        //[in ] reserved
915 *  REFIID        riid,       //[in ] Initial storage interface
916 *  LPVOID*       ppvObject   //[out] Interface* returned
917 */
918 HRESULT WINAPI CFSFolder::BindToStorage(
919     PCUIDLIST_RELATIVE pidl,
920     LPBC pbcReserved,
921     REFIID riid,
922     LPVOID *ppvOut)
923 {
924     FIXME("(%p)->(pidl=%p,%p,%s,%p) stub\n", this, pidl, pbcReserved,
925           shdebugstr_guid (&riid), ppvOut);
926 
927     *ppvOut = NULL;
928     return E_NOTIMPL;
929 }
930 
931 /**************************************************************************
932 *  CFSFolder::CompareIDs
933 */
934 
935 HRESULT WINAPI CFSFolder::CompareIDs(LPARAM lParam,
936                                      PCUIDLIST_RELATIVE pidl1,
937                                      PCUIDLIST_RELATIVE pidl2)
938 {
939     LPPIDLDATA pData1 = _ILGetDataPointer(pidl1);
940     LPPIDLDATA pData2 = _ILGetDataPointer(pidl2);
941     FileStructW* pDataW1 = _ILGetFileStructW(pidl1);
942     FileStructW* pDataW2 = _ILGetFileStructW(pidl2);
943     BOOL bIsFolder1 = _ILIsFolder(pidl1);
944     BOOL bIsFolder2 = _ILIsFolder(pidl2);
945     LPWSTR pExtension1, pExtension2;
946 
947     if (!pDataW1 || !pDataW2 || LOWORD(lParam) >= GENERICSHELLVIEWCOLUMNS)
948         return E_INVALIDARG;
949 
950     /* When sorting between a File and a Folder, the Folder gets sorted first */
951     if (bIsFolder1 != bIsFolder2)
952     {
953         return MAKE_COMPARE_HRESULT(bIsFolder1 ? -1 : 1);
954     }
955 
956     int result;
957     switch (LOWORD(lParam))
958     {
959         case 0: /* Name */
960             result = wcsicmp(pDataW1->wszName, pDataW2->wszName);
961             break;
962         case 1: /* Comments */
963             result = 0;
964             break;
965         case 2: /* Type */
966             pExtension1 = PathFindExtensionW(pDataW1->wszName);
967             pExtension2 = PathFindExtensionW(pDataW2->wszName);
968             result = wcsicmp(pExtension1, pExtension2);
969             break;
970         case 3: /* Size */
971             result = pData1->u.file.dwFileSize - pData2->u.file.dwFileSize;
972             break;
973         case 4: /* Modified */
974             result = pData1->u.file.uFileDate - pData2->u.file.uFileDate;
975             if (result == 0)
976                 result = pData1->u.file.uFileTime - pData2->u.file.uFileTime;
977             break;
978         case 5: /* Attributes */
979             return SHELL32_CompareDetails(this, lParam, pidl1, pidl2);
980     }
981 
982     if (result == 0)
983         return SHELL32_CompareChildren(this, lParam, pidl1, pidl2);
984 
985     return MAKE_COMPARE_HRESULT(result);
986 }
987 
988 /**************************************************************************
989 * CFSFolder::CreateViewObject
990 */
991 HRESULT WINAPI CFSFolder::CreateViewObject(HWND hwndOwner,
992         REFIID riid, LPVOID * ppvOut)
993 {
994     CComPtr<IShellView> pShellView;
995     HRESULT hr = E_INVALIDARG;
996 
997     TRACE ("(%p)->(hwnd=%p,%s,%p)\n", this, hwndOwner, shdebugstr_guid (&riid),
998            ppvOut);
999 
1000     if (ppvOut)
1001     {
1002         *ppvOut = NULL;
1003 
1004         BOOL bIsDropTarget = IsEqualIID (riid, IID_IDropTarget);
1005         BOOL bIsShellView = !bIsDropTarget && IsEqualIID (riid, IID_IShellView);
1006 
1007         if (bIsDropTarget || bIsShellView)
1008         {
1009             DWORD dwDirAttributes = _ILGetFileAttributes(ILFindLastID(m_pidlRoot), NULL, 0);
1010 
1011             if ((dwDirAttributes & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)) != 0)
1012             {
1013                 CLSID clsidFolder;
1014                 hr = SHELL32_GetCLSIDForDirectory(m_sPathTarget, L"UICLSID", &clsidFolder);
1015                 if (SUCCEEDED(hr))
1016                 {
1017                     CComPtr<IPersistFolder> spFolder;
1018                     hr = SHCoCreateInstance(NULL, &clsidFolder, NULL, IID_PPV_ARG(IPersistFolder, &spFolder));
1019                     if (!FAILED_UNEXPECTEDLY(hr))
1020                     {
1021                         hr = spFolder->Initialize(m_pidlRoot);
1022 
1023                         if (!FAILED_UNEXPECTEDLY(hr))
1024                         {
1025                             hr = spFolder->QueryInterface(riid, ppvOut);
1026                         }
1027                     }
1028                 }
1029                 else
1030                 {
1031                     // No desktop.ini, or no UICLSID present, continue as if nothing happened
1032                     hr = E_INVALIDARG;
1033                 }
1034             }
1035         }
1036 
1037         if (!SUCCEEDED(hr))
1038         {
1039             // No UICLSID handler found, continue to the default handlers
1040             if (bIsDropTarget)
1041             {
1042                 hr = CFSDropTarget_CreateInstance(m_sPathTarget, riid, ppvOut);
1043             }
1044             else if (IsEqualIID (riid, IID_IContextMenu))
1045             {
1046                 HKEY hKeys[16];
1047                 UINT cKeys = 0;
1048                 AddClassKeyToArray(L"Directory\\Background", hKeys, &cKeys);
1049 
1050                 DEFCONTEXTMENU dcm;
1051                 dcm.hwnd = hwndOwner;
1052                 dcm.pcmcb = this;
1053                 dcm.pidlFolder = m_pidlRoot;
1054                 dcm.psf = this;
1055                 dcm.cidl = 0;
1056                 dcm.apidl = NULL;
1057                 dcm.cKeys = cKeys;
1058                 dcm.aKeys = hKeys;
1059                 dcm.punkAssociationInfo = NULL;
1060                 hr = SHCreateDefaultContextMenu (&dcm, riid, ppvOut);
1061             }
1062             else if (bIsShellView)
1063             {
1064                 SFV_CREATE sfvparams = {sizeof(SFV_CREATE), this, NULL, this};
1065                 hr = SHCreateShellFolderView(&sfvparams, (IShellView**)ppvOut);
1066             }
1067             else
1068             {
1069                 hr = E_INVALIDARG;
1070             }
1071         }
1072     }
1073     TRACE("-- (%p)->(interface=%p)\n", this, ppvOut);
1074     return hr;
1075 }
1076 
1077 /**************************************************************************
1078 *  CFSFolder::GetAttributesOf
1079 *
1080 * PARAMETERS
1081 *  UINT            cidl,     //[in ] num elements in pidl array
1082 *  LPCITEMIDLIST*  apidl,    //[in ] simple pidl array
1083 *  ULONG*          rgfInOut) //[out] result array
1084 *
1085 */
1086 HRESULT WINAPI CFSFolder::GetAttributesOf(UINT cidl,
1087         PCUITEMID_CHILD_ARRAY apidl, DWORD * rgfInOut)
1088 {
1089     HRESULT hr = S_OK;
1090 
1091     if (!rgfInOut)
1092         return E_INVALIDARG;
1093     if (cidl && !apidl)
1094         return E_INVALIDARG;
1095 
1096     if (*rgfInOut == 0)
1097         *rgfInOut = ~0;
1098 
1099     if(cidl == 0)
1100     {
1101         LPCITEMIDLIST rpidl = ILFindLastID(m_pidlRoot);
1102 
1103         if (_ILIsFolder(rpidl) || _ILIsValue(rpidl))
1104         {
1105             SHELL32_GetFSItemAttributes(this, rpidl, rgfInOut);
1106         }
1107         else if (_ILIsDrive(rpidl))
1108         {
1109             IShellFolder *psfParent = NULL;
1110             hr = SHBindToParent(m_pidlRoot, IID_PPV_ARG(IShellFolder, &psfParent), NULL);
1111             if(SUCCEEDED(hr))
1112             {
1113                 hr = psfParent->GetAttributesOf(1, &rpidl, (SFGAOF*)rgfInOut);
1114                 psfParent->Release();
1115             }
1116         }
1117         else
1118         {
1119             ERR("Got and unknown pidl!\n");
1120         }
1121     }
1122     else
1123     {
1124         while (cidl > 0 && *apidl)
1125         {
1126             pdump(*apidl);
1127             if(_ILIsFolder(*apidl) || _ILIsValue(*apidl))
1128                 SHELL32_GetFSItemAttributes(this, *apidl, rgfInOut);
1129             else
1130                 ERR("Got an unknown type of pidl!!!\n");
1131             apidl++;
1132             cidl--;
1133         }
1134     }
1135     /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */
1136     *rgfInOut &= ~SFGAO_VALIDATE;
1137 
1138     TRACE("-- result=0x%08x\n", *rgfInOut);
1139 
1140     return hr;
1141 }
1142 
1143 /**************************************************************************
1144 *  CFSFolder::GetUIObjectOf
1145 *
1146 * PARAMETERS
1147 *  HWND           hwndOwner, //[in ] Parent window for any output
1148 *  UINT           cidl,      //[in ] array size
1149 *  LPCITEMIDLIST* apidl,     //[in ] simple pidl array
1150 *  REFIID         riid,      //[in ] Requested Interface
1151 *  UINT*          prgfInOut, //[   ] reserved
1152 *  LPVOID*        ppvObject) //[out] Resulting Interface
1153 *
1154 * NOTES
1155 *  This function gets asked to return "view objects" for one or more (multiple
1156 *  select) items:
1157 *  The viewobject typically is an COM object with one of the following
1158 *  interfaces:
1159 *  IExtractIcon,IDataObject,IContextMenu
1160 *  In order to support icon positions in the default Listview your DataObject
1161 *  must implement the SetData method (in addition to GetData :) - the shell
1162 *  passes a barely documented "Icon positions" structure to SetData when the
1163 *  drag starts, and GetData's it if the drop is in another explorer window that
1164 *  needs the positions.
1165 */
1166 HRESULT WINAPI CFSFolder::GetUIObjectOf(HWND hwndOwner,
1167                                         UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
1168                                         REFIID riid, UINT * prgfInOut,
1169                                         LPVOID * ppvOut)
1170 {
1171     LPVOID pObj = NULL;
1172     HRESULT hr = E_INVALIDARG;
1173 
1174     TRACE ("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n",
1175            this, hwndOwner, cidl, apidl, shdebugstr_guid (&riid), prgfInOut, ppvOut);
1176 
1177     if (ppvOut)
1178     {
1179         *ppvOut = NULL;
1180 
1181         if (cidl == 1 && _ILIsValue(apidl[0]))
1182         {
1183             hr = _CreateExtensionUIObject(apidl[0], riid, ppvOut);
1184             if(hr != S_FALSE)
1185                 return hr;
1186         }
1187 
1188         if (IsEqualIID(riid, IID_IContextMenu) && (cidl >= 1))
1189         {
1190             HKEY hKeys[16];
1191             UINT cKeys = 0;
1192             AddFSClassKeysToArray(apidl[0], hKeys, &cKeys);
1193 
1194             DEFCONTEXTMENU dcm;
1195             dcm.hwnd = hwndOwner;
1196             dcm.pcmcb = this;
1197             dcm.pidlFolder = m_pidlRoot;
1198             dcm.psf = this;
1199             dcm.cidl = cidl;
1200             dcm.apidl = apidl;
1201             dcm.cKeys = cKeys;
1202             dcm.aKeys = hKeys;
1203             dcm.punkAssociationInfo = NULL;
1204             hr = SHCreateDefaultContextMenu (&dcm, riid, &pObj);
1205         }
1206         else if (IsEqualIID (riid, IID_IDataObject))
1207         {
1208             if (cidl >= 1)
1209             {
1210                 hr = IDataObject_Constructor (hwndOwner, m_pidlRoot, apidl, cidl, TRUE, (IDataObject **)&pObj);
1211             }
1212             else
1213             {
1214                 hr = E_INVALIDARG;
1215             }
1216         }
1217         else if ((IsEqualIID (riid, IID_IExtractIconA) || IsEqualIID (riid, IID_IExtractIconW)) && (cidl == 1))
1218         {
1219             if (_ILIsValue(apidl[0]))
1220                 hr = _GetIconHandler(apidl[0], riid, (LPVOID*)&pObj);
1221             if (hr != S_OK)
1222                 hr = CFSExtractIcon_CreateInstance(this, apidl[0], riid, &pObj);
1223         }
1224         else if (IsEqualIID (riid, IID_IDropTarget))
1225         {
1226             /* only interested in attempting to bind to shell folders, not files (except exe), so if we fail, rebind to root */
1227             if (cidl != 1 || FAILED(hr = this->_GetDropTarget(apidl[0], (LPVOID*) &pObj)))
1228             {
1229                 hr = CFSDropTarget_CreateInstance(m_sPathTarget, riid, (LPVOID*) &pObj);
1230             }
1231         }
1232         else
1233             hr = E_NOINTERFACE;
1234 
1235         if (SUCCEEDED(hr) && !pObj)
1236             hr = E_OUTOFMEMORY;
1237 
1238         *ppvOut = pObj;
1239     }
1240     TRACE("(%p)->hr=0x%08x\n", this, hr);
1241     return hr;
1242 }
1243 
1244 static const WCHAR AdvancedW[] = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced";
1245 static const WCHAR HideFileExtW[] = L"HideFileExt";
1246 static const WCHAR NeverShowExtW[] = L"NeverShowExt";
1247 
1248 /******************************************************************************
1249  * SHELL_FS_HideExtension [Internal]
1250  *
1251  * Query the registry if the filename extension of a given path should be
1252  * hidden.
1253  *
1254  * PARAMS
1255  *  szPath [I] Relative or absolute path of a file
1256  *
1257  * RETURNS
1258  *  TRUE, if the filename's extension should be hidden
1259  *  FALSE, otherwise.
1260  */
1261 BOOL SHELL_FS_HideExtension(LPWSTR szPath)
1262 {
1263     HKEY hKey;
1264     DWORD dwData;
1265     DWORD dwDataSize = sizeof (DWORD);
1266     BOOL doHide = FALSE; /* The default value is FALSE (win98 at least) */
1267 
1268     if (!RegCreateKeyExW(HKEY_CURRENT_USER, AdvancedW, 0, 0, 0, KEY_ALL_ACCESS, 0, &hKey, 0)) {
1269         if (!RegQueryValueExW(hKey, HideFileExtW, 0, 0, (LPBYTE) &dwData, &dwDataSize))
1270             doHide = dwData;
1271         RegCloseKey (hKey);
1272     }
1273 
1274     if (!doHide) {
1275         LPWSTR ext = PathFindExtensionW(szPath);
1276 
1277         if (*ext != '\0') {
1278             WCHAR classname[MAX_PATH];
1279             LONG classlen = sizeof(classname);
1280 
1281             if (!RegQueryValueW(HKEY_CLASSES_ROOT, ext, classname, &classlen))
1282                 if (!RegOpenKeyW(HKEY_CLASSES_ROOT, classname, &hKey)) {
1283                     if (!RegQueryValueExW(hKey, NeverShowExtW, 0, NULL, NULL, NULL))
1284                         doHide = TRUE;
1285                     RegCloseKey(hKey);
1286                 }
1287         }
1288     }
1289     return doHide;
1290 }
1291 
1292 void SHELL_FS_ProcessDisplayFilename(LPWSTR szPath, DWORD dwFlags)
1293 {
1294     /*FIXME: MSDN also mentions SHGDN_FOREDITING which is not yet handled. */
1295     if (!(dwFlags & SHGDN_FORPARSING) &&
1296         ((dwFlags & SHGDN_INFOLDER) || (dwFlags == SHGDN_NORMAL))) {
1297             if (SHELL_FS_HideExtension(szPath) && szPath[0] != '.')
1298                 PathRemoveExtensionW(szPath);
1299     }
1300 }
1301 
1302 /**************************************************************************
1303 *  CFSFolder::GetDisplayNameOf
1304 *  Retrieves the display name for the specified file object or subfolder
1305 *
1306 * PARAMETERS
1307 *  LPCITEMIDLIST pidl,    //[in ] complex pidl to item
1308 *  DWORD         dwFlags, //[in ] SHGNO formatting flags
1309 *  LPSTRRET      lpName)  //[out] Returned display name
1310 *
1311 * FIXME
1312 *  if the name is in the pidl the ret value should be a STRRET_OFFSET
1313 */
1314 
1315 HRESULT WINAPI CFSFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl,
1316         DWORD dwFlags, LPSTRRET strRet)
1317 {
1318     if (!strRet)
1319         return E_INVALIDARG;
1320 
1321     /* If it is a complex pidl, let the child handle it */
1322     if (!_ILIsPidlSimple (pidl)) /* complex pidl */
1323     {
1324         return SHELL32_GetDisplayNameOfChild(this, pidl, dwFlags, strRet);
1325     }
1326     else if (pidl && !pidl->mkid.cb) /* empty pidl */
1327     {
1328         /* If it is an empty pidl return only the path of the folder */
1329         if ((GET_SHGDN_FOR(dwFlags) & SHGDN_FORPARSING) &&
1330             (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER) &&
1331             m_sPathTarget)
1332         {
1333             return SHSetStrRet(strRet, m_sPathTarget);
1334         }
1335         return E_INVALIDARG;
1336     }
1337 
1338     int len = 0;
1339     LPWSTR pszPath = (LPWSTR)CoTaskMemAlloc((MAX_PATH + 1) * sizeof(WCHAR));
1340     if (!pszPath)
1341         return E_OUTOFMEMORY;
1342 
1343     if ((GET_SHGDN_FOR(dwFlags) & SHGDN_FORPARSING) &&
1344         (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER) &&
1345         m_sPathTarget)
1346     {
1347         lstrcpynW(pszPath, m_sPathTarget, MAX_PATH);
1348         PathAddBackslashW(pszPath);
1349         len = wcslen(pszPath);
1350     }
1351     _ILSimpleGetTextW(pidl, pszPath + len, MAX_PATH + 1 - len);
1352     if (!_ILIsFolder(pidl)) SHELL_FS_ProcessDisplayFilename(pszPath, dwFlags);
1353 
1354     strRet->uType = STRRET_WSTR;
1355     strRet->pOleStr = pszPath;
1356 
1357     TRACE ("-- (%p)->(%s)\n", this, strRet->uType == STRRET_CSTR ? strRet->cStr : debugstr_w(strRet->pOleStr));
1358     return S_OK;
1359 }
1360 
1361 /**************************************************************************
1362 *  CFSFolder::SetNameOf
1363 *  Changes the name of a file object or subfolder, possibly changing its item
1364 *  identifier in the process.
1365 *
1366 * PARAMETERS
1367 *  HWND          hwndOwner,  //[in ] Owner window for output
1368 *  LPCITEMIDLIST pidl,       //[in ] simple pidl of item to change
1369 *  LPCOLESTR     lpszName,   //[in ] the items new display name
1370 *  DWORD         dwFlags,    //[in ] SHGNO formatting flags
1371 *  LPITEMIDLIST* ppidlOut)   //[out] simple pidl returned
1372 */
1373 HRESULT WINAPI CFSFolder::SetNameOf(
1374     HWND hwndOwner,
1375     PCUITEMID_CHILD pidl,
1376     LPCOLESTR lpName,
1377     DWORD dwFlags,
1378     PITEMID_CHILD *pPidlOut)
1379 {
1380     WCHAR szSrc[MAX_PATH + 1], szDest[MAX_PATH + 1];
1381     BOOL bIsFolder = _ILIsFolder (ILFindLastID (pidl));
1382 
1383     TRACE ("(%p)->(%p,pidl=%p,%s,%u,%p)\n", this, hwndOwner, pidl,
1384            debugstr_w (lpName), dwFlags, pPidlOut);
1385 
1386     FileStructW* pDataW = _ILGetFileStructW(pidl);
1387     if (!pDataW)
1388     {
1389         ERR("Got garbage pidl\n");
1390         return E_INVALIDARG;
1391     }
1392 
1393     /* build source path */
1394     PathCombineW(szSrc, m_sPathTarget, pDataW->wszName);
1395 
1396     /* build destination path */
1397     if (dwFlags == SHGDN_NORMAL || dwFlags & SHGDN_INFOLDER)
1398         PathCombineW(szDest, m_sPathTarget, lpName);
1399     else
1400         lstrcpynW(szDest, lpName, MAX_PATH);
1401 
1402     if(!(dwFlags & SHGDN_FORPARSING) && SHELL_FS_HideExtension(szSrc)) {
1403         WCHAR *ext = PathFindExtensionW(szSrc);
1404         if(*ext != '\0') {
1405             INT len = wcslen(szDest);
1406             lstrcpynW(szDest + len, ext, MAX_PATH - len);
1407         }
1408     }
1409 
1410     TRACE ("src=%s dest=%s\n", debugstr_w(szSrc), debugstr_w(szDest));
1411     if (!wcscmp(szSrc, szDest))
1412     {
1413         /* src and destination is the same */
1414         HRESULT hr = S_OK;
1415         if (pPidlOut)
1416             hr = _ILCreateFromPathW(szDest, pPidlOut);
1417 
1418         return hr;
1419     }
1420 
1421     if (MoveFileW (szSrc, szDest))
1422     {
1423         HRESULT hr = S_OK;
1424 
1425         if (pPidlOut)
1426             hr = _ILCreateFromPathW(szDest, pPidlOut);
1427 
1428         SHChangeNotify (bIsFolder ? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM,
1429                         SHCNF_PATHW, szSrc, szDest);
1430 
1431         return hr;
1432     }
1433 
1434     return E_FAIL;
1435 }
1436 
1437 HRESULT WINAPI CFSFolder::GetDefaultSearchGUID(GUID * pguid)
1438 {
1439     FIXME ("(%p)\n", this);
1440     return E_NOTIMPL;
1441 }
1442 
1443 HRESULT WINAPI CFSFolder::EnumSearches(IEnumExtraSearch ** ppenum)
1444 {
1445     FIXME ("(%p)\n", this);
1446     return E_NOTIMPL;
1447 }
1448 
1449 HRESULT WINAPI CFSFolder::GetDefaultColumn(DWORD dwRes,
1450         ULONG * pSort, ULONG * pDisplay)
1451 {
1452     TRACE ("(%p)\n", this);
1453 
1454     if (pSort)
1455         *pSort = 0;
1456     if (pDisplay)
1457         *pDisplay = 0;
1458 
1459     return S_OK;
1460 }
1461 
1462 HRESULT WINAPI CFSFolder::GetDefaultColumnState(UINT iColumn,
1463         DWORD * pcsFlags)
1464 {
1465     TRACE ("(%p)\n", this);
1466 
1467     if (!pcsFlags || iColumn >= GENERICSHELLVIEWCOLUMNS)
1468         return E_INVALIDARG;
1469 
1470     *pcsFlags = GenericSFHeader[iColumn].pcsFlags;
1471 
1472     return S_OK;
1473 }
1474 
1475 HRESULT WINAPI CFSFolder::GetDetailsEx(PCUITEMID_CHILD pidl,
1476                                        const SHCOLUMNID * pscid, VARIANT * pv)
1477 {
1478     FIXME ("(%p)\n", this);
1479 
1480     return E_NOTIMPL;
1481 }
1482 
1483 HRESULT WINAPI CFSFolder::GetDetailsOf(PCUITEMID_CHILD pidl,
1484                                        UINT iColumn, SHELLDETAILS * psd)
1485 {
1486     HRESULT hr = E_FAIL;
1487 
1488     TRACE ("(%p)->(%p %i %p)\n", this, pidl, iColumn, psd);
1489 
1490     if (!psd || iColumn >= GENERICSHELLVIEWCOLUMNS)
1491         return E_INVALIDARG;
1492 
1493     if (!pidl)
1494     {
1495         /* the header titles */
1496         psd->fmt = GenericSFHeader[iColumn].fmt;
1497         psd->cxChar = GenericSFHeader[iColumn].cxChar;
1498         return SHSetStrRet(&psd->str, GenericSFHeader[iColumn].colnameid);
1499     }
1500     else
1501     {
1502         hr = S_OK;
1503         psd->str.uType = STRRET_CSTR;
1504         /* the data from the pidl */
1505         switch (iColumn)
1506         {
1507             case 0:                /* name */
1508                 hr = GetDisplayNameOf (pidl, SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str);
1509                 break;
1510             case 1:                /* FIXME: comments */
1511                 psd->str.cStr[0] = 0;
1512                 break;
1513             case 2:                /* type */
1514                 _ILGetFileType(pidl, psd->str.cStr, MAX_PATH);
1515                 break;
1516             case 3:                /* size */
1517                 _ILGetFileSize(pidl, psd->str.cStr, MAX_PATH);
1518                 break;
1519             case 4:                /* date */
1520                 _ILGetFileDate(pidl, psd->str.cStr, MAX_PATH);
1521                 break;
1522             case 5:                /* attributes */
1523                 _ILGetFileAttributes(pidl, psd->str.cStr, MAX_PATH);
1524                 break;
1525         }
1526     }
1527 
1528     return hr;
1529 }
1530 
1531 HRESULT WINAPI CFSFolder::MapColumnToSCID (UINT column,
1532         SHCOLUMNID * pscid)
1533 {
1534     FIXME ("(%p)\n", this);
1535     return E_NOTIMPL;
1536 }
1537 
1538 /************************************************************************
1539  * CFSFolder::GetClassID
1540  */
1541 HRESULT WINAPI CFSFolder::GetClassID(CLSID * lpClassId)
1542 {
1543     TRACE ("(%p)\n", this);
1544 
1545     if (!lpClassId)
1546         return E_POINTER;
1547 
1548     *lpClassId = *m_pclsid;
1549 
1550     return S_OK;
1551 }
1552 
1553 /************************************************************************
1554  * CFSFolder::Initialize
1555  *
1556  * NOTES
1557  *  m_sPathTarget is not set. Don't know how to handle in a non rooted environment.
1558  */
1559 HRESULT WINAPI CFSFolder::Initialize(PCIDLIST_ABSOLUTE pidl)
1560 {
1561     WCHAR wszTemp[MAX_PATH];
1562 
1563     TRACE ("(%p)->(%p)\n", this, pidl);
1564 
1565     SHFree(m_pidlRoot);     /* free the old pidl */
1566     m_pidlRoot = ILClone (pidl); /* set my pidl */
1567 
1568     SHFree (m_sPathTarget);
1569     m_sPathTarget = NULL;
1570 
1571     /* set my path */
1572     if (SHGetPathFromIDListW (pidl, wszTemp))
1573     {
1574         int len = wcslen(wszTemp);
1575         m_sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
1576         if (!m_sPathTarget)
1577             return E_OUTOFMEMORY;
1578         memcpy(m_sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR));
1579     }
1580 
1581     TRACE ("--(%p)->(%s)\n", this, debugstr_w(m_sPathTarget));
1582     return S_OK;
1583 }
1584 
1585 /**************************************************************************
1586  * CFSFolder::GetCurFolder
1587  */
1588 HRESULT WINAPI CFSFolder::GetCurFolder(PIDLIST_ABSOLUTE * pidl)
1589 {
1590     TRACE ("(%p)->(%p)\n", this, pidl);
1591 
1592     if (!pidl)
1593         return E_POINTER;
1594 
1595     *pidl = ILClone(m_pidlRoot);
1596     return S_OK;
1597 }
1598 
1599 /**************************************************************************
1600  * CFSFolder::InitializeEx
1601  *
1602  * FIXME: error handling
1603  */
1604 HRESULT WINAPI CFSFolder::InitializeEx(IBindCtx * pbc, LPCITEMIDLIST pidlRootx,
1605                                        const PERSIST_FOLDER_TARGET_INFO * ppfti)
1606 {
1607     WCHAR wszTemp[MAX_PATH];
1608 
1609     TRACE("(%p)->(%p,%p,%p)\n", this, pbc, pidlRootx, ppfti);
1610     if (ppfti)
1611         TRACE("--%p %s %s 0x%08x 0x%08x\n",
1612               ppfti->pidlTargetFolder, debugstr_w (ppfti->szTargetParsingName),
1613               debugstr_w (ppfti->szNetworkProvider), ppfti->dwAttributes,
1614               ppfti->csidl);
1615 
1616     pdump (pidlRootx);
1617     if (ppfti && ppfti->pidlTargetFolder)
1618         pdump(ppfti->pidlTargetFolder);
1619 
1620     if (m_pidlRoot)
1621         __SHFreeAndNil(&m_pidlRoot);    /* free the old */
1622     if (m_sPathTarget)
1623         __SHFreeAndNil(&m_sPathTarget);
1624 
1625     /*
1626      * Root path and pidl
1627      */
1628     m_pidlRoot = ILClone(pidlRootx);
1629 
1630     /*
1631      *  the target folder is spezified in csidl OR pidlTargetFolder OR
1632      *  szTargetParsingName
1633      */
1634     if (ppfti)
1635     {
1636         if (ppfti->csidl != -1)
1637         {
1638             if (SHGetSpecialFolderPathW(0, wszTemp, ppfti->csidl,
1639                                         ppfti->csidl & CSIDL_FLAG_CREATE)) {
1640                 int len = wcslen(wszTemp);
1641                 m_sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
1642                 if (!m_sPathTarget)
1643                     return E_OUTOFMEMORY;
1644                 memcpy(m_sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR));
1645             }
1646         }
1647         else if (ppfti->szTargetParsingName[0])
1648         {
1649             int len = wcslen(ppfti->szTargetParsingName);
1650             m_sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
1651             if (!m_sPathTarget)
1652                 return E_OUTOFMEMORY;
1653             memcpy(m_sPathTarget, ppfti->szTargetParsingName,
1654                    (len + 1) * sizeof(WCHAR));
1655         }
1656         else if (ppfti->pidlTargetFolder)
1657         {
1658             if (SHGetPathFromIDListW(ppfti->pidlTargetFolder, wszTemp))
1659             {
1660                 int len = wcslen(wszTemp);
1661                 m_sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
1662                 if (!m_sPathTarget)
1663                     return E_OUTOFMEMORY;
1664                 memcpy(m_sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR));
1665             }
1666         }
1667     }
1668 
1669     TRACE("--(%p)->(target=%s)\n", this, debugstr_w(m_sPathTarget));
1670     pdump(m_pidlRoot);
1671     return (m_sPathTarget) ? S_OK : E_FAIL;
1672 }
1673 
1674 HRESULT WINAPI CFSFolder::GetFolderTargetInfo(PERSIST_FOLDER_TARGET_INFO * ppfti)
1675 {
1676     FIXME("(%p)->(%p)\n", this, ppfti);
1677     ZeroMemory(ppfti, sizeof (*ppfti));
1678     return E_NOTIMPL;
1679 }
1680 
1681 HRESULT CFSFolder::_CreateExtensionUIObject(PCUIDLIST_RELATIVE pidl, REFIID riid, LPVOID *ppvOut)
1682 {
1683     static const WCHAR formatW[] = {'S','h','e','l','l','E','x','\\',
1684         '{','%','0','8','x','-','%','0','4','x','-','%','0','4','x','-',
1685         '%','0','2','x','%','0','2','x','-','%','0','2','x','%','0','2','x',
1686         '%','0','2','x','%','0','2','x','%','0','2','x','%','0','2','x','}',0};
1687     WCHAR buf[MAX_PATH];
1688 
1689     sprintfW(buf, formatW, riid.Data1, riid.Data2, riid.Data3,
1690              riid.Data4[0], riid.Data4[1], riid.Data4[2], riid.Data4[3],
1691              riid.Data4[4], riid.Data4[5], riid.Data4[6], riid.Data4[7]);
1692 
1693     CLSID clsid;
1694     HRESULT hr;
1695 
1696     hr = GetCLSIDForFileType(pidl, buf, &clsid);
1697     if (hr != S_OK)
1698         return hr;
1699 
1700     hr = _CreateShellExtInstance(&clsid, pidl, riid, ppvOut);
1701     if (FAILED_UNEXPECTEDLY(hr))
1702         return hr;
1703 
1704     return S_OK;
1705 }
1706 
1707 HRESULT CFSFolder::_GetDropTarget(LPCITEMIDLIST pidl, LPVOID *ppvOut)
1708 {
1709     HRESULT hr;
1710 
1711     TRACE("CFSFolder::_GetDropTarget entered\n");
1712 
1713     if (_ILIsFolder (pidl))
1714     {
1715         CComPtr<IShellFolder> psfChild;
1716         hr = this->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfChild));
1717         if (FAILED_UNEXPECTEDLY(hr))
1718             return hr;
1719 
1720         return psfChild->CreateViewObject(NULL, IID_IDropTarget, ppvOut);
1721     }
1722 
1723     CLSID clsid;
1724     hr = GetCLSIDForFileType(pidl, L"shellex\\DropHandler", &clsid);
1725     if (hr != S_OK)
1726         return hr;
1727 
1728     hr = _CreateShellExtInstance(&clsid, pidl, IID_IDropTarget, ppvOut);
1729     if (FAILED_UNEXPECTEDLY(hr))
1730         return S_FALSE;
1731 
1732     return S_OK;
1733 }
1734 
1735 HRESULT CFSFolder::_GetIconHandler(LPCITEMIDLIST pidl, REFIID riid, LPVOID *ppvOut)
1736 {
1737     CLSID clsid;
1738     HRESULT hr;
1739 
1740     hr = GetCLSIDForFileType(pidl, L"shellex\\IconHandler", &clsid);
1741     if (hr != S_OK)
1742         return hr;
1743 
1744     hr = _CreateShellExtInstance(&clsid, pidl, riid, ppvOut);
1745     if (FAILED_UNEXPECTEDLY(hr))
1746         return S_FALSE;
1747 
1748     return S_OK;
1749 }
1750 
1751 HRESULT CFSFolder::_CreateShellExtInstance(const CLSID *pclsid, LPCITEMIDLIST pidl, REFIID riid, LPVOID *ppvOut)
1752 {
1753     HRESULT hr;
1754     WCHAR wszPath[MAX_PATH];
1755 
1756     FileStructW* pDataW = _ILGetFileStructW(pidl);
1757     if (!pDataW)
1758     {
1759         ERR("Got garbage pidl\n");
1760         return E_INVALIDARG;
1761     }
1762 
1763     PathCombineW(wszPath, m_sPathTarget, pDataW->wszName);
1764 
1765     CComPtr<IPersistFile> pp;
1766     hr = SHCoCreateInstance(NULL, pclsid, NULL, IID_PPV_ARG(IPersistFile, &pp));
1767     if (FAILED_UNEXPECTEDLY(hr))
1768         return hr;
1769 
1770     pp->Load(wszPath, 0);
1771 
1772     hr = pp->QueryInterface(riid, ppvOut);
1773     if (hr != S_OK)
1774     {
1775         ERR("Failed to query for interface IID_IShellExtInit hr %x pclsid %s\n", hr, wine_dbgstr_guid(pclsid));
1776         return hr;
1777     }
1778     return hr;
1779 }
1780 
1781 HRESULT WINAPI CFSFolder::CallBack(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
1782 {
1783     if (uMsg != DFM_MERGECONTEXTMENU && uMsg != DFM_INVOKECOMMAND)
1784         return S_OK;
1785 
1786     /* no data object means no selection */
1787     if (!pdtobj)
1788     {
1789         if (uMsg == DFM_INVOKECOMMAND && wParam == 0)
1790         {
1791             PUITEMID_CHILD pidlChild = ILClone(ILFindLastID(m_pidlRoot));
1792             LPITEMIDLIST pidlParent = ILClone(m_pidlRoot);
1793             ILRemoveLastID(pidlParent);
1794             HRESULT hr = SH_ShowPropertiesDialog(m_sPathTarget, pidlParent, &pidlChild);
1795             if (FAILED(hr))
1796                 ERR("SH_ShowPropertiesDialog failed\n");
1797             ILFree(pidlChild);
1798             ILFree(pidlParent);
1799         }
1800         else if (uMsg == DFM_MERGECONTEXTMENU)
1801         {
1802             QCMINFO *pqcminfo = (QCMINFO *)lParam;
1803             HMENU hpopup = CreatePopupMenu();
1804             _InsertMenuItemW(hpopup, 0, TRUE, 0, MFT_STRING, MAKEINTRESOURCEW(IDS_PROPERTIES), MFS_ENABLED);
1805             Shell_MergeMenus(pqcminfo->hmenu, hpopup, pqcminfo->indexMenu++, pqcminfo->idCmdFirst, pqcminfo->idCmdLast, MM_ADDSEPARATOR);
1806             DestroyMenu(hpopup);
1807         }
1808 
1809         return S_OK;
1810     }
1811 
1812     if (uMsg != DFM_INVOKECOMMAND || wParam != DFM_CMD_PROPERTIES)
1813         return S_OK;
1814 
1815     return Shell_DefaultContextMenuCallBack(this, pdtobj);
1816 }
1817 
1818 static HBITMAP DoLoadPicture(LPCWSTR pszFileName)
1819 {
1820     // create stream from file
1821     HRESULT hr;
1822     CComPtr<IStream> pStream;
1823     hr = SHCreateStreamOnFileEx(pszFileName, STGM_READ, FILE_ATTRIBUTE_NORMAL,
1824                                 FALSE, NULL, &pStream);
1825     if (FAILED(hr))
1826         return NULL;
1827 
1828     // load the picture
1829     HBITMAP hbm = NULL;
1830     CComPtr<IPicture> pPicture;
1831     OleLoadPicture(pStream, 0, FALSE, IID_IPicture, (LPVOID *)&pPicture);
1832 
1833     // get the bitmap handle
1834     if (pPicture)
1835     {
1836         pPicture->get_Handle((OLE_HANDLE *)&hbm);
1837 
1838         // copy the bitmap handle
1839         hbm = (HBITMAP)CopyImage(hbm, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
1840     }
1841 
1842     return hbm;
1843 }
1844 
1845 HRESULT WINAPI CFSFolder::GetCustomViewInfo(ULONG unknown, SFVM_CUSTOMVIEWINFO_DATA *data)
1846 {
1847     if (data == NULL)
1848     {
1849         return E_POINTER;
1850     }
1851     if (data->cbSize != sizeof(*data))
1852     {
1853         // NOTE: You have to set the cbData member before SFVM_GET_CUSTOMVIEWINFO call.
1854         return E_INVALIDARG;
1855     }
1856 
1857     data->hbmBack = NULL;
1858     data->clrText = CLR_INVALID;
1859     data->clrTextBack = CLR_INVALID;
1860 
1861     WCHAR szPath[MAX_PATH], szIniFile[MAX_PATH];
1862 
1863     // does the folder exists?
1864     if (!SHGetPathFromIDListW(m_pidlRoot, szPath) || !PathIsDirectoryW(szPath))
1865     {
1866         return E_INVALIDARG;
1867     }
1868 
1869     // don't use custom view in network path for security
1870     if (PathIsNetworkPath(szPath))
1871     {
1872         return E_ACCESSDENIED;
1873     }
1874 
1875     // build the ini file path
1876     StringCchCopyW(szIniFile, _countof(szIniFile), szPath);
1877     PathAppend(szIniFile, L"desktop.ini");
1878 
1879     static LPCWSTR TheGUID = L"{BE098140-A513-11D0-A3A4-00C04FD706EC}";
1880     static LPCWSTR Space = L" \t\n\r\f\v";
1881 
1882     // get info from ini file
1883     WCHAR szImage[MAX_PATH], szText[64];
1884 
1885     // load the image
1886     szImage[0] = UNICODE_NULL;
1887     GetPrivateProfileStringW(TheGUID, L"IconArea_Image", L"", szImage, _countof(szImage), szIniFile);
1888     if (szImage[0])
1889     {
1890         StrTrimW(szImage, Space);
1891         if (PathIsRelativeW(szImage))
1892         {
1893             PathAppendW(szPath, szImage);
1894             StringCchCopyW(szImage, _countof(szImage), szPath);
1895         }
1896         data->hbmBack = DoLoadPicture(szImage);
1897     }
1898 
1899     // load the text color
1900     szText[0] = UNICODE_NULL;
1901     GetPrivateProfileStringW(TheGUID, L"IconArea_Text", L"", szText, _countof(szText), szIniFile);
1902     if (szText[0])
1903     {
1904         StrTrimW(szText, Space);
1905 
1906         LPWSTR pchEnd = NULL;
1907         COLORREF cr = (wcstol(szText, &pchEnd, 0) & 0xFFFFFF);
1908 
1909         if (pchEnd && !*pchEnd)
1910             data->clrText = cr;
1911     }
1912 
1913     // load the text background color
1914     szText[0] = UNICODE_NULL;
1915     GetPrivateProfileStringW(TheGUID, L"IconArea_TextBackground", L"", szText, _countof(szText), szIniFile);
1916     if (szText[0])
1917     {
1918         StrTrimW(szText, Space);
1919 
1920         LPWSTR pchEnd = NULL;
1921         COLORREF cr = (wcstol(szText, &pchEnd, 0) & 0xFFFFFF);
1922 
1923         if (pchEnd && !*pchEnd)
1924             data->clrTextBack = cr;
1925     }
1926 
1927     if (data->hbmBack != NULL || data->clrText != CLR_INVALID || data->clrTextBack != CLR_INVALID)
1928         return S_OK;
1929 
1930     return E_FAIL;
1931 }
1932 
1933 HRESULT WINAPI CFSFolder::MessageSFVCB(UINT uMsg, WPARAM wParam, LPARAM lParam)
1934 {
1935     HRESULT hr = E_NOTIMPL;
1936     switch (uMsg)
1937     {
1938     case SFVM_GET_CUSTOMVIEWINFO:
1939         hr = GetCustomViewInfo((ULONG)wParam, (SFVM_CUSTOMVIEWINFO_DATA *)lParam);
1940         break;
1941     }
1942     return hr;
1943 }
1944