1 /*
2 * PROJECT: shell32
3 * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
4 * PURPOSE: file system folder
5 * COPYRIGHT: Copyright 1997 Marcus Meissner
6 * Copyright 1998, 1999, 2002 Juergen Schmied
7 * Copyright 2019-2024 Katayama Hirofumi MZ
8 * Copyright 2020 Mark Jansen (mark.jansen@reactos.org)
9 */
10
11 #include <precomp.h>
12
13 WINE_DEFAULT_DEBUG_CHANNEL (shell);
14
15 static HRESULT SHELL32_GetCLSIDForDirectory(LPCWSTR pwszDir, LPCWSTR KeyName, CLSID* pclsidFolder);
16
ItemIsFolder(PCUITEMID_CHILD pidl)17 static BOOL ItemIsFolder(PCUITEMID_CHILD pidl)
18 {
19 const BYTE mask = PT_FS | PT_FS_FOLDER_FLAG | PT_FS_FILE_FLAG;
20 const BYTE type = _ILGetType(pidl);
21 return (type & mask) == (PT_FS | PT_FS_FOLDER_FLAG) || (type == PT_FS && ILGetNext(pidl));
22 }
23
GetItemFileName(PCUITEMID_CHILD pidl,LPWSTR Buf,UINT cchMax)24 static LPCWSTR GetItemFileName(PCUITEMID_CHILD pidl, LPWSTR Buf, UINT cchMax)
25 {
26 FileStructW* pDataW = _ILGetFileStructW(pidl);
27 if (pDataW)
28 return pDataW->wszName;
29 LPPIDLDATA pdata = _ILGetDataPointer(pidl);
30 if (_ILGetFSType(pidl) & PT_FS_UNICODE_FLAG)
31 return (LPWSTR)pdata->u.file.szNames;
32 if (_ILSimpleGetTextW(pidl, Buf, cchMax))
33 return Buf;
34 return NULL;
35 }
36
IsRealItem(const ITEMIDLIST & idl)37 static BOOL IsRealItem(const ITEMIDLIST &idl)
38 {
39 // PIDLs created with SHSimpleIDListFromPath contain no data, otherwise, the item is "real"
40 FileStruct &fsitem = ((PIDLDATA*)idl.mkid.abID)->u.file;
41 return fsitem.dwFileSize | fsitem.uFileDate;
42 }
43
GetItemDescription(PCUITEMID_CHILD pidl,LPWSTR Buf,UINT cchMax)44 static void GetItemDescription(PCUITEMID_CHILD pidl, LPWSTR Buf, UINT cchMax)
45 {
46 HRESULT hr = E_FAIL;
47 if (ItemIsFolder(pidl))
48 {
49 hr = SHELL32_AssocGetFSDirectoryDescription(Buf, cchMax);
50 }
51 else
52 {
53 WCHAR temp[MAX_PATH];
54 LPCWSTR name = GetItemFileName(pidl, temp, _countof(temp));
55 hr = SHELL32_AssocGetFileDescription(name ? name : L"", Buf, cchMax);
56 }
57 if (FAILED(hr) && cchMax)
58 Buf[0] = UNICODE_NULL;
59 }
60
OpenKeyFromFileType(LPCWSTR pExtension,LPCWSTR KeyName)61 static HKEY OpenKeyFromFileType(LPCWSTR pExtension, LPCWSTR KeyName)
62 {
63 HKEY hkey;
64
65 WCHAR FullName[MAX_PATH];
66 DWORD dwSize = sizeof(FullName);
67 wsprintf(FullName, L"%s\\%s", pExtension, KeyName);
68
69 LONG res = RegOpenKeyExW(HKEY_CLASSES_ROOT, FullName, 0, KEY_READ, &hkey);
70 if (!res)
71 return hkey;
72
73 res = RegGetValueW(HKEY_CLASSES_ROOT, pExtension, NULL, RRF_RT_REG_SZ, NULL, FullName, &dwSize);
74 if (res)
75 {
76 WARN("Failed to get progid for extension %S (%x), error %d\n", pExtension, pExtension, res);
77 return NULL;
78 }
79
80 wcscat(FullName, L"\\");
81 wcscat(FullName, KeyName);
82
83 hkey = NULL;
84 res = RegOpenKeyExW(HKEY_CLASSES_ROOT, FullName, 0, KEY_READ, &hkey);
85 if (res)
86 WARN("Could not open key %S for extension %S\n", KeyName, pExtension);
87
88 return hkey;
89 }
90
ExtensionFromPidl(PCUIDLIST_RELATIVE pidl,LPWSTR Buf,UINT cchMax)91 static LPCWSTR ExtensionFromPidl(PCUIDLIST_RELATIVE pidl, LPWSTR Buf, UINT cchMax)
92 {
93 if (!_ILIsValue(pidl))
94 {
95 ERR("Invalid pidl!\n");
96 return NULL;
97 }
98
99 LPCWSTR name = GetItemFileName(pidl, Buf, cchMax);
100 LPCWSTR pExtension = name ? PathFindExtensionW(name) : NULL;
101 if (!pExtension || *pExtension == UNICODE_NULL)
102 {
103 WARN("No extension for %S!\n", name);
104 return NULL;
105 }
106 return pExtension;
107 }
108
GetCLSIDForFileTypeFromExtension(LPCWSTR pExtension,LPCWSTR KeyName,CLSID * pclsid)109 static HRESULT GetCLSIDForFileTypeFromExtension(LPCWSTR pExtension, LPCWSTR KeyName, CLSID* pclsid)
110 {
111 HKEY hkeyProgId = OpenKeyFromFileType(pExtension, KeyName);
112 if (!hkeyProgId)
113 {
114 WARN("OpenKeyFromFileType failed for key %S\n", KeyName);
115 return S_FALSE;
116 }
117
118 WCHAR wszCLSIDValue[CHARS_IN_GUID];
119 DWORD dwSize = sizeof(wszCLSIDValue);
120 LONG res = RegGetValueW(hkeyProgId, NULL, NULL, RRF_RT_REG_SZ, NULL, wszCLSIDValue, &dwSize);
121 RegCloseKey(hkeyProgId);
122 if (res)
123 {
124 ERR("OpenKeyFromFileType succeeded but RegGetValueW failed\n");
125 return S_FALSE;
126 }
127
128 #if 0
129 {
130 res = RegGetValueW(HKEY_LOCAL_MACHINE,
131 L"Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved",
132 wszCLSIDValue,
133 RRF_RT_REG_SZ,
134 NULL,
135 NULL,
136 NULL);
137 if (res != ERROR_SUCCESS)
138 {
139 ERR("DropHandler extension %S not approved\n", wszName);
140 return E_ACCESSDENIED;
141 }
142 }
143 #endif
144
145 if (RegGetValueW(HKEY_LOCAL_MACHINE,
146 L"Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Blocked",
147 wszCLSIDValue,
148 RRF_RT_REG_SZ,
149 NULL,
150 NULL,
151 NULL) == ERROR_SUCCESS)
152 {
153 ERR("Extension %S not approved\n", wszCLSIDValue);
154 return E_ACCESSDENIED;
155 }
156
157 HRESULT hres = CLSIDFromString (wszCLSIDValue, pclsid);
158 if (FAILED_UNEXPECTEDLY(hres))
159 return hres;
160
161 return S_OK;
162 }
163
GetCLSIDForFileType(PCUIDLIST_RELATIVE pidl,LPCWSTR KeyName,CLSID * pclsid)164 HRESULT GetCLSIDForFileType(PCUIDLIST_RELATIVE pidl, LPCWSTR KeyName, CLSID* pclsid)
165 {
166 WCHAR buf[256];
167 LPCWSTR pExtension = ExtensionFromPidl(pidl, buf, _countof(buf));
168 if (!pExtension)
169 return S_FALSE;
170
171 return GetCLSIDForFileTypeFromExtension(pExtension, KeyName, pclsid);
172 }
173
174 static HRESULT
getDefaultIconLocation(LPWSTR szIconFile,UINT cchMax,int * piIndex,UINT uFlags)175 getDefaultIconLocation(LPWSTR szIconFile, UINT cchMax, int *piIndex, UINT uFlags)
176 {
177 if (!HLM_GetIconW(IDI_SHELL_FOLDER - 1, szIconFile, cchMax, piIndex))
178 {
179 if (!HCR_GetIconW(L"Folder", szIconFile, NULL, cchMax, piIndex))
180 {
181 StringCchCopyW(szIconFile, cchMax, swShell32Name);
182 *piIndex = -IDI_SHELL_FOLDER;
183 }
184 }
185
186 if (uFlags & GIL_OPENICON)
187 {
188 // next icon
189 if (*piIndex < 0)
190 (*piIndex)--;
191 else
192 (*piIndex)++;
193 }
194
195 return S_OK;
196 }
197
198 static BOOL
getShellClassInfo(LPCWSTR Entry,LPWSTR pszValue,DWORD cchValueLen,LPCWSTR IniFile)199 getShellClassInfo(LPCWSTR Entry, LPWSTR pszValue, DWORD cchValueLen, LPCWSTR IniFile)
200 {
201 return GetPrivateProfileStringW(L".ShellClassInfo", Entry, NULL, pszValue, cchValueLen, IniFile);
202 }
203
204 static HRESULT
getIconLocationForFolder(IShellFolder * psf,PCITEMID_CHILD pidl,UINT uFlags,LPWSTR szIconFile,UINT cchMax,int * piIndex,UINT * pwFlags)205 getIconLocationForFolder(IShellFolder * psf, PCITEMID_CHILD pidl, UINT uFlags,
206 LPWSTR szIconFile, UINT cchMax, int *piIndex, UINT *pwFlags)
207 {
208 DWORD dwFileAttrs;
209 WCHAR wszPath[MAX_PATH];
210 WCHAR wszIniFullPath[MAX_PATH];
211
212 if (uFlags & GIL_DEFAULTICON)
213 goto Quit;
214
215 // get path
216 if (!ILGetDisplayNameExW(psf, pidl, wszPath, 0))
217 goto Quit;
218 if (!PathIsDirectoryW(wszPath))
219 goto Quit;
220
221 // read-only or system folder?
222 dwFileAttrs = _ILGetFileAttributes(ILFindLastID(pidl), NULL, 0);
223 if ((dwFileAttrs & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)) == 0)
224 goto Quit;
225
226 // build the full path of ini file
227 StringCchCopyW(wszIniFullPath, _countof(wszIniFullPath), wszPath);
228 PathAppendW(wszIniFullPath, L"desktop.ini");
229
230 WCHAR wszValue[MAX_PATH], wszTemp[MAX_PATH];
231 if (getShellClassInfo(L"IconFile", wszValue, _countof(wszValue), wszIniFullPath))
232 {
233 // wszValue --> wszTemp
234 ExpandEnvironmentStringsW(wszValue, wszTemp, _countof(wszTemp));
235
236 // wszPath + wszTemp --> wszPath
237 if (PathIsRelativeW(wszTemp))
238 PathAppendW(wszPath, wszTemp);
239 else
240 StringCchCopyW(wszPath, _countof(wszPath), wszTemp);
241
242 // wszPath --> szIconFile
243 GetFullPathNameW(wszPath, cchMax, szIconFile, NULL);
244
245 *piIndex = GetPrivateProfileIntW(L".ShellClassInfo", L"IconIndex", 0, wszIniFullPath);
246 return S_OK;
247 }
248 else if (getShellClassInfo(L"CLSID", wszValue, _countof(wszValue), wszIniFullPath) &&
249 HCR_GetIconW(wszValue, szIconFile, NULL, cchMax, piIndex))
250 {
251 return S_OK;
252 }
253 else if (getShellClassInfo(L"CLSID2", wszValue, _countof(wszValue), wszIniFullPath) &&
254 HCR_GetIconW(wszValue, szIconFile, NULL, cchMax, piIndex))
255 {
256 return S_OK;
257 }
258 else if (getShellClassInfo(L"IconResource", wszValue, _countof(wszValue), wszIniFullPath))
259 {
260 // wszValue --> wszTemp
261 ExpandEnvironmentStringsW(wszValue, wszTemp, _countof(wszTemp));
262
263 // parse the icon location
264 *piIndex = PathParseIconLocationW(wszTemp);
265
266 // wszPath + wszTemp --> wszPath
267 if (PathIsRelativeW(wszTemp))
268 PathAppendW(wszPath, wszTemp);
269 else
270 StringCchCopyW(wszPath, _countof(wszPath), wszTemp);
271
272 // wszPath --> szIconFile
273 GetFullPathNameW(wszPath, cchMax, szIconFile, NULL);
274 return S_OK;
275 }
276
277 Quit:
278 return getDefaultIconLocation(szIconFile, cchMax, piIndex, uFlags);
279 }
280
CFSExtractIcon_CreateInstance(IShellFolder * psf,LPCITEMIDLIST pidl,REFIID iid,LPVOID * ppvOut)281 HRESULT CFSExtractIcon_CreateInstance(IShellFolder * psf, LPCITEMIDLIST pidl, REFIID iid, LPVOID * ppvOut)
282 {
283 CComPtr<IDefaultExtractIconInit> initIcon;
284 HRESULT hr;
285 int icon_idx = 0;
286 UINT flags = 0; // FIXME: Use it!
287 WCHAR wTemp[MAX_PATH] = L"";
288
289 hr = SHCreateDefaultExtractIcon(IID_PPV_ARG(IDefaultExtractIconInit,&initIcon));
290 if (FAILED(hr))
291 return hr;
292
293 if (_ILIsFolder (pidl))
294 {
295 if (SUCCEEDED(getIconLocationForFolder(psf,
296 pidl, 0, wTemp, _countof(wTemp),
297 &icon_idx,
298 &flags)))
299 {
300 initIcon->SetNormalIcon(wTemp, icon_idx);
301 // FIXME: if/when getIconLocationForFolder does something for
302 // GIL_FORSHORTCUT, code below should be uncommented. and
303 // the following line removed.
304 initIcon->SetShortcutIcon(wTemp, icon_idx);
305 }
306 if (SUCCEEDED(getIconLocationForFolder(psf,
307 pidl, GIL_DEFAULTICON, wTemp, _countof(wTemp),
308 &icon_idx,
309 &flags)))
310 {
311 initIcon->SetDefaultIcon(wTemp, icon_idx);
312 }
313 // if (SUCCEEDED(getIconLocationForFolder(psf,
314 // pidl, GIL_FORSHORTCUT, wTemp, _countof(wTemp),
315 // &icon_idx,
316 // &flags)))
317 // {
318 // initIcon->SetShortcutIcon(wTemp, icon_idx);
319 // }
320 if (SUCCEEDED(getIconLocationForFolder(psf,
321 pidl, GIL_OPENICON, wTemp, _countof(wTemp),
322 &icon_idx,
323 &flags)))
324 {
325 initIcon->SetOpenIcon(wTemp, icon_idx);
326 }
327 }
328 else
329 {
330 WCHAR extbuf[256];
331 LPCWSTR pExtension = ExtensionFromPidl(pidl, extbuf, _countof(extbuf));
332 HKEY hkey = pExtension ? OpenKeyFromFileType(pExtension, L"DefaultIcon") : NULL;
333 if (!hkey)
334 WARN("Could not open DefaultIcon key!\n");
335
336 DWORD dwSize = sizeof(wTemp);
337 if (hkey && !SHQueryValueExW(hkey, NULL, NULL, NULL, wTemp, &dwSize))
338 {
339 WCHAR sNum[5];
340 if (ParseFieldW (wTemp, 2, sNum, 5))
341 icon_idx = _wtoi(sNum);
342 else
343 icon_idx = 0; /* sometimes the icon number is missing */
344 ParseFieldW (wTemp, 1, wTemp, MAX_PATH);
345 PathUnquoteSpacesW(wTemp);
346
347 if (!wcscmp(L"%1", wTemp)) /* icon is in the file */
348 {
349 ILGetDisplayNameExW(psf, pidl, wTemp, ILGDN_FORPARSING);
350 icon_idx = 0;
351
352 INT ret = PrivateExtractIconsW(wTemp, 0, 0, 0, NULL, NULL, 0, 0);
353 if (ret <= 0)
354 {
355 StringCbCopyW(wTemp, sizeof(wTemp), swShell32Name);
356 if (lstrcmpiW(pExtension, L".exe") == 0 || lstrcmpiW(pExtension, L".scr") == 0)
357 icon_idx = -IDI_SHELL_EXE;
358 else
359 icon_idx = -IDI_SHELL_DOCUMENT;
360 }
361 }
362
363 initIcon->SetNormalIcon(wTemp, icon_idx);
364 }
365 else
366 {
367 initIcon->SetNormalIcon(swShell32Name, 0);
368 }
369
370 if (hkey)
371 RegCloseKey(hkey);
372 }
373
374 return initIcon->QueryInterface(iid, ppvOut);
375 }
376
377 /*
378 CFileSysEnum should do an initial FindFirstFile and do a FindNextFile as each file is
379 returned by Next. When the enumerator is created, it can do numerous additional operations
380 including formatting a drive, reconnecting a network share drive, and requesting a disk
381 be inserted in a removable drive.
382 */
383
384
385 class CFileSysEnum :
386 public CEnumIDListBase
387 {
388 private:
_AddFindResult(LPWSTR sParentDir,const WIN32_FIND_DATAW & FindData,DWORD dwFlags)389 HRESULT _AddFindResult(LPWSTR sParentDir, const WIN32_FIND_DATAW& FindData, DWORD dwFlags)
390 {
391 #define SUPER_HIDDEN (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)
392
393 // Does it need special handling because it is hidden?
394 if (FindData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
395 {
396 DWORD dwHidden = FindData.dwFileAttributes & SUPER_HIDDEN;
397
398 // Is it hidden, but are we not asked to include hidden?
399 if (dwHidden == FILE_ATTRIBUTE_HIDDEN && !(dwFlags & SHCONTF_INCLUDEHIDDEN))
400 return S_OK;
401
402 // Is it a system file, but are we not asked to include those?
403 if (dwHidden == SUPER_HIDDEN && !(dwFlags & SHCONTF_INCLUDESUPERHIDDEN))
404 return S_OK;
405 }
406
407 BOOL bDirectory = (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
408
409 HRESULT hr;
410 if (bDirectory)
411 {
412 // Skip the current and parent directory nodes
413 if (!strcmpW(FindData.cFileName, L".") || !strcmpW(FindData.cFileName, L".."))
414 return S_OK;
415
416 // Does this directory need special handling?
417 if ((FindData.dwFileAttributes & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)) != 0)
418 {
419 WCHAR Tmp[MAX_PATH];
420 CLSID clsidFolder;
421
422 PathCombineW(Tmp, sParentDir, FindData.cFileName);
423
424 hr = SHELL32_GetCLSIDForDirectory(Tmp, L"CLSID", &clsidFolder);
425 if (SUCCEEDED(hr))
426 {
427 ERR("Got CLSID override for '%S'\n", Tmp);
428 }
429 }
430 }
431 else
432 {
433 CLSID clsidFile;
434 LPWSTR pExtension = PathFindExtensionW(FindData.cFileName);
435 if (pExtension)
436 {
437 // FIXME: Cache this?
438 hr = GetCLSIDForFileTypeFromExtension(pExtension, L"CLSID", &clsidFile);
439 if (hr == S_OK)
440 {
441 HKEY hkey;
442 hr = SHRegGetCLSIDKeyW(clsidFile, L"ShellFolder", FALSE, FALSE, &hkey);
443 if (SUCCEEDED(hr))
444 {
445 ::RegCloseKey(hkey);
446
447 // This should be presented as directory!
448 bDirectory = TRUE;
449 TRACE("Treating '%S' as directory!\n", FindData.cFileName);
450 }
451 }
452 }
453 }
454
455 LPITEMIDLIST pidl = NULL;
456 if (bDirectory)
457 {
458 if (dwFlags & SHCONTF_FOLDERS)
459 {
460 TRACE("(%p)-> (folder=%s)\n", this, debugstr_w(FindData.cFileName));
461 pidl = _ILCreateFromFindDataW(&FindData);
462 }
463 }
464 else
465 {
466 if (dwFlags & SHCONTF_NONFOLDERS)
467 {
468 TRACE("(%p)-> (file =%s)\n", this, debugstr_w(FindData.cFileName));
469 pidl = _ILCreateFromFindDataW(&FindData);
470 }
471 }
472
473 if (pidl && !AddToEnumList(pidl))
474 {
475 FAILED_UNEXPECTEDLY(E_FAIL);
476 return E_FAIL;
477 }
478
479 return S_OK;
480 }
481
482 public:
CFileSysEnum()483 CFileSysEnum()
484 {
485
486 }
487
~CFileSysEnum()488 ~CFileSysEnum()
489 {
490 }
491
Initialize(LPWSTR sPathTarget,DWORD dwFlags)492 HRESULT WINAPI Initialize(LPWSTR sPathTarget, DWORD dwFlags)
493 {
494 TRACE("(%p)->(path=%s flags=0x%08x)\n", this, debugstr_w(sPathTarget), dwFlags);
495
496 if (!sPathTarget || !sPathTarget[0])
497 {
498 WARN("No path for CFileSysEnum, empty result!\n");
499 return S_FALSE;
500 }
501
502 WCHAR szFindPattern[MAX_PATH];
503 HRESULT hr = StringCchCopyW(szFindPattern, _countof(szFindPattern), sPathTarget);
504 if (FAILED_UNEXPECTEDLY(hr))
505 return hr;
506
507 /* FIXME: UNSAFE CRAP */
508 PathAddBackslashW(szFindPattern);
509
510 hr = StringCchCatW(szFindPattern, _countof(szFindPattern), L"*.*");
511 if (FAILED_UNEXPECTEDLY(hr))
512 return hr;
513
514
515 WIN32_FIND_DATAW FindData;
516 HANDLE hFind = FindFirstFileW(szFindPattern, &FindData);
517 if (hFind == INVALID_HANDLE_VALUE)
518 return HRESULT_FROM_WIN32(GetLastError());
519
520 do
521 {
522 hr = _AddFindResult(sPathTarget, FindData, dwFlags);
523
524 if (FAILED_UNEXPECTEDLY(hr))
525 break;
526
527 } while(FindNextFileW(hFind, &FindData));
528
529 if (SUCCEEDED(hr))
530 {
531 DWORD dwError = GetLastError();
532 if (dwError != ERROR_NO_MORE_FILES)
533 {
534 hr = HRESULT_FROM_WIN32(dwError);
535 FAILED_UNEXPECTEDLY(hr);
536 }
537 }
538 TRACE("(%p)->(hr=0x%08x)\n", this, hr);
539 FindClose(hFind);
540 return hr;
541 }
542
543 BEGIN_COM_MAP(CFileSysEnum)
544 COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList)
545 END_COM_MAP()
546 };
547
548
549 /***********************************************************************
550 * IShellFolder implementation
551 */
552
CFSFolder()553 CFSFolder::CFSFolder()
554 {
555 m_pclsid = &CLSID_ShellFSFolder;
556 m_sPathTarget = NULL;
557 m_pidlRoot = NULL;
558 m_bGroupPolicyActive = 0;
559 }
560
~CFSFolder()561 CFSFolder::~CFSFolder()
562 {
563 TRACE("-- destroying IShellFolder(%p)\n", this);
564
565 SHFree(m_pidlRoot);
566 SHFree(m_sPathTarget);
567 }
568
569 static const shvheader GenericSFHeader[] = {
570 {IDS_SHV_COLUMN_NAME, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 18},
571 {IDS_SHV_COLUMN_SIZE, SHCOLSTATE_TYPE_INT | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
572 {IDS_SHV_COLUMN_TYPE, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 10},
573 {IDS_SHV_COLUMN_MODIFIED, SHCOLSTATE_TYPE_DATE | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 15},
574 {IDS_SHV_COLUMN_ATTRIBUTES, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 8},
575 {IDS_SHV_COLUMN_COMMENTS, SHCOLSTATE_TYPE_STR | SHCOLSTATE_SLOW, LVCFMT_LEFT, 10}, // We don't currently support comments but CRegFolder does
576 };
577
578 #define GENERICSHELLVIEWCOLUMNS _countof(GenericSFHeader)
579
GetFSColumnDetails(UINT iColumn,SHELLDETAILS & sd)580 HRESULT CFSFolder::GetFSColumnDetails(UINT iColumn, SHELLDETAILS &sd)
581 {
582 if (iColumn >= _countof(GenericSFHeader))
583 return E_INVALIDARG;
584
585 sd.fmt = GenericSFHeader[iColumn].fmt;
586 sd.cxChar = GenericSFHeader[iColumn].cxChar;
587 return SHSetStrRet(&sd.str, GenericSFHeader[iColumn].colnameid);
588 }
589
GetDefaultFSColumnState(UINT iColumn,SHCOLSTATEF & csFlags)590 HRESULT CFSFolder::GetDefaultFSColumnState(UINT iColumn, SHCOLSTATEF &csFlags)
591 {
592 if (iColumn >= _countof(GenericSFHeader))
593 return E_INVALIDARG;
594
595 csFlags = GenericSFHeader[iColumn].colstate;
596 return S_OK;
597 }
598
SHELL32_GetCLSIDForDirectory(LPCWSTR pwszDir,LPCWSTR KeyName,CLSID * pclsidFolder)599 static HRESULT SHELL32_GetCLSIDForDirectory(LPCWSTR pwszDir, LPCWSTR KeyName, CLSID* pclsidFolder)
600 {
601 WCHAR wszCLSIDValue[CHARS_IN_GUID];
602 WCHAR wszDesktopIni[MAX_PATH];
603
604 StringCchCopyW(wszDesktopIni, MAX_PATH, pwszDir);
605 StringCchCatW(wszDesktopIni, MAX_PATH, L"\\desktop.ini");
606
607 if (GetPrivateProfileStringW(L".ShellClassInfo",
608 KeyName,
609 L"",
610 wszCLSIDValue,
611 CHARS_IN_GUID,
612 wszDesktopIni))
613 {
614 return CLSIDFromString(wszCLSIDValue, pclsidFolder);
615 }
616 return E_FAIL;
617 }
618
SHELL32_GetFSItemAttributes(IShellFolder * psf,LPCITEMIDLIST pidl,LPDWORD pdwAttributes)619 HRESULT SHELL32_GetFSItemAttributes(IShellFolder * psf, LPCITEMIDLIST pidl, LPDWORD pdwAttributes)
620 {
621 DWORD dwFileAttributes, dwShellAttributes;
622
623 if (!_ILIsFolderOrFile(pidl))
624 {
625 ERR("Got wrong type of pidl!\n");
626 *pdwAttributes &= SFGAO_CANLINK;
627 return S_OK;
628 }
629
630 dwFileAttributes = _ILGetFileAttributes(pidl, NULL, 0);
631
632 /* Set common attributes */
633 dwShellAttributes = SFGAO_CANCOPY | SFGAO_CANMOVE | SFGAO_CANLINK | SFGAO_CANRENAME | SFGAO_CANDELETE |
634 SFGAO_HASPROPSHEET | SFGAO_DROPTARGET | SFGAO_FILESYSTEM;
635
636 BOOL bDirectory = (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
637
638 if (SFGAO_VALIDATE & *pdwAttributes)
639 {
640 STRRET strret;
641 LPWSTR path;
642 if (SUCCEEDED(psf->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &strret)) &&
643 SUCCEEDED(StrRetToStrW(&strret, pidl, &path)))
644 {
645 BOOL exists = PathFileExistsW(path);
646 SHFree(path);
647 if (!exists)
648 return E_FAIL;
649 }
650 }
651
652 if (!bDirectory)
653 {
654 // https://git.reactos.org/?p=reactos.git;a=blob;f=dll/shellext/zipfldr/res/zipfldr.rgs;hb=032b5aacd233cd7b83ab6282aad638c161fdc400#l9
655 WCHAR szFileName[MAX_PATH];
656 LPWSTR pExtension;
657 BOOL hasName = _ILSimpleGetTextW(pidl, szFileName, _countof(szFileName));
658 dwShellAttributes |= SFGAO_STREAM;
659
660 // Vista+ feature: Hidden files with a leading tilde treated as super-hidden
661 // See https://devblogs.microsoft.com/oldnewthing/20170526-00/?p=96235
662 if (hasName && szFileName[0] == '~' && (dwFileAttributes & FILE_ATTRIBUTE_HIDDEN))
663 dwShellAttributes |= SFGAO_HIDDEN | SFGAO_SYSTEM;
664
665 if (hasName && (pExtension = PathFindExtensionW(szFileName)))
666 {
667 CLSID clsidFile;
668 // FIXME: Cache this?
669 HRESULT hr = GetCLSIDForFileTypeFromExtension(pExtension, L"CLSID", &clsidFile);
670 if (hr == S_OK)
671 {
672 HKEY hkey;
673 hr = SHRegGetCLSIDKeyW(clsidFile, L"ShellFolder", FALSE, FALSE, &hkey);
674 if (SUCCEEDED(hr))
675 {
676 DWORD dwAttributes = 0;
677 DWORD dwSize = sizeof(dwAttributes);
678 LSTATUS Status;
679
680 Status = SHRegGetValueW(hkey, NULL, L"Attributes", RRF_RT_REG_DWORD, NULL, &dwAttributes, &dwSize);
681 if (Status == STATUS_SUCCESS)
682 {
683 TRACE("Augmenting '%S' with dwAttributes=0x%x\n", szFileName, dwAttributes);
684 dwShellAttributes |= dwAttributes;
685 }
686 ::RegCloseKey(hkey);
687
688 // This should be presented as directory!
689 bDirectory = (dwAttributes & SFGAO_FOLDER) != 0 || dwAttributes == 0;
690 TRACE("Treating '%S' as directory!\n", szFileName);
691 }
692 }
693 }
694 }
695
696 // This is a directory
697 if (bDirectory)
698 {
699 dwShellAttributes |= (SFGAO_FOLDER | /*SFGAO_HASSUBFOLDER |*/ SFGAO_STORAGE);
700
701 // Is this a real directory?
702 if (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
703 {
704 dwShellAttributes |= (SFGAO_FILESYSANCESTOR | SFGAO_STORAGEANCESTOR);
705 }
706 }
707
708 if (dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
709 dwShellAttributes |= SFGAO_HIDDEN | SFGAO_GHOSTED;
710
711 if (dwFileAttributes & FILE_ATTRIBUTE_READONLY)
712 dwShellAttributes |= SFGAO_READONLY;
713
714 if (dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)
715 dwShellAttributes |= SFGAO_SYSTEM;
716
717 if (dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED)
718 dwShellAttributes |= SFGAO_COMPRESSED;
719
720 if (dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED)
721 dwShellAttributes |= SFGAO_ENCRYPTED;
722
723 if ((SFGAO_NONENUMERATED & *pdwAttributes) && (dwFileAttributes & FILE_ATTRIBUTE_HIDDEN))
724 {
725 SHCONTF shcf = SHELL_GetDefaultFolderEnumSHCONTF();
726 if ((!(shcf & SHCONTF_INCLUDEHIDDEN)) || ((dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) && !(shcf & SHCONTF_INCLUDESUPERHIDDEN)))
727 dwShellAttributes |= SFGAO_NONENUMERATED;
728 }
729
730 if (SFGAO_LINK & *pdwAttributes)
731 {
732 WCHAR ext[MAX_PATH];
733
734 if (_ILGetExtension(pidl, ext, _countof(ext)) && !lstrcmpiW(ext, L"lnk"))
735 dwShellAttributes |= SFGAO_LINK;
736 }
737
738 if (SFGAO_HASSUBFOLDER & *pdwAttributes)
739 {
740 CComPtr<IShellFolder> psf2;
741 if (SUCCEEDED(psf->BindToObject(pidl, 0, IID_PPV_ARG(IShellFolder, &psf2))))
742 {
743 CComPtr<IEnumIDList> pEnumIL;
744 if (SUCCEEDED(psf2->EnumObjects(0, SHCONTF_FOLDERS, &pEnumIL)))
745 {
746 if (pEnumIL->Skip(1) == S_OK)
747 dwShellAttributes |= SFGAO_HASSUBFOLDER;
748 }
749 }
750 }
751
752 *pdwAttributes = dwShellAttributes;
753
754 TRACE ("-- 0x%08x\n", *pdwAttributes);
755 return S_OK;
756 }
757
758 // This method is typically invoked from SHSimpleIDListFromPathA/W.
_ParseSimple(_In_ LPOLESTR lpszDisplayName,_Inout_ WIN32_FIND_DATAW * pFind,_Out_ LPITEMIDLIST * ppidl)759 HRESULT CFSFolder::_ParseSimple(
760 _In_ LPOLESTR lpszDisplayName,
761 _Inout_ WIN32_FIND_DATAW *pFind,
762 _Out_ LPITEMIDLIST *ppidl)
763 {
764 HRESULT hr;
765 LPWSTR pchNext = lpszDisplayName;
766
767 *ppidl = NULL;
768
769 const DWORD finalattr = pFind->dwFileAttributes;
770 const DWORD finalsizelo = pFind->nFileSizeLow;
771 LPITEMIDLIST pidl;
772 for (hr = S_OK; SUCCEEDED(hr); hr = SHILAppend(pidl, ppidl))
773 {
774 hr = Shell_NextElement(&pchNext, pFind->cFileName, _countof(pFind->cFileName), FALSE);
775 if (hr != S_OK)
776 break;
777
778 pFind->dwFileAttributes = pchNext ? FILE_ATTRIBUTE_DIRECTORY : finalattr;
779 pFind->nFileSizeLow = pchNext ? 0 : finalsizelo;
780 pidl = _ILCreateFromFindDataW(pFind);
781 if (!pidl)
782 {
783 hr = E_OUTOFMEMORY;
784 break;
785 }
786 }
787
788 if (SUCCEEDED(hr))
789 return S_OK;
790
791 if (*ppidl)
792 {
793 ILFree(*ppidl);
794 *ppidl = NULL;
795 }
796
797 return hr;
798 }
799
_GetFindDataFromName(_In_ LPCWSTR pszName,_Out_ WIN32_FIND_DATAW * pFind)800 BOOL CFSFolder::_GetFindDataFromName(_In_ LPCWSTR pszName, _Out_ WIN32_FIND_DATAW *pFind)
801 {
802 WCHAR szPath[MAX_PATH];
803 lstrcpynW(szPath, m_sPathTarget, _countof(szPath));
804 PathAppendW(szPath, pszName);
805
806 HANDLE hFind = ::FindFirstFileW(szPath, pFind);
807 if (hFind == INVALID_HANDLE_VALUE)
808 return FALSE;
809
810 ::FindClose(hFind);
811 return TRUE;
812 }
813
_CreateIDListFromName(LPCWSTR pszName,DWORD attrs,IBindCtx * pbc,LPITEMIDLIST * ppidl)814 HRESULT CFSFolder::_CreateIDListFromName(LPCWSTR pszName, DWORD attrs, IBindCtx *pbc, LPITEMIDLIST *ppidl)
815 {
816 *ppidl = NULL;
817
818 if (PathIsDosDevice(pszName))
819 return HRESULT_FROM_WIN32(ERROR_BAD_DEVICE);
820
821 WIN32_FIND_DATAW FindData = { 0 };
822
823 HRESULT hr = S_OK;
824 if (attrs == ULONG_MAX) // Invalid attributes
825 {
826 if (!_GetFindDataFromName(pszName, &FindData))
827 hr = HRESULT_FROM_WIN32(::GetLastError());
828 }
829 else // Pretend as an item of attrs
830 {
831 StringCchCopyW(FindData.cFileName, _countof(FindData.cFileName), pszName);
832 FindData.dwFileAttributes = attrs;
833 }
834
835 if (FAILED(hr))
836 return hr;
837
838 *ppidl = _ILCreateFromFindDataW(&FindData);
839 if (!*ppidl)
840 return E_OUTOFMEMORY;
841
842 return S_OK;
843 }
844
845 /**************************************************************************
846 * CFSFolder::ParseDisplayName {SHELL32}
847 *
848 * Parse a display name.
849 *
850 * PARAMS
851 * hwndOwner [in] Parent window for any message's
852 * pbc [in] optional FileSystemBindData context
853 * lpszDisplayName [in] Unicode displayname.
854 * pchEaten [out] (unicode) characters processed
855 * ppidl [out] complex pidl to item
856 * pdwAttributes [out] items attributes
857 *
858 * NOTES
859 * Every folder tries to parse only its own (the leftmost) pidl and creates a
860 * subfolder to evaluate the remaining parts.
861 * Now we can parse into namespaces implemented by shell extensions
862 *
863 * Behaviour on win98: lpszDisplayName=NULL -> crash
864 * lpszDisplayName="" -> returns mycoputer-pidl
865 *
866 * FIXME
867 * pdwAttributes is not set
868 * pchEaten is not set like in windows
869 */
ParseDisplayName(HWND hwndOwner,LPBC pbc,LPOLESTR lpszDisplayName,DWORD * pchEaten,PIDLIST_RELATIVE * ppidl,DWORD * pdwAttributes)870 HRESULT WINAPI CFSFolder::ParseDisplayName(HWND hwndOwner,
871 LPBC pbc,
872 LPOLESTR lpszDisplayName,
873 DWORD *pchEaten, PIDLIST_RELATIVE *ppidl,
874 DWORD *pdwAttributes)
875 {
876 TRACE ("(%p)->(HWND=%p,%p,%p=%s,%p,pidl=%p,%p)\n",
877 this, hwndOwner, pbc, lpszDisplayName, debugstr_w (lpszDisplayName),
878 pchEaten, ppidl, pdwAttributes);
879
880 if (!ppidl)
881 return E_INVALIDARG;
882
883 *ppidl = NULL;
884
885 if (!lpszDisplayName)
886 return E_INVALIDARG;
887
888 HRESULT hr;
889 WIN32_FIND_DATAW FindData;
890 if (SHIsFileSysBindCtx(pbc, &FindData) == S_OK)
891 {
892 CComHeapPtr<ITEMIDLIST> pidlTemp;
893 hr = _ParseSimple(lpszDisplayName, &FindData, &pidlTemp);
894 if (SUCCEEDED(hr) && pdwAttributes && *pdwAttributes)
895 {
896 LPCITEMIDLIST pidlLast = ILFindLastID(pidlTemp);
897 GetAttributesOf(1, &pidlLast, pdwAttributes);
898 }
899
900 if (SUCCEEDED(hr))
901 *ppidl = pidlTemp.Detach();
902 }
903 else
904 {
905 INT cchElement = lstrlenW(lpszDisplayName) + 1;
906 LPWSTR pszElement = (LPWSTR)_alloca(cchElement * sizeof(WCHAR));
907 LPWSTR pchNext = lpszDisplayName;
908 hr = Shell_NextElement(&pchNext, pszElement, cchElement, TRUE);
909 if (FAILED(hr))
910 return hr;
911
912 hr = _CreateIDListFromName(pszElement, ULONG_MAX, pbc, ppidl);
913 if (FAILED(hr))
914 {
915 if (pchNext) // Is there the next element?
916 {
917 // pszElement seems like a directory
918 if (_GetFindDataFromName(pszElement, &FindData) &&
919 (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
920 {
921 hr = _CreateIDListFromName(pszElement, FILE_ATTRIBUTE_DIRECTORY, pbc, ppidl);
922 }
923 }
924 else
925 {
926 // pszElement seems like a non-directory
927 if ((hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) ||
928 hr == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND)) &&
929 (BindCtx_GetMode(pbc, 0) & STGM_CREATE))
930 {
931 // Pretend like a normal file
932 hr = _CreateIDListFromName(pszElement, FILE_ATTRIBUTE_NORMAL, pbc, ppidl);
933 }
934 }
935 }
936
937 if (SUCCEEDED(hr))
938 {
939 if (pchNext) // Is there next?
940 {
941 CComPtr<IShellFolder> psfChild;
942 hr = BindToObject(*ppidl, pbc, IID_PPV_ARG(IShellFolder, &psfChild));
943 if (FAILED(hr))
944 return hr;
945
946 DWORD chEaten;
947 CComHeapPtr<ITEMIDLIST> pidlChild;
948 hr = psfChild->ParseDisplayName(hwndOwner, pbc, pchNext, &chEaten, &pidlChild,
949 pdwAttributes);
950
951 // Append pidlChild to ppidl
952 if (SUCCEEDED(hr))
953 hr = SHILAppend(pidlChild.Detach(), ppidl);
954 }
955 else if (pdwAttributes && *pdwAttributes)
956 {
957 GetAttributesOf(1, (LPCITEMIDLIST*)ppidl, pdwAttributes);
958 }
959 }
960 }
961
962 TRACE("(%p)->(-- pidl=%p ret=0x%08x)\n", this, ppidl ? *ppidl : 0, hr);
963
964 return hr;
965 }
966
967 /**************************************************************************
968 * CFSFolder::EnumObjects
969 * PARAMETERS
970 * HWND hwndOwner, //[in ] Parent Window
971 * DWORD grfFlags, //[in ] SHCONTF enumeration mask
972 * LPENUMIDLIST* ppenumIDList //[out] IEnumIDList interface
973 */
EnumObjects(HWND hwndOwner,DWORD dwFlags,LPENUMIDLIST * ppEnumIDList)974 HRESULT WINAPI CFSFolder::EnumObjects(
975 HWND hwndOwner,
976 DWORD dwFlags,
977 LPENUMIDLIST *ppEnumIDList)
978 {
979 return ShellObjectCreatorInit<CFileSysEnum>(m_sPathTarget, dwFlags, IID_PPV_ARG(IEnumIDList, ppEnumIDList));
980 }
981
982 /**************************************************************************
983 * CFSFolder::BindToObject
984 * PARAMETERS
985 * LPCITEMIDLIST pidl, //[in ] relative pidl to open
986 * LPBC pbc, //[in ] optional FileSystemBindData context
987 * REFIID riid, //[in ] Initial Interface
988 * LPVOID* ppvObject //[out] Interface*
989 */
BindToObject(PCUIDLIST_RELATIVE pidl,LPBC pbc,REFIID riid,LPVOID * ppvOut)990 HRESULT WINAPI CFSFolder::BindToObject(
991 PCUIDLIST_RELATIVE pidl,
992 LPBC pbc,
993 REFIID riid,
994 LPVOID * ppvOut)
995 {
996 TRACE("(%p)->(pidl=%p,%p,%s,%p)\n", this, pidl, pbc,
997 shdebugstr_guid(&riid), ppvOut);
998
999 CComPtr<IShellFolder> pSF;
1000 HRESULT hr;
1001
1002 if (!m_pidlRoot || !ppvOut || !pidl || !pidl->mkid.cb)
1003 {
1004 ERR("CFSFolder::BindToObject: Invalid parameters\n");
1005 return E_INVALIDARG;
1006 }
1007
1008 /* Get the pidl data */
1009 FileStruct* pData = &_ILGetDataPointer(pidl)->u.file;
1010 WCHAR szNameBuf[MAX_PATH];
1011 LPCWSTR pszName = GetItemFileName(pidl, szNameBuf, _countof(szNameBuf));
1012 if (!pszName)
1013 {
1014 ERR("CFSFolder::BindToObject: Invalid pidl!\n");
1015 return E_INVALIDARG;
1016 }
1017
1018 *ppvOut = NULL;
1019
1020 /* Create the target folder info */
1021 PERSIST_FOLDER_TARGET_INFO pfti = {0};
1022 pfti.dwAttributes = -1;
1023 pfti.csidl = -1;
1024 PathCombineW(pfti.szTargetParsingName, m_sPathTarget, pszName);
1025
1026 /* Get the CLSID to bind to */
1027 CLSID clsidFolder;
1028 if (_ILIsFolder(pidl))
1029 {
1030 if ((pData->uFileAttribs & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)) != 0)
1031 {
1032 hr = SHELL32_GetCLSIDForDirectory(pfti.szTargetParsingName, L"CLSID", &clsidFolder);
1033
1034 if (SUCCEEDED(hr))
1035 {
1036 /* We got a GUID from a desktop.ini, let's try it */
1037 hr = SHELL32_BindToSF(m_pidlRoot, &pfti, pidl, &clsidFolder, riid, ppvOut);
1038 if (SUCCEEDED(hr))
1039 {
1040 TRACE("-- returning (%p) %08x, (%s)\n", *ppvOut, hr, wine_dbgstr_guid(&clsidFolder));
1041 return hr;
1042 }
1043
1044 /* Something went wrong, re-try it with a normal ShellFSFolder */
1045 ERR("CFSFolder::BindToObject: %s failed to bind, using fallback (0x%08x)\n", wine_dbgstr_guid(&clsidFolder), hr);
1046 }
1047 }
1048 /* No system folder or the custom class failed */
1049 clsidFolder = CLSID_ShellFSFolder;
1050 }
1051 else
1052 {
1053 hr = GetCLSIDForFileType(pidl, L"CLSID", &clsidFolder);
1054 if (hr == S_FALSE)
1055 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
1056 if (hr != S_OK)
1057 return hr;
1058 }
1059
1060 hr = SHELL32_BindToSF(m_pidlRoot, &pfti, pidl, &clsidFolder, riid, ppvOut);
1061 if (FAILED_UNEXPECTEDLY(hr))
1062 return hr;
1063
1064 TRACE ("-- returning (%p) %08x\n", *ppvOut, hr);
1065
1066 return S_OK;
1067
1068 }
1069
1070 /**************************************************************************
1071 * CFSFolder::BindToStorage
1072 * PARAMETERS
1073 * LPCITEMIDLIST pidl, //[in ] complex pidl to store
1074 * LPBC pbc, //[in ] reserved
1075 * REFIID riid, //[in ] Initial storage interface
1076 * LPVOID* ppvObject //[out] Interface* returned
1077 */
BindToStorage(PCUIDLIST_RELATIVE pidl,LPBC pbcReserved,REFIID riid,LPVOID * ppvOut)1078 HRESULT WINAPI CFSFolder::BindToStorage(
1079 PCUIDLIST_RELATIVE pidl,
1080 LPBC pbcReserved,
1081 REFIID riid,
1082 LPVOID *ppvOut)
1083 {
1084 FIXME("(%p)->(pidl=%p,%p,%s,%p) stub\n", this, pidl, pbcReserved,
1085 shdebugstr_guid (&riid), ppvOut);
1086
1087 *ppvOut = NULL;
1088 return E_NOTIMPL;
1089 }
1090
CompareSortFoldersFirst(LPCITEMIDLIST pidl1,LPCITEMIDLIST pidl2)1091 HRESULT CFSFolder::CompareSortFoldersFirst(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
1092 {
1093 BOOL bIsFolder1 = _ILIsFolder(pidl1), bIsFolder2 = _ILIsFolder(pidl2);
1094 // When sorting between a File and a Folder, the Folder gets sorted first
1095 if (bIsFolder1 != bIsFolder2)
1096 {
1097 // ...but only if neither of them were generated by SHSimpleIDListFromPath
1098 // because in that case we cannot tell if it's a file or a folder.
1099 if (pidl1 && IsRealItem(*pidl1) && pidl2 && IsRealItem(*pidl2))
1100 return MAKE_COMPARE_HRESULT(bIsFolder1 ? -1 : 1);
1101 }
1102 return MAKE_SCODE(SEVERITY_ERROR, FACILITY_SHELL, S_EQUAL);
1103 }
1104
1105 /**************************************************************************
1106 * CFSFolder::CompareIDs
1107 */
1108
CompareIDs(LPARAM lParam,PCUIDLIST_RELATIVE pidl1,PCUIDLIST_RELATIVE pidl2)1109 HRESULT WINAPI CFSFolder::CompareIDs(LPARAM lParam,
1110 PCUIDLIST_RELATIVE pidl1,
1111 PCUIDLIST_RELATIVE pidl2)
1112 {
1113 WCHAR szNameBuf1[MAX_PATH], szNameBuf2[_countof(szNameBuf1)];
1114 LPCWSTR pszName1 = GetItemFileName(pidl1, szNameBuf1, _countof(szNameBuf1));
1115 LPCWSTR pszName2 = GetItemFileName(pidl2, szNameBuf2, _countof(szNameBuf2));
1116 if (!pszName1 || !pszName2 || LOWORD(lParam) >= GENERICSHELLVIEWCOLUMNS)
1117 return E_INVALIDARG;
1118
1119 LPPIDLDATA pData1 = _ILGetDataPointer(pidl1);
1120 LPPIDLDATA pData2 = _ILGetDataPointer(pidl2);
1121 LPWSTR pExtension1, pExtension2;
1122
1123 HRESULT hr = CompareSortFoldersFirst(pidl1, pidl2);
1124 if (SUCCEEDED(hr))
1125 return hr;
1126 int result = 0;
1127 switch (LOWORD(lParam))
1128 {
1129 case SHFSF_COL_NAME:
1130 result = _wcsicmp(pszName1, pszName2);
1131 break;
1132 case SHFSF_COL_SIZE:
1133 if (pData1->u.file.dwFileSize > pData2->u.file.dwFileSize)
1134 result = 1;
1135 else if (pData1->u.file.dwFileSize < pData2->u.file.dwFileSize)
1136 result = -1;
1137 else
1138 result = 0;
1139 break;
1140 case SHFSF_COL_TYPE:
1141 // FIXME: Compare the type strings from SHGetFileInfo
1142 pExtension1 = PathFindExtensionW(pszName1);
1143 pExtension2 = PathFindExtensionW(pszName2);
1144 result = _wcsicmp(pExtension1, pExtension2);
1145 break;
1146 case SHFSF_COL_MDATE:
1147 result = pData1->u.file.uFileDate - pData2->u.file.uFileDate;
1148 if (result == 0)
1149 result = pData1->u.file.uFileTime - pData2->u.file.uFileTime;
1150 break;
1151 case SHFSF_COL_FATTS:
1152 return SHELL32_CompareDetails(this, lParam, pidl1, pidl2);
1153 case SHFSF_COL_COMMENT:
1154 result = 0;
1155 break;
1156 default:
1157 if (_ILIsPidlSimple(pidl1) || _ILIsPidlSimple(pidl2))
1158 ERR("Unknown column %u, can't compare\n", LOWORD(lParam));
1159 else
1160 TRACE("Unknown column %u, deferring to the subfolder\n", LOWORD(lParam));
1161 }
1162
1163 if (result == 0)
1164 return SHELL32_CompareChildren(this, lParam, pidl1, pidl2);
1165
1166 return MAKE_COMPARE_HRESULT(result);
1167 }
1168
1169 /**************************************************************************
1170 * CFSFolder::CreateViewObject
1171 */
CreateViewObject(HWND hwndOwner,REFIID riid,LPVOID * ppvOut)1172 HRESULT WINAPI CFSFolder::CreateViewObject(HWND hwndOwner,
1173 REFIID riid, LPVOID * ppvOut)
1174 {
1175 CComPtr<IShellView> pShellView;
1176 HRESULT hr = E_INVALIDARG;
1177
1178 TRACE ("(%p)->(hwnd=%p,%s,%p)\n", this, hwndOwner, shdebugstr_guid (&riid),
1179 ppvOut);
1180
1181 if (ppvOut)
1182 {
1183 *ppvOut = NULL;
1184
1185 BOOL bIsDropTarget = IsEqualIID (riid, IID_IDropTarget);
1186 BOOL bIsShellView = !bIsDropTarget && IsEqualIID (riid, IID_IShellView);
1187
1188 if (bIsDropTarget || bIsShellView)
1189 {
1190 DWORD dwDirAttributes = _ILGetFileAttributes(ILFindLastID(m_pidlRoot), NULL, 0);
1191
1192 if ((dwDirAttributes & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)) != 0)
1193 {
1194 CLSID clsidFolder;
1195 hr = SHELL32_GetCLSIDForDirectory(m_sPathTarget, L"UICLSID", &clsidFolder);
1196 if (SUCCEEDED(hr))
1197 {
1198 CComPtr<IPersistFolder> spFolder;
1199 hr = SHCoCreateInstance(NULL, &clsidFolder, NULL, IID_PPV_ARG(IPersistFolder, &spFolder));
1200 if (!FAILED_UNEXPECTEDLY(hr))
1201 {
1202 hr = spFolder->Initialize(m_pidlRoot);
1203
1204 if (!FAILED_UNEXPECTEDLY(hr))
1205 {
1206 hr = spFolder->QueryInterface(riid, ppvOut);
1207 }
1208 }
1209 }
1210 else
1211 {
1212 // No desktop.ini, or no UICLSID present, continue as if nothing happened
1213 hr = E_INVALIDARG;
1214 }
1215 }
1216 }
1217
1218 if (!SUCCEEDED(hr))
1219 {
1220 // No UICLSID handler found, continue to the default handlers
1221 if (bIsDropTarget)
1222 {
1223 hr = CFSDropTarget_CreateInstance(m_sPathTarget, riid, ppvOut);
1224 }
1225 else if (IsEqualIID (riid, IID_IContextMenu))
1226 {
1227 HKEY hKeys[16];
1228 UINT cKeys = 0;
1229 AddClassKeyToArray(L"Directory\\Background", hKeys, &cKeys);
1230
1231 DEFCONTEXTMENU dcm;
1232 dcm.hwnd = hwndOwner;
1233 dcm.pcmcb = this;
1234 dcm.pidlFolder = m_pidlRoot;
1235 dcm.psf = this;
1236 dcm.cidl = 0;
1237 dcm.apidl = NULL;
1238 dcm.cKeys = cKeys;
1239 dcm.aKeys = hKeys;
1240 dcm.punkAssociationInfo = NULL;
1241 hr = SHCreateDefaultContextMenu (&dcm, riid, ppvOut);
1242 }
1243 else if (bIsShellView)
1244 {
1245 SFV_CREATE sfvparams = {sizeof(SFV_CREATE), this, NULL, this};
1246 hr = SHCreateShellFolderView(&sfvparams, (IShellView**)ppvOut);
1247 }
1248 else
1249 {
1250 hr = E_INVALIDARG;
1251 }
1252 }
1253 }
1254 TRACE("-- (%p)->(interface=%p)\n", this, ppvOut);
1255 return hr;
1256 }
1257
1258 /**************************************************************************
1259 * CFSFolder::GetAttributesOf
1260 *
1261 * PARAMETERS
1262 * UINT cidl, //[in ] num elements in pidl array
1263 * LPCITEMIDLIST* apidl, //[in ] simple pidl array
1264 * ULONG* rgfInOut) //[out] result array
1265 *
1266 */
GetAttributesOf(UINT cidl,PCUITEMID_CHILD_ARRAY apidl,DWORD * rgfInOut)1267 HRESULT WINAPI CFSFolder::GetAttributesOf(UINT cidl,
1268 PCUITEMID_CHILD_ARRAY apidl, DWORD * rgfInOut)
1269 {
1270 HRESULT hr = S_OK;
1271
1272 if (!rgfInOut)
1273 return E_INVALIDARG;
1274 if (cidl && !apidl)
1275 return E_INVALIDARG;
1276
1277 if (*rgfInOut == 0)
1278 *rgfInOut = ~0;
1279
1280 if(cidl == 0)
1281 {
1282 LPCITEMIDLIST rpidl = ILFindLastID(m_pidlRoot);
1283
1284 if (_ILIsFolderOrFile(rpidl))
1285 {
1286 SHELL32_GetFSItemAttributes(this, rpidl, rgfInOut);
1287 }
1288 else if (_ILIsDrive(rpidl))
1289 {
1290 IShellFolder *psfParent = NULL;
1291 hr = SHBindToParent(m_pidlRoot, IID_PPV_ARG(IShellFolder, &psfParent), NULL);
1292 if(SUCCEEDED(hr))
1293 {
1294 hr = psfParent->GetAttributesOf(1, &rpidl, (SFGAOF*)rgfInOut);
1295 psfParent->Release();
1296 }
1297 }
1298 else
1299 {
1300 ERR("Got and unknown pidl!\n");
1301 }
1302 }
1303 else
1304 {
1305 while (cidl > 0 && *apidl)
1306 {
1307 pdump(*apidl);
1308 if (_ILIsFolderOrFile(*apidl))
1309 SHELL32_GetFSItemAttributes(this, *apidl, rgfInOut);
1310 else
1311 ERR("Got an unknown type of pidl!!!\n");
1312 apidl++;
1313 cidl--;
1314 }
1315 }
1316 /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */
1317 *rgfInOut &= ~SFGAO_VALIDATE;
1318
1319 TRACE("-- result=0x%08x\n", *rgfInOut);
1320
1321 return hr;
1322 }
1323
1324 /**************************************************************************
1325 * CFSFolder::GetUIObjectOf
1326 *
1327 * PARAMETERS
1328 * HWND hwndOwner, //[in ] Parent window for any output
1329 * UINT cidl, //[in ] array size
1330 * LPCITEMIDLIST* apidl, //[in ] simple pidl array
1331 * REFIID riid, //[in ] Requested Interface
1332 * UINT* prgfInOut, //[ ] reserved
1333 * LPVOID* ppvObject) //[out] Resulting Interface
1334 *
1335 * NOTES
1336 * This function gets asked to return "view objects" for one or more (multiple
1337 * select) items:
1338 * The viewobject typically is an COM object with one of the following
1339 * interfaces:
1340 * IExtractIcon,IDataObject,IContextMenu
1341 * In order to support icon positions in the default Listview your DataObject
1342 * must implement the SetData method (in addition to GetData :) - the shell
1343 * passes a barely documented "Icon positions" structure to SetData when the
1344 * drag starts, and GetData's it if the drop is in another explorer window that
1345 * needs the positions.
1346 */
GetUIObjectOf(HWND hwndOwner,UINT cidl,PCUITEMID_CHILD_ARRAY apidl,REFIID riid,UINT * prgfInOut,LPVOID * ppvOut)1347 HRESULT WINAPI CFSFolder::GetUIObjectOf(HWND hwndOwner,
1348 UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
1349 REFIID riid, UINT * prgfInOut,
1350 LPVOID * ppvOut)
1351 {
1352 LPVOID pObj = NULL;
1353 HRESULT hr = E_INVALIDARG;
1354
1355 TRACE ("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n",
1356 this, hwndOwner, cidl, apidl, shdebugstr_guid (&riid), prgfInOut, ppvOut);
1357
1358 if (ppvOut)
1359 {
1360 *ppvOut = NULL;
1361
1362 if (cidl == 1 && _ILIsValue(apidl[0]))
1363 {
1364 hr = _CreateExtensionUIObject(apidl[0], riid, ppvOut);
1365 if(hr != S_FALSE)
1366 return hr;
1367 }
1368
1369 if (IsEqualIID(riid, IID_IContextMenu) && (cidl >= 1))
1370 {
1371 HKEY hKeys[16];
1372 UINT cKeys = 0;
1373 AddFSClassKeysToArray(cidl, apidl, hKeys, &cKeys);
1374
1375 DEFCONTEXTMENU dcm;
1376 dcm.hwnd = hwndOwner;
1377 dcm.pcmcb = this;
1378 dcm.pidlFolder = m_pidlRoot;
1379 dcm.psf = this;
1380 dcm.cidl = cidl;
1381 dcm.apidl = apidl;
1382 dcm.cKeys = cKeys;
1383 dcm.aKeys = hKeys;
1384 dcm.punkAssociationInfo = NULL;
1385 hr = SHCreateDefaultContextMenu (&dcm, riid, &pObj);
1386 }
1387 else if (IsEqualIID (riid, IID_IDataObject))
1388 {
1389 if (cidl >= 1)
1390 {
1391 hr = IDataObject_Constructor (hwndOwner, m_pidlRoot, apidl, cidl, TRUE, (IDataObject **)&pObj);
1392 }
1393 else
1394 {
1395 hr = E_INVALIDARG;
1396 }
1397 }
1398 else if ((IsEqualIID (riid, IID_IExtractIconA) || IsEqualIID (riid, IID_IExtractIconW)) && (cidl == 1))
1399 {
1400 if (_ILIsValue(apidl[0]))
1401 hr = _GetIconHandler(apidl[0], riid, (LPVOID*)&pObj);
1402 if (hr != S_OK)
1403 hr = CFSExtractIcon_CreateInstance(this, apidl[0], riid, &pObj);
1404 }
1405 else if (IsEqualIID (riid, IID_IDropTarget))
1406 {
1407 /* only interested in attempting to bind to shell folders, not files (except exe), so if we fail, rebind to root */
1408 if (cidl != 1 || FAILED(hr = this->_GetDropTarget(apidl[0], (LPVOID*) &pObj)))
1409 {
1410 hr = CFSDropTarget_CreateInstance(m_sPathTarget, riid, (LPVOID*) &pObj);
1411 }
1412 }
1413 else
1414 hr = E_NOINTERFACE;
1415
1416 if (SUCCEEDED(hr) && !pObj)
1417 hr = E_OUTOFMEMORY;
1418
1419 *ppvOut = pObj;
1420 }
1421 TRACE("(%p)->hr=0x%08x\n", this, hr);
1422 return hr;
1423 }
1424
1425 /******************************************************************************
1426 * SHELL_FS_HideExtension [Internal]
1427 *
1428 * Query the registry if the filename extension of a given path should be
1429 * hidden.
1430 *
1431 * PARAMS
1432 * szPath [I] Relative or absolute path of a file
1433 *
1434 * RETURNS
1435 * TRUE, if the filename's extension should be hidden
1436 * FALSE, otherwise.
1437 */
SHELL_FS_HideExtension(LPCWSTR szPath)1438 BOOL SHELL_FS_HideExtension(LPCWSTR szPath)
1439 {
1440 HKEY hKey;
1441 BOOL doHide = FALSE; /* The default value is FALSE (win98 at least) */
1442 LONG lError;
1443
1444 doHide = !SHELL_GetSetting(SSF_SHOWEXTENSIONS, fShowExtensions);
1445
1446 if (!doHide)
1447 {
1448 LPCWSTR DotExt = PathFindExtensionW(szPath);
1449 if (*DotExt != 0)
1450 {
1451 WCHAR classname[MAX_PATH];
1452 LONG classlen = sizeof(classname);
1453 lError = RegQueryValueW(HKEY_CLASSES_ROOT, DotExt, classname, &classlen);
1454 if (lError == ERROR_SUCCESS)
1455 {
1456 lError = RegOpenKeyW(HKEY_CLASSES_ROOT, classname, &hKey);
1457 if (lError == ERROR_SUCCESS)
1458 {
1459 lError = RegQueryValueExW(hKey, L"NeverShowExt", NULL, NULL, NULL, NULL);
1460 if (lError == ERROR_SUCCESS)
1461 doHide = TRUE;
1462
1463 RegCloseKey(hKey);
1464 }
1465 }
1466 }
1467 }
1468
1469 return doHide;
1470 }
1471
SHELL_FS_ProcessDisplayFilename(LPWSTR szPath,DWORD dwFlags)1472 void SHELL_FS_ProcessDisplayFilename(LPWSTR szPath, DWORD dwFlags)
1473 {
1474 /*FIXME: MSDN also mentions SHGDN_FOREDITING which is not yet handled. */
1475 if (!(dwFlags & SHGDN_FORPARSING) &&
1476 ((dwFlags & SHGDN_INFOLDER) || (dwFlags == SHGDN_NORMAL))) {
1477 if (SHELL_FS_HideExtension(szPath) && szPath[0] != '.')
1478 PathRemoveExtensionW(szPath);
1479 }
1480 }
1481
1482 /**************************************************************************
1483 * CFSFolder::GetDisplayNameOf
1484 * Retrieves the display name for the specified file object or subfolder
1485 *
1486 * PARAMETERS
1487 * LPCITEMIDLIST pidl, //[in ] complex pidl to item
1488 * DWORD dwFlags, //[in ] SHGNO formatting flags
1489 * LPSTRRET lpName) //[out] Returned display name
1490 *
1491 * FIXME
1492 * if the name is in the pidl the ret value should be a STRRET_OFFSET
1493 */
1494
GetDisplayNameOf(PCUITEMID_CHILD pidl,DWORD dwFlags,LPSTRRET strRet)1495 HRESULT WINAPI CFSFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl,
1496 DWORD dwFlags, LPSTRRET strRet)
1497 {
1498 if (!strRet)
1499 return E_INVALIDARG;
1500
1501 /* If it is a complex pidl, let the child handle it */
1502 if (!_ILIsPidlSimple (pidl)) /* complex pidl */
1503 {
1504 return SHELL32_GetDisplayNameOfChild(this, pidl, dwFlags, strRet);
1505 }
1506 else if (pidl && !pidl->mkid.cb) /* empty pidl */
1507 {
1508 /* If it is an empty pidl return only the path of the folder */
1509 if ((GET_SHGDN_FOR(dwFlags) & SHGDN_FORPARSING) &&
1510 (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER) &&
1511 m_sPathTarget)
1512 {
1513 return SHSetStrRet(strRet, m_sPathTarget);
1514 }
1515 return E_INVALIDARG;
1516 }
1517
1518 int len = 0;
1519 LPWSTR pszPath = (LPWSTR)CoTaskMemAlloc((MAX_PATH + 1) * sizeof(WCHAR));
1520 if (!pszPath)
1521 return E_OUTOFMEMORY;
1522
1523 if ((GET_SHGDN_FOR(dwFlags) & SHGDN_FORPARSING) &&
1524 (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER) &&
1525 m_sPathTarget)
1526 {
1527 lstrcpynW(pszPath, m_sPathTarget, MAX_PATH);
1528 PathAddBackslashW(pszPath);
1529 len = wcslen(pszPath);
1530 }
1531 _ILSimpleGetTextW(pidl, pszPath + len, MAX_PATH + 1 - len);
1532 if (!_ILIsFolder(pidl)) SHELL_FS_ProcessDisplayFilename(pszPath, dwFlags);
1533
1534 strRet->uType = STRRET_WSTR;
1535 strRet->pOleStr = pszPath;
1536
1537 TRACE ("-- (%p)->(%s)\n", this, strRet->uType == STRRET_CSTR ? strRet->cStr : debugstr_w(strRet->pOleStr));
1538 return S_OK;
1539 }
1540
1541 /**************************************************************************
1542 * CFSFolder::SetNameOf
1543 * Changes the name of a file object or subfolder, possibly changing its item
1544 * identifier in the process.
1545 *
1546 * PARAMETERS
1547 * HWND hwndOwner, //[in ] Owner window for output
1548 * LPCITEMIDLIST pidl, //[in ] simple pidl of item to change
1549 * LPCOLESTR lpszName, //[in ] the items new display name
1550 * DWORD dwFlags, //[in ] SHGNO formatting flags
1551 * LPITEMIDLIST* ppidlOut) //[out] simple pidl returned
1552 */
SetNameOf(HWND hwndOwner,PCUITEMID_CHILD pidl,LPCOLESTR lpName,DWORD dwFlags,PITEMID_CHILD * pPidlOut)1553 HRESULT WINAPI CFSFolder::SetNameOf(
1554 HWND hwndOwner,
1555 PCUITEMID_CHILD pidl,
1556 LPCOLESTR lpName,
1557 DWORD dwFlags,
1558 PITEMID_CHILD *pPidlOut)
1559 {
1560 WCHAR szSrc[MAX_PATH + 1], szDest[MAX_PATH + 1], szNameBuf[MAX_PATH];
1561 BOOL bIsFolder = _ILIsFolder (ILFindLastID (pidl));
1562
1563 TRACE ("(%p)->(%p,pidl=%p,%s,%u,%p)\n", this, hwndOwner, pidl,
1564 debugstr_w (lpName), dwFlags, pPidlOut);
1565
1566 LPCWSTR pszName = GetItemFileName(pidl, szNameBuf, _countof(szNameBuf));
1567 if (!pszName)
1568 {
1569 ERR("Got garbage pidl:\n");
1570 pdump_always(pidl);
1571 return E_INVALIDARG;
1572 }
1573
1574 /* build source path */
1575 PathCombineW(szSrc, m_sPathTarget, pszName);
1576
1577 /* build destination path */
1578 if (dwFlags == SHGDN_NORMAL || dwFlags & SHGDN_INFOLDER)
1579 PathCombineW(szDest, m_sPathTarget, lpName);
1580 else
1581 lstrcpynW(szDest, lpName, MAX_PATH);
1582
1583 if (!(dwFlags & SHGDN_FORPARSING) && !bIsFolder && SHELL_FS_HideExtension(szSrc))
1584 {
1585 LPCWSTR ext = PathFindExtensionW(szSrc);
1586 if (*ext)
1587 PathAddExtensionW(szDest, ext);
1588 }
1589
1590 HRESULT hr = S_OK;
1591 TRACE ("src=%s dest=%s\n", debugstr_w(szSrc), debugstr_w(szDest));
1592 if (!wcscmp(szSrc, szDest))
1593 {
1594 /* src and destination is the same */
1595 if (pPidlOut)
1596 hr = SHILClone(pidl, pPidlOut);
1597 }
1598 else if (MoveFileW(szSrc, szDest))
1599 {
1600 if (pPidlOut)
1601 hr = ParseDisplayName(hwndOwner, NULL, PathFindFileNameW(szDest), NULL, pPidlOut, NULL);
1602
1603 SHChangeNotify(bIsFolder ? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM, SHCNF_PATHW, szSrc, szDest);
1604 }
1605 else
1606 {
1607 hr = HResultFromWin32(GetLastError());
1608 }
1609 return hr;
1610 }
1611
GetDefaultSearchGUID(GUID * pguid)1612 HRESULT WINAPI CFSFolder::GetDefaultSearchGUID(GUID * pguid)
1613 {
1614 FIXME ("(%p)\n", this);
1615 return E_NOTIMPL;
1616 }
1617
EnumSearches(IEnumExtraSearch ** ppenum)1618 HRESULT WINAPI CFSFolder::EnumSearches(IEnumExtraSearch ** ppenum)
1619 {
1620 FIXME ("(%p)\n", this);
1621 return E_NOTIMPL;
1622 }
1623
GetDefaultColumn(DWORD dwRes,ULONG * pSort,ULONG * pDisplay)1624 HRESULT WINAPI CFSFolder::GetDefaultColumn(DWORD dwRes,
1625 ULONG * pSort, ULONG * pDisplay)
1626 {
1627 TRACE ("(%p)\n", this);
1628
1629 if (pSort)
1630 *pSort = 0;
1631 if (pDisplay)
1632 *pDisplay = 0;
1633
1634 return S_OK;
1635 }
1636
GetDefaultColumnState(UINT iColumn,SHCOLSTATEF * pcsFlags)1637 HRESULT WINAPI CFSFolder::GetDefaultColumnState(UINT iColumn,
1638 SHCOLSTATEF *pcsFlags)
1639 {
1640 TRACE ("(%p)\n", this);
1641
1642 if (!pcsFlags)
1643 return E_INVALIDARG;
1644 else
1645 return GetDefaultFSColumnState(iColumn, *pcsFlags);
1646 }
1647
GetDetailsEx(PCUITEMID_CHILD pidl,const SHCOLUMNID * pscid,VARIANT * pv)1648 HRESULT WINAPI CFSFolder::GetDetailsEx(PCUITEMID_CHILD pidl,
1649 const SHCOLUMNID * pscid, VARIANT * pv)
1650 {
1651 FIXME ("(%p)\n", this);
1652
1653 return E_NOTIMPL;
1654 }
1655
GetDetailsOf(PCUITEMID_CHILD pidl,UINT iColumn,SHELLDETAILS * psd)1656 HRESULT WINAPI CFSFolder::GetDetailsOf(PCUITEMID_CHILD pidl,
1657 UINT iColumn, SHELLDETAILS * psd)
1658 {
1659 HRESULT hr = E_FAIL;
1660
1661 TRACE ("(%p)->(%p %i %p)\n", this, pidl, iColumn, psd);
1662
1663 if (!psd || iColumn >= GENERICSHELLVIEWCOLUMNS)
1664 return E_INVALIDARG;
1665
1666 if (!pidl)
1667 {
1668 /* the header titles */
1669 return GetFSColumnDetails(iColumn, *psd);
1670 }
1671 else
1672 {
1673 FILETIME ft;
1674 hr = S_OK;
1675 psd->str.uType = STRRET_WSTR;
1676 if (iColumn != SHFSF_COL_NAME)
1677 {
1678 psd->str.pOleStr = (LPWSTR)CoTaskMemAlloc(MAX_PATH * sizeof(WCHAR));
1679 if (!psd->str.pOleStr)
1680 return E_OUTOFMEMORY;
1681 }
1682 /* the data from the pidl */
1683 switch (iColumn)
1684 {
1685 case SHFSF_COL_NAME:
1686 hr = GetDisplayNameOf(pidl, SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str);
1687 break;
1688 case SHFSF_COL_SIZE:
1689 _ILGetFileSize(pidl, psd->str.pOleStr, MAX_PATH);
1690 break;
1691 case SHFSF_COL_TYPE:
1692 GetItemDescription(pidl, psd->str.pOleStr, MAX_PATH);
1693 break;
1694 case SHFSF_COL_MDATE:
1695 if (!_ILGetFileDateTime(pidl, &ft) || FAILED(FormatDateTime(ft, psd->str.pOleStr, MAX_PATH)))
1696 {
1697 *psd->str.pOleStr = UNICODE_NULL;
1698 hr = S_FALSE;
1699 }
1700 break;
1701 case SHFSF_COL_FATTS:
1702 _ILGetFileAttributes(pidl, psd->str.pOleStr, MAX_PATH);
1703 break;
1704 case SHFSF_COL_COMMENT:
1705 psd->str.pOleStr[0] = UNICODE_NULL; // TODO: Extract comment from .lnk files? desktop.ini?
1706 break;
1707 #if DBG
1708 default:
1709 ERR("Missing case for column %d\n", iColumn);
1710 #else
1711 DEFAULT_UNREACHABLE;
1712 #endif
1713 }
1714 }
1715
1716 return hr;
1717 }
1718
MapColumnToSCID(UINT column,SHCOLUMNID * pscid)1719 HRESULT WINAPI CFSFolder::MapColumnToSCID (UINT column,
1720 SHCOLUMNID * pscid)
1721 {
1722 FIXME ("(%p)\n", this);
1723 return E_NOTIMPL;
1724 }
1725
1726 /************************************************************************
1727 * CFSFolder::GetClassID
1728 */
GetClassID(CLSID * lpClassId)1729 HRESULT WINAPI CFSFolder::GetClassID(CLSID * lpClassId)
1730 {
1731 TRACE ("(%p)\n", this);
1732
1733 if (!lpClassId)
1734 return E_POINTER;
1735
1736 *lpClassId = *m_pclsid;
1737
1738 return S_OK;
1739 }
1740
1741 /************************************************************************
1742 * CFSFolder::Initialize
1743 *
1744 * NOTES
1745 * m_sPathTarget is not set. Don't know how to handle in a non rooted environment.
1746 */
Initialize(PCIDLIST_ABSOLUTE pidl)1747 HRESULT WINAPI CFSFolder::Initialize(PCIDLIST_ABSOLUTE pidl)
1748 {
1749 WCHAR wszTemp[MAX_PATH];
1750
1751 TRACE ("(%p)->(%p)\n", this, pidl);
1752
1753 SHFree(m_pidlRoot); /* free the old pidl */
1754 m_pidlRoot = ILClone (pidl); /* set my pidl */
1755
1756 SHFree (m_sPathTarget);
1757 m_sPathTarget = NULL;
1758
1759 /* set my path */
1760 HRESULT hr = E_FAIL;
1761 if (SHGetPathFromIDListW (pidl, wszTemp))
1762 hr = SHStrDupW(wszTemp, &m_sPathTarget);
1763
1764 TRACE ("--(%p)->(%s)\n", this, debugstr_w(m_sPathTarget));
1765 return hr;
1766 }
1767
1768 /**************************************************************************
1769 * CFSFolder::GetCurFolder
1770 */
GetCurFolder(PIDLIST_ABSOLUTE * pidl)1771 HRESULT WINAPI CFSFolder::GetCurFolder(PIDLIST_ABSOLUTE * pidl)
1772 {
1773 TRACE ("(%p)->(%p)\n", this, pidl);
1774
1775 if (!pidl)
1776 return E_POINTER;
1777
1778 *pidl = ILClone(m_pidlRoot);
1779 return S_OK;
1780 }
1781
1782 /**************************************************************************
1783 * CFSFolder::InitializeEx
1784 *
1785 * FIXME: error handling
1786 */
InitializeEx(IBindCtx * pbc,LPCITEMIDLIST pidlRootx,const PERSIST_FOLDER_TARGET_INFO * ppfti)1787 HRESULT WINAPI CFSFolder::InitializeEx(IBindCtx * pbc, LPCITEMIDLIST pidlRootx,
1788 const PERSIST_FOLDER_TARGET_INFO * ppfti)
1789 {
1790 WCHAR wszTemp[MAX_PATH];
1791
1792 TRACE("(%p)->(%p,%p,%p)\n", this, pbc, pidlRootx, ppfti);
1793 if (ppfti)
1794 TRACE("--%p %s %s 0x%08x 0x%08x\n",
1795 ppfti->pidlTargetFolder, debugstr_w (ppfti->szTargetParsingName),
1796 debugstr_w (ppfti->szNetworkProvider), ppfti->dwAttributes,
1797 ppfti->csidl);
1798
1799 pdump (pidlRootx);
1800 if (ppfti && ppfti->pidlTargetFolder)
1801 pdump(ppfti->pidlTargetFolder);
1802
1803 if (m_pidlRoot)
1804 __SHFreeAndNil(&m_pidlRoot); /* free the old */
1805 if (m_sPathTarget)
1806 __SHFreeAndNil(&m_sPathTarget);
1807
1808 /*
1809 * Root path and pidl
1810 */
1811 m_pidlRoot = ILClone(pidlRootx);
1812
1813 /*
1814 * the target folder is spezified in csidl OR pidlTargetFolder OR
1815 * szTargetParsingName
1816 */
1817 HRESULT hr = E_FAIL;
1818 if (ppfti)
1819 {
1820 if (ppfti->csidl != -1)
1821 {
1822 BOOL create = ppfti->csidl & CSIDL_FLAG_CREATE;
1823 if (SHGetSpecialFolderPathW(0, wszTemp, ppfti->csidl, create))
1824 hr = SHStrDupW(wszTemp, &m_sPathTarget);
1825 }
1826 else if (ppfti->szTargetParsingName[0])
1827 {
1828 hr = SHStrDupW(ppfti->szTargetParsingName, &m_sPathTarget);
1829 }
1830 else if (ppfti->pidlTargetFolder)
1831 {
1832 if (SHGetPathFromIDListW(ppfti->pidlTargetFolder, wszTemp))
1833 hr = SHStrDupW(wszTemp, &m_sPathTarget);
1834 }
1835 }
1836 TRACE("--(%p)->(target=%s)\n", this, debugstr_w(m_sPathTarget));
1837 pdump(m_pidlRoot);
1838 return hr;
1839 }
1840
GetFolderTargetInfo(PERSIST_FOLDER_TARGET_INFO * ppfti)1841 HRESULT WINAPI CFSFolder::GetFolderTargetInfo(PERSIST_FOLDER_TARGET_INFO * ppfti)
1842 {
1843 FIXME("(%p)->(%p)\n", this, ppfti);
1844 ZeroMemory(ppfti, sizeof (*ppfti));
1845 return E_NOTIMPL;
1846 }
1847
_CreateExtensionUIObject(PCUIDLIST_RELATIVE pidl,REFIID riid,LPVOID * ppvOut)1848 HRESULT CFSFolder::_CreateExtensionUIObject(PCUIDLIST_RELATIVE pidl, REFIID riid, LPVOID *ppvOut)
1849 {
1850 WCHAR buf[MAX_PATH];
1851
1852 sprintfW(buf, L"ShellEx\\{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
1853 riid.Data1, riid.Data2, riid.Data3,
1854 riid.Data4[0], riid.Data4[1], riid.Data4[2], riid.Data4[3],
1855 riid.Data4[4], riid.Data4[5], riid.Data4[6], riid.Data4[7]);
1856
1857 CLSID clsid;
1858 HRESULT hr;
1859
1860 hr = GetCLSIDForFileType(pidl, buf, &clsid);
1861 if (hr != S_OK)
1862 return hr;
1863
1864 hr = _CreateShellExtInstance(&clsid, pidl, riid, ppvOut);
1865 if (FAILED_UNEXPECTEDLY(hr))
1866 return hr;
1867
1868 return S_OK;
1869 }
1870
_GetDropTarget(LPCITEMIDLIST pidl,LPVOID * ppvOut)1871 HRESULT CFSFolder::_GetDropTarget(LPCITEMIDLIST pidl, LPVOID *ppvOut)
1872 {
1873 HRESULT hr;
1874
1875 TRACE("CFSFolder::_GetDropTarget entered\n");
1876
1877 if (_ILIsFolder (pidl))
1878 {
1879 CComPtr<IShellFolder> psfChild;
1880 hr = this->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfChild));
1881 if (FAILED_UNEXPECTEDLY(hr))
1882 return hr;
1883
1884 return psfChild->CreateViewObject(NULL, IID_IDropTarget, ppvOut);
1885 }
1886
1887 CLSID clsid;
1888 hr = GetCLSIDForFileType(pidl, L"shellex\\DropHandler", &clsid);
1889 if (hr != S_OK)
1890 return hr;
1891
1892 hr = _CreateShellExtInstance(&clsid, pidl, IID_IDropTarget, ppvOut);
1893 if (FAILED_UNEXPECTEDLY(hr))
1894 return S_FALSE;
1895
1896 return S_OK;
1897 }
1898
_GetIconHandler(LPCITEMIDLIST pidl,REFIID riid,LPVOID * ppvOut)1899 HRESULT CFSFolder::_GetIconHandler(LPCITEMIDLIST pidl, REFIID riid, LPVOID *ppvOut)
1900 {
1901 CLSID clsid;
1902 HRESULT hr;
1903
1904 hr = GetCLSIDForFileType(pidl, L"shellex\\IconHandler", &clsid);
1905 if (hr != S_OK)
1906 return hr;
1907
1908 hr = _CreateShellExtInstance(&clsid, pidl, riid, ppvOut);
1909 if (FAILED_UNEXPECTEDLY(hr))
1910 return S_FALSE;
1911
1912 return S_OK;
1913 }
1914
_CreateShellExtInstance(const CLSID * pclsid,LPCITEMIDLIST pidl,REFIID riid,LPVOID * ppvOut)1915 HRESULT CFSFolder::_CreateShellExtInstance(const CLSID *pclsid, LPCITEMIDLIST pidl, REFIID riid, LPVOID *ppvOut)
1916 {
1917 HRESULT hr;
1918 WCHAR wszPath[MAX_PATH], szNameBuf[MAX_PATH];
1919
1920 LPCWSTR pszName = GetItemFileName(pidl, szNameBuf, _countof(szNameBuf));
1921 if (!pszName)
1922 {
1923 ERR("Got garbage pidl\n");
1924 pdump_always(pidl);
1925 return E_INVALIDARG;
1926 }
1927 PathCombineW(wszPath, m_sPathTarget, pszName);
1928
1929 CComPtr<IPersistFile> pp;
1930 hr = SHCoCreateInstance(NULL, pclsid, NULL, IID_PPV_ARG(IPersistFile, &pp));
1931 if (FAILED_UNEXPECTEDLY(hr))
1932 return hr;
1933
1934 pp->Load(wszPath, 0);
1935
1936 hr = pp->QueryInterface(riid, ppvOut);
1937 if (hr != S_OK)
1938 {
1939 ERR("Failed to query for interface IID_IShellExtInit hr %x pclsid %s\n", hr, wine_dbgstr_guid(pclsid));
1940 return hr;
1941 }
1942 return hr;
1943 }
1944
CallBack(IShellFolder * psf,HWND hwndOwner,IDataObject * pdtobj,UINT uMsg,WPARAM wParam,LPARAM lParam)1945 HRESULT WINAPI CFSFolder::CallBack(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
1946 {
1947 enum { IDC_PROPERTIES };
1948 /* no data object means no selection */
1949 if (!pdtobj)
1950 {
1951 if (uMsg == DFM_INVOKECOMMAND && wParam == IDC_PROPERTIES)
1952 {
1953 return SHELL_ShowItemIDListProperties(m_pidlRoot);
1954 }
1955 else if (uMsg == DFM_MERGECONTEXTMENU)
1956 {
1957 QCMINFO *pqcminfo = (QCMINFO *)lParam;
1958 HMENU hpopup = CreatePopupMenu();
1959 _InsertMenuItemW(hpopup, 0, TRUE, IDC_PROPERTIES, MFT_STRING, MAKEINTRESOURCEW(IDS_PROPERTIES), MFS_ENABLED);
1960 pqcminfo->idCmdFirst = Shell_MergeMenus(pqcminfo->hmenu, hpopup, pqcminfo->indexMenu, pqcminfo->idCmdFirst, pqcminfo->idCmdLast, MM_ADDSEPARATOR);
1961 DestroyMenu(hpopup);
1962 return S_OK;
1963 }
1964 }
1965 return SHELL32_DefaultContextMenuCallBack(psf, pdtobj, uMsg);
1966 }
1967
DoLoadPicture(LPCWSTR pszFileName)1968 static HBITMAP DoLoadPicture(LPCWSTR pszFileName)
1969 {
1970 // create stream from file
1971 HRESULT hr;
1972 CComPtr<IStream> pStream;
1973 hr = SHCreateStreamOnFileEx(pszFileName, STGM_READ, FILE_ATTRIBUTE_NORMAL,
1974 FALSE, NULL, &pStream);
1975 if (FAILED(hr))
1976 return NULL;
1977
1978 // load the picture
1979 HBITMAP hbm = NULL;
1980 CComPtr<IPicture> pPicture;
1981 OleLoadPicture(pStream, 0, FALSE, IID_IPicture, (LPVOID *)&pPicture);
1982
1983 // get the bitmap handle
1984 if (pPicture)
1985 {
1986 pPicture->get_Handle((OLE_HANDLE *)&hbm);
1987
1988 // copy the bitmap handle
1989 hbm = (HBITMAP)CopyImage(hbm, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
1990 }
1991
1992 return hbm;
1993 }
1994
GetCustomViewInfo(ULONG unknown,SFVM_CUSTOMVIEWINFO_DATA * data)1995 HRESULT WINAPI CFSFolder::GetCustomViewInfo(ULONG unknown, SFVM_CUSTOMVIEWINFO_DATA *data)
1996 {
1997 if (data == NULL)
1998 {
1999 return E_POINTER;
2000 }
2001 if (data->cbSize != sizeof(*data))
2002 {
2003 // NOTE: You have to set the cbData member before SFVM_GET_CUSTOMVIEWINFO call.
2004 return E_INVALIDARG;
2005 }
2006
2007 data->hbmBack = NULL;
2008 data->clrText = CLR_INVALID;
2009 data->clrTextBack = CLR_INVALID;
2010
2011 WCHAR szPath[MAX_PATH], szIniFile[MAX_PATH];
2012
2013 // does the folder exists?
2014 if (!SHGetPathFromIDListW(m_pidlRoot, szPath) || !PathIsDirectoryW(szPath))
2015 {
2016 return E_INVALIDARG;
2017 }
2018
2019 // don't use custom view in network path for security
2020 if (PathIsNetworkPath(szPath))
2021 {
2022 return E_ACCESSDENIED;
2023 }
2024
2025 // build the ini file path
2026 StringCchCopyW(szIniFile, _countof(szIniFile), szPath);
2027 PathAppend(szIniFile, L"desktop.ini");
2028
2029 static LPCWSTR TheGUID = L"{BE098140-A513-11D0-A3A4-00C04FD706EC}";
2030 static LPCWSTR Space = L" \t\n\r\f\v";
2031
2032 // get info from ini file
2033 WCHAR szImage[MAX_PATH], szText[64];
2034
2035 // load the image
2036 szImage[0] = UNICODE_NULL;
2037 GetPrivateProfileStringW(TheGUID, L"IconArea_Image", L"", szImage, _countof(szImage), szIniFile);
2038 if (szImage[0])
2039 {
2040 StrTrimW(szImage, Space);
2041 if (PathIsRelativeW(szImage))
2042 {
2043 PathAppendW(szPath, szImage);
2044 StringCchCopyW(szImage, _countof(szImage), szPath);
2045 }
2046 data->hbmBack = DoLoadPicture(szImage);
2047 }
2048
2049 // load the text color
2050 szText[0] = UNICODE_NULL;
2051 GetPrivateProfileStringW(TheGUID, L"IconArea_Text", L"", szText, _countof(szText), szIniFile);
2052 if (szText[0])
2053 {
2054 StrTrimW(szText, Space);
2055
2056 LPWSTR pchEnd = NULL;
2057 COLORREF cr = (wcstol(szText, &pchEnd, 0) & 0xFFFFFF);
2058
2059 if (pchEnd && !*pchEnd)
2060 data->clrText = cr;
2061 }
2062
2063 // load the text background color
2064 szText[0] = UNICODE_NULL;
2065 GetPrivateProfileStringW(TheGUID, L"IconArea_TextBackground", L"", szText, _countof(szText), szIniFile);
2066 if (szText[0])
2067 {
2068 StrTrimW(szText, Space);
2069
2070 LPWSTR pchEnd = NULL;
2071 COLORREF cr = (wcstol(szText, &pchEnd, 0) & 0xFFFFFF);
2072
2073 if (pchEnd && !*pchEnd)
2074 data->clrTextBack = cr;
2075 }
2076
2077 if (data->hbmBack != NULL || data->clrText != CLR_INVALID || data->clrTextBack != CLR_INVALID)
2078 return S_OK;
2079
2080 return E_FAIL;
2081 }
2082
MessageSFVCB(UINT uMsg,WPARAM wParam,LPARAM lParam)2083 HRESULT WINAPI CFSFolder::MessageSFVCB(UINT uMsg, WPARAM wParam, LPARAM lParam)
2084 {
2085 HRESULT hr = E_NOTIMPL;
2086 switch (uMsg)
2087 {
2088 case SFVM_GET_CUSTOMVIEWINFO:
2089 hr = GetCustomViewInfo((ULONG)wParam, (SFVM_CUSTOMVIEWINFO_DATA *)lParam);
2090 break;
2091 case SFVM_GETCOMMANDDIR:
2092 if (m_sPathTarget)
2093 hr = StringCchCopyW((PWSTR)lParam, wParam, m_sPathTarget);
2094 break;
2095 }
2096 return hr;
2097 }
2098
FormatDateTime(const FILETIME & ft,LPWSTR Buf,UINT cchBuf)2099 HRESULT CFSFolder::FormatDateTime(const FILETIME &ft, LPWSTR Buf, UINT cchBuf)
2100 {
2101 FILETIME lft;
2102 SYSTEMTIME time;
2103 FileTimeToLocalFileTime(&ft, &lft);
2104 FileTimeToSystemTime(&lft, &time);
2105 UINT ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &time, NULL, Buf, cchBuf);
2106 if (ret > 0 && ret < cchBuf)
2107 {
2108 /* Append space + time without seconds */
2109 Buf[ret-1] = ' ';
2110 GetTimeFormatW(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &time, NULL, &Buf[ret], cchBuf - ret);
2111 }
2112 return ret ? S_OK : E_FAIL;
2113 }
2114
FormatSize(UINT64 size,LPWSTR Buf,UINT cchBuf)2115 HRESULT CFSFolder::FormatSize(UINT64 size, LPWSTR Buf, UINT cchBuf)
2116 {
2117 if (StrFormatKBSizeW(size, Buf, cchBuf))
2118 return S_OK;
2119 if (cchBuf)
2120 *Buf = UNICODE_NULL;
2121 return E_FAIL;
2122 }
2123