[SHELLFIND] Simplify PIDLs to paths
[reactos.git] / dll / win32 / browseui / shellfind / CFindFolder.cpp
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 ((ULONG_PTR)HIWORD((ULONG_PTR)dwTypeData) == 0)
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 (!(LPITEMIDLIST)lpFSPidl)
111 {
112 ERR("Failed to create pidl from path\n");
113 return 0;
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 CComPtr<CFindFolder> pFindFolder;
164 };
165
166 template<typename TChar, typename TString, int (&StrNCmp)(const TChar *, const TChar *, size_t)>
167 static const TChar* StrStrN(const TChar *lpFirst, const TString &lpSrch, UINT cchMax)
168 {
169 if (!lpFirst || lpSrch.IsEmpty() || !cchMax)
170 return NULL;
171
172 for (UINT i = cchMax; i > 0 && *lpFirst; i--, lpFirst++)
173 {
174 if (!StrNCmp(lpFirst, lpSrch, lpSrch.GetLength()))
175 return (const TChar*)lpFirst;
176 }
177
178 return NULL;
179 }
180
181 template<typename TChar, typename TString, int (&StrNCmp)(const TChar *, const TChar *, size_t)>
182 static UINT StrStrNCount(const TChar *lpFirst, const TString &lpSrch, UINT cchMax)
183 {
184 const TChar *lpSearchEnd = lpFirst + cchMax;
185 UINT uCount = 0;
186 while (lpFirst < lpSearchEnd && (lpFirst = StrStrN<TChar, TString, StrNCmp>(lpFirst, lpSrch, cchMax)))
187 {
188 uCount++;
189 lpFirst += lpSrch.GetLength();
190 cchMax = lpSearchEnd - lpFirst;
191 }
192 return uCount;
193 }
194
195 static UINT StrStrCountNIA(const CHAR *lpFirst, const CStringA &lpSrch, UINT cchMax)
196 {
197 return StrStrNCount<CHAR, CStringA, _strnicmp>(lpFirst, lpSrch, cchMax);
198 }
199
200 static UINT StrStrCountNIW(const WCHAR *lpFirst, const CStringW &lpSrch, UINT cchMax)
201 {
202 return StrStrNCount<WCHAR, CStringW, _wcsnicmp>(lpFirst, lpSrch, cchMax);
203 }
204
205 static UINT SearchFile(LPCWSTR lpFilePath, _SearchData *pSearchData)
206 {
207 HANDLE hFile = CreateFileW(lpFilePath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
208 if (hFile == INVALID_HANDLE_VALUE)
209 return 0;
210
211 DWORD size = GetFileSize(hFile, NULL);
212 HANDLE hFileMap = CreateFileMappingW(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
213 CloseHandle(hFile);
214 if (hFileMap == INVALID_HANDLE_VALUE)
215 return 0;
216
217 LPBYTE lpFileContent = (LPBYTE) MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 0);
218 CloseHandle(hFileMap);
219 if (!lpFileContent)
220 return 0;
221
222 UINT uMatches = 0;
223 // Check for UTF-16 BOM
224 if (size >= 2 && lpFileContent[0] == 0xFF && lpFileContent[1] == 0xFE)
225 {
226 uMatches = StrStrCountNIW((LPCWSTR) lpFileContent, pSearchData->szQueryW, size / sizeof(WCHAR));
227 }
228 else
229 {
230 uMatches = StrStrCountNIA((LPCSTR) lpFileContent, pSearchData->szQueryA, size / sizeof(CHAR));
231 }
232
233 UnmapViewOfFile(lpFileContent);
234
235 return uMatches;
236 }
237
238 static UINT RecursiveFind(LPCWSTR lpPath, _SearchData *pSearchData)
239 {
240 if (WaitForSingleObject(pSearchData->hStopEvent, 0) != WAIT_TIMEOUT)
241 return 0;
242
243 WCHAR szPath[MAX_PATH];
244 WIN32_FIND_DATAW FindData;
245 HANDLE hFindFile;
246 BOOL bMoreFiles = TRUE;
247 UINT uTotalFound = 0;
248
249 PathCombineW(szPath, lpPath, L"*.*");
250
251 for (hFindFile = FindFirstFileW(szPath, &FindData);
252 bMoreFiles && hFindFile != INVALID_HANDLE_VALUE;
253 bMoreFiles = FindNextFileW(hFindFile, &FindData))
254 {
255 if (!wcscmp(FindData.cFileName, L".") || !wcscmp(FindData.cFileName, L".."))
256 continue;
257
258 PathCombineW(szPath, lpPath, FindData.cFileName);
259
260 if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
261 {
262 CStringW status;
263 status.Format(IDS_SEARCH_FOLDER, FindData.cFileName);
264 PostMessageW(pSearchData->hwnd, WM_SEARCH_UPDATE_STATUS, 0, (LPARAM) StrDupW(status.GetBuffer()));
265
266 uTotalFound += RecursiveFind(szPath, pSearchData);
267 }
268 else if ((pSearchData->szFileName.IsEmpty() || PathMatchSpecW(FindData.cFileName, pSearchData->szFileName))
269 && (pSearchData->szQueryA.IsEmpty() || SearchFile(szPath, pSearchData)))
270 {
271 uTotalFound++;
272 PostMessageW(pSearchData->hwnd, WM_SEARCH_ADD_RESULT, 0, (LPARAM) StrDupW(szPath));
273 }
274 }
275
276 if (hFindFile != INVALID_HANDLE_VALUE)
277 FindClose(hFindFile);
278
279 return uTotalFound;
280 }
281
282 DWORD WINAPI CFindFolder::SearchThreadProc(LPVOID lpParameter)
283 {
284 _SearchData *data = static_cast<_SearchData*>(lpParameter);
285
286 data->pFindFolder->NotifyConnections(DISPID_SEARCHSTART);
287
288 UINT uTotalFound = RecursiveFind(data->szPath, data);
289
290 data->pFindFolder->NotifyConnections(DISPID_SEARCHCOMPLETE);
291
292 CStringW status;
293 status.Format(IDS_SEARCH_FILES_FOUND, uTotalFound);
294 ::PostMessageW(data->hwnd, WM_SEARCH_UPDATE_STATUS, 0, (LPARAM) StrDupW(status.GetBuffer()));
295 ::SendMessageW(data->hwnd, WM_SEARCH_STOP, 0, 0);
296
297 CloseHandle(data->hStopEvent);
298 delete data;
299
300 return 0;
301 }
302
303 void CFindFolder::NotifyConnections(DISPID id)
304 {
305 DISPPARAMS dispatchParams = {0};
306 CComDynamicUnkArray &subscribers =
307 IConnectionPointImpl<CFindFolder, &DIID_DSearchCommandEvents>::m_vec;
308 for (IUnknown** pSubscriber = subscribers.begin(); pSubscriber < subscribers.end(); pSubscriber++)
309 {
310 if (!*pSubscriber)
311 continue;
312
313 CComPtr<IDispatch> pDispatch;
314 HRESULT hResult = (*pSubscriber)->QueryInterface(IID_PPV_ARG(IDispatch, &pDispatch));
315 if (!FAILED_UNEXPECTEDLY(hResult))
316 pDispatch->Invoke(id, GUID_NULL, 0, DISPATCH_METHOD, &dispatchParams, NULL, NULL, NULL);
317 }
318 }
319
320 LRESULT CFindFolder::StartSearch(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
321 {
322 if (!lParam)
323 return 0;
324
325 // Clear all previous search results
326 UINT uItemIndex;
327 m_shellFolderView->RemoveObject(NULL, &uItemIndex);
328
329 _SearchData* pSearchData = new _SearchData();
330 pSearchData->pFindFolder = this;
331 pSearchData->hwnd = m_hWnd;
332
333 SearchStart *pSearchParams = (SearchStart *) lParam;
334 pSearchData->szPath = pSearchParams->szPath;
335 pSearchData->szFileName = pSearchParams->szFileName;
336 pSearchData->szQueryA = pSearchParams->szQuery;
337 pSearchData->szQueryW = pSearchParams->szQuery;
338 SHFree(pSearchParams);
339
340 if (m_hStopEvent)
341 SetEvent(m_hStopEvent);
342 pSearchData->hStopEvent = m_hStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
343
344 if (!SHCreateThread(SearchThreadProc, pSearchData, NULL, NULL))
345 {
346 SHFree(pSearchData);
347 return HRESULT_FROM_WIN32(GetLastError());
348 }
349
350 return S_OK;
351 }
352
353 LRESULT CFindFolder::StopSearch(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
354 {
355 if (m_hStopEvent)
356 {
357 SetEvent(m_hStopEvent);
358 m_hStopEvent = NULL;
359 }
360 return 0;
361 }
362
363 LRESULT CFindFolder::AddResult(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
364 {
365 if (!lParam)
366 return 0;
367
368 CComHeapPtr<WCHAR> lpPath((LPWSTR) lParam);
369
370 CComHeapPtr<ITEMIDLIST> lpSearchPidl(_ILCreate(lpPath));
371 if (lpSearchPidl)
372 {
373 UINT uItemIndex;
374 m_shellFolderView->AddObject(lpSearchPidl, &uItemIndex);
375 }
376
377 return 0;
378 }
379
380 LRESULT CFindFolder::UpdateStatus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
381 {
382 CComHeapPtr<WCHAR> status((LPWSTR) lParam);
383 if (m_shellBrowser)
384 {
385 m_shellBrowser->SetStatusTextSB(status);
386 }
387
388 return S_OK;
389 }
390
391 // *** IShellFolder2 methods ***
392 STDMETHODIMP CFindFolder::GetDefaultSearchGUID(GUID *pguid)
393 {
394 UNIMPLEMENTED;
395 return E_NOTIMPL;
396 }
397
398 STDMETHODIMP CFindFolder::EnumSearches(IEnumExtraSearch **ppenum)
399 {
400 UNIMPLEMENTED;
401 return E_NOTIMPL;
402 }
403
404 STDMETHODIMP CFindFolder::GetDefaultColumn(DWORD, ULONG *pSort, ULONG *pDisplay)
405 {
406 if (pSort)
407 *pSort = 0;
408 if (pDisplay)
409 *pDisplay = 0;
410 return S_OK;
411 }
412
413 STDMETHODIMP CFindFolder::GetDefaultColumnState(UINT iColumn, DWORD *pcsFlags)
414 {
415 if (!pcsFlags)
416 return E_INVALIDARG;
417 if (iColumn >= _countof(g_ColumnDefs))
418 return m_pisfInner->GetDefaultColumnState(iColumn - _countof(g_ColumnDefs) + 1, pcsFlags);
419 *pcsFlags = g_ColumnDefs[iColumn].dwDefaultState;
420 return S_OK;
421 }
422
423 STDMETHODIMP CFindFolder::GetDetailsEx(PCUITEMID_CHILD pidl, const SHCOLUMNID *pscid, VARIANT *pv)
424 {
425 return m_pisfInner->GetDetailsEx(pidl, pscid, pv);
426 }
427
428 STDMETHODIMP CFindFolder::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDETAILS *pDetails)
429 {
430 if (iColumn >= _countof(g_ColumnDefs))
431 return m_pisfInner->GetDetailsOf(_ILGetFSPidl(pidl), iColumn - _countof(g_ColumnDefs) + 1, pDetails);
432
433 pDetails->cxChar = g_ColumnDefs[iColumn].cxChar;
434 pDetails->fmt = g_ColumnDefs[iColumn].fmt;
435
436 if (!pidl)
437 return SHSetStrRet(&pDetails->str, _AtlBaseModule.GetResourceInstance(), g_ColumnDefs[iColumn].iResource);
438
439 if (iColumn == 1)
440 {
441 return SHSetStrRet(&pDetails->str, _ILGetPath(pidl));
442 }
443
444 return GetDisplayNameOf(pidl, SHGDN_NORMAL, &pDetails->str);
445 }
446
447 STDMETHODIMP CFindFolder::MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid)
448 {
449 UNIMPLEMENTED;
450 return E_NOTIMPL;
451 }
452
453 // *** IShellFolder methods ***
454 STDMETHODIMP CFindFolder::ParseDisplayName(HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName, ULONG *pchEaten,
455 PIDLIST_RELATIVE *ppidl, ULONG *pdwAttributes)
456 {
457 UNIMPLEMENTED;
458 return E_NOTIMPL;
459 }
460
461 STDMETHODIMP CFindFolder::EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList)
462 {
463 *ppEnumIDList = NULL;
464 return S_FALSE;
465 }
466
467 STDMETHODIMP CFindFolder::BindToObject(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut)
468 {
469 UNIMPLEMENTED;
470 return E_NOTIMPL;
471 }
472
473 STDMETHODIMP CFindFolder::BindToStorage(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut)
474 {
475 UNIMPLEMENTED;
476 return E_NOTIMPL;
477 }
478
479 STDMETHODIMP CFindFolder::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2)
480 {
481 return m_pisfInner->CompareIDs(lParam, _ILGetFSPidl(pidl1), _ILGetFSPidl(pidl2));
482 }
483
484 STDMETHODIMP CFindFolder::CreateViewObject(HWND hwndOwner, REFIID riid, LPVOID *ppvOut)
485 {
486 if (riid == IID_IShellView)
487 {
488 SFV_CREATE sfvparams = {};
489 sfvparams.cbSize = sizeof(SFV_CREATE);
490 sfvparams.pshf = this;
491 sfvparams.psfvcb = this;
492 HRESULT hr = SHCreateShellFolderView(&sfvparams, (IShellView **) ppvOut);
493 if (FAILED_UNEXPECTEDLY(hr))
494 {
495 return hr;
496 }
497
498 return ((IShellView * ) * ppvOut)->QueryInterface(IID_PPV_ARG(IShellFolderView, &m_shellFolderView));
499 }
500 return E_NOINTERFACE;
501 }
502
503 STDMETHODIMP CFindFolder::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, DWORD *rgfInOut)
504 {
505 CComHeapPtr<PCITEMID_CHILD> aFSPidl;
506 aFSPidl.Allocate(cidl);
507 for (UINT i = 0; i < cidl; i++)
508 {
509 aFSPidl[i] = _ILGetFSPidl(apidl[i]);
510 }
511
512 return m_pisfInner->GetAttributesOf(cidl, aFSPidl, rgfInOut);
513 }
514
515 class CFindFolderContextMenu :
516 public IContextMenu,
517 public CComObjectRootEx<CComMultiThreadModelNoCS>
518 {
519 CComPtr<IContextMenu> m_pInner;
520 CComPtr<IShellFolderView> m_shellFolderView;
521 UINT m_firstCmdId;
522 static const UINT ADDITIONAL_MENU_ITEMS = 2;
523
524 //// *** IContextMenu methods ***
525 STDMETHODIMP QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
526 {
527 m_firstCmdId = indexMenu;
528 _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst++, MFT_STRING, MAKEINTRESOURCEW(IDS_SEARCH_OPEN_FOLDER), MFS_ENABLED);
529 _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst++, MFT_SEPARATOR, NULL, 0);
530 return m_pInner->QueryContextMenu(hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags);
531 }
532
533 STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
534 {
535 if (!IS_INTRESOURCE(lpcmi->lpVerb))
536 {
537 return m_pInner->InvokeCommand(lpcmi);
538 }
539
540 if (LOWORD(lpcmi->lpVerb) < m_firstCmdId + ADDITIONAL_MENU_ITEMS)
541 {
542 PCUITEMID_CHILD *apidl;
543 UINT cidl;
544 HRESULT hResult = m_shellFolderView->GetSelectedObjects(&apidl, &cidl);
545 if (FAILED_UNEXPECTEDLY(hResult))
546 return hResult;
547
548 for (UINT i = 0; i < cidl; i++)
549 {
550 CComHeapPtr<ITEMIDLIST> folderPidl(ILCreateFromPathW(_ILGetPath(apidl[i])));
551 if (!folderPidl)
552 return E_OUTOFMEMORY;
553 LPCITEMIDLIST pidl = _ILGetFSPidl(apidl[i]);
554 SHOpenFolderAndSelectItems(folderPidl, 1, &pidl, 0);
555 }
556 return S_OK;
557 }
558
559 CMINVOKECOMMANDINFOEX actualCmdInfo;
560 memcpy(&actualCmdInfo, lpcmi, lpcmi->cbSize);
561 actualCmdInfo.lpVerb -= ADDITIONAL_MENU_ITEMS;
562 return m_pInner->InvokeCommand((CMINVOKECOMMANDINFO *)&actualCmdInfo);
563 }
564
565 STDMETHODIMP GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen)
566 {
567 return m_pInner->GetCommandString(idCommand, uFlags, lpReserved, lpszName, uMaxNameLen);
568 }
569
570 public:
571 static HRESULT Create(IShellFolderView *pShellFolderView, IContextMenu *pInnerContextMenu, IContextMenu **pContextMenu)
572 {
573 CComObject<CFindFolderContextMenu> *pObj;
574 HRESULT hResult = CComObject<CFindFolderContextMenu>::CreateInstance(&pObj);
575 if (FAILED_UNEXPECTEDLY(hResult))
576 return hResult;
577 pObj->m_shellFolderView = pShellFolderView;
578 pObj->m_pInner = pInnerContextMenu;
579 return pObj->QueryInterface(IID_PPV_ARG(IContextMenu, pContextMenu));
580 }
581
582 BEGIN_COM_MAP(CFindFolderContextMenu)
583 COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu)
584 END_COM_MAP()
585 };
586
587 STDMETHODIMP CFindFolder::GetUIObjectOf(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, REFIID riid,
588 UINT *prgfInOut, LPVOID *ppvOut)
589 {
590 if (cidl <= 0)
591 {
592 return m_pisfInner->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, ppvOut);
593 }
594
595 CComHeapPtr<PCITEMID_CHILD> aFSPidl;
596 aFSPidl.Allocate(cidl);
597 for (UINT i = 0; i < cidl; i++)
598 {
599 aFSPidl[i] = _ILGetFSPidl(apidl[i]);
600 }
601
602 if (riid == IID_IContextMenu)
603 {
604 CComHeapPtr<ITEMIDLIST> folderPidl(ILCreateFromPathW(_ILGetPath(apidl[0])));
605 if (!folderPidl)
606 return E_OUTOFMEMORY;
607 CComPtr<IShellFolder> pDesktopFolder;
608 HRESULT hResult = SHGetDesktopFolder(&pDesktopFolder);
609 if (FAILED_UNEXPECTEDLY(hResult))
610 return hResult;
611 CComPtr<IShellFolder> pShellFolder;
612 hResult = pDesktopFolder->BindToObject(folderPidl, NULL, IID_PPV_ARG(IShellFolder, &pShellFolder));
613 if (FAILED_UNEXPECTEDLY(hResult))
614 return hResult;
615 CComPtr<IContextMenu> pContextMenu;
616 hResult = pShellFolder->GetUIObjectOf(hwndOwner, cidl, aFSPidl, riid, prgfInOut, (LPVOID *)&pContextMenu);
617 if (FAILED_UNEXPECTEDLY(hResult))
618 return hResult;
619 return CFindFolderContextMenu::Create(m_shellFolderView, pContextMenu, (IContextMenu **)ppvOut);
620 }
621
622 return m_pisfInner->GetUIObjectOf(hwndOwner, cidl, aFSPidl, riid, prgfInOut, ppvOut);
623 }
624
625 STDMETHODIMP CFindFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET pName)
626 {
627 return m_pisfInner->GetDisplayNameOf(_ILGetFSPidl(pidl), dwFlags, pName);
628 }
629
630 STDMETHODIMP CFindFolder::SetNameOf(HWND hwndOwner, PCUITEMID_CHILD pidl, LPCOLESTR lpName, DWORD dwFlags,
631 PITEMID_CHILD *pPidlOut)
632 {
633 UNIMPLEMENTED;
634 return E_NOTIMPL;
635 }
636
637 //// *** IShellFolderViewCB method ***
638 STDMETHODIMP CFindFolder::MessageSFVCB(UINT uMsg, WPARAM wParam, LPARAM lParam)
639 {
640 switch (uMsg)
641 {
642 case SFVM_DEFVIEWMODE:
643 {
644 FOLDERVIEWMODE *pViewMode = (FOLDERVIEWMODE *) lParam;
645 *pViewMode = FVM_DETAILS;
646 return S_OK;
647 }
648 case SFVM_WINDOWCREATED:
649 {
650 // Subclass window to receive window messages
651 SubclassWindow((HWND) wParam);
652
653 // Get shell browser for updating status bar text
654 CComPtr<IServiceProvider> pServiceProvider;
655 HRESULT hr = m_shellFolderView->QueryInterface(IID_PPV_ARG(IServiceProvider, &pServiceProvider));
656 if (FAILED_UNEXPECTEDLY(hr))
657 return hr;
658 hr = pServiceProvider->QueryService(SID_SShellBrowser, IID_PPV_ARG(IShellBrowser, &m_shellBrowser));
659 if (FAILED_UNEXPECTEDLY(hr))
660 return hr;
661
662 // Open search bar
663 CComPtr<IWebBrowser2> pWebBrowser2;
664 hr = m_shellBrowser->QueryInterface(IID_PPV_ARG(IWebBrowser2, &pWebBrowser2));
665 if (FAILED_UNEXPECTEDLY(hr))
666 return hr;
667 WCHAR pwszGuid[MAX_PATH];
668 StringFromGUID2(CLSID_FileSearchBand, pwszGuid, _countof(pwszGuid));
669 CComVariant searchBar(pwszGuid);
670 return pWebBrowser2->ShowBrowserBar(&searchBar, NULL, NULL);
671 }
672 }
673 return E_NOTIMPL;
674 }
675
676 //// *** IPersistFolder2 methods ***
677 STDMETHODIMP CFindFolder::GetCurFolder(LPITEMIDLIST *pidl)
678 {
679 *pidl = ILClone(m_pidl);
680 return S_OK;
681 }
682
683 // *** IPersistFolder methods ***
684 STDMETHODIMP CFindFolder::Initialize(LPCITEMIDLIST pidl)
685 {
686 m_pidl = ILClone(pidl);
687 if (!m_pidl)
688 return E_OUTOFMEMORY;
689
690 return SHELL32_CoCreateInitSF(m_pidl,
691 NULL,
692 NULL,
693 &CLSID_ShellFSFolder,
694 IID_PPV_ARG(IShellFolder2, &m_pisfInner));
695 }
696
697 // *** IPersist methods ***
698 STDMETHODIMP CFindFolder::GetClassID(CLSID *pClassId)
699 {
700 if (pClassId == NULL)
701 return E_INVALIDARG;
702 *pClassId = CLSID_FindFolder;
703 return S_OK;
704 }