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