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