1 #include "CFindFolder.h"
4 WINE_DEFAULT_DEBUG_CHANNEL(shellfind
);
6 // FIXME: Remove this declaration after the function has been fully implemented
9 SHOpenFolderAndSelectItems(LPITEMIDLIST pidlFolder
,
11 PCUITEMID_CHILD_ARRAY apidl
,
14 struct FolderViewColumns
22 static FolderViewColumns g_ColumnDefs
[] =
24 {L
"Name", SHCOLSTATE_TYPE_STR
| SHCOLSTATE_ONBYDEFAULT
, LVCFMT_LEFT
, 30},
25 {L
"In Folder", SHCOLSTATE_TYPE_STR
| SHCOLSTATE_ONBYDEFAULT
, LVCFMT_LEFT
, 30},
26 {L
"Relevance", SHCOLSTATE_TYPE_STR
, LVCFMT_LEFT
, 0}
29 CFindFolder::CFindFolder() :
34 static LPITEMIDLIST
_ILCreate(LPCWSTR lpszPath
)
36 CComHeapPtr
<ITEMIDLIST
> lpFSPidl(ILCreateFromPathW(lpszPath
));
37 if (!(LPITEMIDLIST
)lpFSPidl
)
39 ERR("Failed to create pidl from path\n");
42 LPITEMIDLIST lpLastFSPidl
= ILFindLastID(lpFSPidl
);
44 int pathLen
= (wcslen(lpszPath
) + 1) * sizeof(WCHAR
);
45 int cbData
= sizeof(WORD
) + pathLen
+ lpLastFSPidl
->mkid
.cb
;
46 LPITEMIDLIST pidl
= (LPITEMIDLIST
) SHAlloc(cbData
+ sizeof(WORD
));
50 LPBYTE p
= (LPBYTE
) pidl
;
51 *((WORD
*) p
) = cbData
;
54 memcpy(p
, lpszPath
, pathLen
);
57 memcpy(p
, lpLastFSPidl
, lpLastFSPidl
->mkid
.cb
);
58 p
+= lpLastFSPidl
->mkid
.cb
;
65 static LPCWSTR
_ILGetPath(LPCITEMIDLIST pidl
)
67 if (!pidl
|| !pidl
->mkid
.cb
)
69 return (LPCWSTR
) pidl
->mkid
.abID
;
72 static LPCITEMIDLIST
_ILGetFSPidl(LPCITEMIDLIST pidl
)
74 if (!pidl
|| !pidl
->mkid
.cb
)
76 return (LPCITEMIDLIST
) ((LPBYTE
) pidl
->mkid
.abID
77 + ((wcslen((LPCWSTR
) pidl
->mkid
.abID
) + 1) * sizeof(WCHAR
)));
88 CComPtr
<CFindFolder
> pFindFolder
;
91 template<typename TChar
, typename TString
, int (&StrNCmp
)(const TChar
*, const TChar
*, size_t)>
92 static const TChar
* StrStrN(const TChar
*lpFirst
, const TString
&lpSrch
, UINT cchMax
)
94 if (!lpFirst
|| lpSrch
.IsEmpty() || !cchMax
)
97 for (UINT i
= cchMax
; i
> 0 && *lpFirst
; i
--, lpFirst
++)
99 if (!StrNCmp(lpFirst
, lpSrch
, lpSrch
.GetLength()))
100 return (const TChar
*)lpFirst
;
106 template<typename TChar
, typename TString
, int (&StrNCmp
)(const TChar
*, const TChar
*, size_t)>
107 static UINT
StrStrNCount(const TChar
*lpFirst
, const TString
&lpSrch
, UINT cchMax
)
109 const TChar
*lpSearchEnd
= lpFirst
+ cchMax
;
111 while (lpFirst
< lpSearchEnd
&& (lpFirst
= StrStrN
<TChar
, TString
, StrNCmp
>(lpFirst
, lpSrch
, cchMax
)))
114 lpFirst
+= lpSrch
.GetLength();
115 cchMax
= lpSearchEnd
- lpFirst
;
120 static UINT
StrStrCountNIA(const CHAR
*lpFirst
, const CStringA
&lpSrch
, UINT cchMax
)
122 return StrStrNCount
<CHAR
, CStringA
, _strnicmp
>(lpFirst
, lpSrch
, cchMax
);
125 static UINT
StrStrCountNIW(const WCHAR
*lpFirst
, const CStringW
&lpSrch
, UINT cchMax
)
127 return StrStrNCount
<WCHAR
, CStringW
, _wcsnicmp
>(lpFirst
, lpSrch
, cchMax
);
130 static UINT
SearchFile(LPCWSTR lpFilePath
, _SearchData
*pSearchData
)
132 HANDLE hFile
= CreateFileW(lpFilePath
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_READONLY
, NULL
);
133 if (hFile
== INVALID_HANDLE_VALUE
)
136 DWORD size
= GetFileSize(hFile
, NULL
);
137 HANDLE hFileMap
= CreateFileMappingW(hFile
, NULL
, PAGE_READONLY
, 0, 0, NULL
);
139 if (hFileMap
== INVALID_HANDLE_VALUE
)
142 LPBYTE lpFileContent
= (LPBYTE
) MapViewOfFile(hFileMap
, FILE_MAP_READ
, 0, 0, 0);
143 CloseHandle(hFileMap
);
148 // Check for UTF-16 BOM
149 if (size
>= 2 && lpFileContent
[0] == 0xFF && lpFileContent
[1] == 0xFE)
151 uMatches
= StrStrCountNIW((LPCWSTR
) lpFileContent
, pSearchData
->szQueryW
, size
/ sizeof(WCHAR
));
155 uMatches
= StrStrCountNIA((LPCSTR
) lpFileContent
, pSearchData
->szQueryA
, size
/ sizeof(CHAR
));
158 UnmapViewOfFile(lpFileContent
);
163 static UINT
RecursiveFind(LPCWSTR lpPath
, _SearchData
*pSearchData
)
165 if (WaitForSingleObject(pSearchData
->hStopEvent
, 0) != WAIT_TIMEOUT
)
168 WCHAR szPath
[MAX_PATH
];
169 WIN32_FIND_DATAW FindData
;
171 BOOL bMoreFiles
= TRUE
;
172 UINT uTotalFound
= 0;
174 PathCombineW(szPath
, lpPath
, L
"*.*");
176 for (hFindFile
= FindFirstFileW(szPath
, &FindData
);
177 bMoreFiles
&& hFindFile
!= INVALID_HANDLE_VALUE
;
178 bMoreFiles
= FindNextFileW(hFindFile
, &FindData
))
180 if (!wcscmp(FindData
.cFileName
, L
".") || !wcscmp(FindData
.cFileName
, L
".."))
183 PathCombineW(szPath
, lpPath
, FindData
.cFileName
);
185 if (FindData
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
188 status
.Format(IDS_SEARCH_FOLDER
, FindData
.cFileName
);
189 PostMessageW(pSearchData
->hwnd
, WM_SEARCH_UPDATE_STATUS
, 0, (LPARAM
) StrDupW(status
.GetBuffer()));
191 uTotalFound
+= RecursiveFind(szPath
, pSearchData
);
193 else if ((pSearchData
->szFileName
.IsEmpty() || PathMatchSpecW(FindData
.cFileName
, pSearchData
->szFileName
))
194 && (pSearchData
->szQueryA
.IsEmpty() || SearchFile(szPath
, pSearchData
)))
197 PostMessageW(pSearchData
->hwnd
, WM_SEARCH_ADD_RESULT
, 0, (LPARAM
) StrDupW(szPath
));
201 if (hFindFile
!= INVALID_HANDLE_VALUE
)
202 FindClose(hFindFile
);
207 DWORD WINAPI
CFindFolder::SearchThreadProc(LPVOID lpParameter
)
209 _SearchData
*data
= static_cast<_SearchData
*>(lpParameter
);
211 data
->pFindFolder
->NotifyConnections(DISPID_SEARCHSTART
);
213 UINT uTotalFound
= RecursiveFind(data
->szPath
, data
);
215 data
->pFindFolder
->NotifyConnections(DISPID_SEARCHCOMPLETE
);
218 status
.Format(IDS_SEARCH_FILES_FOUND
, uTotalFound
);
219 ::PostMessageW(data
->hwnd
, WM_SEARCH_UPDATE_STATUS
, 0, (LPARAM
) StrDupW(status
.GetBuffer()));
226 void CFindFolder::NotifyConnections(DISPID id
)
228 DISPPARAMS dispatchParams
= {0};
229 CComDynamicUnkArray
&subscribers
=
230 IConnectionPointImpl
<CFindFolder
, &DIID_DSearchCommandEvents
>::m_vec
;
231 for (IUnknown
** pSubscriber
= subscribers
.begin(); pSubscriber
< subscribers
.end(); pSubscriber
++)
236 CComPtr
<IDispatch
> pDispatch
;
237 HRESULT hResult
= (*pSubscriber
)->QueryInterface(IID_PPV_ARG(IDispatch
, &pDispatch
));
238 if (!FAILED_UNEXPECTEDLY(hResult
))
239 pDispatch
->Invoke(id
, GUID_NULL
, 0, DISPATCH_METHOD
, &dispatchParams
, NULL
, NULL
, NULL
);
243 LRESULT
CFindFolder::StartSearch(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
)
248 // Clear all previous search results
250 m_shellFolderView
->RemoveObject(NULL
, &uItemIndex
);
252 _SearchData
* pSearchData
= new _SearchData();
253 pSearchData
->pFindFolder
= this;
254 pSearchData
->hwnd
= m_hWnd
;
256 SearchStart
*pSearchParams
= (SearchStart
*) lParam
;
257 pSearchData
->szPath
= pSearchParams
->szPath
;
258 pSearchData
->szFileName
= pSearchParams
->szFileName
;
259 pSearchData
->szQueryA
= pSearchParams
->szQuery
;
260 pSearchData
->szQueryW
= pSearchParams
->szQuery
;
261 SHFree(pSearchParams
);
264 SetEvent(m_hStopEvent
);
265 pSearchData
->hStopEvent
= m_hStopEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
267 if (!SHCreateThread(SearchThreadProc
, pSearchData
, NULL
, NULL
))
270 return HRESULT_FROM_WIN32(GetLastError());
276 LRESULT
CFindFolder::StopSearch(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
)
280 SetEvent(m_hStopEvent
);
286 LRESULT
CFindFolder::AddResult(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
)
291 CComHeapPtr
<WCHAR
> lpPath((LPWSTR
) lParam
);
293 CComHeapPtr
<ITEMIDLIST
> lpSearchPidl(_ILCreate(lpPath
));
297 m_shellFolderView
->AddObject(lpSearchPidl
, &uItemIndex
);
303 LRESULT
CFindFolder::UpdateStatus(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
)
305 CComHeapPtr
<WCHAR
> status((LPWSTR
) lParam
);
308 m_shellBrowser
->SetStatusTextSB(status
);
314 // *** IShellFolder2 methods ***
315 STDMETHODIMP
CFindFolder::GetDefaultSearchGUID(GUID
*pguid
)
321 STDMETHODIMP
CFindFolder::EnumSearches(IEnumExtraSearch
**ppenum
)
327 STDMETHODIMP
CFindFolder::GetDefaultColumn(DWORD
, ULONG
*pSort
, ULONG
*pDisplay
)
336 STDMETHODIMP
CFindFolder::GetDefaultColumnState(UINT iColumn
, DWORD
*pcsFlags
)
340 if (iColumn
>= _countof(g_ColumnDefs
))
341 return m_pisfInner
->GetDefaultColumnState(iColumn
- _countof(g_ColumnDefs
) + 1, pcsFlags
);
342 *pcsFlags
= g_ColumnDefs
[iColumn
].dwDefaultState
;
346 STDMETHODIMP
CFindFolder::GetDetailsEx(PCUITEMID_CHILD pidl
, const SHCOLUMNID
*pscid
, VARIANT
*pv
)
348 return m_pisfInner
->GetDetailsEx(pidl
, pscid
, pv
);
351 STDMETHODIMP
CFindFolder::GetDetailsOf(PCUITEMID_CHILD pidl
, UINT iColumn
, SHELLDETAILS
*pDetails
)
353 if (iColumn
>= _countof(g_ColumnDefs
))
354 return m_pisfInner
->GetDetailsOf(_ILGetFSPidl(pidl
), iColumn
- _countof(g_ColumnDefs
) + 1, pDetails
);
356 pDetails
->cxChar
= g_ColumnDefs
[iColumn
].cxChar
;
357 pDetails
->fmt
= g_ColumnDefs
[iColumn
].fmt
;
360 return SHSetStrRet(&pDetails
->str
, g_ColumnDefs
[iColumn
].wzColumnName
);
364 WCHAR path
[MAX_PATH
];
365 wcscpy(path
, _ILGetPath(pidl
));
366 PathRemoveFileSpecW(path
);
367 return SHSetStrRet(&pDetails
->str
, path
);
370 return GetDisplayNameOf(pidl
, SHGDN_NORMAL
, &pDetails
->str
);
373 STDMETHODIMP
CFindFolder::MapColumnToSCID(UINT iColumn
, SHCOLUMNID
*pscid
)
379 // *** IShellFolder methods ***
380 STDMETHODIMP
CFindFolder::ParseDisplayName(HWND hwndOwner
, LPBC pbc
, LPOLESTR lpszDisplayName
, ULONG
*pchEaten
,
381 PIDLIST_RELATIVE
*ppidl
, ULONG
*pdwAttributes
)
387 STDMETHODIMP
CFindFolder::EnumObjects(HWND hwndOwner
, DWORD dwFlags
, LPENUMIDLIST
*ppEnumIDList
)
389 *ppEnumIDList
= NULL
;
393 STDMETHODIMP
CFindFolder::BindToObject(PCUIDLIST_RELATIVE pidl
, LPBC pbcReserved
, REFIID riid
, LPVOID
*ppvOut
)
399 STDMETHODIMP
CFindFolder::BindToStorage(PCUIDLIST_RELATIVE pidl
, LPBC pbcReserved
, REFIID riid
, LPVOID
*ppvOut
)
405 STDMETHODIMP
CFindFolder::CompareIDs(LPARAM lParam
, PCUIDLIST_RELATIVE pidl1
, PCUIDLIST_RELATIVE pidl2
)
407 return m_pisfInner
->CompareIDs(lParam
, _ILGetFSPidl(pidl1
), _ILGetFSPidl(pidl2
));
410 STDMETHODIMP
CFindFolder::CreateViewObject(HWND hwndOwner
, REFIID riid
, LPVOID
*ppvOut
)
412 if (riid
== IID_IShellView
)
414 SFV_CREATE sfvparams
= {};
415 sfvparams
.cbSize
= sizeof(SFV_CREATE
);
416 sfvparams
.pshf
= this;
417 sfvparams
.psfvcb
= this;
418 HRESULT hr
= SHCreateShellFolderView(&sfvparams
, (IShellView
**) ppvOut
);
419 if (FAILED_UNEXPECTEDLY(hr
))
424 return ((IShellView
* ) * ppvOut
)->QueryInterface(IID_PPV_ARG(IShellFolderView
, &m_shellFolderView
));
426 return E_NOINTERFACE
;
429 STDMETHODIMP
CFindFolder::GetAttributesOf(UINT cidl
, PCUITEMID_CHILD_ARRAY apidl
, DWORD
*rgfInOut
)
431 CComHeapPtr
<PCITEMID_CHILD
> aFSPidl
;
432 aFSPidl
.Allocate(cidl
);
433 for (UINT i
= 0; i
< cidl
; i
++)
435 aFSPidl
[i
] = _ILGetFSPidl(apidl
[i
]);
438 return m_pisfInner
->GetAttributesOf(cidl
, aFSPidl
, rgfInOut
);
441 STDMETHODIMP
CFindFolder::GetUIObjectOf(HWND hwndOwner
, UINT cidl
, PCUITEMID_CHILD_ARRAY apidl
, REFIID riid
,
442 UINT
*prgfInOut
, LPVOID
*ppvOut
)
444 if (riid
== IID_IDataObject
&& cidl
== 1)
446 WCHAR path
[MAX_PATH
];
447 wcscpy(path
, (LPCWSTR
) apidl
[0]->mkid
.abID
);
448 PathRemoveFileSpecW(path
);
449 CComHeapPtr
<ITEMIDLIST
> rootPidl(ILCreateFromPathW(path
));
451 return E_OUTOFMEMORY
;
452 PCITEMID_CHILD aFSPidl
[1];
453 aFSPidl
[0] = _ILGetFSPidl(apidl
[0]);
454 return IDataObject_Constructor(hwndOwner
, rootPidl
, aFSPidl
, cidl
, (IDataObject
**) ppvOut
);
459 return m_pisfInner
->GetUIObjectOf(hwndOwner
, cidl
, apidl
, riid
, prgfInOut
, ppvOut
);
462 PCITEMID_CHILD
*aFSPidl
= new PCITEMID_CHILD
[cidl
];
463 for (UINT i
= 0; i
< cidl
; i
++)
465 aFSPidl
[i
] = _ILGetFSPidl(apidl
[i
]);
468 if (riid
== IID_IContextMenu
)
472 AddFSClassKeysToArray(aFSPidl
[0], hKeys
, &cKeys
);
475 dcm
.hwnd
= hwndOwner
;
477 dcm
.pidlFolder
= m_pidl
;
483 dcm
.punkAssociationInfo
= NULL
;
484 HRESULT hr
= SHCreateDefaultContextMenu(&dcm
, riid
, ppvOut
);
490 HRESULT hr
= m_pisfInner
->GetUIObjectOf(hwndOwner
, cidl
, aFSPidl
, riid
, prgfInOut
, ppvOut
);
496 STDMETHODIMP
CFindFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl
, DWORD dwFlags
, LPSTRRET pName
)
498 return m_pisfInner
->GetDisplayNameOf(_ILGetFSPidl(pidl
), dwFlags
, pName
);
501 STDMETHODIMP
CFindFolder::SetNameOf(HWND hwndOwner
, PCUITEMID_CHILD pidl
, LPCOLESTR lpName
, DWORD dwFlags
,
502 PITEMID_CHILD
*pPidlOut
)
508 //// *** IShellFolderViewCB method ***
509 STDMETHODIMP
CFindFolder::MessageSFVCB(UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
513 case SFVM_DEFVIEWMODE
:
515 FOLDERVIEWMODE
*pViewMode
= (FOLDERVIEWMODE
*) lParam
;
516 *pViewMode
= FVM_DETAILS
;
519 case SFVM_WINDOWCREATED
:
521 SubclassWindow((HWND
) wParam
);
523 CComPtr
<IServiceProvider
> pServiceProvider
;
524 HRESULT hr
= m_shellFolderView
->QueryInterface(IID_PPV_ARG(IServiceProvider
, &pServiceProvider
));
525 if (FAILED_UNEXPECTEDLY(hr
))
529 return pServiceProvider
->QueryService(SID_SShellBrowser
, IID_PPV_ARG(IShellBrowser
, &m_shellBrowser
));
535 //// *** IContextMenuCB method ***
536 STDMETHODIMP
CFindFolder::CallBack(IShellFolder
*psf
, HWND hwndOwner
, IDataObject
*pdtobj
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
540 case DFM_MERGECONTEXTMENU
:
542 QCMINFO
*pqcminfo
= (QCMINFO
*) lParam
;
543 _InsertMenuItemW(pqcminfo
->hmenu
, pqcminfo
->indexMenu
++, TRUE
, pqcminfo
->idCmdFirst
++, MFT_SEPARATOR
, NULL
, 0);
544 _InsertMenuItemW(pqcminfo
->hmenu
, pqcminfo
->indexMenu
++, TRUE
, pqcminfo
->idCmdFirst
++, MFT_STRING
, L
"Open Containing Folder", MFS_ENABLED
);
545 _InsertMenuItemW(pqcminfo
->hmenu
, pqcminfo
->indexMenu
++, TRUE
, pqcminfo
->idCmdFirst
++, MFT_SEPARATOR
, NULL
, 0);
548 case DFM_INVOKECOMMAND
:
549 case DFM_INVOKECOMMANDEX
:
554 PCUITEMID_CHILD
*apidl
;
556 HRESULT hr
= m_shellFolderView
->GetSelectedObjects(&apidl
, &cidl
);
557 if (FAILED_UNEXPECTEDLY(hr
))
560 for (UINT i
= 0; i
< cidl
; i
++)
562 CComHeapPtr
<ITEMIDLIST
> pidl
;
564 hr
= SHILCreateFromPathW((LPCWSTR
) apidl
[i
]->mkid
.abID
, &pidl
, &attrs
);
567 SHOpenFolderAndSelectItems(NULL
, 1, &pidl
, 0);
573 case DFM_GETDEFSTATICID
:
576 return Shell_DefaultContextMenuCallBack(m_pisfInner
, pdtobj
);
579 //// *** IPersistFolder2 methods ***
580 STDMETHODIMP
CFindFolder::GetCurFolder(LPITEMIDLIST
*pidl
)
582 *pidl
= ILClone(m_pidl
);
586 // *** IPersistFolder methods ***
587 STDMETHODIMP
CFindFolder::Initialize(LPCITEMIDLIST pidl
)
589 m_pidl
= ILClone(pidl
);
591 return E_OUTOFMEMORY
;
593 return SHELL32_CoCreateInitSF(m_pidl
,
596 &CLSID_ShellFSFolder
,
597 IID_PPV_ARG(IShellFolder2
, &m_pisfInner
));
600 // *** IPersist methods ***
601 STDMETHODIMP
CFindFolder::GetClassID(CLSID
*pClassId
)
603 if (pClassId
== NULL
)
605 memcpy(pClassId
, &CLSID_FindFolder
, sizeof(CLSID
));