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