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