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("SHOULD DO SOMETHING WITH CLSID?\n"); 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 clsidFolder = CLSID_ShellFSFolder; 887 888 if ((pData->uFileAttribs & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)) != 0) 889 SHELL32_GetCLSIDForDirectory(pfti.szTargetParsingName, L"CLSID", &clsidFolder); 890 } 891 else 892 { 893 hr = GetCLSIDForFileType(pidl, L"CLSID", &clsidFolder); 894 if (hr == S_FALSE) 895 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); 896 if (hr != S_OK) 897 return hr; 898 } 899 900 hr = SHELL32_BindToSF(m_pidlRoot, &pfti, pidl, &clsidFolder, riid, ppvOut); 901 if (FAILED_UNEXPECTEDLY(hr)) 902 return hr; 903 904 TRACE ("-- returning (%p) %08x\n", *ppvOut, hr); 905 906 return S_OK; 907 908 } 909 910 /************************************************************************** 911 * CFSFolder::BindToStorage 912 * PARAMETERS 913 * LPCITEMIDLIST pidl, //[in ] complex pidl to store 914 * LPBC pbc, //[in ] reserved 915 * REFIID riid, //[in ] Initial storage interface 916 * LPVOID* ppvObject //[out] Interface* returned 917 */ 918 HRESULT WINAPI CFSFolder::BindToStorage( 919 PCUIDLIST_RELATIVE pidl, 920 LPBC pbcReserved, 921 REFIID riid, 922 LPVOID *ppvOut) 923 { 924 FIXME("(%p)->(pidl=%p,%p,%s,%p) stub\n", this, pidl, pbcReserved, 925 shdebugstr_guid (&riid), ppvOut); 926 927 *ppvOut = NULL; 928 return E_NOTIMPL; 929 } 930 931 /************************************************************************** 932 * CFSFolder::CompareIDs 933 */ 934 935 HRESULT WINAPI CFSFolder::CompareIDs(LPARAM lParam, 936 PCUIDLIST_RELATIVE pidl1, 937 PCUIDLIST_RELATIVE pidl2) 938 { 939 LPPIDLDATA pData1 = _ILGetDataPointer(pidl1); 940 LPPIDLDATA pData2 = _ILGetDataPointer(pidl2); 941 FileStructW* pDataW1 = _ILGetFileStructW(pidl1); 942 FileStructW* pDataW2 = _ILGetFileStructW(pidl2); 943 BOOL bIsFolder1 = _ILIsFolder(pidl1); 944 BOOL bIsFolder2 = _ILIsFolder(pidl2); 945 LPWSTR pExtension1, pExtension2; 946 947 if (!pDataW1 || !pDataW2 || LOWORD(lParam) >= GENERICSHELLVIEWCOLUMNS) 948 return E_INVALIDARG; 949 950 /* When sorting between a File and a Folder, the Folder gets sorted first */ 951 if (bIsFolder1 != bIsFolder2) 952 { 953 return MAKE_COMPARE_HRESULT(bIsFolder1 ? -1 : 1); 954 } 955 956 int result; 957 switch (LOWORD(lParam)) 958 { 959 case 0: /* Name */ 960 result = wcsicmp(pDataW1->wszName, pDataW2->wszName); 961 break; 962 case 1: /* Comments */ 963 result = 0; 964 break; 965 case 2: /* Type */ 966 pExtension1 = PathFindExtensionW(pDataW1->wszName); 967 pExtension2 = PathFindExtensionW(pDataW2->wszName); 968 result = wcsicmp(pExtension1, pExtension2); 969 break; 970 case 3: /* Size */ 971 if (pData1->u.file.dwFileSize > pData2->u.file.dwFileSize) 972 result = 1; 973 else if (pData1->u.file.dwFileSize < pData2->u.file.dwFileSize) 974 result = -1; 975 else 976 result = 0; 977 break; 978 case 4: /* Modified */ 979 result = pData1->u.file.uFileDate - pData2->u.file.uFileDate; 980 if (result == 0) 981 result = pData1->u.file.uFileTime - pData2->u.file.uFileTime; 982 break; 983 case 5: /* Attributes */ 984 return SHELL32_CompareDetails(this, lParam, pidl1, pidl2); 985 } 986 987 if (result == 0) 988 return SHELL32_CompareChildren(this, lParam, pidl1, pidl2); 989 990 return MAKE_COMPARE_HRESULT(result); 991 } 992 993 /************************************************************************** 994 * CFSFolder::CreateViewObject 995 */ 996 HRESULT WINAPI CFSFolder::CreateViewObject(HWND hwndOwner, 997 REFIID riid, LPVOID * ppvOut) 998 { 999 CComPtr<IShellView> pShellView; 1000 HRESULT hr = E_INVALIDARG; 1001 1002 TRACE ("(%p)->(hwnd=%p,%s,%p)\n", this, hwndOwner, shdebugstr_guid (&riid), 1003 ppvOut); 1004 1005 if (ppvOut) 1006 { 1007 *ppvOut = NULL; 1008 1009 BOOL bIsDropTarget = IsEqualIID (riid, IID_IDropTarget); 1010 BOOL bIsShellView = !bIsDropTarget && IsEqualIID (riid, IID_IShellView); 1011 1012 if (bIsDropTarget || bIsShellView) 1013 { 1014 DWORD dwDirAttributes = _ILGetFileAttributes(ILFindLastID(m_pidlRoot), NULL, 0); 1015 1016 if ((dwDirAttributes & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)) != 0) 1017 { 1018 CLSID clsidFolder; 1019 hr = SHELL32_GetCLSIDForDirectory(m_sPathTarget, L"UICLSID", &clsidFolder); 1020 if (SUCCEEDED(hr)) 1021 { 1022 CComPtr<IPersistFolder> spFolder; 1023 hr = SHCoCreateInstance(NULL, &clsidFolder, NULL, IID_PPV_ARG(IPersistFolder, &spFolder)); 1024 if (!FAILED_UNEXPECTEDLY(hr)) 1025 { 1026 hr = spFolder->Initialize(m_pidlRoot); 1027 1028 if (!FAILED_UNEXPECTEDLY(hr)) 1029 { 1030 hr = spFolder->QueryInterface(riid, ppvOut); 1031 } 1032 } 1033 } 1034 else 1035 { 1036 // No desktop.ini, or no UICLSID present, continue as if nothing happened 1037 hr = E_INVALIDARG; 1038 } 1039 } 1040 } 1041 1042 if (!SUCCEEDED(hr)) 1043 { 1044 // No UICLSID handler found, continue to the default handlers 1045 if (bIsDropTarget) 1046 { 1047 hr = CFSDropTarget_CreateInstance(m_sPathTarget, riid, ppvOut); 1048 } 1049 else if (IsEqualIID (riid, IID_IContextMenu)) 1050 { 1051 HKEY hKeys[16]; 1052 UINT cKeys = 0; 1053 AddClassKeyToArray(L"Directory\\Background", hKeys, &cKeys); 1054 1055 DEFCONTEXTMENU dcm; 1056 dcm.hwnd = hwndOwner; 1057 dcm.pcmcb = this; 1058 dcm.pidlFolder = m_pidlRoot; 1059 dcm.psf = this; 1060 dcm.cidl = 0; 1061 dcm.apidl = NULL; 1062 dcm.cKeys = cKeys; 1063 dcm.aKeys = hKeys; 1064 dcm.punkAssociationInfo = NULL; 1065 hr = SHCreateDefaultContextMenu (&dcm, riid, ppvOut); 1066 } 1067 else if (bIsShellView) 1068 { 1069 SFV_CREATE sfvparams = {sizeof(SFV_CREATE), this, NULL, this}; 1070 hr = SHCreateShellFolderView(&sfvparams, (IShellView**)ppvOut); 1071 } 1072 else 1073 { 1074 hr = E_INVALIDARG; 1075 } 1076 } 1077 } 1078 TRACE("-- (%p)->(interface=%p)\n", this, ppvOut); 1079 return hr; 1080 } 1081 1082 /************************************************************************** 1083 * CFSFolder::GetAttributesOf 1084 * 1085 * PARAMETERS 1086 * UINT cidl, //[in ] num elements in pidl array 1087 * LPCITEMIDLIST* apidl, //[in ] simple pidl array 1088 * ULONG* rgfInOut) //[out] result array 1089 * 1090 */ 1091 HRESULT WINAPI CFSFolder::GetAttributesOf(UINT cidl, 1092 PCUITEMID_CHILD_ARRAY apidl, DWORD * rgfInOut) 1093 { 1094 HRESULT hr = S_OK; 1095 1096 if (!rgfInOut) 1097 return E_INVALIDARG; 1098 if (cidl && !apidl) 1099 return E_INVALIDARG; 1100 1101 if (*rgfInOut == 0) 1102 *rgfInOut = ~0; 1103 1104 if(cidl == 0) 1105 { 1106 LPCITEMIDLIST rpidl = ILFindLastID(m_pidlRoot); 1107 1108 if (_ILIsFolder(rpidl) || _ILIsValue(rpidl)) 1109 { 1110 SHELL32_GetFSItemAttributes(this, rpidl, rgfInOut); 1111 } 1112 else if (_ILIsDrive(rpidl)) 1113 { 1114 IShellFolder *psfParent = NULL; 1115 hr = SHBindToParent(m_pidlRoot, IID_PPV_ARG(IShellFolder, &psfParent), NULL); 1116 if(SUCCEEDED(hr)) 1117 { 1118 hr = psfParent->GetAttributesOf(1, &rpidl, (SFGAOF*)rgfInOut); 1119 psfParent->Release(); 1120 } 1121 } 1122 else 1123 { 1124 ERR("Got and unknown pidl!\n"); 1125 } 1126 } 1127 else 1128 { 1129 while (cidl > 0 && *apidl) 1130 { 1131 pdump(*apidl); 1132 if(_ILIsFolder(*apidl) || _ILIsValue(*apidl)) 1133 SHELL32_GetFSItemAttributes(this, *apidl, rgfInOut); 1134 else 1135 ERR("Got an unknown type of pidl!!!\n"); 1136 apidl++; 1137 cidl--; 1138 } 1139 } 1140 /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */ 1141 *rgfInOut &= ~SFGAO_VALIDATE; 1142 1143 TRACE("-- result=0x%08x\n", *rgfInOut); 1144 1145 return hr; 1146 } 1147 1148 /************************************************************************** 1149 * CFSFolder::GetUIObjectOf 1150 * 1151 * PARAMETERS 1152 * HWND hwndOwner, //[in ] Parent window for any output 1153 * UINT cidl, //[in ] array size 1154 * LPCITEMIDLIST* apidl, //[in ] simple pidl array 1155 * REFIID riid, //[in ] Requested Interface 1156 * UINT* prgfInOut, //[ ] reserved 1157 * LPVOID* ppvObject) //[out] Resulting Interface 1158 * 1159 * NOTES 1160 * This function gets asked to return "view objects" for one or more (multiple 1161 * select) items: 1162 * The viewobject typically is an COM object with one of the following 1163 * interfaces: 1164 * IExtractIcon,IDataObject,IContextMenu 1165 * In order to support icon positions in the default Listview your DataObject 1166 * must implement the SetData method (in addition to GetData :) - the shell 1167 * passes a barely documented "Icon positions" structure to SetData when the 1168 * drag starts, and GetData's it if the drop is in another explorer window that 1169 * needs the positions. 1170 */ 1171 HRESULT WINAPI CFSFolder::GetUIObjectOf(HWND hwndOwner, 1172 UINT cidl, PCUITEMID_CHILD_ARRAY apidl, 1173 REFIID riid, UINT * prgfInOut, 1174 LPVOID * ppvOut) 1175 { 1176 LPVOID pObj = NULL; 1177 HRESULT hr = E_INVALIDARG; 1178 1179 TRACE ("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n", 1180 this, hwndOwner, cidl, apidl, shdebugstr_guid (&riid), prgfInOut, ppvOut); 1181 1182 if (ppvOut) 1183 { 1184 *ppvOut = NULL; 1185 1186 if (cidl == 1 && _ILIsValue(apidl[0])) 1187 { 1188 hr = _CreateExtensionUIObject(apidl[0], riid, ppvOut); 1189 if(hr != S_FALSE) 1190 return hr; 1191 } 1192 1193 if (IsEqualIID(riid, IID_IContextMenu) && (cidl >= 1)) 1194 { 1195 HKEY hKeys[16]; 1196 UINT cKeys = 0; 1197 AddFSClassKeysToArray(apidl[0], hKeys, &cKeys); 1198 1199 DEFCONTEXTMENU dcm; 1200 dcm.hwnd = hwndOwner; 1201 dcm.pcmcb = this; 1202 dcm.pidlFolder = m_pidlRoot; 1203 dcm.psf = this; 1204 dcm.cidl = cidl; 1205 dcm.apidl = apidl; 1206 dcm.cKeys = cKeys; 1207 dcm.aKeys = hKeys; 1208 dcm.punkAssociationInfo = NULL; 1209 hr = SHCreateDefaultContextMenu (&dcm, riid, &pObj); 1210 } 1211 else if (IsEqualIID (riid, IID_IDataObject)) 1212 { 1213 if (cidl >= 1) 1214 { 1215 hr = IDataObject_Constructor (hwndOwner, m_pidlRoot, apidl, cidl, TRUE, (IDataObject **)&pObj); 1216 } 1217 else 1218 { 1219 hr = E_INVALIDARG; 1220 } 1221 } 1222 else if ((IsEqualIID (riid, IID_IExtractIconA) || IsEqualIID (riid, IID_IExtractIconW)) && (cidl == 1)) 1223 { 1224 if (_ILIsValue(apidl[0])) 1225 hr = _GetIconHandler(apidl[0], riid, (LPVOID*)&pObj); 1226 if (hr != S_OK) 1227 hr = CFSExtractIcon_CreateInstance(this, apidl[0], riid, &pObj); 1228 } 1229 else if (IsEqualIID (riid, IID_IDropTarget)) 1230 { 1231 /* only interested in attempting to bind to shell folders, not files (except exe), so if we fail, rebind to root */ 1232 if (cidl != 1 || FAILED(hr = this->_GetDropTarget(apidl[0], (LPVOID*) &pObj))) 1233 { 1234 hr = CFSDropTarget_CreateInstance(m_sPathTarget, riid, (LPVOID*) &pObj); 1235 } 1236 } 1237 else 1238 hr = E_NOINTERFACE; 1239 1240 if (SUCCEEDED(hr) && !pObj) 1241 hr = E_OUTOFMEMORY; 1242 1243 *ppvOut = pObj; 1244 } 1245 TRACE("(%p)->hr=0x%08x\n", this, hr); 1246 return hr; 1247 } 1248 1249 static const WCHAR AdvancedW[] = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced"; 1250 static const WCHAR HideFileExtW[] = L"HideFileExt"; 1251 static const WCHAR NeverShowExtW[] = L"NeverShowExt"; 1252 1253 /****************************************************************************** 1254 * SHELL_FS_HideExtension [Internal] 1255 * 1256 * Query the registry if the filename extension of a given path should be 1257 * hidden. 1258 * 1259 * PARAMS 1260 * szPath [I] Relative or absolute path of a file 1261 * 1262 * RETURNS 1263 * TRUE, if the filename's extension should be hidden 1264 * FALSE, otherwise. 1265 */ 1266 BOOL SHELL_FS_HideExtension(LPWSTR szPath) 1267 { 1268 HKEY hKey; 1269 DWORD dwData; 1270 DWORD dwDataSize = sizeof (DWORD); 1271 BOOL doHide = FALSE; /* The default value is FALSE (win98 at least) */ 1272 1273 if (!RegCreateKeyExW(HKEY_CURRENT_USER, AdvancedW, 0, 0, 0, KEY_ALL_ACCESS, 0, &hKey, 0)) { 1274 if (!RegQueryValueExW(hKey, HideFileExtW, 0, 0, (LPBYTE) &dwData, &dwDataSize)) 1275 doHide = dwData; 1276 RegCloseKey (hKey); 1277 } 1278 1279 if (!doHide) { 1280 LPWSTR ext = PathFindExtensionW(szPath); 1281 1282 if (*ext != '\0') { 1283 WCHAR classname[MAX_PATH]; 1284 LONG classlen = sizeof(classname); 1285 1286 if (!RegQueryValueW(HKEY_CLASSES_ROOT, ext, classname, &classlen)) 1287 if (!RegOpenKeyW(HKEY_CLASSES_ROOT, classname, &hKey)) { 1288 if (!RegQueryValueExW(hKey, NeverShowExtW, 0, NULL, NULL, NULL)) 1289 doHide = TRUE; 1290 RegCloseKey(hKey); 1291 } 1292 } 1293 } 1294 return doHide; 1295 } 1296 1297 void SHELL_FS_ProcessDisplayFilename(LPWSTR szPath, DWORD dwFlags) 1298 { 1299 /*FIXME: MSDN also mentions SHGDN_FOREDITING which is not yet handled. */ 1300 if (!(dwFlags & SHGDN_FORPARSING) && 1301 ((dwFlags & SHGDN_INFOLDER) || (dwFlags == SHGDN_NORMAL))) { 1302 if (SHELL_FS_HideExtension(szPath) && szPath[0] != '.') 1303 PathRemoveExtensionW(szPath); 1304 } 1305 } 1306 1307 /************************************************************************** 1308 * CFSFolder::GetDisplayNameOf 1309 * Retrieves the display name for the specified file object or subfolder 1310 * 1311 * PARAMETERS 1312 * LPCITEMIDLIST pidl, //[in ] complex pidl to item 1313 * DWORD dwFlags, //[in ] SHGNO formatting flags 1314 * LPSTRRET lpName) //[out] Returned display name 1315 * 1316 * FIXME 1317 * if the name is in the pidl the ret value should be a STRRET_OFFSET 1318 */ 1319 1320 HRESULT WINAPI CFSFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl, 1321 DWORD dwFlags, LPSTRRET strRet) 1322 { 1323 if (!strRet) 1324 return E_INVALIDARG; 1325 1326 /* If it is a complex pidl, let the child handle it */ 1327 if (!_ILIsPidlSimple (pidl)) /* complex pidl */ 1328 { 1329 return SHELL32_GetDisplayNameOfChild(this, pidl, dwFlags, strRet); 1330 } 1331 else if (pidl && !pidl->mkid.cb) /* empty pidl */ 1332 { 1333 /* If it is an empty pidl return only the path of the folder */ 1334 if ((GET_SHGDN_FOR(dwFlags) & SHGDN_FORPARSING) && 1335 (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER) && 1336 m_sPathTarget) 1337 { 1338 return SHSetStrRet(strRet, m_sPathTarget); 1339 } 1340 return E_INVALIDARG; 1341 } 1342 1343 int len = 0; 1344 LPWSTR pszPath = (LPWSTR)CoTaskMemAlloc((MAX_PATH + 1) * sizeof(WCHAR)); 1345 if (!pszPath) 1346 return E_OUTOFMEMORY; 1347 1348 if ((GET_SHGDN_FOR(dwFlags) & SHGDN_FORPARSING) && 1349 (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER) && 1350 m_sPathTarget) 1351 { 1352 lstrcpynW(pszPath, m_sPathTarget, MAX_PATH); 1353 PathAddBackslashW(pszPath); 1354 len = wcslen(pszPath); 1355 } 1356 _ILSimpleGetTextW(pidl, pszPath + len, MAX_PATH + 1 - len); 1357 if (!_ILIsFolder(pidl)) SHELL_FS_ProcessDisplayFilename(pszPath, dwFlags); 1358 1359 strRet->uType = STRRET_WSTR; 1360 strRet->pOleStr = pszPath; 1361 1362 TRACE ("-- (%p)->(%s)\n", this, strRet->uType == STRRET_CSTR ? strRet->cStr : debugstr_w(strRet->pOleStr)); 1363 return S_OK; 1364 } 1365 1366 /************************************************************************** 1367 * CFSFolder::SetNameOf 1368 * Changes the name of a file object or subfolder, possibly changing its item 1369 * identifier in the process. 1370 * 1371 * PARAMETERS 1372 * HWND hwndOwner, //[in ] Owner window for output 1373 * LPCITEMIDLIST pidl, //[in ] simple pidl of item to change 1374 * LPCOLESTR lpszName, //[in ] the items new display name 1375 * DWORD dwFlags, //[in ] SHGNO formatting flags 1376 * LPITEMIDLIST* ppidlOut) //[out] simple pidl returned 1377 */ 1378 HRESULT WINAPI CFSFolder::SetNameOf( 1379 HWND hwndOwner, 1380 PCUITEMID_CHILD pidl, 1381 LPCOLESTR lpName, 1382 DWORD dwFlags, 1383 PITEMID_CHILD *pPidlOut) 1384 { 1385 WCHAR szSrc[MAX_PATH + 1], szDest[MAX_PATH + 1]; 1386 BOOL bIsFolder = _ILIsFolder (ILFindLastID (pidl)); 1387 1388 TRACE ("(%p)->(%p,pidl=%p,%s,%u,%p)\n", this, hwndOwner, pidl, 1389 debugstr_w (lpName), dwFlags, pPidlOut); 1390 1391 FileStructW* pDataW = _ILGetFileStructW(pidl); 1392 if (!pDataW) 1393 { 1394 ERR("Got garbage pidl\n"); 1395 return E_INVALIDARG; 1396 } 1397 1398 /* build source path */ 1399 PathCombineW(szSrc, m_sPathTarget, pDataW->wszName); 1400 1401 /* build destination path */ 1402 if (dwFlags == SHGDN_NORMAL || dwFlags & SHGDN_INFOLDER) 1403 PathCombineW(szDest, m_sPathTarget, lpName); 1404 else 1405 lstrcpynW(szDest, lpName, MAX_PATH); 1406 1407 if(!(dwFlags & SHGDN_FORPARSING) && SHELL_FS_HideExtension(szSrc)) { 1408 WCHAR *ext = PathFindExtensionW(szSrc); 1409 if(*ext != '\0') { 1410 INT len = wcslen(szDest); 1411 lstrcpynW(szDest + len, ext, MAX_PATH - len); 1412 } 1413 } 1414 1415 TRACE ("src=%s dest=%s\n", debugstr_w(szSrc), debugstr_w(szDest)); 1416 if (!wcscmp(szSrc, szDest)) 1417 { 1418 /* src and destination is the same */ 1419 HRESULT hr = S_OK; 1420 if (pPidlOut) 1421 hr = _ILCreateFromPathW(szDest, pPidlOut); 1422 1423 return hr; 1424 } 1425 1426 if (MoveFileW (szSrc, szDest)) 1427 { 1428 HRESULT hr = S_OK; 1429 1430 if (pPidlOut) 1431 hr = _ILCreateFromPathW(szDest, pPidlOut); 1432 1433 SHChangeNotify (bIsFolder ? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM, 1434 SHCNF_PATHW, szSrc, szDest); 1435 1436 return hr; 1437 } 1438 1439 return E_FAIL; 1440 } 1441 1442 HRESULT WINAPI CFSFolder::GetDefaultSearchGUID(GUID * pguid) 1443 { 1444 FIXME ("(%p)\n", this); 1445 return E_NOTIMPL; 1446 } 1447 1448 HRESULT WINAPI CFSFolder::EnumSearches(IEnumExtraSearch ** ppenum) 1449 { 1450 FIXME ("(%p)\n", this); 1451 return E_NOTIMPL; 1452 } 1453 1454 HRESULT WINAPI CFSFolder::GetDefaultColumn(DWORD dwRes, 1455 ULONG * pSort, ULONG * pDisplay) 1456 { 1457 TRACE ("(%p)\n", this); 1458 1459 if (pSort) 1460 *pSort = 0; 1461 if (pDisplay) 1462 *pDisplay = 0; 1463 1464 return S_OK; 1465 } 1466 1467 HRESULT WINAPI CFSFolder::GetDefaultColumnState(UINT iColumn, 1468 DWORD * pcsFlags) 1469 { 1470 TRACE ("(%p)\n", this); 1471 1472 if (!pcsFlags || iColumn >= GENERICSHELLVIEWCOLUMNS) 1473 return E_INVALIDARG; 1474 1475 *pcsFlags = GenericSFHeader[iColumn].pcsFlags; 1476 1477 return S_OK; 1478 } 1479 1480 HRESULT WINAPI CFSFolder::GetDetailsEx(PCUITEMID_CHILD pidl, 1481 const SHCOLUMNID * pscid, VARIANT * pv) 1482 { 1483 FIXME ("(%p)\n", this); 1484 1485 return E_NOTIMPL; 1486 } 1487 1488 HRESULT WINAPI CFSFolder::GetDetailsOf(PCUITEMID_CHILD pidl, 1489 UINT iColumn, SHELLDETAILS * psd) 1490 { 1491 HRESULT hr = E_FAIL; 1492 1493 TRACE ("(%p)->(%p %i %p)\n", this, pidl, iColumn, psd); 1494 1495 if (!psd || iColumn >= GENERICSHELLVIEWCOLUMNS) 1496 return E_INVALIDARG; 1497 1498 if (!pidl) 1499 { 1500 /* the header titles */ 1501 psd->fmt = GenericSFHeader[iColumn].fmt; 1502 psd->cxChar = GenericSFHeader[iColumn].cxChar; 1503 return SHSetStrRet(&psd->str, GenericSFHeader[iColumn].colnameid); 1504 } 1505 else 1506 { 1507 hr = S_OK; 1508 psd->str.uType = STRRET_CSTR; 1509 /* the data from the pidl */ 1510 switch (iColumn) 1511 { 1512 case 0: /* name */ 1513 hr = GetDisplayNameOf (pidl, SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str); 1514 break; 1515 case 1: /* FIXME: comments */ 1516 psd->str.cStr[0] = 0; 1517 break; 1518 case 2: /* type */ 1519 _ILGetFileType(pidl, psd->str.cStr, MAX_PATH); 1520 break; 1521 case 3: /* size */ 1522 _ILGetFileSize(pidl, psd->str.cStr, MAX_PATH); 1523 break; 1524 case 4: /* date */ 1525 _ILGetFileDate(pidl, psd->str.cStr, MAX_PATH); 1526 break; 1527 case 5: /* attributes */ 1528 _ILGetFileAttributes(pidl, psd->str.cStr, MAX_PATH); 1529 break; 1530 } 1531 } 1532 1533 return hr; 1534 } 1535 1536 HRESULT WINAPI CFSFolder::MapColumnToSCID (UINT column, 1537 SHCOLUMNID * pscid) 1538 { 1539 FIXME ("(%p)\n", this); 1540 return E_NOTIMPL; 1541 } 1542 1543 /************************************************************************ 1544 * CFSFolder::GetClassID 1545 */ 1546 HRESULT WINAPI CFSFolder::GetClassID(CLSID * lpClassId) 1547 { 1548 TRACE ("(%p)\n", this); 1549 1550 if (!lpClassId) 1551 return E_POINTER; 1552 1553 *lpClassId = *m_pclsid; 1554 1555 return S_OK; 1556 } 1557 1558 /************************************************************************ 1559 * CFSFolder::Initialize 1560 * 1561 * NOTES 1562 * m_sPathTarget is not set. Don't know how to handle in a non rooted environment. 1563 */ 1564 HRESULT WINAPI CFSFolder::Initialize(PCIDLIST_ABSOLUTE pidl) 1565 { 1566 WCHAR wszTemp[MAX_PATH]; 1567 1568 TRACE ("(%p)->(%p)\n", this, pidl); 1569 1570 SHFree(m_pidlRoot); /* free the old pidl */ 1571 m_pidlRoot = ILClone (pidl); /* set my pidl */ 1572 1573 SHFree (m_sPathTarget); 1574 m_sPathTarget = NULL; 1575 1576 /* set my path */ 1577 if (SHGetPathFromIDListW (pidl, wszTemp)) 1578 { 1579 int len = wcslen(wszTemp); 1580 m_sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR)); 1581 if (!m_sPathTarget) 1582 return E_OUTOFMEMORY; 1583 memcpy(m_sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR)); 1584 } 1585 1586 TRACE ("--(%p)->(%s)\n", this, debugstr_w(m_sPathTarget)); 1587 return S_OK; 1588 } 1589 1590 /************************************************************************** 1591 * CFSFolder::GetCurFolder 1592 */ 1593 HRESULT WINAPI CFSFolder::GetCurFolder(PIDLIST_ABSOLUTE * pidl) 1594 { 1595 TRACE ("(%p)->(%p)\n", this, pidl); 1596 1597 if (!pidl) 1598 return E_POINTER; 1599 1600 *pidl = ILClone(m_pidlRoot); 1601 return S_OK; 1602 } 1603 1604 /************************************************************************** 1605 * CFSFolder::InitializeEx 1606 * 1607 * FIXME: error handling 1608 */ 1609 HRESULT WINAPI CFSFolder::InitializeEx(IBindCtx * pbc, LPCITEMIDLIST pidlRootx, 1610 const PERSIST_FOLDER_TARGET_INFO * ppfti) 1611 { 1612 WCHAR wszTemp[MAX_PATH]; 1613 1614 TRACE("(%p)->(%p,%p,%p)\n", this, pbc, pidlRootx, ppfti); 1615 if (ppfti) 1616 TRACE("--%p %s %s 0x%08x 0x%08x\n", 1617 ppfti->pidlTargetFolder, debugstr_w (ppfti->szTargetParsingName), 1618 debugstr_w (ppfti->szNetworkProvider), ppfti->dwAttributes, 1619 ppfti->csidl); 1620 1621 pdump (pidlRootx); 1622 if (ppfti && ppfti->pidlTargetFolder) 1623 pdump(ppfti->pidlTargetFolder); 1624 1625 if (m_pidlRoot) 1626 __SHFreeAndNil(&m_pidlRoot); /* free the old */ 1627 if (m_sPathTarget) 1628 __SHFreeAndNil(&m_sPathTarget); 1629 1630 /* 1631 * Root path and pidl 1632 */ 1633 m_pidlRoot = ILClone(pidlRootx); 1634 1635 /* 1636 * the target folder is spezified in csidl OR pidlTargetFolder OR 1637 * szTargetParsingName 1638 */ 1639 if (ppfti) 1640 { 1641 if (ppfti->csidl != -1) 1642 { 1643 if (SHGetSpecialFolderPathW(0, wszTemp, ppfti->csidl, 1644 ppfti->csidl & CSIDL_FLAG_CREATE)) { 1645 int len = wcslen(wszTemp); 1646 m_sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR)); 1647 if (!m_sPathTarget) 1648 return E_OUTOFMEMORY; 1649 memcpy(m_sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR)); 1650 } 1651 } 1652 else if (ppfti->szTargetParsingName[0]) 1653 { 1654 int len = wcslen(ppfti->szTargetParsingName); 1655 m_sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR)); 1656 if (!m_sPathTarget) 1657 return E_OUTOFMEMORY; 1658 memcpy(m_sPathTarget, ppfti->szTargetParsingName, 1659 (len + 1) * sizeof(WCHAR)); 1660 } 1661 else if (ppfti->pidlTargetFolder) 1662 { 1663 if (SHGetPathFromIDListW(ppfti->pidlTargetFolder, wszTemp)) 1664 { 1665 int len = wcslen(wszTemp); 1666 m_sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR)); 1667 if (!m_sPathTarget) 1668 return E_OUTOFMEMORY; 1669 memcpy(m_sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR)); 1670 } 1671 } 1672 } 1673 1674 TRACE("--(%p)->(target=%s)\n", this, debugstr_w(m_sPathTarget)); 1675 pdump(m_pidlRoot); 1676 return (m_sPathTarget) ? S_OK : E_FAIL; 1677 } 1678 1679 HRESULT WINAPI CFSFolder::GetFolderTargetInfo(PERSIST_FOLDER_TARGET_INFO * ppfti) 1680 { 1681 FIXME("(%p)->(%p)\n", this, ppfti); 1682 ZeroMemory(ppfti, sizeof (*ppfti)); 1683 return E_NOTIMPL; 1684 } 1685 1686 HRESULT CFSFolder::_CreateExtensionUIObject(PCUIDLIST_RELATIVE pidl, REFIID riid, LPVOID *ppvOut) 1687 { 1688 static const WCHAR formatW[] = {'S','h','e','l','l','E','x','\\', 1689 '{','%','0','8','x','-','%','0','4','x','-','%','0','4','x','-', 1690 '%','0','2','x','%','0','2','x','-','%','0','2','x','%','0','2','x', 1691 '%','0','2','x','%','0','2','x','%','0','2','x','%','0','2','x','}',0}; 1692 WCHAR buf[MAX_PATH]; 1693 1694 sprintfW(buf, formatW, riid.Data1, riid.Data2, riid.Data3, 1695 riid.Data4[0], riid.Data4[1], riid.Data4[2], riid.Data4[3], 1696 riid.Data4[4], riid.Data4[5], riid.Data4[6], riid.Data4[7]); 1697 1698 CLSID clsid; 1699 HRESULT hr; 1700 1701 hr = GetCLSIDForFileType(pidl, buf, &clsid); 1702 if (hr != S_OK) 1703 return hr; 1704 1705 hr = _CreateShellExtInstance(&clsid, pidl, riid, ppvOut); 1706 if (FAILED_UNEXPECTEDLY(hr)) 1707 return hr; 1708 1709 return S_OK; 1710 } 1711 1712 HRESULT CFSFolder::_GetDropTarget(LPCITEMIDLIST pidl, LPVOID *ppvOut) 1713 { 1714 HRESULT hr; 1715 1716 TRACE("CFSFolder::_GetDropTarget entered\n"); 1717 1718 if (_ILIsFolder (pidl)) 1719 { 1720 CComPtr<IShellFolder> psfChild; 1721 hr = this->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfChild)); 1722 if (FAILED_UNEXPECTEDLY(hr)) 1723 return hr; 1724 1725 return psfChild->CreateViewObject(NULL, IID_IDropTarget, ppvOut); 1726 } 1727 1728 CLSID clsid; 1729 hr = GetCLSIDForFileType(pidl, L"shellex\\DropHandler", &clsid); 1730 if (hr != S_OK) 1731 return hr; 1732 1733 hr = _CreateShellExtInstance(&clsid, pidl, IID_IDropTarget, ppvOut); 1734 if (FAILED_UNEXPECTEDLY(hr)) 1735 return S_FALSE; 1736 1737 return S_OK; 1738 } 1739 1740 HRESULT CFSFolder::_GetIconHandler(LPCITEMIDLIST pidl, REFIID riid, LPVOID *ppvOut) 1741 { 1742 CLSID clsid; 1743 HRESULT hr; 1744 1745 hr = GetCLSIDForFileType(pidl, L"shellex\\IconHandler", &clsid); 1746 if (hr != S_OK) 1747 return hr; 1748 1749 hr = _CreateShellExtInstance(&clsid, pidl, riid, ppvOut); 1750 if (FAILED_UNEXPECTEDLY(hr)) 1751 return S_FALSE; 1752 1753 return S_OK; 1754 } 1755 1756 HRESULT CFSFolder::_CreateShellExtInstance(const CLSID *pclsid, LPCITEMIDLIST pidl, REFIID riid, LPVOID *ppvOut) 1757 { 1758 HRESULT hr; 1759 WCHAR wszPath[MAX_PATH]; 1760 1761 FileStructW* pDataW = _ILGetFileStructW(pidl); 1762 if (!pDataW) 1763 { 1764 ERR("Got garbage pidl\n"); 1765 return E_INVALIDARG; 1766 } 1767 1768 PathCombineW(wszPath, m_sPathTarget, pDataW->wszName); 1769 1770 CComPtr<IPersistFile> pp; 1771 hr = SHCoCreateInstance(NULL, pclsid, NULL, IID_PPV_ARG(IPersistFile, &pp)); 1772 if (FAILED_UNEXPECTEDLY(hr)) 1773 return hr; 1774 1775 pp->Load(wszPath, 0); 1776 1777 hr = pp->QueryInterface(riid, ppvOut); 1778 if (hr != S_OK) 1779 { 1780 ERR("Failed to query for interface IID_IShellExtInit hr %x pclsid %s\n", hr, wine_dbgstr_guid(pclsid)); 1781 return hr; 1782 } 1783 return hr; 1784 } 1785 1786 HRESULT WINAPI CFSFolder::CallBack(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam) 1787 { 1788 if (uMsg != DFM_MERGECONTEXTMENU && uMsg != DFM_INVOKECOMMAND) 1789 return S_OK; 1790 1791 /* no data object means no selection */ 1792 if (!pdtobj) 1793 { 1794 if (uMsg == DFM_INVOKECOMMAND && wParam == 0) 1795 { 1796 PUITEMID_CHILD pidlChild = ILClone(ILFindLastID(m_pidlRoot)); 1797 LPITEMIDLIST pidlParent = ILClone(m_pidlRoot); 1798 ILRemoveLastID(pidlParent); 1799 HRESULT hr = SH_ShowPropertiesDialog(m_sPathTarget, pidlParent, &pidlChild); 1800 if (FAILED(hr)) 1801 ERR("SH_ShowPropertiesDialog failed\n"); 1802 ILFree(pidlChild); 1803 ILFree(pidlParent); 1804 } 1805 else if (uMsg == DFM_MERGECONTEXTMENU) 1806 { 1807 QCMINFO *pqcminfo = (QCMINFO *)lParam; 1808 HMENU hpopup = CreatePopupMenu(); 1809 _InsertMenuItemW(hpopup, 0, TRUE, 0, MFT_STRING, MAKEINTRESOURCEW(IDS_PROPERTIES), MFS_ENABLED); 1810 Shell_MergeMenus(pqcminfo->hmenu, hpopup, pqcminfo->indexMenu++, pqcminfo->idCmdFirst, pqcminfo->idCmdLast, MM_ADDSEPARATOR); 1811 DestroyMenu(hpopup); 1812 } 1813 1814 return S_OK; 1815 } 1816 1817 if (uMsg != DFM_INVOKECOMMAND || wParam != DFM_CMD_PROPERTIES) 1818 return S_OK; 1819 1820 return Shell_DefaultContextMenuCallBack(this, pdtobj); 1821 } 1822 1823 static HBITMAP DoLoadPicture(LPCWSTR pszFileName) 1824 { 1825 // create stream from file 1826 HRESULT hr; 1827 CComPtr<IStream> pStream; 1828 hr = SHCreateStreamOnFileEx(pszFileName, STGM_READ, FILE_ATTRIBUTE_NORMAL, 1829 FALSE, NULL, &pStream); 1830 if (FAILED(hr)) 1831 return NULL; 1832 1833 // load the picture 1834 HBITMAP hbm = NULL; 1835 CComPtr<IPicture> pPicture; 1836 OleLoadPicture(pStream, 0, FALSE, IID_IPicture, (LPVOID *)&pPicture); 1837 1838 // get the bitmap handle 1839 if (pPicture) 1840 { 1841 pPicture->get_Handle((OLE_HANDLE *)&hbm); 1842 1843 // copy the bitmap handle 1844 hbm = (HBITMAP)CopyImage(hbm, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION); 1845 } 1846 1847 return hbm; 1848 } 1849 1850 HRESULT WINAPI CFSFolder::GetCustomViewInfo(ULONG unknown, SFVM_CUSTOMVIEWINFO_DATA *data) 1851 { 1852 if (data == NULL) 1853 { 1854 return E_POINTER; 1855 } 1856 if (data->cbSize != sizeof(*data)) 1857 { 1858 // NOTE: You have to set the cbData member before SFVM_GET_CUSTOMVIEWINFO call. 1859 return E_INVALIDARG; 1860 } 1861 1862 data->hbmBack = NULL; 1863 data->clrText = CLR_INVALID; 1864 data->clrTextBack = CLR_INVALID; 1865 1866 WCHAR szPath[MAX_PATH], szIniFile[MAX_PATH]; 1867 1868 // does the folder exists? 1869 if (!SHGetPathFromIDListW(m_pidlRoot, szPath) || !PathIsDirectoryW(szPath)) 1870 { 1871 return E_INVALIDARG; 1872 } 1873 1874 // don't use custom view in network path for security 1875 if (PathIsNetworkPath(szPath)) 1876 { 1877 return E_ACCESSDENIED; 1878 } 1879 1880 // build the ini file path 1881 StringCchCopyW(szIniFile, _countof(szIniFile), szPath); 1882 PathAppend(szIniFile, L"desktop.ini"); 1883 1884 static LPCWSTR TheGUID = L"{BE098140-A513-11D0-A3A4-00C04FD706EC}"; 1885 static LPCWSTR Space = L" \t\n\r\f\v"; 1886 1887 // get info from ini file 1888 WCHAR szImage[MAX_PATH], szText[64]; 1889 1890 // load the image 1891 szImage[0] = UNICODE_NULL; 1892 GetPrivateProfileStringW(TheGUID, L"IconArea_Image", L"", szImage, _countof(szImage), szIniFile); 1893 if (szImage[0]) 1894 { 1895 StrTrimW(szImage, Space); 1896 if (PathIsRelativeW(szImage)) 1897 { 1898 PathAppendW(szPath, szImage); 1899 StringCchCopyW(szImage, _countof(szImage), szPath); 1900 } 1901 data->hbmBack = DoLoadPicture(szImage); 1902 } 1903 1904 // load the text color 1905 szText[0] = UNICODE_NULL; 1906 GetPrivateProfileStringW(TheGUID, L"IconArea_Text", L"", szText, _countof(szText), szIniFile); 1907 if (szText[0]) 1908 { 1909 StrTrimW(szText, Space); 1910 1911 LPWSTR pchEnd = NULL; 1912 COLORREF cr = (wcstol(szText, &pchEnd, 0) & 0xFFFFFF); 1913 1914 if (pchEnd && !*pchEnd) 1915 data->clrText = cr; 1916 } 1917 1918 // load the text background color 1919 szText[0] = UNICODE_NULL; 1920 GetPrivateProfileStringW(TheGUID, L"IconArea_TextBackground", L"", szText, _countof(szText), szIniFile); 1921 if (szText[0]) 1922 { 1923 StrTrimW(szText, Space); 1924 1925 LPWSTR pchEnd = NULL; 1926 COLORREF cr = (wcstol(szText, &pchEnd, 0) & 0xFFFFFF); 1927 1928 if (pchEnd && !*pchEnd) 1929 data->clrTextBack = cr; 1930 } 1931 1932 if (data->hbmBack != NULL || data->clrText != CLR_INVALID || data->clrTextBack != CLR_INVALID) 1933 return S_OK; 1934 1935 return E_FAIL; 1936 } 1937 1938 HRESULT WINAPI CFSFolder::MessageSFVCB(UINT uMsg, WPARAM wParam, LPARAM lParam) 1939 { 1940 HRESULT hr = E_NOTIMPL; 1941 switch (uMsg) 1942 { 1943 case SFVM_GET_CUSTOMVIEWINFO: 1944 hr = GetCustomViewInfo((ULONG)wParam, (SFVM_CUSTOMVIEWINFO_DATA *)lParam); 1945 break; 1946 } 1947 return hr; 1948 } 1949