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