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 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 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 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 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 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 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 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 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 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 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 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 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: 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: 483 CFileSysEnum() 484 { 485 486 } 487 488 ~CFileSysEnum() 489 { 490 } 491 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 553 CFSFolder::CFSFolder() 554 { 555 m_pclsid = &CLSID_ShellFSFolder; 556 m_sPathTarget = NULL; 557 m_pidlRoot = NULL; 558 m_bGroupPolicyActive = 0; 559 } 560 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 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 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 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 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. 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 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 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 */ 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 */ 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 */ 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 */ 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 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 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 */ 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 */ 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 */ 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 */ 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 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 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 */ 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 1612 HRESULT WINAPI CFSFolder::GetDefaultSearchGUID(GUID * pguid) 1613 { 1614 FIXME ("(%p)\n", this); 1615 return E_NOTIMPL; 1616 } 1617 1618 HRESULT WINAPI CFSFolder::EnumSearches(IEnumExtraSearch ** ppenum) 1619 { 1620 FIXME ("(%p)\n", this); 1621 return E_NOTIMPL; 1622 } 1623 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 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 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 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 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 */ 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 */ 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 */ 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 */ 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 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 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 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 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 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 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 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 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 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 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 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