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