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