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