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 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 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 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 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 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 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 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 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 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: 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: 452 CFileSysEnum() 453 { 454 455 } 456 457 ~CFileSysEnum() 458 { 459 } 460 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 522 CFSFolder::CFSFolder() 523 { 524 m_pclsid = &CLSID_ShellFSFolder; 525 m_sPathTarget = NULL; 526 m_pidlRoot = NULL; 527 m_bGroupPolicyActive = 0; 528 } 529 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 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 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 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 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 628 // Vista+ feature: Hidden files with a leading tilde treated as super-hidden 629 // See https://devblogs.microsoft.com/oldnewthing/20170526-00/?p=96235 630 if (hasName && szFileName[0] == '~' && (dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)) 631 dwShellAttributes |= SFGAO_HIDDEN | SFGAO_SYSTEM; 632 633 if (hasName && (pExtension = PathFindExtensionW(szFileName))) 634 { 635 CLSID clsidFile; 636 // FIXME: Cache this? 637 HRESULT hr = GetCLSIDForFileTypeFromExtension(pExtension, L"CLSID", &clsidFile); 638 if (hr == S_OK) 639 { 640 HKEY hkey; 641 hr = SHRegGetCLSIDKeyW(clsidFile, L"ShellFolder", FALSE, FALSE, &hkey); 642 if (SUCCEEDED(hr)) 643 { 644 DWORD dwAttributes = 0; 645 DWORD dwSize = sizeof(dwAttributes); 646 LSTATUS Status; 647 648 Status = SHRegGetValueW(hkey, NULL, L"Attributes", RRF_RT_REG_DWORD, NULL, &dwAttributes, &dwSize); 649 if (Status == STATUS_SUCCESS) 650 { 651 TRACE("Augmenting '%S' with dwAttributes=0x%x\n", szFileName, dwAttributes); 652 dwShellAttributes |= dwAttributes; 653 } 654 ::RegCloseKey(hkey); 655 656 // This should be presented as directory! 657 bDirectory = (dwAttributes & SFGAO_FOLDER) != 0 || dwAttributes == 0; 658 TRACE("Treating '%S' as directory!\n", szFileName); 659 } 660 } 661 } 662 } 663 664 // This is a directory 665 if (bDirectory) 666 { 667 dwShellAttributes |= (SFGAO_FOLDER | /*SFGAO_HASSUBFOLDER |*/ SFGAO_STORAGE); 668 669 // Is this a real directory? 670 if (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 671 { 672 dwShellAttributes |= (SFGAO_FILESYSANCESTOR | SFGAO_STORAGEANCESTOR); 673 } 674 } 675 else 676 { 677 dwShellAttributes |= SFGAO_STREAM; 678 } 679 680 if (dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) 681 dwShellAttributes |= SFGAO_HIDDEN | SFGAO_GHOSTED; 682 683 if (dwFileAttributes & FILE_ATTRIBUTE_READONLY) 684 dwShellAttributes |= SFGAO_READONLY; 685 686 if (dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) 687 dwShellAttributes |= SFGAO_SYSTEM; 688 689 if (dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) 690 dwShellAttributes |= SFGAO_COMPRESSED; 691 692 if (dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) 693 dwShellAttributes |= SFGAO_ENCRYPTED; 694 695 if ((SFGAO_NONENUMERATED & *pdwAttributes) && (dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)) 696 { 697 SHCONTF shcf = SHELL_GetDefaultFolderEnumSHCONTF(); 698 if ((!(shcf & SHCONTF_INCLUDEHIDDEN)) || ((dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) && !(shcf & SHCONTF_INCLUDESUPERHIDDEN))) 699 dwShellAttributes |= SFGAO_NONENUMERATED; 700 } 701 702 if (SFGAO_LINK & *pdwAttributes) 703 { 704 WCHAR ext[MAX_PATH]; 705 706 if (_ILGetExtension(pidl, ext, _countof(ext)) && !lstrcmpiW(ext, L"lnk")) 707 dwShellAttributes |= SFGAO_LINK; 708 } 709 710 if (SFGAO_HASSUBFOLDER & *pdwAttributes) 711 { 712 CComPtr<IShellFolder> psf2; 713 if (SUCCEEDED(psf->BindToObject(pidl, 0, IID_PPV_ARG(IShellFolder, &psf2)))) 714 { 715 CComPtr<IEnumIDList> pEnumIL; 716 if (SUCCEEDED(psf2->EnumObjects(0, SHCONTF_FOLDERS, &pEnumIL))) 717 { 718 if (pEnumIL->Skip(1) == S_OK) 719 dwShellAttributes |= SFGAO_HASSUBFOLDER; 720 } 721 } 722 } 723 724 *pdwAttributes = dwShellAttributes; 725 726 TRACE ("-- 0x%08x\n", *pdwAttributes); 727 return S_OK; 728 } 729 730 // This method is typically invoked from SHSimpleIDListFromPathA/W. 731 HRESULT CFSFolder::_ParseSimple( 732 _In_ LPOLESTR lpszDisplayName, 733 _Inout_ WIN32_FIND_DATAW *pFind, 734 _Out_ LPITEMIDLIST *ppidl) 735 { 736 HRESULT hr; 737 LPWSTR pchNext = lpszDisplayName; 738 739 *ppidl = NULL; 740 741 const DWORD finalattr = pFind->dwFileAttributes; 742 const DWORD finalsizelo = pFind->nFileSizeLow; 743 LPITEMIDLIST pidl; 744 for (hr = S_OK; SUCCEEDED(hr); hr = SHILAppend(pidl, ppidl)) 745 { 746 hr = Shell_NextElement(&pchNext, pFind->cFileName, _countof(pFind->cFileName), FALSE); 747 if (hr != S_OK) 748 break; 749 750 pFind->dwFileAttributes = pchNext ? FILE_ATTRIBUTE_DIRECTORY : finalattr; 751 pFind->nFileSizeLow = pchNext ? 0 : finalsizelo; 752 pidl = _ILCreateFromFindDataW(pFind); 753 if (!pidl) 754 { 755 hr = E_OUTOFMEMORY; 756 break; 757 } 758 } 759 760 if (SUCCEEDED(hr)) 761 return S_OK; 762 763 if (*ppidl) 764 { 765 ILFree(*ppidl); 766 *ppidl = NULL; 767 } 768 769 return hr; 770 } 771 772 BOOL CFSFolder::_GetFindDataFromName(_In_ LPCWSTR pszName, _Out_ WIN32_FIND_DATAW *pFind) 773 { 774 WCHAR szPath[MAX_PATH]; 775 lstrcpynW(szPath, m_sPathTarget, _countof(szPath)); 776 PathAppendW(szPath, pszName); 777 778 HANDLE hFind = ::FindFirstFileW(szPath, pFind); 779 if (hFind == INVALID_HANDLE_VALUE) 780 return FALSE; 781 782 ::FindClose(hFind); 783 return TRUE; 784 } 785 786 HRESULT CFSFolder::_CreateIDListFromName(LPCWSTR pszName, DWORD attrs, IBindCtx *pbc, LPITEMIDLIST *ppidl) 787 { 788 *ppidl = NULL; 789 790 if (PathIsDosDevice(pszName)) 791 return HRESULT_FROM_WIN32(ERROR_BAD_DEVICE); 792 793 WIN32_FIND_DATAW FindData = { 0 }; 794 795 HRESULT hr = S_OK; 796 if (attrs == ULONG_MAX) // Invalid attributes 797 { 798 if (!_GetFindDataFromName(pszName, &FindData)) 799 hr = HRESULT_FROM_WIN32(::GetLastError()); 800 } 801 else // Pretend as an item of attrs 802 { 803 StringCchCopyW(FindData.cFileName, _countof(FindData.cFileName), pszName); 804 FindData.dwFileAttributes = attrs; 805 } 806 807 if (FAILED(hr)) 808 return hr; 809 810 *ppidl = _ILCreateFromFindDataW(&FindData); 811 if (!*ppidl) 812 return E_OUTOFMEMORY; 813 814 return S_OK; 815 } 816 817 /************************************************************************** 818 * CFSFolder::ParseDisplayName {SHELL32} 819 * 820 * Parse a display name. 821 * 822 * PARAMS 823 * hwndOwner [in] Parent window for any message's 824 * pbc [in] optional FileSystemBindData context 825 * lpszDisplayName [in] Unicode displayname. 826 * pchEaten [out] (unicode) characters processed 827 * ppidl [out] complex pidl to item 828 * pdwAttributes [out] items attributes 829 * 830 * NOTES 831 * Every folder tries to parse only its own (the leftmost) pidl and creates a 832 * subfolder to evaluate the remaining parts. 833 * Now we can parse into namespaces implemented by shell extensions 834 * 835 * Behaviour on win98: lpszDisplayName=NULL -> crash 836 * lpszDisplayName="" -> returns mycoputer-pidl 837 * 838 * FIXME 839 * pdwAttributes is not set 840 * pchEaten is not set like in windows 841 */ 842 HRESULT WINAPI CFSFolder::ParseDisplayName(HWND hwndOwner, 843 LPBC pbc, 844 LPOLESTR lpszDisplayName, 845 DWORD *pchEaten, PIDLIST_RELATIVE *ppidl, 846 DWORD *pdwAttributes) 847 { 848 TRACE ("(%p)->(HWND=%p,%p,%p=%s,%p,pidl=%p,%p)\n", 849 this, hwndOwner, pbc, lpszDisplayName, debugstr_w (lpszDisplayName), 850 pchEaten, ppidl, pdwAttributes); 851 852 if (!ppidl) 853 return E_INVALIDARG; 854 855 *ppidl = NULL; 856 857 if (!lpszDisplayName) 858 return E_INVALIDARG; 859 860 HRESULT hr; 861 WIN32_FIND_DATAW FindData; 862 if (SHIsFileSysBindCtx(pbc, &FindData) == S_OK) 863 { 864 CComHeapPtr<ITEMIDLIST> pidlTemp; 865 hr = _ParseSimple(lpszDisplayName, &FindData, &pidlTemp); 866 if (SUCCEEDED(hr) && pdwAttributes && *pdwAttributes) 867 { 868 LPCITEMIDLIST pidlLast = ILFindLastID(pidlTemp); 869 GetAttributesOf(1, &pidlLast, pdwAttributes); 870 } 871 872 if (SUCCEEDED(hr)) 873 *ppidl = pidlTemp.Detach(); 874 } 875 else 876 { 877 INT cchElement = lstrlenW(lpszDisplayName) + 1; 878 LPWSTR pszElement = (LPWSTR)alloca(cchElement * sizeof(WCHAR)); 879 LPWSTR pchNext = lpszDisplayName; 880 hr = Shell_NextElement(&pchNext, pszElement, cchElement, TRUE); 881 if (FAILED(hr)) 882 return hr; 883 884 hr = _CreateIDListFromName(pszElement, ULONG_MAX, pbc, ppidl); 885 if (FAILED(hr)) 886 { 887 if (pchNext) // Is there the next element? 888 { 889 // pszElement seems like a directory 890 if (_GetFindDataFromName(pszElement, &FindData) && 891 (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) 892 { 893 hr = _CreateIDListFromName(pszElement, FILE_ATTRIBUTE_DIRECTORY, pbc, ppidl); 894 } 895 } 896 else 897 { 898 // pszElement seems like a non-directory 899 if ((hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) || 900 hr == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND)) && 901 (BindCtx_GetMode(pbc, 0) & STGM_CREATE)) 902 { 903 // Pretend like a normal file 904 hr = _CreateIDListFromName(pszElement, FILE_ATTRIBUTE_NORMAL, pbc, ppidl); 905 } 906 } 907 } 908 909 if (SUCCEEDED(hr)) 910 { 911 if (pchNext) // Is there next? 912 { 913 CComPtr<IShellFolder> psfChild; 914 hr = BindToObject(*ppidl, pbc, IID_PPV_ARG(IShellFolder, &psfChild)); 915 if (FAILED(hr)) 916 return hr; 917 918 DWORD chEaten; 919 CComHeapPtr<ITEMIDLIST> pidlChild; 920 hr = psfChild->ParseDisplayName(hwndOwner, pbc, pchNext, &chEaten, &pidlChild, 921 pdwAttributes); 922 923 // Append pidlChild to ppidl 924 if (SUCCEEDED(hr)) 925 hr = SHILAppend(pidlChild.Detach(), ppidl); 926 } 927 else if (pdwAttributes && *pdwAttributes) 928 { 929 GetAttributesOf(1, (LPCITEMIDLIST*)ppidl, pdwAttributes); 930 } 931 } 932 } 933 934 TRACE("(%p)->(-- pidl=%p ret=0x%08x)\n", this, ppidl ? *ppidl : 0, hr); 935 936 return hr; 937 } 938 939 /************************************************************************** 940 * CFSFolder::EnumObjects 941 * PARAMETERS 942 * HWND hwndOwner, //[in ] Parent Window 943 * DWORD grfFlags, //[in ] SHCONTF enumeration mask 944 * LPENUMIDLIST* ppenumIDList //[out] IEnumIDList interface 945 */ 946 HRESULT WINAPI CFSFolder::EnumObjects( 947 HWND hwndOwner, 948 DWORD dwFlags, 949 LPENUMIDLIST *ppEnumIDList) 950 { 951 return ShellObjectCreatorInit<CFileSysEnum>(m_sPathTarget, dwFlags, IID_PPV_ARG(IEnumIDList, ppEnumIDList)); 952 } 953 954 /************************************************************************** 955 * CFSFolder::BindToObject 956 * PARAMETERS 957 * LPCITEMIDLIST pidl, //[in ] relative pidl to open 958 * LPBC pbc, //[in ] optional FileSystemBindData context 959 * REFIID riid, //[in ] Initial Interface 960 * LPVOID* ppvObject //[out] Interface* 961 */ 962 HRESULT WINAPI CFSFolder::BindToObject( 963 PCUIDLIST_RELATIVE pidl, 964 LPBC pbc, 965 REFIID riid, 966 LPVOID * ppvOut) 967 { 968 TRACE("(%p)->(pidl=%p,%p,%s,%p)\n", this, pidl, pbc, 969 shdebugstr_guid(&riid), ppvOut); 970 971 CComPtr<IShellFolder> pSF; 972 HRESULT hr; 973 974 if (!m_pidlRoot || !ppvOut || !pidl || !pidl->mkid.cb) 975 { 976 ERR("CFSFolder::BindToObject: Invalid parameters\n"); 977 return E_INVALIDARG; 978 } 979 980 /* Get the pidl data */ 981 FileStruct* pData = &_ILGetDataPointer(pidl)->u.file; 982 FileStructW* pDataW = _ILGetFileStructW(pidl); 983 984 if (!pDataW) 985 { 986 ERR("CFSFolder::BindToObject: Invalid pidl!\n"); 987 return E_INVALIDARG; 988 } 989 990 *ppvOut = NULL; 991 992 /* Create the target folder info */ 993 PERSIST_FOLDER_TARGET_INFO pfti = {0}; 994 pfti.dwAttributes = -1; 995 pfti.csidl = -1; 996 PathCombineW(pfti.szTargetParsingName, m_sPathTarget, pDataW->wszName); 997 998 /* Get the CLSID to bind to */ 999 CLSID clsidFolder; 1000 if (_ILIsFolder(pidl)) 1001 { 1002 if ((pData->uFileAttribs & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)) != 0) 1003 { 1004 hr = SHELL32_GetCLSIDForDirectory(pfti.szTargetParsingName, L"CLSID", &clsidFolder); 1005 1006 if (SUCCEEDED(hr)) 1007 { 1008 /* We got a GUID from a desktop.ini, let's try it */ 1009 hr = SHELL32_BindToSF(m_pidlRoot, &pfti, pidl, &clsidFolder, riid, ppvOut); 1010 if (SUCCEEDED(hr)) 1011 { 1012 TRACE("-- returning (%p) %08x, (%s)\n", *ppvOut, hr, wine_dbgstr_guid(&clsidFolder)); 1013 return hr; 1014 } 1015 1016 /* Something went wrong, re-try it with a normal ShellFSFolder */ 1017 ERR("CFSFolder::BindToObject: %s failed to bind, using fallback (0x%08x)\n", wine_dbgstr_guid(&clsidFolder), hr); 1018 } 1019 } 1020 /* No system folder or the custom class failed */ 1021 clsidFolder = CLSID_ShellFSFolder; 1022 } 1023 else 1024 { 1025 hr = GetCLSIDForFileType(pidl, L"CLSID", &clsidFolder); 1026 if (hr == S_FALSE) 1027 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); 1028 if (hr != S_OK) 1029 return hr; 1030 } 1031 1032 hr = SHELL32_BindToSF(m_pidlRoot, &pfti, pidl, &clsidFolder, riid, ppvOut); 1033 if (FAILED_UNEXPECTEDLY(hr)) 1034 return hr; 1035 1036 TRACE ("-- returning (%p) %08x\n", *ppvOut, hr); 1037 1038 return S_OK; 1039 1040 } 1041 1042 /************************************************************************** 1043 * CFSFolder::BindToStorage 1044 * PARAMETERS 1045 * LPCITEMIDLIST pidl, //[in ] complex pidl to store 1046 * LPBC pbc, //[in ] reserved 1047 * REFIID riid, //[in ] Initial storage interface 1048 * LPVOID* ppvObject //[out] Interface* returned 1049 */ 1050 HRESULT WINAPI CFSFolder::BindToStorage( 1051 PCUIDLIST_RELATIVE pidl, 1052 LPBC pbcReserved, 1053 REFIID riid, 1054 LPVOID *ppvOut) 1055 { 1056 FIXME("(%p)->(pidl=%p,%p,%s,%p) stub\n", this, pidl, pbcReserved, 1057 shdebugstr_guid (&riid), ppvOut); 1058 1059 *ppvOut = NULL; 1060 return E_NOTIMPL; 1061 } 1062 1063 /************************************************************************** 1064 * CFSFolder::CompareIDs 1065 */ 1066 1067 HRESULT WINAPI CFSFolder::CompareIDs(LPARAM lParam, 1068 PCUIDLIST_RELATIVE pidl1, 1069 PCUIDLIST_RELATIVE pidl2) 1070 { 1071 LPPIDLDATA pData1 = _ILGetDataPointer(pidl1); 1072 LPPIDLDATA pData2 = _ILGetDataPointer(pidl2); 1073 FileStructW* pDataW1 = _ILGetFileStructW(pidl1); 1074 FileStructW* pDataW2 = _ILGetFileStructW(pidl2); 1075 BOOL bIsFolder1 = _ILIsFolder(pidl1); 1076 BOOL bIsFolder2 = _ILIsFolder(pidl2); 1077 LPWSTR pExtension1, pExtension2; 1078 1079 if (!pDataW1 || !pDataW2 || LOWORD(lParam) >= GENERICSHELLVIEWCOLUMNS) 1080 return E_INVALIDARG; 1081 1082 /* When sorting between a File and a Folder, the Folder gets sorted first */ 1083 if (bIsFolder1 != bIsFolder2) 1084 { 1085 return MAKE_COMPARE_HRESULT(bIsFolder1 ? -1 : 1); 1086 } 1087 1088 int result = 0; 1089 switch (LOWORD(lParam)) 1090 { 1091 case SHFSF_COL_NAME: 1092 result = wcsicmp(pDataW1->wszName, pDataW2->wszName); 1093 break; 1094 case SHFSF_COL_SIZE: 1095 if (pData1->u.file.dwFileSize > pData2->u.file.dwFileSize) 1096 result = 1; 1097 else if (pData1->u.file.dwFileSize < pData2->u.file.dwFileSize) 1098 result = -1; 1099 else 1100 result = 0; 1101 break; 1102 case SHFSF_COL_TYPE: 1103 pExtension1 = PathFindExtensionW(pDataW1->wszName); 1104 pExtension2 = PathFindExtensionW(pDataW2->wszName); 1105 result = wcsicmp(pExtension1, pExtension2); 1106 break; 1107 case SHFSF_COL_MDATE: 1108 result = pData1->u.file.uFileDate - pData2->u.file.uFileDate; 1109 if (result == 0) 1110 result = pData1->u.file.uFileTime - pData2->u.file.uFileTime; 1111 break; 1112 case SHFSF_COL_FATTS: 1113 return SHELL32_CompareDetails(this, lParam, pidl1, pidl2); 1114 case SHFSF_COL_COMMENT: 1115 result = 0; 1116 break; 1117 default: 1118 if (_ILIsPidlSimple(pidl1) || _ILIsPidlSimple(pidl2)) 1119 ERR("Unknown column %u, can't compare\n", LOWORD(lParam)); 1120 else 1121 TRACE("Unknown column %u, deferring to the subfolder\n", LOWORD(lParam)); 1122 } 1123 1124 if (result == 0) 1125 return SHELL32_CompareChildren(this, lParam, pidl1, pidl2); 1126 1127 return MAKE_COMPARE_HRESULT(result); 1128 } 1129 1130 /************************************************************************** 1131 * CFSFolder::CreateViewObject 1132 */ 1133 HRESULT WINAPI CFSFolder::CreateViewObject(HWND hwndOwner, 1134 REFIID riid, LPVOID * ppvOut) 1135 { 1136 CComPtr<IShellView> pShellView; 1137 HRESULT hr = E_INVALIDARG; 1138 1139 TRACE ("(%p)->(hwnd=%p,%s,%p)\n", this, hwndOwner, shdebugstr_guid (&riid), 1140 ppvOut); 1141 1142 if (ppvOut) 1143 { 1144 *ppvOut = NULL; 1145 1146 BOOL bIsDropTarget = IsEqualIID (riid, IID_IDropTarget); 1147 BOOL bIsShellView = !bIsDropTarget && IsEqualIID (riid, IID_IShellView); 1148 1149 if (bIsDropTarget || bIsShellView) 1150 { 1151 DWORD dwDirAttributes = _ILGetFileAttributes(ILFindLastID(m_pidlRoot), NULL, 0); 1152 1153 if ((dwDirAttributes & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)) != 0) 1154 { 1155 CLSID clsidFolder; 1156 hr = SHELL32_GetCLSIDForDirectory(m_sPathTarget, L"UICLSID", &clsidFolder); 1157 if (SUCCEEDED(hr)) 1158 { 1159 CComPtr<IPersistFolder> spFolder; 1160 hr = SHCoCreateInstance(NULL, &clsidFolder, NULL, IID_PPV_ARG(IPersistFolder, &spFolder)); 1161 if (!FAILED_UNEXPECTEDLY(hr)) 1162 { 1163 hr = spFolder->Initialize(m_pidlRoot); 1164 1165 if (!FAILED_UNEXPECTEDLY(hr)) 1166 { 1167 hr = spFolder->QueryInterface(riid, ppvOut); 1168 } 1169 } 1170 } 1171 else 1172 { 1173 // No desktop.ini, or no UICLSID present, continue as if nothing happened 1174 hr = E_INVALIDARG; 1175 } 1176 } 1177 } 1178 1179 if (!SUCCEEDED(hr)) 1180 { 1181 // No UICLSID handler found, continue to the default handlers 1182 if (bIsDropTarget) 1183 { 1184 hr = CFSDropTarget_CreateInstance(m_sPathTarget, riid, ppvOut); 1185 } 1186 else if (IsEqualIID (riid, IID_IContextMenu)) 1187 { 1188 HKEY hKeys[16]; 1189 UINT cKeys = 0; 1190 AddClassKeyToArray(L"Directory\\Background", hKeys, &cKeys); 1191 1192 DEFCONTEXTMENU dcm; 1193 dcm.hwnd = hwndOwner; 1194 dcm.pcmcb = this; 1195 dcm.pidlFolder = m_pidlRoot; 1196 dcm.psf = this; 1197 dcm.cidl = 0; 1198 dcm.apidl = NULL; 1199 dcm.cKeys = cKeys; 1200 dcm.aKeys = hKeys; 1201 dcm.punkAssociationInfo = NULL; 1202 hr = SHCreateDefaultContextMenu (&dcm, riid, ppvOut); 1203 } 1204 else if (bIsShellView) 1205 { 1206 SFV_CREATE sfvparams = {sizeof(SFV_CREATE), this, NULL, this}; 1207 hr = SHCreateShellFolderView(&sfvparams, (IShellView**)ppvOut); 1208 } 1209 else 1210 { 1211 hr = E_INVALIDARG; 1212 } 1213 } 1214 } 1215 TRACE("-- (%p)->(interface=%p)\n", this, ppvOut); 1216 return hr; 1217 } 1218 1219 /************************************************************************** 1220 * CFSFolder::GetAttributesOf 1221 * 1222 * PARAMETERS 1223 * UINT cidl, //[in ] num elements in pidl array 1224 * LPCITEMIDLIST* apidl, //[in ] simple pidl array 1225 * ULONG* rgfInOut) //[out] result array 1226 * 1227 */ 1228 HRESULT WINAPI CFSFolder::GetAttributesOf(UINT cidl, 1229 PCUITEMID_CHILD_ARRAY apidl, DWORD * rgfInOut) 1230 { 1231 HRESULT hr = S_OK; 1232 1233 if (!rgfInOut) 1234 return E_INVALIDARG; 1235 if (cidl && !apidl) 1236 return E_INVALIDARG; 1237 1238 if (*rgfInOut == 0) 1239 *rgfInOut = ~0; 1240 1241 if(cidl == 0) 1242 { 1243 LPCITEMIDLIST rpidl = ILFindLastID(m_pidlRoot); 1244 1245 if (_ILIsFolder(rpidl) || _ILIsValue(rpidl)) 1246 { 1247 SHELL32_GetFSItemAttributes(this, rpidl, rgfInOut); 1248 } 1249 else if (_ILIsDrive(rpidl)) 1250 { 1251 IShellFolder *psfParent = NULL; 1252 hr = SHBindToParent(m_pidlRoot, IID_PPV_ARG(IShellFolder, &psfParent), NULL); 1253 if(SUCCEEDED(hr)) 1254 { 1255 hr = psfParent->GetAttributesOf(1, &rpidl, (SFGAOF*)rgfInOut); 1256 psfParent->Release(); 1257 } 1258 } 1259 else 1260 { 1261 ERR("Got and unknown pidl!\n"); 1262 } 1263 } 1264 else 1265 { 1266 while (cidl > 0 && *apidl) 1267 { 1268 pdump(*apidl); 1269 if(_ILIsFolder(*apidl) || _ILIsValue(*apidl)) 1270 SHELL32_GetFSItemAttributes(this, *apidl, rgfInOut); 1271 else 1272 ERR("Got an unknown type of pidl!!!\n"); 1273 apidl++; 1274 cidl--; 1275 } 1276 } 1277 /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */ 1278 *rgfInOut &= ~SFGAO_VALIDATE; 1279 1280 TRACE("-- result=0x%08x\n", *rgfInOut); 1281 1282 return hr; 1283 } 1284 1285 /************************************************************************** 1286 * CFSFolder::GetUIObjectOf 1287 * 1288 * PARAMETERS 1289 * HWND hwndOwner, //[in ] Parent window for any output 1290 * UINT cidl, //[in ] array size 1291 * LPCITEMIDLIST* apidl, //[in ] simple pidl array 1292 * REFIID riid, //[in ] Requested Interface 1293 * UINT* prgfInOut, //[ ] reserved 1294 * LPVOID* ppvObject) //[out] Resulting Interface 1295 * 1296 * NOTES 1297 * This function gets asked to return "view objects" for one or more (multiple 1298 * select) items: 1299 * The viewobject typically is an COM object with one of the following 1300 * interfaces: 1301 * IExtractIcon,IDataObject,IContextMenu 1302 * In order to support icon positions in the default Listview your DataObject 1303 * must implement the SetData method (in addition to GetData :) - the shell 1304 * passes a barely documented "Icon positions" structure to SetData when the 1305 * drag starts, and GetData's it if the drop is in another explorer window that 1306 * needs the positions. 1307 */ 1308 HRESULT WINAPI CFSFolder::GetUIObjectOf(HWND hwndOwner, 1309 UINT cidl, PCUITEMID_CHILD_ARRAY apidl, 1310 REFIID riid, UINT * prgfInOut, 1311 LPVOID * ppvOut) 1312 { 1313 LPVOID pObj = NULL; 1314 HRESULT hr = E_INVALIDARG; 1315 1316 TRACE ("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n", 1317 this, hwndOwner, cidl, apidl, shdebugstr_guid (&riid), prgfInOut, ppvOut); 1318 1319 if (ppvOut) 1320 { 1321 *ppvOut = NULL; 1322 1323 if (cidl == 1 && _ILIsValue(apidl[0])) 1324 { 1325 hr = _CreateExtensionUIObject(apidl[0], riid, ppvOut); 1326 if(hr != S_FALSE) 1327 return hr; 1328 } 1329 1330 if (IsEqualIID(riid, IID_IContextMenu) && (cidl >= 1)) 1331 { 1332 HKEY hKeys[16]; 1333 UINT cKeys = 0; 1334 AddFSClassKeysToArray(cidl, apidl, hKeys, &cKeys); 1335 1336 DEFCONTEXTMENU dcm; 1337 dcm.hwnd = hwndOwner; 1338 dcm.pcmcb = this; 1339 dcm.pidlFolder = m_pidlRoot; 1340 dcm.psf = this; 1341 dcm.cidl = cidl; 1342 dcm.apidl = apidl; 1343 dcm.cKeys = cKeys; 1344 dcm.aKeys = hKeys; 1345 dcm.punkAssociationInfo = NULL; 1346 hr = SHCreateDefaultContextMenu (&dcm, riid, &pObj); 1347 } 1348 else if (IsEqualIID (riid, IID_IDataObject)) 1349 { 1350 if (cidl >= 1) 1351 { 1352 hr = IDataObject_Constructor (hwndOwner, m_pidlRoot, apidl, cidl, TRUE, (IDataObject **)&pObj); 1353 } 1354 else 1355 { 1356 hr = E_INVALIDARG; 1357 } 1358 } 1359 else if ((IsEqualIID (riid, IID_IExtractIconA) || IsEqualIID (riid, IID_IExtractIconW)) && (cidl == 1)) 1360 { 1361 if (_ILIsValue(apidl[0])) 1362 hr = _GetIconHandler(apidl[0], riid, (LPVOID*)&pObj); 1363 if (hr != S_OK) 1364 hr = CFSExtractIcon_CreateInstance(this, apidl[0], riid, &pObj); 1365 } 1366 else if (IsEqualIID (riid, IID_IDropTarget)) 1367 { 1368 /* only interested in attempting to bind to shell folders, not files (except exe), so if we fail, rebind to root */ 1369 if (cidl != 1 || FAILED(hr = this->_GetDropTarget(apidl[0], (LPVOID*) &pObj))) 1370 { 1371 hr = CFSDropTarget_CreateInstance(m_sPathTarget, riid, (LPVOID*) &pObj); 1372 } 1373 } 1374 else 1375 hr = E_NOINTERFACE; 1376 1377 if (SUCCEEDED(hr) && !pObj) 1378 hr = E_OUTOFMEMORY; 1379 1380 *ppvOut = pObj; 1381 } 1382 TRACE("(%p)->hr=0x%08x\n", this, hr); 1383 return hr; 1384 } 1385 1386 /****************************************************************************** 1387 * SHELL_FS_HideExtension [Internal] 1388 * 1389 * Query the registry if the filename extension of a given path should be 1390 * hidden. 1391 * 1392 * PARAMS 1393 * szPath [I] Relative or absolute path of a file 1394 * 1395 * RETURNS 1396 * TRUE, if the filename's extension should be hidden 1397 * FALSE, otherwise. 1398 */ 1399 BOOL SHELL_FS_HideExtension(LPCWSTR szPath) 1400 { 1401 HKEY hKey; 1402 BOOL doHide = FALSE; /* The default value is FALSE (win98 at least) */ 1403 LONG lError; 1404 1405 doHide = !SHELL_GetSetting(SSF_SHOWEXTENSIONS, fShowExtensions); 1406 1407 if (!doHide) 1408 { 1409 LPCWSTR DotExt = PathFindExtensionW(szPath); 1410 if (*DotExt != 0) 1411 { 1412 WCHAR classname[MAX_PATH]; 1413 LONG classlen = sizeof(classname); 1414 lError = RegQueryValueW(HKEY_CLASSES_ROOT, DotExt, classname, &classlen); 1415 if (lError == ERROR_SUCCESS) 1416 { 1417 lError = RegOpenKeyW(HKEY_CLASSES_ROOT, classname, &hKey); 1418 if (lError == ERROR_SUCCESS) 1419 { 1420 lError = RegQueryValueExW(hKey, L"NeverShowExt", NULL, NULL, NULL, NULL); 1421 if (lError == ERROR_SUCCESS) 1422 doHide = TRUE; 1423 1424 RegCloseKey(hKey); 1425 } 1426 } 1427 } 1428 } 1429 1430 return doHide; 1431 } 1432 1433 void SHELL_FS_ProcessDisplayFilename(LPWSTR szPath, DWORD dwFlags) 1434 { 1435 /*FIXME: MSDN also mentions SHGDN_FOREDITING which is not yet handled. */ 1436 if (!(dwFlags & SHGDN_FORPARSING) && 1437 ((dwFlags & SHGDN_INFOLDER) || (dwFlags == SHGDN_NORMAL))) { 1438 if (SHELL_FS_HideExtension(szPath) && szPath[0] != '.') 1439 PathRemoveExtensionW(szPath); 1440 } 1441 } 1442 1443 /************************************************************************** 1444 * CFSFolder::GetDisplayNameOf 1445 * Retrieves the display name for the specified file object or subfolder 1446 * 1447 * PARAMETERS 1448 * LPCITEMIDLIST pidl, //[in ] complex pidl to item 1449 * DWORD dwFlags, //[in ] SHGNO formatting flags 1450 * LPSTRRET lpName) //[out] Returned display name 1451 * 1452 * FIXME 1453 * if the name is in the pidl the ret value should be a STRRET_OFFSET 1454 */ 1455 1456 HRESULT WINAPI CFSFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl, 1457 DWORD dwFlags, LPSTRRET strRet) 1458 { 1459 if (!strRet) 1460 return E_INVALIDARG; 1461 1462 /* If it is a complex pidl, let the child handle it */ 1463 if (!_ILIsPidlSimple (pidl)) /* complex pidl */ 1464 { 1465 return SHELL32_GetDisplayNameOfChild(this, pidl, dwFlags, strRet); 1466 } 1467 else if (pidl && !pidl->mkid.cb) /* empty pidl */ 1468 { 1469 /* If it is an empty pidl return only the path of the folder */ 1470 if ((GET_SHGDN_FOR(dwFlags) & SHGDN_FORPARSING) && 1471 (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER) && 1472 m_sPathTarget) 1473 { 1474 return SHSetStrRet(strRet, m_sPathTarget); 1475 } 1476 return E_INVALIDARG; 1477 } 1478 1479 int len = 0; 1480 LPWSTR pszPath = (LPWSTR)CoTaskMemAlloc((MAX_PATH + 1) * sizeof(WCHAR)); 1481 if (!pszPath) 1482 return E_OUTOFMEMORY; 1483 1484 if ((GET_SHGDN_FOR(dwFlags) & SHGDN_FORPARSING) && 1485 (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER) && 1486 m_sPathTarget) 1487 { 1488 lstrcpynW(pszPath, m_sPathTarget, MAX_PATH); 1489 PathAddBackslashW(pszPath); 1490 len = wcslen(pszPath); 1491 } 1492 _ILSimpleGetTextW(pidl, pszPath + len, MAX_PATH + 1 - len); 1493 if (!_ILIsFolder(pidl)) SHELL_FS_ProcessDisplayFilename(pszPath, dwFlags); 1494 1495 strRet->uType = STRRET_WSTR; 1496 strRet->pOleStr = pszPath; 1497 1498 TRACE ("-- (%p)->(%s)\n", this, strRet->uType == STRRET_CSTR ? strRet->cStr : debugstr_w(strRet->pOleStr)); 1499 return S_OK; 1500 } 1501 1502 /************************************************************************** 1503 * CFSFolder::SetNameOf 1504 * Changes the name of a file object or subfolder, possibly changing its item 1505 * identifier in the process. 1506 * 1507 * PARAMETERS 1508 * HWND hwndOwner, //[in ] Owner window for output 1509 * LPCITEMIDLIST pidl, //[in ] simple pidl of item to change 1510 * LPCOLESTR lpszName, //[in ] the items new display name 1511 * DWORD dwFlags, //[in ] SHGNO formatting flags 1512 * LPITEMIDLIST* ppidlOut) //[out] simple pidl returned 1513 */ 1514 HRESULT WINAPI CFSFolder::SetNameOf( 1515 HWND hwndOwner, 1516 PCUITEMID_CHILD pidl, 1517 LPCOLESTR lpName, 1518 DWORD dwFlags, 1519 PITEMID_CHILD *pPidlOut) 1520 { 1521 WCHAR szSrc[MAX_PATH + 1], szDest[MAX_PATH + 1]; 1522 BOOL bIsFolder = _ILIsFolder (ILFindLastID (pidl)); 1523 1524 TRACE ("(%p)->(%p,pidl=%p,%s,%u,%p)\n", this, hwndOwner, pidl, 1525 debugstr_w (lpName), dwFlags, pPidlOut); 1526 1527 FileStructW* pDataW = _ILGetFileStructW(pidl); 1528 if (!pDataW) 1529 { 1530 ERR("Got garbage pidl:\n"); 1531 pdump_always(pidl); 1532 return E_INVALIDARG; 1533 } 1534 1535 /* build source path */ 1536 PathCombineW(szSrc, m_sPathTarget, pDataW->wszName); // FIXME: PIDLs without wide string 1537 1538 /* build destination path */ 1539 if (dwFlags == SHGDN_NORMAL || dwFlags & SHGDN_INFOLDER) 1540 PathCombineW(szDest, m_sPathTarget, lpName); 1541 else 1542 lstrcpynW(szDest, lpName, MAX_PATH); 1543 1544 if (!(dwFlags & SHGDN_FORPARSING) && !bIsFolder && SHELL_FS_HideExtension(szSrc)) 1545 { 1546 LPCWSTR ext = PathFindExtensionW(szSrc); 1547 if (*ext) 1548 PathAddExtensionW(szDest, ext); 1549 } 1550 1551 HRESULT hr = S_OK; 1552 TRACE ("src=%s dest=%s\n", debugstr_w(szSrc), debugstr_w(szDest)); 1553 if (!wcscmp(szSrc, szDest)) 1554 { 1555 /* src and destination is the same */ 1556 if (pPidlOut) 1557 hr = SHILClone(pidl, pPidlOut); 1558 } 1559 else if (MoveFileW(szSrc, szDest)) 1560 { 1561 if (pPidlOut) 1562 hr = ParseDisplayName(hwndOwner, NULL, PathFindFileNameW(szDest), NULL, pPidlOut, NULL); 1563 1564 SHChangeNotify(bIsFolder ? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM, SHCNF_PATHW, szSrc, szDest); 1565 } 1566 else 1567 { 1568 hr = HResultFromWin32(GetLastError()); 1569 } 1570 return hr; 1571 } 1572 1573 HRESULT WINAPI CFSFolder::GetDefaultSearchGUID(GUID * pguid) 1574 { 1575 FIXME ("(%p)\n", this); 1576 return E_NOTIMPL; 1577 } 1578 1579 HRESULT WINAPI CFSFolder::EnumSearches(IEnumExtraSearch ** ppenum) 1580 { 1581 FIXME ("(%p)\n", this); 1582 return E_NOTIMPL; 1583 } 1584 1585 HRESULT WINAPI CFSFolder::GetDefaultColumn(DWORD dwRes, 1586 ULONG * pSort, ULONG * pDisplay) 1587 { 1588 TRACE ("(%p)\n", this); 1589 1590 if (pSort) 1591 *pSort = 0; 1592 if (pDisplay) 1593 *pDisplay = 0; 1594 1595 return S_OK; 1596 } 1597 1598 HRESULT WINAPI CFSFolder::GetDefaultColumnState(UINT iColumn, 1599 SHCOLSTATEF *pcsFlags) 1600 { 1601 TRACE ("(%p)\n", this); 1602 1603 if (!pcsFlags) 1604 return E_INVALIDARG; 1605 else 1606 return GetDefaultFSColumnState(iColumn, *pcsFlags); 1607 } 1608 1609 HRESULT WINAPI CFSFolder::GetDetailsEx(PCUITEMID_CHILD pidl, 1610 const SHCOLUMNID * pscid, VARIANT * pv) 1611 { 1612 FIXME ("(%p)\n", this); 1613 1614 return E_NOTIMPL; 1615 } 1616 1617 HRESULT WINAPI CFSFolder::GetDetailsOf(PCUITEMID_CHILD pidl, 1618 UINT iColumn, SHELLDETAILS * psd) 1619 { 1620 HRESULT hr = E_FAIL; 1621 1622 TRACE ("(%p)->(%p %i %p)\n", this, pidl, iColumn, psd); 1623 1624 if (!psd || iColumn >= GENERICSHELLVIEWCOLUMNS) 1625 return E_INVALIDARG; 1626 1627 if (!pidl) 1628 { 1629 /* the header titles */ 1630 return GetFSColumnDetails(iColumn, *psd); 1631 } 1632 else 1633 { 1634 hr = S_OK; 1635 psd->str.uType = STRRET_WSTR; 1636 psd->str.pOleStr = (LPWSTR)CoTaskMemAlloc(MAX_PATH * sizeof(WCHAR)); 1637 /* the data from the pidl */ 1638 switch (iColumn) 1639 { 1640 case SHFSF_COL_NAME: 1641 hr = GetDisplayNameOf (pidl, SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str); 1642 break; 1643 case SHFSF_COL_SIZE: 1644 _ILGetFileSize(pidl, psd->str.pOleStr, MAX_PATH); 1645 break; 1646 case SHFSF_COL_TYPE: 1647 _ILGetFileType(pidl, psd->str.pOleStr, MAX_PATH); 1648 break; 1649 case SHFSF_COL_MDATE: 1650 _ILGetFileDate(pidl, psd->str.pOleStr, MAX_PATH); 1651 break; 1652 case SHFSF_COL_FATTS: 1653 _ILGetFileAttributes(pidl, psd->str.pOleStr, MAX_PATH); 1654 break; 1655 case SHFSF_COL_COMMENT: 1656 psd->str.pOleStr[0] = UNICODE_NULL; // TODO: Extract comment from .lnk files? desktop.ini? 1657 break; 1658 #if DBG 1659 default: 1660 ERR("Missing case for column %d\n", iColumn); 1661 #else 1662 DEFAULT_UNREACHABLE; 1663 #endif 1664 } 1665 } 1666 1667 return hr; 1668 } 1669 1670 HRESULT WINAPI CFSFolder::MapColumnToSCID (UINT column, 1671 SHCOLUMNID * pscid) 1672 { 1673 FIXME ("(%p)\n", this); 1674 return E_NOTIMPL; 1675 } 1676 1677 /************************************************************************ 1678 * CFSFolder::GetClassID 1679 */ 1680 HRESULT WINAPI CFSFolder::GetClassID(CLSID * lpClassId) 1681 { 1682 TRACE ("(%p)\n", this); 1683 1684 if (!lpClassId) 1685 return E_POINTER; 1686 1687 *lpClassId = *m_pclsid; 1688 1689 return S_OK; 1690 } 1691 1692 /************************************************************************ 1693 * CFSFolder::Initialize 1694 * 1695 * NOTES 1696 * m_sPathTarget is not set. Don't know how to handle in a non rooted environment. 1697 */ 1698 HRESULT WINAPI CFSFolder::Initialize(PCIDLIST_ABSOLUTE pidl) 1699 { 1700 WCHAR wszTemp[MAX_PATH]; 1701 1702 TRACE ("(%p)->(%p)\n", this, pidl); 1703 1704 SHFree(m_pidlRoot); /* free the old pidl */ 1705 m_pidlRoot = ILClone (pidl); /* set my pidl */ 1706 1707 SHFree (m_sPathTarget); 1708 m_sPathTarget = NULL; 1709 1710 /* set my path */ 1711 if (SHGetPathFromIDListW (pidl, wszTemp)) 1712 { 1713 int len = wcslen(wszTemp); 1714 m_sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR)); 1715 if (!m_sPathTarget) 1716 return E_OUTOFMEMORY; 1717 memcpy(m_sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR)); 1718 } 1719 1720 TRACE ("--(%p)->(%s)\n", this, debugstr_w(m_sPathTarget)); 1721 return S_OK; 1722 } 1723 1724 /************************************************************************** 1725 * CFSFolder::GetCurFolder 1726 */ 1727 HRESULT WINAPI CFSFolder::GetCurFolder(PIDLIST_ABSOLUTE * pidl) 1728 { 1729 TRACE ("(%p)->(%p)\n", this, pidl); 1730 1731 if (!pidl) 1732 return E_POINTER; 1733 1734 *pidl = ILClone(m_pidlRoot); 1735 return S_OK; 1736 } 1737 1738 /************************************************************************** 1739 * CFSFolder::InitializeEx 1740 * 1741 * FIXME: error handling 1742 */ 1743 HRESULT WINAPI CFSFolder::InitializeEx(IBindCtx * pbc, LPCITEMIDLIST pidlRootx, 1744 const PERSIST_FOLDER_TARGET_INFO * ppfti) 1745 { 1746 WCHAR wszTemp[MAX_PATH]; 1747 1748 TRACE("(%p)->(%p,%p,%p)\n", this, pbc, pidlRootx, ppfti); 1749 if (ppfti) 1750 TRACE("--%p %s %s 0x%08x 0x%08x\n", 1751 ppfti->pidlTargetFolder, debugstr_w (ppfti->szTargetParsingName), 1752 debugstr_w (ppfti->szNetworkProvider), ppfti->dwAttributes, 1753 ppfti->csidl); 1754 1755 pdump (pidlRootx); 1756 if (ppfti && ppfti->pidlTargetFolder) 1757 pdump(ppfti->pidlTargetFolder); 1758 1759 if (m_pidlRoot) 1760 __SHFreeAndNil(&m_pidlRoot); /* free the old */ 1761 if (m_sPathTarget) 1762 __SHFreeAndNil(&m_sPathTarget); 1763 1764 /* 1765 * Root path and pidl 1766 */ 1767 m_pidlRoot = ILClone(pidlRootx); 1768 1769 /* 1770 * the target folder is spezified in csidl OR pidlTargetFolder OR 1771 * szTargetParsingName 1772 */ 1773 if (ppfti) 1774 { 1775 if (ppfti->csidl != -1) 1776 { 1777 if (SHGetSpecialFolderPathW(0, wszTemp, ppfti->csidl, 1778 ppfti->csidl & CSIDL_FLAG_CREATE)) { 1779 int len = wcslen(wszTemp); 1780 m_sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR)); 1781 if (!m_sPathTarget) 1782 return E_OUTOFMEMORY; 1783 memcpy(m_sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR)); 1784 } 1785 } 1786 else if (ppfti->szTargetParsingName[0]) 1787 { 1788 int len = wcslen(ppfti->szTargetParsingName); 1789 m_sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR)); 1790 if (!m_sPathTarget) 1791 return E_OUTOFMEMORY; 1792 memcpy(m_sPathTarget, ppfti->szTargetParsingName, 1793 (len + 1) * sizeof(WCHAR)); 1794 } 1795 else if (ppfti->pidlTargetFolder) 1796 { 1797 if (SHGetPathFromIDListW(ppfti->pidlTargetFolder, wszTemp)) 1798 { 1799 int len = wcslen(wszTemp); 1800 m_sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR)); 1801 if (!m_sPathTarget) 1802 return E_OUTOFMEMORY; 1803 memcpy(m_sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR)); 1804 } 1805 } 1806 } 1807 1808 TRACE("--(%p)->(target=%s)\n", this, debugstr_w(m_sPathTarget)); 1809 pdump(m_pidlRoot); 1810 return (m_sPathTarget) ? S_OK : E_FAIL; 1811 } 1812 1813 HRESULT WINAPI CFSFolder::GetFolderTargetInfo(PERSIST_FOLDER_TARGET_INFO * ppfti) 1814 { 1815 FIXME("(%p)->(%p)\n", this, ppfti); 1816 ZeroMemory(ppfti, sizeof (*ppfti)); 1817 return E_NOTIMPL; 1818 } 1819 1820 HRESULT CFSFolder::_CreateExtensionUIObject(PCUIDLIST_RELATIVE pidl, REFIID riid, LPVOID *ppvOut) 1821 { 1822 WCHAR buf[MAX_PATH]; 1823 1824 sprintfW(buf, L"ShellEx\\{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}", 1825 riid.Data1, riid.Data2, riid.Data3, 1826 riid.Data4[0], riid.Data4[1], riid.Data4[2], riid.Data4[3], 1827 riid.Data4[4], riid.Data4[5], riid.Data4[6], riid.Data4[7]); 1828 1829 CLSID clsid; 1830 HRESULT hr; 1831 1832 hr = GetCLSIDForFileType(pidl, buf, &clsid); 1833 if (hr != S_OK) 1834 return hr; 1835 1836 hr = _CreateShellExtInstance(&clsid, pidl, riid, ppvOut); 1837 if (FAILED_UNEXPECTEDLY(hr)) 1838 return hr; 1839 1840 return S_OK; 1841 } 1842 1843 HRESULT CFSFolder::_GetDropTarget(LPCITEMIDLIST pidl, LPVOID *ppvOut) 1844 { 1845 HRESULT hr; 1846 1847 TRACE("CFSFolder::_GetDropTarget entered\n"); 1848 1849 if (_ILIsFolder (pidl)) 1850 { 1851 CComPtr<IShellFolder> psfChild; 1852 hr = this->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfChild)); 1853 if (FAILED_UNEXPECTEDLY(hr)) 1854 return hr; 1855 1856 return psfChild->CreateViewObject(NULL, IID_IDropTarget, ppvOut); 1857 } 1858 1859 CLSID clsid; 1860 hr = GetCLSIDForFileType(pidl, L"shellex\\DropHandler", &clsid); 1861 if (hr != S_OK) 1862 return hr; 1863 1864 hr = _CreateShellExtInstance(&clsid, pidl, IID_IDropTarget, ppvOut); 1865 if (FAILED_UNEXPECTEDLY(hr)) 1866 return S_FALSE; 1867 1868 return S_OK; 1869 } 1870 1871 HRESULT CFSFolder::_GetIconHandler(LPCITEMIDLIST pidl, REFIID riid, LPVOID *ppvOut) 1872 { 1873 CLSID clsid; 1874 HRESULT hr; 1875 1876 hr = GetCLSIDForFileType(pidl, L"shellex\\IconHandler", &clsid); 1877 if (hr != S_OK) 1878 return hr; 1879 1880 hr = _CreateShellExtInstance(&clsid, pidl, riid, ppvOut); 1881 if (FAILED_UNEXPECTEDLY(hr)) 1882 return S_FALSE; 1883 1884 return S_OK; 1885 } 1886 1887 HRESULT CFSFolder::_CreateShellExtInstance(const CLSID *pclsid, LPCITEMIDLIST pidl, REFIID riid, LPVOID *ppvOut) 1888 { 1889 HRESULT hr; 1890 WCHAR wszPath[MAX_PATH]; 1891 1892 FileStructW* pDataW = _ILGetFileStructW(pidl); 1893 if (!pDataW) 1894 { 1895 ERR("Got garbage pidl\n"); 1896 pdump_always(pidl); 1897 return E_INVALIDARG; 1898 } 1899 1900 PathCombineW(wszPath, m_sPathTarget, pDataW->wszName); 1901 1902 CComPtr<IPersistFile> pp; 1903 hr = SHCoCreateInstance(NULL, pclsid, NULL, IID_PPV_ARG(IPersistFile, &pp)); 1904 if (FAILED_UNEXPECTEDLY(hr)) 1905 return hr; 1906 1907 pp->Load(wszPath, 0); 1908 1909 hr = pp->QueryInterface(riid, ppvOut); 1910 if (hr != S_OK) 1911 { 1912 ERR("Failed to query for interface IID_IShellExtInit hr %x pclsid %s\n", hr, wine_dbgstr_guid(pclsid)); 1913 return hr; 1914 } 1915 return hr; 1916 } 1917 1918 HRESULT WINAPI CFSFolder::CallBack(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam) 1919 { 1920 enum { IDC_PROPERTIES }; 1921 /* no data object means no selection */ 1922 if (!pdtobj) 1923 { 1924 if (uMsg == DFM_INVOKECOMMAND && wParam == IDC_PROPERTIES) 1925 { 1926 // Create an data object 1927 CComHeapPtr<ITEMID_CHILD> pidlChild(ILClone(ILFindLastID(m_pidlRoot))); 1928 CComHeapPtr<ITEMIDLIST> pidlParent(ILClone(m_pidlRoot)); 1929 ILRemoveLastID(pidlParent); 1930 1931 CComPtr<IDataObject> pDataObj; 1932 HRESULT hr = SHCreateDataObject(pidlParent, 1, &pidlChild.m_pData, NULL, IID_PPV_ARG(IDataObject, &pDataObj)); 1933 if (!FAILED_UNEXPECTEDLY(hr)) 1934 { 1935 // Ask for a title to display 1936 CComHeapPtr<WCHAR> wszName; 1937 if (!FAILED_UNEXPECTEDLY(SHGetNameFromIDList(m_pidlRoot, SIGDN_PARENTRELATIVEPARSING, &wszName))) 1938 { 1939 BOOL bSuccess = SH_ShowPropertiesDialog(wszName, pDataObj); 1940 if (!bSuccess) 1941 ERR("SH_ShowPropertiesDialog failed\n"); 1942 } 1943 } 1944 return hr; 1945 } 1946 else if (uMsg == DFM_MERGECONTEXTMENU) 1947 { 1948 QCMINFO *pqcminfo = (QCMINFO *)lParam; 1949 HMENU hpopup = CreatePopupMenu(); 1950 _InsertMenuItemW(hpopup, 0, TRUE, IDC_PROPERTIES, MFT_STRING, MAKEINTRESOURCEW(IDS_PROPERTIES), MFS_ENABLED); 1951 pqcminfo->idCmdFirst = Shell_MergeMenus(pqcminfo->hmenu, hpopup, pqcminfo->indexMenu, pqcminfo->idCmdFirst, pqcminfo->idCmdLast, MM_ADDSEPARATOR); 1952 DestroyMenu(hpopup); 1953 return S_OK; 1954 } 1955 } 1956 return SHELL32_DefaultContextMenuCallBack(psf, pdtobj, uMsg); 1957 } 1958 1959 static HBITMAP DoLoadPicture(LPCWSTR pszFileName) 1960 { 1961 // create stream from file 1962 HRESULT hr; 1963 CComPtr<IStream> pStream; 1964 hr = SHCreateStreamOnFileEx(pszFileName, STGM_READ, FILE_ATTRIBUTE_NORMAL, 1965 FALSE, NULL, &pStream); 1966 if (FAILED(hr)) 1967 return NULL; 1968 1969 // load the picture 1970 HBITMAP hbm = NULL; 1971 CComPtr<IPicture> pPicture; 1972 OleLoadPicture(pStream, 0, FALSE, IID_IPicture, (LPVOID *)&pPicture); 1973 1974 // get the bitmap handle 1975 if (pPicture) 1976 { 1977 pPicture->get_Handle((OLE_HANDLE *)&hbm); 1978 1979 // copy the bitmap handle 1980 hbm = (HBITMAP)CopyImage(hbm, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION); 1981 } 1982 1983 return hbm; 1984 } 1985 1986 HRESULT WINAPI CFSFolder::GetCustomViewInfo(ULONG unknown, SFVM_CUSTOMVIEWINFO_DATA *data) 1987 { 1988 if (data == NULL) 1989 { 1990 return E_POINTER; 1991 } 1992 if (data->cbSize != sizeof(*data)) 1993 { 1994 // NOTE: You have to set the cbData member before SFVM_GET_CUSTOMVIEWINFO call. 1995 return E_INVALIDARG; 1996 } 1997 1998 data->hbmBack = NULL; 1999 data->clrText = CLR_INVALID; 2000 data->clrTextBack = CLR_INVALID; 2001 2002 WCHAR szPath[MAX_PATH], szIniFile[MAX_PATH]; 2003 2004 // does the folder exists? 2005 if (!SHGetPathFromIDListW(m_pidlRoot, szPath) || !PathIsDirectoryW(szPath)) 2006 { 2007 return E_INVALIDARG; 2008 } 2009 2010 // don't use custom view in network path for security 2011 if (PathIsNetworkPath(szPath)) 2012 { 2013 return E_ACCESSDENIED; 2014 } 2015 2016 // build the ini file path 2017 StringCchCopyW(szIniFile, _countof(szIniFile), szPath); 2018 PathAppend(szIniFile, L"desktop.ini"); 2019 2020 static LPCWSTR TheGUID = L"{BE098140-A513-11D0-A3A4-00C04FD706EC}"; 2021 static LPCWSTR Space = L" \t\n\r\f\v"; 2022 2023 // get info from ini file 2024 WCHAR szImage[MAX_PATH], szText[64]; 2025 2026 // load the image 2027 szImage[0] = UNICODE_NULL; 2028 GetPrivateProfileStringW(TheGUID, L"IconArea_Image", L"", szImage, _countof(szImage), szIniFile); 2029 if (szImage[0]) 2030 { 2031 StrTrimW(szImage, Space); 2032 if (PathIsRelativeW(szImage)) 2033 { 2034 PathAppendW(szPath, szImage); 2035 StringCchCopyW(szImage, _countof(szImage), szPath); 2036 } 2037 data->hbmBack = DoLoadPicture(szImage); 2038 } 2039 2040 // load the text color 2041 szText[0] = UNICODE_NULL; 2042 GetPrivateProfileStringW(TheGUID, L"IconArea_Text", L"", szText, _countof(szText), szIniFile); 2043 if (szText[0]) 2044 { 2045 StrTrimW(szText, Space); 2046 2047 LPWSTR pchEnd = NULL; 2048 COLORREF cr = (wcstol(szText, &pchEnd, 0) & 0xFFFFFF); 2049 2050 if (pchEnd && !*pchEnd) 2051 data->clrText = cr; 2052 } 2053 2054 // load the text background color 2055 szText[0] = UNICODE_NULL; 2056 GetPrivateProfileStringW(TheGUID, L"IconArea_TextBackground", L"", szText, _countof(szText), szIniFile); 2057 if (szText[0]) 2058 { 2059 StrTrimW(szText, Space); 2060 2061 LPWSTR pchEnd = NULL; 2062 COLORREF cr = (wcstol(szText, &pchEnd, 0) & 0xFFFFFF); 2063 2064 if (pchEnd && !*pchEnd) 2065 data->clrTextBack = cr; 2066 } 2067 2068 if (data->hbmBack != NULL || data->clrText != CLR_INVALID || data->clrTextBack != CLR_INVALID) 2069 return S_OK; 2070 2071 return E_FAIL; 2072 } 2073 2074 HRESULT WINAPI CFSFolder::MessageSFVCB(UINT uMsg, WPARAM wParam, LPARAM lParam) 2075 { 2076 HRESULT hr = E_NOTIMPL; 2077 switch (uMsg) 2078 { 2079 case SFVM_GET_CUSTOMVIEWINFO: 2080 hr = GetCustomViewInfo((ULONG)wParam, (SFVM_CUSTOMVIEWINFO_DATA *)lParam); 2081 break; 2082 } 2083 return hr; 2084 } 2085