1 /* 2 * PROJECT: ReactOS shell extensions 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: dll/shellext/ntobjshex/ntobjfolder.h 5 * PURPOSE: NT Object Namespace shell extension 6 * PROGRAMMERS: David Quintana <gigaherz@gmail.com> 7 */ 8 #pragma once 9 10 extern const GUID CLSID_NtObjectFolder; 11 12 class CFolderViewCB : 13 public CComObjectRootEx<CComMultiThreadModelNoCS>, 14 public IShellFolderViewCB 15 { 16 IShellView* m_View; 17 18 public: 19 20 CFolderViewCB() : m_View(NULL) {} 21 virtual ~CFolderViewCB() {} 22 23 virtual HRESULT STDMETHODCALLTYPE MessageSFVCB(UINT uMsg, WPARAM wParam, LPARAM lParam) 24 { 25 switch (uMsg) 26 { 27 case SFVM_DEFVIEWMODE: 28 { 29 FOLDERVIEWMODE* pViewMode = (FOLDERVIEWMODE*)lParam; 30 *pViewMode = FVM_DETAILS; 31 return S_OK; 32 } 33 case SFVM_COLUMNCLICK: 34 return S_FALSE; 35 case SFVM_BACKGROUNDENUM: 36 return S_OK; 37 } 38 39 DbgPrint("MessageSFVCB unimplemented %d %08x %08x\n", uMsg, wParam, lParam); 40 return E_NOTIMPL; 41 } 42 43 virtual HRESULT STDMETHODCALLTYPE Initialize(IShellView* psv) 44 { 45 m_View = psv; 46 return S_OK; 47 } 48 49 DECLARE_NOT_AGGREGATABLE(CFolderViewCB) 50 DECLARE_PROTECT_FINAL_CONSTRUCT() 51 52 BEGIN_COM_MAP(CFolderViewCB) 53 COM_INTERFACE_ENTRY_IID(IID_IShellFolderViewCB, IShellFolderViewCB) 54 END_COM_MAP() 55 }; 56 57 template<class TSelf, typename TItemId, class TExtractIcon> 58 class CCommonFolder : 59 public CComObjectRootEx<CComMultiThreadModelNoCS>, 60 public IShellFolder2, 61 public IPersistFolder2 62 { 63 protected: 64 WCHAR m_NtPath[MAX_PATH]; 65 66 LPITEMIDLIST m_shellPidl; 67 68 public: 69 70 CCommonFolder() : 71 m_shellPidl(NULL) 72 { 73 } 74 75 virtual ~CCommonFolder() 76 { 77 if (m_shellPidl) 78 ILFree(m_shellPidl); 79 } 80 81 // IShellFolder 82 virtual HRESULT STDMETHODCALLTYPE ParseDisplayName( 83 HWND hwndOwner, 84 LPBC pbcReserved, 85 LPOLESTR lpszDisplayName, 86 ULONG *pchEaten, 87 LPITEMIDLIST *ppidl, 88 ULONG *pdwAttributes) 89 { 90 if (!ppidl) 91 return E_POINTER; 92 93 if (pchEaten) 94 *pchEaten = 0; 95 96 if (pdwAttributes) 97 *pdwAttributes = 0; 98 99 TRACE("CCommonFolder::ParseDisplayName name=%S (ntPath=%S)\n", lpszDisplayName, m_NtPath); 100 101 const TItemId * info; 102 IEnumIDList * it; 103 HRESULT hr = EnumObjects(hwndOwner, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &it); 104 if (FAILED(hr)) 105 return hr; 106 107 PWSTR end = StrChrW(lpszDisplayName, '\\'); 108 int length = end ? end - lpszDisplayName : wcslen(lpszDisplayName); 109 110 while (TRUE) 111 { 112 hr = it->Next(1, ppidl, NULL); 113 114 if (FAILED(hr)) 115 return hr; 116 117 if (hr != S_OK) 118 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); 119 120 hr = GetInfoFromPidl(*ppidl, &info); 121 if (FAILED_UNEXPECTEDLY(hr)) 122 return hr; 123 124 if (StrCmpNW(info->entryName, lpszDisplayName, length) == 0) 125 break; 126 } 127 128 // if has remaining path to parse (and the path didn't just terminate in a backslash) 129 if (end && wcslen(end) > 1) 130 { 131 CComPtr<IShellFolder> psfChild; 132 hr = BindToObject(*ppidl, pbcReserved, IID_PPV_ARG(IShellFolder, &psfChild)); 133 if (FAILED_UNEXPECTEDLY(hr)) 134 return hr; 135 136 LPITEMIDLIST child; 137 hr = psfChild->ParseDisplayName(hwndOwner, pbcReserved, end + 1, pchEaten, &child, pdwAttributes); 138 if (FAILED(hr)) 139 return hr; 140 141 LPITEMIDLIST old = *ppidl; 142 *ppidl = ILCombine(old, child); 143 ILFree(old); 144 145 // Count the path separator 146 if (pchEaten) 147 (*pchEaten) += 1; 148 } 149 else 150 { 151 if (pdwAttributes) 152 *pdwAttributes = ConvertAttributes(info, pdwAttributes); 153 } 154 155 if (pchEaten) 156 *pchEaten += wcslen(info->entryName); 157 158 return S_OK; 159 } 160 161 virtual HRESULT STDMETHODCALLTYPE EnumObjects( 162 HWND hwndOwner, 163 SHCONTF grfFlags, 164 IEnumIDList **ppenumIDList) PURE; 165 166 virtual HRESULT STDMETHODCALLTYPE BindToObject( 167 LPCITEMIDLIST pidl, 168 LPBC pbcReserved, 169 REFIID riid, 170 void **ppvOut) 171 { 172 const TItemId * info; 173 174 if (IsEqualIID(riid, IID_IShellFolder)) 175 { 176 HRESULT hr = GetInfoFromPidl(pidl, &info); 177 if (FAILED_UNEXPECTEDLY(hr)) 178 return hr; 179 180 WCHAR path[MAX_PATH]; 181 182 StringCbCopyW(path, sizeof(path), m_NtPath); 183 PathAppendW(path, info->entryName); 184 185 LPITEMIDLIST first = ILCloneFirst(pidl); 186 LPCITEMIDLIST rest = ILGetNext(pidl); 187 188 LPITEMIDLIST fullPidl = ILCombine(m_shellPidl, first); 189 190 CComPtr<IShellFolder> psfChild; 191 hr = InternalBindToObject(path, info, first, rest, fullPidl, pbcReserved, &psfChild); 192 193 ILFree(fullPidl); 194 ILFree(first); 195 196 if (FAILED(hr)) 197 return hr; 198 199 if (hr == S_FALSE) 200 return S_OK; 201 202 if (rest->mkid.cb > 0) 203 { 204 return psfChild->BindToObject(rest, pbcReserved, riid, ppvOut); 205 } 206 207 return psfChild->QueryInterface(riid, ppvOut); 208 } 209 210 return E_NOTIMPL; 211 } 212 213 protected: 214 virtual HRESULT STDMETHODCALLTYPE InternalBindToObject( 215 PWSTR path, 216 const TItemId * info, 217 LPITEMIDLIST first, 218 LPCITEMIDLIST rest, 219 LPITEMIDLIST fullPidl, 220 LPBC pbcReserved, 221 IShellFolder** ppsfChild) PURE; 222 223 virtual HRESULT STDMETHODCALLTYPE ResolveSymLink( 224 const TItemId * info, 225 LPITEMIDLIST * fullPidl) 226 { 227 return E_NOTIMPL; 228 } 229 230 public: 231 232 virtual HRESULT STDMETHODCALLTYPE BindToStorage( 233 LPCITEMIDLIST pidl, 234 LPBC pbcReserved, 235 REFIID riid, 236 void **ppvObj) 237 { 238 UNIMPLEMENTED; 239 return E_NOTIMPL; 240 } 241 242 virtual HRESULT STDMETHODCALLTYPE CompareIDs( 243 LPARAM lParam, 244 LPCITEMIDLIST pidl1, 245 LPCITEMIDLIST pidl2) 246 { 247 HRESULT hr; 248 249 TRACE("CompareIDs %d\n", lParam); 250 251 const TItemId * id1; 252 hr = GetInfoFromPidl(pidl1, &id1); 253 if (FAILED(hr)) 254 return E_INVALIDARG; 255 256 const TItemId * id2; 257 hr = GetInfoFromPidl(pidl2, &id2); 258 if (FAILED(hr)) 259 return E_INVALIDARG; 260 261 hr = CompareIDs(lParam, id1, id2); 262 if (hr != S_EQUAL) 263 return hr; 264 265 // The wollowing snipped is basically SHELL32_CompareChildren 266 267 PUIDLIST_RELATIVE rest1 = ILGetNext(pidl1); 268 PUIDLIST_RELATIVE rest2 = ILGetNext(pidl2); 269 270 bool isEmpty1 = (rest1->mkid.cb == 0); 271 bool isEmpty2 = (rest2->mkid.cb == 0); 272 273 if (isEmpty1 || isEmpty2) 274 return MAKE_COMPARE_HRESULT(isEmpty2 - isEmpty1); 275 276 LPCITEMIDLIST first1 = ILCloneFirst(pidl1); 277 if (!first1) 278 return E_OUTOFMEMORY; 279 280 CComPtr<IShellFolder> psfNext; 281 hr = BindToObject(first1, NULL, IID_PPV_ARG(IShellFolder, &psfNext)); 282 if (FAILED_UNEXPECTEDLY(hr)) 283 return hr; 284 285 return psfNext->CompareIDs(lParam, rest1, rest2); 286 } 287 288 protected: 289 virtual HRESULT STDMETHODCALLTYPE CompareName( 290 LPARAM lParam, 291 const TItemId * first, 292 const TItemId * second) 293 { 294 bool f1 = IsFolder(first); 295 bool f2 = IsFolder(second); 296 297 HRESULT hr = MAKE_COMPARE_HRESULT(f2 - f1); 298 if (hr != S_EQUAL) 299 return hr; 300 301 bool canonical = (lParam & 0xFFFF0000) == SHCIDS_CANONICALONLY; 302 if (canonical) 303 { 304 // Shortcut: avoid comparing contents if not necessary when the results are not for display. 305 hr = MAKE_COMPARE_HRESULT(second->entryNameLength - first->entryNameLength); 306 if (hr != S_EQUAL) 307 return hr; 308 309 int minlength = min(first->entryNameLength, second->entryNameLength); 310 if (minlength > 0) 311 { 312 hr = MAKE_COMPARE_HRESULT(memcmp(first->entryName, second->entryName, minlength)); 313 if (hr != S_EQUAL) 314 return hr; 315 } 316 317 return S_EQUAL; 318 } 319 320 int minlength = min(first->entryNameLength, second->entryNameLength); 321 if (minlength > 0) 322 { 323 hr = MAKE_COMPARE_HRESULT(StrCmpNW(first->entryName, second->entryName, minlength / sizeof(WCHAR))); 324 if (hr != S_EQUAL) 325 return hr; 326 } 327 328 return MAKE_COMPARE_HRESULT(second->entryNameLength - first->entryNameLength); 329 } 330 331 public: 332 virtual HRESULT STDMETHODCALLTYPE CreateViewObject( 333 HWND hwndOwner, 334 REFIID riid, 335 void **ppvOut) 336 { 337 if (!IsEqualIID(riid, IID_IShellView)) 338 return E_NOINTERFACE; 339 340 _CComObject<CFolderViewCB> *pcb; 341 342 HRESULT hr = _CComObject<CFolderViewCB>::CreateInstance(&pcb); 343 if (FAILED(hr)) 344 return hr; 345 346 pcb->AddRef(); 347 348 SFV_CREATE sfv; 349 sfv.cbSize = sizeof(sfv); 350 sfv.pshf = this; 351 sfv.psvOuter = NULL; 352 sfv.psfvcb = pcb; 353 354 IShellView* view; 355 356 hr = SHCreateShellFolderView(&sfv, &view); 357 if (FAILED(hr)) 358 return hr; 359 360 pcb->Initialize(view); 361 362 pcb->Release(); 363 364 *ppvOut = view; 365 366 return S_OK; 367 } 368 369 virtual HRESULT STDMETHODCALLTYPE GetAttributesOf( 370 UINT cidl, 371 PCUITEMID_CHILD_ARRAY apidl, 372 SFGAOF *rgfInOut) 373 { 374 const TItemId * info; 375 376 TRACE("GetAttributesOf %d\n", cidl); 377 378 if (cidl == 0) 379 { 380 *rgfInOut &= SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_BROWSABLE; 381 return S_OK; 382 } 383 384 for (int i = 0; i < (int)cidl; i++) 385 { 386 PCUITEMID_CHILD pidl = apidl[i]; 387 388 HRESULT hr = GetInfoFromPidl(pidl, &info); 389 if (FAILED_UNEXPECTEDLY(hr)) 390 return hr; 391 392 // Update attributes. 393 *rgfInOut = ConvertAttributes(info, rgfInOut); 394 } 395 396 return S_OK; 397 } 398 399 virtual HRESULT STDMETHODCALLTYPE GetUIObjectOf( 400 HWND hwndOwner, 401 UINT cidl, 402 PCUITEMID_CHILD_ARRAY apidl, 403 REFIID riid, 404 UINT *prgfInOut, 405 void **ppvOut) 406 { 407 DWORD res; 408 TRACE("GetUIObjectOf\n"); 409 410 if (IsEqualIID(riid, IID_IContextMenu) || 411 IsEqualIID(riid, IID_IContextMenu2) || 412 IsEqualIID(riid, IID_IContextMenu3)) 413 { 414 CComPtr<IContextMenu> pcm; 415 416 HKEY keys[1]; 417 418 LPITEMIDLIST parent = m_shellPidl; 419 420 CComPtr<IShellFolder> psfParent = this; 421 422 LPCITEMIDLIST child; 423 424 int nkeys = _countof(keys); 425 if (cidl == 1 && IsSymLink(apidl[0])) 426 { 427 const TItemId * info; 428 HRESULT hr = GetInfoFromPidl(apidl[0], &info); 429 if (FAILED(hr)) 430 return hr; 431 432 LPITEMIDLIST target; 433 hr = ResolveSymLink(info, &target); 434 if (FAILED(hr)) 435 return hr; 436 437 CComPtr<IShellFolder> psfTarget; 438 hr = ::SHBindToParent(target, IID_PPV_ARG(IShellFolder, &psfTarget), &child); 439 if (FAILED(hr)) 440 { 441 ILFree(target); 442 return hr; 443 } 444 445 parent = ILClone(target); 446 ILRemoveLastID(parent); 447 psfParent = psfTarget; 448 449 apidl = &child; 450 } 451 452 if (cidl == 1 && IsFolder(apidl[0])) 453 { 454 res = RegOpenKey(HKEY_CLASSES_ROOT, L"Folder", keys + 0); 455 if (!NT_SUCCESS(res)) 456 return HRESULT_FROM_NT(res); 457 } 458 else 459 { 460 nkeys = 0; 461 } 462 463 HRESULT hr = CDefFolderMenu_Create2(parent, hwndOwner, cidl, apidl, psfParent, DefCtxMenuCallback, nkeys, keys, &pcm); 464 if (FAILED_UNEXPECTEDLY(hr)) 465 return hr; 466 467 return pcm->QueryInterface(riid, ppvOut); 468 } 469 470 if (IsEqualIID(riid, IID_IExtractIconW)) 471 { 472 return ShellObjectCreatorInit<TExtractIcon>(m_NtPath, m_shellPidl, cidl, apidl, riid, ppvOut); 473 } 474 475 if (IsEqualIID(riid, IID_IDataObject)) 476 { 477 return CIDLData_CreateFromIDArray(m_shellPidl, cidl, apidl, (IDataObject**)ppvOut); 478 } 479 480 if (IsEqualIID(riid, IID_IQueryAssociations)) 481 { 482 if (cidl == 1 && IsFolder(apidl[0])) 483 { 484 CComPtr<IQueryAssociations> pqa; 485 HRESULT hr = AssocCreate(CLSID_QueryAssociations, IID_PPV_ARG(IQueryAssociations, &pqa)); 486 if (FAILED_UNEXPECTEDLY(hr)) 487 return hr; 488 489 hr = pqa->Init(ASSOCF_INIT_DEFAULTTOFOLDER, L"NTObjShEx.NTDirectory", NULL, hwndOwner); 490 if (FAILED_UNEXPECTEDLY(hr)) 491 return hr; 492 493 return pqa->QueryInterface(riid, ppvOut); 494 } 495 } 496 497 return E_NOTIMPL; 498 } 499 500 virtual HRESULT STDMETHODCALLTYPE GetDisplayNameOf( 501 LPCITEMIDLIST pidl, 502 SHGDNF uFlags, 503 STRRET *lpName) 504 { 505 const TItemId * info; 506 507 TRACE("GetDisplayNameOf %p\n", pidl); 508 509 HRESULT hr = GetInfoFromPidl(pidl, &info); 510 if (FAILED_UNEXPECTEDLY(hr)) 511 return hr; 512 513 if (GET_SHGDN_FOR(uFlags) & SHGDN_FOREDITING) 514 { 515 hr = MakeStrRetFromString(info->entryName, info->entryNameLength, lpName); 516 if (FAILED_UNEXPECTEDLY(hr)) 517 return hr; 518 } 519 520 WCHAR path[MAX_PATH] = { 0 }; 521 522 if (GET_SHGDN_FOR(uFlags) & SHGDN_FORPARSING) 523 { 524 if (GET_SHGDN_RELATION(uFlags) != SHGDN_INFOLDER) 525 { 526 hr = GetFullName(m_shellPidl, uFlags, path, _countof(path)); 527 if (FAILED_UNEXPECTEDLY(hr)) 528 return hr; 529 } 530 } 531 532 PathAppendW(path, info->entryName); 533 534 LPCITEMIDLIST pidlNext = ILGetNext(pidl); 535 if (pidlNext && pidlNext->mkid.cb > 0) 536 { 537 LPITEMIDLIST pidlFirst = ILCloneFirst(pidl); 538 539 CComPtr<IShellFolder> psfChild; 540 hr = BindToObject(pidlFirst, NULL, IID_PPV_ARG(IShellFolder, &psfChild)); 541 if (FAILED_UNEXPECTEDLY(hr)) 542 return hr; 543 544 WCHAR temp[MAX_PATH]; 545 STRRET childName; 546 547 hr = psfChild->GetDisplayNameOf(pidlNext, uFlags | SHGDN_INFOLDER, &childName); 548 if (FAILED_UNEXPECTEDLY(hr)) 549 return hr; 550 551 hr = StrRetToBufW(&childName, pidlNext, temp, _countof(temp)); 552 if (FAILED_UNEXPECTEDLY(hr)) 553 return hr; 554 555 PathAppendW(path, temp); 556 557 ILFree(pidlFirst); 558 } 559 560 hr = MakeStrRetFromString(path, lpName); 561 if (FAILED_UNEXPECTEDLY(hr)) 562 return hr; 563 564 return S_OK; 565 } 566 567 virtual HRESULT STDMETHODCALLTYPE SetNameOf( 568 HWND hwnd, 569 LPCITEMIDLIST pidl, 570 LPCOLESTR lpszName, 571 SHGDNF uFlags, 572 LPITEMIDLIST *ppidlOut) 573 { 574 UNIMPLEMENTED; 575 return E_NOTIMPL; 576 } 577 578 // IShellFolder2 579 virtual HRESULT STDMETHODCALLTYPE GetDefaultSearchGUID( 580 GUID *lpguid) 581 { 582 UNIMPLEMENTED; 583 return E_NOTIMPL; 584 } 585 586 virtual HRESULT STDMETHODCALLTYPE EnumSearches( 587 IEnumExtraSearch **ppenum) 588 { 589 UNIMPLEMENTED; 590 return E_NOTIMPL; 591 } 592 593 virtual HRESULT STDMETHODCALLTYPE GetDefaultColumn( 594 DWORD dwReserved, 595 ULONG *pSort, 596 ULONG *pDisplay) 597 { 598 if (pSort) 599 *pSort = 0; 600 if (pDisplay) 601 *pDisplay = 0; 602 return S_OK; 603 } 604 605 virtual HRESULT STDMETHODCALLTYPE GetDefaultColumnState( 606 UINT iColumn, 607 SHCOLSTATEF *pcsFlags) PURE; 608 609 virtual HRESULT STDMETHODCALLTYPE GetDetailsEx( 610 LPCITEMIDLIST pidl, 611 const SHCOLUMNID *pscid, 612 VARIANT *pv) PURE; 613 614 virtual HRESULT STDMETHODCALLTYPE GetDetailsOf( 615 LPCITEMIDLIST pidl, 616 UINT iColumn, 617 SHELLDETAILS *psd) PURE; 618 619 virtual HRESULT STDMETHODCALLTYPE MapColumnToSCID( 620 UINT iColumn, 621 SHCOLUMNID *pscid) PURE; 622 623 // IPersist 624 virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID *lpClassId) 625 { 626 if (!lpClassId) 627 return E_POINTER; 628 629 *lpClassId = CLSID_NtObjectFolder; 630 return S_OK; 631 } 632 633 // IPersistFolder 634 virtual HRESULT STDMETHODCALLTYPE Initialize(PCIDLIST_ABSOLUTE pidl) 635 { 636 m_shellPidl = ILClone(pidl); 637 638 StringCbCopyW(m_NtPath, sizeof(m_NtPath), L"\\"); 639 640 return S_OK; 641 } 642 643 // IPersistFolder2 644 virtual HRESULT STDMETHODCALLTYPE GetCurFolder(PIDLIST_ABSOLUTE * pidl) 645 { 646 if (pidl) 647 *pidl = ILClone(m_shellPidl); 648 if (!m_shellPidl) 649 return S_FALSE; 650 return S_OK; 651 } 652 653 // Internal 654 protected: 655 virtual HRESULT STDMETHODCALLTYPE CompareIDs( 656 LPARAM lParam, 657 const TItemId * first, 658 const TItemId * second) PURE; 659 660 virtual ULONG STDMETHODCALLTYPE ConvertAttributes( 661 const TItemId * entry, 662 PULONG inMask) PURE; 663 664 virtual BOOL STDMETHODCALLTYPE IsFolder(LPCITEMIDLIST pcidl) 665 { 666 const TItemId * info; 667 668 HRESULT hr = GetInfoFromPidl(pcidl, &info); 669 if (FAILED(hr)) 670 return hr; 671 672 return IsFolder(info); 673 } 674 675 virtual BOOL STDMETHODCALLTYPE IsFolder(const TItemId * info) PURE; 676 677 virtual BOOL STDMETHODCALLTYPE IsSymLink(LPCITEMIDLIST pcidl) 678 { 679 const TItemId * info; 680 681 HRESULT hr = GetInfoFromPidl(pcidl, &info); 682 if (FAILED(hr)) 683 return hr; 684 685 return IsSymLink(info); 686 } 687 688 virtual BOOL STDMETHODCALLTYPE IsSymLink(const TItemId * info) 689 { 690 return FALSE; 691 } 692 693 virtual HRESULT GetInfoFromPidl(LPCITEMIDLIST pcidl, const TItemId ** pentry) PURE; 694 695 public: 696 static HRESULT CALLBACK DefCtxMenuCallback(IShellFolder * /*psf*/, HWND /*hwnd*/, IDataObject * /*pdtobj*/, UINT uMsg, WPARAM /*wParam*/, LPARAM /*lParam*/) 697 { 698 switch (uMsg) 699 { 700 case DFM_MERGECONTEXTMENU: 701 return S_OK; 702 case DFM_INVOKECOMMAND: 703 case DFM_INVOKECOMMANDEX: 704 case DFM_GETDEFSTATICID: // Required for Windows 7 to pick a default 705 return S_FALSE; 706 } 707 return E_NOTIMPL; 708 } 709 710 DECLARE_NOT_AGGREGATABLE(TSelf) 711 DECLARE_PROTECT_FINAL_CONSTRUCT() 712 713 BEGIN_COM_MAP(TSelf) 714 COM_INTERFACE_ENTRY_IID(IID_IShellFolder, IShellFolder) 715 COM_INTERFACE_ENTRY_IID(IID_IShellFolder2, IShellFolder2) 716 COM_INTERFACE_ENTRY_IID(IID_IPersist, IPersist) 717 COM_INTERFACE_ENTRY_IID(IID_IPersistFolder, IPersistFolder) 718 COM_INTERFACE_ENTRY_IID(IID_IPersistFolder2, IPersistFolder2) 719 END_COM_MAP() 720 721 };