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