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