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