1 /* 2 * PROJECT: ReactOS Search Shell Extension 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: Search results folder 5 * COPYRIGHT: Copyright 2019 Brock Mammen 6 */ 7 8 #include "CFindFolder.h" 9 #include <exdispid.h> 10 11 WINE_DEFAULT_DEBUG_CHANNEL(shellfind); 12 13 // FIXME: Remove this declaration after the function has been fully implemented 14 EXTERN_C HRESULT 15 WINAPI 16 SHOpenFolderAndSelectItems(LPITEMIDLIST pidlFolder, 17 UINT cidl, 18 PCUITEMID_CHILD_ARRAY apidl, 19 DWORD dwFlags); 20 21 static HRESULT SHELL32_CoCreateInitSF(LPCITEMIDLIST pidlRoot, PERSIST_FOLDER_TARGET_INFO* ppfti, 22 LPCITEMIDLIST pidlChild, const GUID* clsid, REFIID riid, LPVOID *ppvOut) 23 { 24 HRESULT hr; 25 CComPtr<IShellFolder> pShellFolder; 26 27 hr = SHCoCreateInstance(NULL, clsid, NULL, IID_PPV_ARG(IShellFolder, &pShellFolder)); 28 if (FAILED(hr)) 29 return hr; 30 31 LPITEMIDLIST pidlAbsolute = ILCombine (pidlRoot, pidlChild); 32 CComPtr<IPersistFolder> ppf; 33 CComPtr<IPersistFolder3> ppf3; 34 35 if (ppfti && SUCCEEDED(pShellFolder->QueryInterface(IID_PPV_ARG(IPersistFolder3, &ppf3)))) 36 { 37 ppf3->InitializeEx(NULL, pidlAbsolute, ppfti); 38 } 39 else if (SUCCEEDED(pShellFolder->QueryInterface(IID_PPV_ARG(IPersistFolder, &ppf)))) 40 { 41 ppf->Initialize(pidlAbsolute); 42 } 43 ILFree (pidlAbsolute); 44 45 return pShellFolder->QueryInterface(riid, ppvOut); 46 } 47 48 static void WINAPI _InsertMenuItemW( 49 HMENU hMenu, 50 UINT indexMenu, 51 BOOL fByPosition, 52 UINT wID, 53 UINT fType, 54 LPCWSTR dwTypeData, 55 UINT fState) 56 { 57 MENUITEMINFOW mii; 58 WCHAR wszText[100]; 59 60 ZeroMemory(&mii, sizeof(mii)); 61 mii.cbSize = sizeof(mii); 62 if (fType == MFT_SEPARATOR) 63 mii.fMask = MIIM_ID | MIIM_TYPE; 64 else if (fType == MFT_STRING) 65 { 66 mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE; 67 if (IS_INTRESOURCE(dwTypeData)) 68 { 69 if (LoadStringW(_AtlBaseModule.GetResourceInstance(), LOWORD((ULONG_PTR)dwTypeData), wszText, _countof(wszText))) 70 mii.dwTypeData = wszText; 71 else 72 { 73 ERR("failed to load string %p\n", dwTypeData); 74 return; 75 } 76 } 77 else 78 mii.dwTypeData = (LPWSTR)dwTypeData; 79 mii.fState = fState; 80 } 81 82 mii.wID = wID; 83 mii.fType = fType; 84 InsertMenuItemW(hMenu, indexMenu, fByPosition, &mii); 85 } 86 87 struct FolderViewColumns 88 { 89 int iResource; 90 DWORD dwDefaultState; 91 int fmt; 92 int cxChar; 93 }; 94 95 static FolderViewColumns g_ColumnDefs[] = 96 { 97 {IDS_COL_NAME, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 30}, 98 {IDS_COL_LOCATION, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 30}, 99 {IDS_COL_RELEVANCE, SHCOLSTATE_TYPE_STR, LVCFMT_LEFT, 0} 100 }; 101 102 CFindFolder::CFindFolder() : 103 m_hStopEvent(NULL) 104 { 105 } 106 107 static LPITEMIDLIST _ILCreate(LPCWSTR lpszPath) 108 { 109 CComHeapPtr<ITEMIDLIST> lpFSPidl(ILCreateFromPathW(lpszPath)); 110 if (!lpFSPidl) 111 { 112 ERR("Failed to create pidl from path\n"); 113 return NULL; 114 } 115 LPITEMIDLIST lpLastFSPidl = ILFindLastID(lpFSPidl); 116 117 int pathLen = (PathFindFileNameW(lpszPath) - lpszPath) * sizeof(WCHAR); 118 int cbData = sizeof(WORD) + pathLen + lpLastFSPidl->mkid.cb; 119 LPITEMIDLIST pidl = (LPITEMIDLIST) SHAlloc(cbData + sizeof(WORD)); 120 if (!pidl) 121 return NULL; 122 123 LPBYTE p = (LPBYTE) pidl; 124 *((WORD *) p) = cbData; 125 p += sizeof(WORD); 126 127 memcpy(p, lpszPath, pathLen); 128 p += pathLen - sizeof(WCHAR); 129 *((WCHAR *) p) = '\0'; 130 p += sizeof(WCHAR); 131 132 memcpy(p, lpLastFSPidl, lpLastFSPidl->mkid.cb); 133 p += lpLastFSPidl->mkid.cb; 134 135 *((WORD *) p) = 0; 136 137 return pidl; 138 } 139 140 static LPCWSTR _ILGetPath(LPCITEMIDLIST pidl) 141 { 142 if (!pidl || !pidl->mkid.cb) 143 return NULL; 144 return (LPCWSTR) pidl->mkid.abID; 145 } 146 147 static LPCITEMIDLIST _ILGetFSPidl(LPCITEMIDLIST pidl) 148 { 149 if (!pidl || !pidl->mkid.cb) 150 return pidl; 151 return (LPCITEMIDLIST) ((LPBYTE) pidl->mkid.abID 152 + ((wcslen((LPCWSTR) pidl->mkid.abID) + 1) * sizeof(WCHAR))); 153 } 154 155 struct _SearchData 156 { 157 HWND hwnd; 158 HANDLE hStopEvent; 159 CStringW szPath; 160 CStringW szFileName; 161 CStringA szQueryA; 162 CStringW szQueryW; 163 BOOL SearchHidden; 164 CComPtr<CFindFolder> pFindFolder; 165 }; 166 167 template<typename TChar, typename TString, int (&StrNCmp)(const TChar *, const TChar *, size_t)> 168 static const TChar* StrStrN(const TChar *lpFirst, const TString &lpSrch, UINT cchMax) 169 { 170 if (!lpFirst || lpSrch.IsEmpty() || !cchMax) 171 return NULL; 172 173 for (UINT i = cchMax; i > 0 && *lpFirst; i--, lpFirst++) 174 { 175 if (!StrNCmp(lpFirst, lpSrch, lpSrch.GetLength())) 176 return (const TChar*)lpFirst; 177 } 178 179 return NULL; 180 } 181 182 template<typename TChar, typename TString, int (&StrNCmp)(const TChar *, const TChar *, size_t)> 183 static UINT StrStrNCount(const TChar *lpFirst, const TString &lpSrch, UINT cchMax) 184 { 185 const TChar *lpSearchEnd = lpFirst + cchMax; 186 UINT uCount = 0; 187 while (lpFirst < lpSearchEnd && (lpFirst = StrStrN<TChar, TString, StrNCmp>(lpFirst, lpSrch, cchMax))) 188 { 189 uCount++; 190 lpFirst += lpSrch.GetLength(); 191 cchMax = lpSearchEnd - lpFirst; 192 } 193 return uCount; 194 } 195 196 static UINT StrStrCountNIA(const CHAR *lpFirst, const CStringA &lpSrch, UINT cchMax) 197 { 198 return StrStrNCount<CHAR, CStringA, _strnicmp>(lpFirst, lpSrch, cchMax); 199 } 200 201 static UINT StrStrCountNIW(const WCHAR *lpFirst, const CStringW &lpSrch, UINT cchMax) 202 { 203 return StrStrNCount<WCHAR, CStringW, _wcsnicmp>(lpFirst, lpSrch, cchMax); 204 } 205 206 static UINT SearchFile(LPCWSTR lpFilePath, _SearchData *pSearchData) 207 { 208 HANDLE hFile = CreateFileW(lpFilePath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); 209 if (hFile == INVALID_HANDLE_VALUE) 210 return 0; 211 212 DWORD size = GetFileSize(hFile, NULL); 213 HANDLE hFileMap = CreateFileMappingW(hFile, NULL, PAGE_READONLY, 0, 0, NULL); 214 CloseHandle(hFile); 215 if (hFileMap == INVALID_HANDLE_VALUE) 216 return 0; 217 218 LPBYTE lpFileContent = (LPBYTE) MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 0); 219 CloseHandle(hFileMap); 220 if (!lpFileContent) 221 return 0; 222 223 UINT uMatches = 0; 224 // Check for UTF-16 BOM 225 if (size >= 2 && lpFileContent[0] == 0xFF && lpFileContent[1] == 0xFE) 226 { 227 uMatches = StrStrCountNIW((LPCWSTR) lpFileContent, pSearchData->szQueryW, size / sizeof(WCHAR)); 228 } 229 else 230 { 231 uMatches = StrStrCountNIA((LPCSTR) lpFileContent, pSearchData->szQueryA, size / sizeof(CHAR)); 232 } 233 234 UnmapViewOfFile(lpFileContent); 235 236 return uMatches; 237 } 238 239 static BOOL FileNameMatch(LPCWSTR FindDataFileName, _SearchData *pSearchData) 240 { 241 if (pSearchData->szFileName.IsEmpty() || PathMatchSpecW(FindDataFileName, pSearchData->szFileName)) 242 { 243 return TRUE; 244 } 245 return FALSE; 246 } 247 248 static BOOL ContentsMatch(LPCWSTR szPath, _SearchData *pSearchData) 249 { 250 if (pSearchData->szQueryA.IsEmpty() || SearchFile(szPath, pSearchData)) 251 { 252 return TRUE; 253 } 254 return FALSE; 255 } 256 257 static BOOL AttribHiddenMatch(DWORD FileAttributes, _SearchData *pSearchData) 258 { 259 if (!(FileAttributes & FILE_ATTRIBUTE_HIDDEN) || (pSearchData->SearchHidden)) 260 { 261 return TRUE; 262 } 263 return FALSE; 264 } 265 266 static UINT RecursiveFind(LPCWSTR lpPath, _SearchData *pSearchData) 267 { 268 if (WaitForSingleObject(pSearchData->hStopEvent, 0) != WAIT_TIMEOUT) 269 return 0; 270 271 WCHAR szPath[MAX_PATH]; 272 WIN32_FIND_DATAW FindData; 273 HANDLE hFindFile; 274 BOOL bMoreFiles = TRUE; 275 UINT uTotalFound = 0; 276 277 PathCombineW(szPath, lpPath, L"*.*"); 278 279 for (hFindFile = FindFirstFileW(szPath, &FindData); 280 bMoreFiles && hFindFile != INVALID_HANDLE_VALUE; 281 bMoreFiles = FindNextFileW(hFindFile, &FindData)) 282 { 283 if (!wcscmp(FindData.cFileName, L".") || !wcscmp(FindData.cFileName, L"..")) 284 continue; 285 286 PathCombineW(szPath, lpPath, FindData.cFileName); 287 288 if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 289 { 290 CStringW status; 291 if (FileNameMatch(FindData.cFileName, pSearchData) 292 && AttribHiddenMatch(FindData.dwFileAttributes, pSearchData)) 293 { 294 PostMessageW(pSearchData->hwnd, WM_SEARCH_ADD_RESULT, 0, (LPARAM) StrDupW(szPath)); 295 uTotalFound++; 296 } 297 status.Format(IDS_SEARCH_FOLDER, FindData.cFileName); 298 PostMessageW(pSearchData->hwnd, WM_SEARCH_UPDATE_STATUS, 0, (LPARAM) StrDupW(status.GetBuffer())); 299 300 uTotalFound += RecursiveFind(szPath, pSearchData); 301 } 302 else if (FileNameMatch(FindData.cFileName, pSearchData) 303 && AttribHiddenMatch(FindData.dwFileAttributes, pSearchData) 304 && ContentsMatch(szPath, pSearchData)) 305 { 306 uTotalFound++; 307 PostMessageW(pSearchData->hwnd, WM_SEARCH_ADD_RESULT, 0, (LPARAM) StrDupW(szPath)); 308 } 309 } 310 311 if (hFindFile != INVALID_HANDLE_VALUE) 312 FindClose(hFindFile); 313 314 return uTotalFound; 315 } 316 317 DWORD WINAPI CFindFolder::SearchThreadProc(LPVOID lpParameter) 318 { 319 _SearchData *data = static_cast<_SearchData*>(lpParameter); 320 321 data->pFindFolder->NotifyConnections(DISPID_SEARCHSTART); 322 323 UINT uTotalFound = RecursiveFind(data->szPath, data); 324 325 data->pFindFolder->NotifyConnections(DISPID_SEARCHCOMPLETE); 326 327 CStringW status; 328 status.Format(IDS_SEARCH_FILES_FOUND, uTotalFound); 329 ::PostMessageW(data->hwnd, WM_SEARCH_UPDATE_STATUS, 0, (LPARAM) StrDupW(status.GetBuffer())); 330 ::SendMessageW(data->hwnd, WM_SEARCH_STOP, 0, 0); 331 332 CloseHandle(data->hStopEvent); 333 delete data; 334 335 return 0; 336 } 337 338 void CFindFolder::NotifyConnections(DISPID id) 339 { 340 DISPPARAMS dispatchParams = {0}; 341 CComDynamicUnkArray &subscribers = 342 IConnectionPointImpl<CFindFolder, &DIID_DSearchCommandEvents>::m_vec; 343 for (IUnknown** pSubscriber = subscribers.begin(); pSubscriber < subscribers.end(); pSubscriber++) 344 { 345 if (!*pSubscriber) 346 continue; 347 348 CComPtr<IDispatch> pDispatch; 349 HRESULT hResult = (*pSubscriber)->QueryInterface(IID_PPV_ARG(IDispatch, &pDispatch)); 350 if (!FAILED_UNEXPECTEDLY(hResult)) 351 pDispatch->Invoke(id, GUID_NULL, 0, DISPATCH_METHOD, &dispatchParams, NULL, NULL, NULL); 352 } 353 } 354 355 LRESULT CFindFolder::StartSearch(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) 356 { 357 HKEY hkey; 358 DWORD size = sizeof(DWORD); 359 DWORD result; 360 DWORD SearchHiddenValue = 0; 361 362 if (!lParam) 363 return 0; 364 365 // Clear all previous search results 366 UINT uItemIndex; 367 m_shellFolderView->RemoveObject(NULL, &uItemIndex); 368 369 _SearchData* pSearchData = new _SearchData(); 370 pSearchData->pFindFolder = this; 371 pSearchData->hwnd = m_hWnd; 372 373 SearchStart *pSearchParams = (SearchStart *) lParam; 374 pSearchData->szPath = pSearchParams->szPath; 375 pSearchData->szFileName = pSearchParams->szFileName; 376 pSearchData->szQueryA = pSearchParams->szQuery; 377 pSearchData->szQueryW = pSearchParams->szQuery; 378 pSearchData->SearchHidden = pSearchParams->SearchHidden; 379 SHFree(pSearchParams); 380 381 TRACE("pSearchParams->SearchHidden is '%d'.\n", pSearchData->SearchHidden); 382 383 if (pSearchData->SearchHidden) 384 SearchHiddenValue = 1; 385 else 386 SearchHiddenValue = 0; 387 388 /* Placing the code to save the changed settings to the registry here has the effect of not saving any changes */ 389 /* to the registry unless the user clicks on the "Search" button. This is the same as what we see in Windows. */ 390 result = RegOpenKeyEx(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer", 0, KEY_SET_VALUE, &hkey); 391 if (result == ERROR_SUCCESS) 392 { 393 if (RegSetValueExW(hkey, L"SearchHidden", NULL, REG_DWORD, (const BYTE*)&SearchHiddenValue, size) == ERROR_SUCCESS) 394 { 395 TRACE("SearchHidden is '%d'.\n", SearchHiddenValue); 396 } 397 else 398 { 399 ERR("RegSetValueEx for \"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\SearchHidden\" Failed.\n"); 400 } 401 RegCloseKey(hkey); 402 } 403 else 404 { 405 ERR("RegOpenKey for \"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\" Failed.\n"); 406 } 407 408 if (m_hStopEvent) 409 SetEvent(m_hStopEvent); 410 pSearchData->hStopEvent = m_hStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); 411 412 if (!SHCreateThread(SearchThreadProc, pSearchData, NULL, NULL)) 413 { 414 SHFree(pSearchData); 415 return 0; 416 } 417 418 return 0; 419 } 420 421 LRESULT CFindFolder::StopSearch(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) 422 { 423 if (m_hStopEvent) 424 { 425 SetEvent(m_hStopEvent); 426 m_hStopEvent = NULL; 427 } 428 return 0; 429 } 430 431 LRESULT CFindFolder::AddResult(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) 432 { 433 if (!lParam) 434 return 0; 435 436 CComHeapPtr<WCHAR> lpPath((LPWSTR) lParam); 437 438 CComHeapPtr<ITEMIDLIST> lpSearchPidl(_ILCreate(lpPath)); 439 if (lpSearchPidl) 440 { 441 UINT uItemIndex; 442 m_shellFolderView->AddObject(lpSearchPidl, &uItemIndex); 443 } 444 445 return 0; 446 } 447 448 LRESULT CFindFolder::UpdateStatus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) 449 { 450 CComHeapPtr<WCHAR> status((LPWSTR) lParam); 451 if (m_shellBrowser) 452 { 453 m_shellBrowser->SetStatusTextSB(status); 454 } 455 456 return 0; 457 } 458 459 // *** IShellFolder2 methods *** 460 STDMETHODIMP CFindFolder::GetDefaultSearchGUID(GUID *pguid) 461 { 462 UNIMPLEMENTED; 463 return E_NOTIMPL; 464 } 465 466 STDMETHODIMP CFindFolder::EnumSearches(IEnumExtraSearch **ppenum) 467 { 468 UNIMPLEMENTED; 469 return E_NOTIMPL; 470 } 471 472 STDMETHODIMP CFindFolder::GetDefaultColumn(DWORD, ULONG *pSort, ULONG *pDisplay) 473 { 474 if (pSort) 475 *pSort = 0; 476 if (pDisplay) 477 *pDisplay = 0; 478 return S_OK; 479 } 480 481 STDMETHODIMP CFindFolder::GetDefaultColumnState(UINT iColumn, DWORD *pcsFlags) 482 { 483 if (!pcsFlags) 484 return E_INVALIDARG; 485 if (iColumn >= _countof(g_ColumnDefs)) 486 return m_pisfInner->GetDefaultColumnState(iColumn - _countof(g_ColumnDefs) + 1, pcsFlags); 487 *pcsFlags = g_ColumnDefs[iColumn].dwDefaultState; 488 return S_OK; 489 } 490 491 STDMETHODIMP CFindFolder::GetDetailsEx(PCUITEMID_CHILD pidl, const SHCOLUMNID *pscid, VARIANT *pv) 492 { 493 return m_pisfInner->GetDetailsEx(pidl, pscid, pv); 494 } 495 496 STDMETHODIMP CFindFolder::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDETAILS *pDetails) 497 { 498 if (iColumn >= _countof(g_ColumnDefs)) 499 return m_pisfInner->GetDetailsOf(_ILGetFSPidl(pidl), iColumn - _countof(g_ColumnDefs) + 1, pDetails); 500 501 pDetails->cxChar = g_ColumnDefs[iColumn].cxChar; 502 pDetails->fmt = g_ColumnDefs[iColumn].fmt; 503 504 if (!pidl) 505 return SHSetStrRet(&pDetails->str, _AtlBaseModule.GetResourceInstance(), g_ColumnDefs[iColumn].iResource); 506 507 if (iColumn == 1) 508 { 509 return SHSetStrRet(&pDetails->str, _ILGetPath(pidl)); 510 } 511 512 return GetDisplayNameOf(pidl, SHGDN_NORMAL, &pDetails->str); 513 } 514 515 STDMETHODIMP CFindFolder::MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid) 516 { 517 UNIMPLEMENTED; 518 return E_NOTIMPL; 519 } 520 521 // *** IShellFolder methods *** 522 STDMETHODIMP CFindFolder::ParseDisplayName(HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName, ULONG *pchEaten, 523 PIDLIST_RELATIVE *ppidl, ULONG *pdwAttributes) 524 { 525 UNIMPLEMENTED; 526 return E_NOTIMPL; 527 } 528 529 STDMETHODIMP CFindFolder::EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList) 530 { 531 *ppEnumIDList = NULL; 532 return S_FALSE; 533 } 534 535 STDMETHODIMP CFindFolder::BindToObject(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut) 536 { 537 UNIMPLEMENTED; 538 return E_NOTIMPL; 539 } 540 541 STDMETHODIMP CFindFolder::BindToStorage(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut) 542 { 543 UNIMPLEMENTED; 544 return E_NOTIMPL; 545 } 546 547 STDMETHODIMP CFindFolder::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2) 548 { 549 return m_pisfInner->CompareIDs(lParam, _ILGetFSPidl(pidl1), _ILGetFSPidl(pidl2)); 550 } 551 552 STDMETHODIMP CFindFolder::CreateViewObject(HWND hwndOwner, REFIID riid, LPVOID *ppvOut) 553 { 554 if (riid == IID_IShellView) 555 { 556 SFV_CREATE sfvparams = {}; 557 sfvparams.cbSize = sizeof(SFV_CREATE); 558 sfvparams.pshf = this; 559 sfvparams.psfvcb = this; 560 HRESULT hr = SHCreateShellFolderView(&sfvparams, (IShellView **) ppvOut); 561 if (FAILED_UNEXPECTEDLY(hr)) 562 { 563 return hr; 564 } 565 566 return ((IShellView * ) * ppvOut)->QueryInterface(IID_PPV_ARG(IShellFolderView, &m_shellFolderView)); 567 } 568 return E_NOINTERFACE; 569 } 570 571 STDMETHODIMP CFindFolder::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, DWORD *rgfInOut) 572 { 573 CComHeapPtr<PCITEMID_CHILD> aFSPidl; 574 aFSPidl.Allocate(cidl); 575 for (UINT i = 0; i < cidl; i++) 576 { 577 aFSPidl[i] = _ILGetFSPidl(apidl[i]); 578 } 579 580 return m_pisfInner->GetAttributesOf(cidl, aFSPidl, rgfInOut); 581 } 582 583 class CFindFolderContextMenu : 584 public IContextMenu, 585 public CComObjectRootEx<CComMultiThreadModelNoCS> 586 { 587 CComPtr<IContextMenu> m_pInner; 588 CComPtr<IShellFolderView> m_shellFolderView; 589 UINT m_firstCmdId; 590 static const UINT ADDITIONAL_MENU_ITEMS = 2; 591 592 //// *** IContextMenu methods *** 593 STDMETHODIMP QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) 594 { 595 m_firstCmdId = indexMenu; 596 _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst++, MFT_STRING, MAKEINTRESOURCEW(IDS_SEARCH_OPEN_FOLDER), MFS_ENABLED); 597 _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst++, MFT_SEPARATOR, NULL, 0); 598 return m_pInner->QueryContextMenu(hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags); 599 } 600 601 STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi) 602 { 603 if (!IS_INTRESOURCE(lpcmi->lpVerb)) 604 { 605 return m_pInner->InvokeCommand(lpcmi); 606 } 607 608 if (LOWORD(lpcmi->lpVerb) < m_firstCmdId + ADDITIONAL_MENU_ITEMS) 609 { 610 PCUITEMID_CHILD *apidl; 611 UINT cidl; 612 HRESULT hResult = m_shellFolderView->GetSelectedObjects(&apidl, &cidl); 613 if (FAILED_UNEXPECTEDLY(hResult)) 614 return hResult; 615 616 for (UINT i = 0; i < cidl; i++) 617 { 618 CComHeapPtr<ITEMIDLIST> folderPidl(ILCreateFromPathW(_ILGetPath(apidl[i]))); 619 if (!folderPidl) 620 return E_OUTOFMEMORY; 621 CComHeapPtr<ITEMIDLIST> filePidl(ILCombine(folderPidl, _ILGetFSPidl(apidl[i]))); 622 if (!filePidl) 623 return E_OUTOFMEMORY; 624 SHOpenFolderAndSelectItems(folderPidl, 1, &filePidl, 0); 625 } 626 return S_OK; 627 } 628 629 CMINVOKECOMMANDINFOEX actualCmdInfo; 630 memcpy(&actualCmdInfo, lpcmi, lpcmi->cbSize); 631 actualCmdInfo.lpVerb -= ADDITIONAL_MENU_ITEMS; 632 return m_pInner->InvokeCommand((CMINVOKECOMMANDINFO *)&actualCmdInfo); 633 } 634 635 STDMETHODIMP GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen) 636 { 637 return m_pInner->GetCommandString(idCommand, uFlags, lpReserved, lpszName, uMaxNameLen); 638 } 639 640 public: 641 static HRESULT Create(IShellFolderView *pShellFolderView, IContextMenu *pInnerContextMenu, IContextMenu **pContextMenu) 642 { 643 CComObject<CFindFolderContextMenu> *pObj; 644 HRESULT hResult = CComObject<CFindFolderContextMenu>::CreateInstance(&pObj); 645 if (FAILED_UNEXPECTEDLY(hResult)) 646 return hResult; 647 pObj->m_shellFolderView = pShellFolderView; 648 pObj->m_pInner = pInnerContextMenu; 649 return pObj->QueryInterface(IID_PPV_ARG(IContextMenu, pContextMenu)); 650 } 651 652 BEGIN_COM_MAP(CFindFolderContextMenu) 653 COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu) 654 END_COM_MAP() 655 }; 656 657 STDMETHODIMP CFindFolder::GetUIObjectOf(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, REFIID riid, 658 UINT *prgfInOut, LPVOID *ppvOut) 659 { 660 if (cidl <= 0) 661 { 662 return m_pisfInner->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, ppvOut); 663 } 664 665 CComHeapPtr<PCITEMID_CHILD> aFSPidl; 666 aFSPidl.Allocate(cidl); 667 for (UINT i = 0; i < cidl; i++) 668 { 669 aFSPidl[i] = _ILGetFSPidl(apidl[i]); 670 } 671 672 if (riid == IID_IContextMenu) 673 { 674 CComHeapPtr<ITEMIDLIST> folderPidl(ILCreateFromPathW(_ILGetPath(apidl[0]))); 675 if (!folderPidl) 676 return E_OUTOFMEMORY; 677 CComPtr<IShellFolder> pDesktopFolder; 678 HRESULT hResult = SHGetDesktopFolder(&pDesktopFolder); 679 if (FAILED_UNEXPECTEDLY(hResult)) 680 return hResult; 681 CComPtr<IShellFolder> pShellFolder; 682 hResult = pDesktopFolder->BindToObject(folderPidl, NULL, IID_PPV_ARG(IShellFolder, &pShellFolder)); 683 if (FAILED_UNEXPECTEDLY(hResult)) 684 return hResult; 685 CComPtr<IContextMenu> pContextMenu; 686 hResult = pShellFolder->GetUIObjectOf(hwndOwner, cidl, aFSPidl, riid, prgfInOut, (LPVOID *)&pContextMenu); 687 if (FAILED_UNEXPECTEDLY(hResult)) 688 return hResult; 689 return CFindFolderContextMenu::Create(m_shellFolderView, pContextMenu, (IContextMenu **)ppvOut); 690 } 691 692 return m_pisfInner->GetUIObjectOf(hwndOwner, cidl, aFSPidl, riid, prgfInOut, ppvOut); 693 } 694 695 STDMETHODIMP CFindFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET pName) 696 { 697 return m_pisfInner->GetDisplayNameOf(_ILGetFSPidl(pidl), dwFlags, pName); 698 } 699 700 STDMETHODIMP CFindFolder::SetNameOf(HWND hwndOwner, PCUITEMID_CHILD pidl, LPCOLESTR lpName, DWORD dwFlags, 701 PITEMID_CHILD *pPidlOut) 702 { 703 UNIMPLEMENTED; 704 return E_NOTIMPL; 705 } 706 707 //// *** IShellFolderViewCB method *** 708 STDMETHODIMP CFindFolder::MessageSFVCB(UINT uMsg, WPARAM wParam, LPARAM lParam) 709 { 710 switch (uMsg) 711 { 712 case SFVM_DEFVIEWMODE: 713 { 714 FOLDERVIEWMODE *pViewMode = (FOLDERVIEWMODE *) lParam; 715 *pViewMode = FVM_DETAILS; 716 return S_OK; 717 } 718 case SFVM_WINDOWCREATED: 719 { 720 // Subclass window to receive window messages 721 SubclassWindow((HWND) wParam); 722 723 // Get shell browser for updating status bar text 724 CComPtr<IServiceProvider> pServiceProvider; 725 HRESULT hr = m_shellFolderView->QueryInterface(IID_PPV_ARG(IServiceProvider, &pServiceProvider)); 726 if (FAILED_UNEXPECTEDLY(hr)) 727 return hr; 728 hr = pServiceProvider->QueryService(SID_SShellBrowser, IID_PPV_ARG(IShellBrowser, &m_shellBrowser)); 729 if (FAILED_UNEXPECTEDLY(hr)) 730 return hr; 731 732 // Open search bar 733 CComPtr<IWebBrowser2> pWebBrowser2; 734 hr = m_shellBrowser->QueryInterface(IID_PPV_ARG(IWebBrowser2, &pWebBrowser2)); 735 if (FAILED_UNEXPECTEDLY(hr)) 736 return hr; 737 WCHAR pwszGuid[MAX_PATH]; 738 StringFromGUID2(CLSID_FileSearchBand, pwszGuid, _countof(pwszGuid)); 739 CComVariant searchBar(pwszGuid); 740 return pWebBrowser2->ShowBrowserBar(&searchBar, NULL, NULL); 741 } 742 } 743 return E_NOTIMPL; 744 } 745 746 //// *** IPersistFolder2 methods *** 747 STDMETHODIMP CFindFolder::GetCurFolder(PIDLIST_ABSOLUTE *pidl) 748 { 749 *pidl = ILClone(m_pidl); 750 return S_OK; 751 } 752 753 // *** IPersistFolder methods *** 754 STDMETHODIMP CFindFolder::Initialize(PCIDLIST_ABSOLUTE pidl) 755 { 756 m_pidl = ILClone(pidl); 757 if (!m_pidl) 758 return E_OUTOFMEMORY; 759 760 return SHELL32_CoCreateInitSF(m_pidl, 761 NULL, 762 NULL, 763 &CLSID_ShellFSFolder, 764 IID_PPV_ARG(IShellFolder2, &m_pisfInner)); 765 } 766 767 // *** IPersist methods *** 768 STDMETHODIMP CFindFolder::GetClassID(CLSID *pClassId) 769 { 770 if (pClassId == NULL) 771 return E_INVALIDARG; 772 *pClassId = CLSID_FindFolder; 773 return S_OK; 774 } 775