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