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
)));
84 SearchStart
*pSearchParams
;
85 CFindFolder
*pFindFolder
;
88 static LPCSTR WINAPI
StrStrNA(LPCSTR lpFirst
, LPCSTR lpSrch
, UINT cchMax
)
93 if (!lpFirst
|| !lpSrch
|| !*lpSrch
|| !cchMax
)
98 for (i
= cchMax
; *lpFirst
&& (i
> 0); i
--, lpFirst
++)
100 if (!strncmp(lpFirst
, lpSrch
, len
))
101 return (LPCSTR
)lpFirst
;
107 static UINT
SearchFile(LPCWSTR lpFilePath
, _SearchData
*pSearchData
)
109 HANDLE hFile
= CreateFileW(lpFilePath
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_READONLY
, NULL
);
110 if (hFile
== INVALID_HANDLE_VALUE
)
113 DWORD size
= GetFileSize(hFile
, NULL
);
114 HANDLE hFileMap
= CreateFileMappingW(hFile
, NULL
, PAGE_READONLY
, 0, 0, NULL
);
116 if (hFileMap
== INVALID_HANDLE_VALUE
)
119 LPBYTE lpFileContent
= (LPBYTE
) MapViewOfFile(hFileMap
, FILE_MAP_READ
, 0, 0, 0);
120 CloseHandle(hFileMap
);
125 if ((size
>= 2) && (lpFileContent
[0] == 0xFF) && (lpFileContent
[1] == 0xFE))
128 LPCWSTR lpSearchPos
= (LPCWSTR
) lpFileContent
;
129 DWORD dwCharsRemaining
= size
/ sizeof(WCHAR
);
130 const LPCWSTR lpSearchEnd
= (LPCWSTR
) lpFileContent
+ dwCharsRemaining
;
131 const LPCWSTR lpszQuery
= pSearchData
->pSearchParams
->szQuery
;
132 const size_t queryLen
= wcslen(lpszQuery
);
133 while ((lpSearchPos
= StrStrNW(lpSearchPos
, lpszQuery
, dwCharsRemaining
))
134 && lpSearchPos
< lpSearchEnd
)
137 lpSearchPos
+= queryLen
;
138 dwCharsRemaining
-= queryLen
;
143 DWORD len
= WideCharToMultiByte(CP_ACP
, 0, pSearchData
->pSearchParams
->szQuery
, -1, NULL
, 0, NULL
, NULL
);
144 const LPSTR lpszQuery
= new CHAR
[len
];
145 WideCharToMultiByte(CP_ACP
, 0, pSearchData
->pSearchParams
->szQuery
, -1, lpszQuery
, len
, NULL
, NULL
);
146 LPCSTR lpSearchPos
= (LPCSTR
) lpFileContent
;
147 DWORD dwCharsRemaining
= size
;
148 const LPCSTR lpSearchEnd
= (LPCSTR
) lpFileContent
+ dwCharsRemaining
;
149 const size_t queryLen
= len
;
150 while ((lpSearchPos
= StrStrNA(lpSearchPos
, lpszQuery
, dwCharsRemaining
))
151 && lpSearchPos
< lpSearchEnd
)
154 lpSearchPos
+= queryLen
;
155 dwCharsRemaining
-= queryLen
;
159 UnmapViewOfFile(lpFileContent
);
164 static VOID
RecursiveFind(LPCWSTR lpPath
, _SearchData
*pSearchData
)
166 if (WaitForSingleObject(pSearchData
->hStopEvent
, 0) != WAIT_TIMEOUT
)
169 WCHAR szPath
[MAX_PATH
];
170 WIN32_FIND_DATAW FindData
;
172 BOOL bMoreFiles
= TRUE
;
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
)
187 CStringW
* status
= new CStringW();
188 status
->Format(L
"Searching '%s'", FindData
.cFileName
);
189 PostMessageW(pSearchData
->hwnd
, WM_SEARCH_UPDATE_STATUS
, 0, (LPARAM
) status
);
191 RecursiveFind(szPath
, pSearchData
);
193 else if (pSearchData
->szFileName
.IsEmpty() || PathMatchSpecW(FindData
.cFileName
, pSearchData
->szFileName
))
195 DbgPrint("Searching file: '%S'\n", szPath
);
196 UINT uMatches
= SearchFile(szPath
, pSearchData
);
199 ::PostMessageW(pSearchData
->hwnd
, WM_SEARCH_ADD_RESULT
, 0, (LPARAM
) StrDupW(szPath
));
204 if (hFindFile
!= INVALID_HANDLE_VALUE
)
205 FindClose(hFindFile
);
208 DWORD WINAPI
CFindFolder::_SearchThreadProc(LPVOID lpParameter
)
210 _SearchData
*data
= static_cast<_SearchData
*>(lpParameter
);
211 SearchStart
* params
= (SearchStart
*) data
->pSearchParams
;
213 data
->pFindFolder
->NotifyConnections(DISPID_SEARCHSTART
);
215 RecursiveFind(params
->szPath
, data
);
217 data
->pFindFolder
->NotifyConnections(DISPID_SEARCHCOMPLETE
);
225 void CFindFolder::NotifyConnections(DISPID id
)
227 DISPPARAMS dispatchParams
= {0};
228 CComDynamicUnkArray
&subscribers
=
229 IConnectionPointImpl
<CFindFolder
, &DIID_DSearchCommandEvents
>::m_vec
;
230 for (IUnknown
** pSubscriber
= subscribers
.begin(); pSubscriber
< subscribers
.end(); pSubscriber
++)
235 CComPtr
<IDispatch
> pDispatch
;
236 HRESULT hResult
= (*pSubscriber
)->QueryInterface(IID_PPV_ARG(IDispatch
, &pDispatch
));
237 if (!FAILED_UNEXPECTEDLY(hResult
))
238 pDispatch
->Invoke(id
, GUID_NULL
, 0, DISPATCH_METHOD
, &dispatchParams
, NULL
, NULL
, NULL
);
242 LRESULT
CFindFolder::StartSearch(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
)
247 // Clear all previous search results
249 m_shellFolderView
->RemoveObject(NULL
, &uItemIndex
);
251 _SearchData
* pSearchData
= new _SearchData();
252 pSearchData
->pFindFolder
= this;
253 pSearchData
->hwnd
= m_hWnd
;
255 SetEvent(m_hStopEvent
);
256 pSearchData
->hStopEvent
= m_hStopEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
257 pSearchData
->pSearchParams
= (SearchStart
*) lParam
;
259 if (!SHCreateThread(_SearchThreadProc
, pSearchData
, NULL
, NULL
))
261 SHFree(pSearchData
->pSearchParams
);
263 return HRESULT_FROM_WIN32(GetLastError());
269 LRESULT
CFindFolder::AddResult(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
)
274 CComHeapPtr
<WCHAR
> lpPath((LPWSTR
) lParam
);
276 CComHeapPtr
<ITEMIDLIST
> lpSearchPidl(_ILCreate(lpPath
));
280 m_shellFolderView
->AddObject(lpSearchPidl
, &uItemIndex
);
286 LRESULT
CFindFolder::UpdateStatus(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
)
288 CStringW
*status
= (CStringW
*) lParam
;
291 m_shellBrowser
->SetStatusTextSB(status
->GetBuffer());
298 // *** IShellFolder2 methods ***
299 STDMETHODIMP
CFindFolder::GetDefaultSearchGUID(GUID
*pguid
)
305 STDMETHODIMP
CFindFolder::EnumSearches(IEnumExtraSearch
**ppenum
)
311 STDMETHODIMP
CFindFolder::GetDefaultColumn(DWORD
, ULONG
*pSort
, ULONG
*pDisplay
)
320 STDMETHODIMP
CFindFolder::GetDefaultColumnState(UINT iColumn
, DWORD
*pcsFlags
)
324 if (iColumn
>= _countof(g_ColumnDefs
))
325 return m_pisfInner
->GetDefaultColumnState(iColumn
- _countof(g_ColumnDefs
) + 1, pcsFlags
);
326 *pcsFlags
= g_ColumnDefs
[iColumn
].dwDefaultState
;
330 STDMETHODIMP
CFindFolder::GetDetailsEx(PCUITEMID_CHILD pidl
, const SHCOLUMNID
*pscid
, VARIANT
*pv
)
332 return m_pisfInner
->GetDetailsEx(pidl
, pscid
, pv
);
335 STDMETHODIMP
CFindFolder::GetDetailsOf(PCUITEMID_CHILD pidl
, UINT iColumn
, SHELLDETAILS
*pDetails
)
337 if (iColumn
>= _countof(g_ColumnDefs
))
338 return m_pisfInner
->GetDetailsOf(_ILGetFSPidl(pidl
), iColumn
- _countof(g_ColumnDefs
) + 1, pDetails
);
340 pDetails
->cxChar
= g_ColumnDefs
[iColumn
].cxChar
;
341 pDetails
->fmt
= g_ColumnDefs
[iColumn
].fmt
;
344 return SHSetStrRet(&pDetails
->str
, g_ColumnDefs
[iColumn
].wzColumnName
);
348 WCHAR path
[MAX_PATH
];
349 wcscpy(path
, _ILGetPath(pidl
));
350 PathRemoveFileSpecW(path
);
351 return SHSetStrRet(&pDetails
->str
, path
);
354 return GetDisplayNameOf(pidl
, SHGDN_NORMAL
, &pDetails
->str
);
357 STDMETHODIMP
CFindFolder::MapColumnToSCID(UINT iColumn
, SHCOLUMNID
*pscid
)
363 // *** IShellFolder methods ***
364 STDMETHODIMP
CFindFolder::ParseDisplayName(HWND hwndOwner
, LPBC pbc
, LPOLESTR lpszDisplayName
, ULONG
*pchEaten
,
365 PIDLIST_RELATIVE
*ppidl
, ULONG
*pdwAttributes
)
371 STDMETHODIMP
CFindFolder::EnumObjects(HWND hwndOwner
, DWORD dwFlags
, LPENUMIDLIST
*ppEnumIDList
)
373 *ppEnumIDList
= NULL
;
377 STDMETHODIMP
CFindFolder::BindToObject(PCUIDLIST_RELATIVE pidl
, LPBC pbcReserved
, REFIID riid
, LPVOID
*ppvOut
)
383 STDMETHODIMP
CFindFolder::BindToStorage(PCUIDLIST_RELATIVE pidl
, LPBC pbcReserved
, REFIID riid
, LPVOID
*ppvOut
)
389 STDMETHODIMP
CFindFolder::CompareIDs(LPARAM lParam
, PCUIDLIST_RELATIVE pidl1
, PCUIDLIST_RELATIVE pidl2
)
391 return m_pisfInner
->CompareIDs(lParam
, _ILGetFSPidl(pidl1
), _ILGetFSPidl(pidl2
));
394 STDMETHODIMP
CFindFolder::CreateViewObject(HWND hwndOwner
, REFIID riid
, LPVOID
*ppvOut
)
396 if (riid
== IID_IShellView
)
398 SFV_CREATE sfvparams
= {};
399 sfvparams
.cbSize
= sizeof(SFV_CREATE
);
400 sfvparams
.pshf
= this;
401 sfvparams
.psfvcb
= this;
402 HRESULT hr
= SHCreateShellFolderView(&sfvparams
, (IShellView
**) ppvOut
);
403 if (FAILED_UNEXPECTEDLY(hr
))
408 return ((IShellView
* ) * ppvOut
)->QueryInterface(IID_PPV_ARG(IShellFolderView
, &m_shellFolderView
));
410 return E_NOINTERFACE
;
413 STDMETHODIMP
CFindFolder::GetAttributesOf(UINT cidl
, PCUITEMID_CHILD_ARRAY apidl
, DWORD
*rgfInOut
)
415 CComHeapPtr
<PCITEMID_CHILD
> aFSPidl
;
416 aFSPidl
.Allocate(cidl
);
417 for (UINT i
= 0; i
< cidl
; i
++)
419 aFSPidl
[i
] = _ILGetFSPidl(apidl
[i
]);
422 return m_pisfInner
->GetAttributesOf(cidl
, aFSPidl
, rgfInOut
);
425 STDMETHODIMP
CFindFolder::GetUIObjectOf(HWND hwndOwner
, UINT cidl
, PCUITEMID_CHILD_ARRAY apidl
, REFIID riid
,
426 UINT
*prgfInOut
, LPVOID
*ppvOut
)
428 if (riid
== IID_IDataObject
&& cidl
== 1)
430 WCHAR path
[MAX_PATH
];
431 wcscpy(path
, (LPCWSTR
) apidl
[0]->mkid
.abID
);
432 PathRemoveFileSpecW(path
);
433 CComHeapPtr
<ITEMIDLIST
> rootPidl(ILCreateFromPathW(path
));
435 return E_OUTOFMEMORY
;
436 PCITEMID_CHILD aFSPidl
[1];
437 aFSPidl
[0] = _ILGetFSPidl(apidl
[0]);
438 return IDataObject_Constructor(hwndOwner
, rootPidl
, aFSPidl
, cidl
, (IDataObject
**) ppvOut
);
443 return m_pisfInner
->GetUIObjectOf(hwndOwner
, cidl
, apidl
, riid
, prgfInOut
, ppvOut
);
446 PCITEMID_CHILD
*aFSPidl
= new PCITEMID_CHILD
[cidl
];
447 for (UINT i
= 0; i
< cidl
; i
++)
449 aFSPidl
[i
] = _ILGetFSPidl(apidl
[i
]);
452 if (riid
== IID_IContextMenu
)
456 AddFSClassKeysToArray(aFSPidl
[0], hKeys
, &cKeys
);
459 dcm
.hwnd
= hwndOwner
;
461 dcm
.pidlFolder
= m_pidl
;
467 dcm
.punkAssociationInfo
= NULL
;
468 HRESULT hr
= SHCreateDefaultContextMenu(&dcm
, riid
, ppvOut
);
474 HRESULT hr
= m_pisfInner
->GetUIObjectOf(hwndOwner
, cidl
, aFSPidl
, riid
, prgfInOut
, ppvOut
);
480 STDMETHODIMP
CFindFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl
, DWORD dwFlags
, LPSTRRET pName
)
482 return m_pisfInner
->GetDisplayNameOf(_ILGetFSPidl(pidl
), dwFlags
, pName
);
485 STDMETHODIMP
CFindFolder::SetNameOf(HWND hwndOwner
, PCUITEMID_CHILD pidl
, LPCOLESTR lpName
, DWORD dwFlags
,
486 PITEMID_CHILD
*pPidlOut
)
492 //// *** IShellFolderViewCB method ***
493 STDMETHODIMP
CFindFolder::MessageSFVCB(UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
497 case SFVM_DEFVIEWMODE
:
499 FOLDERVIEWMODE
*pViewMode
= (FOLDERVIEWMODE
*) lParam
;
500 *pViewMode
= FVM_DETAILS
;
503 case SFVM_WINDOWCREATED
:
505 SubclassWindow((HWND
) wParam
);
507 CComPtr
<IServiceProvider
> pServiceProvider
;
508 HRESULT hr
= m_shellFolderView
->QueryInterface(IID_PPV_ARG(IServiceProvider
, &pServiceProvider
));
509 if (FAILED_UNEXPECTEDLY(hr
))
513 return pServiceProvider
->QueryService(SID_SShellBrowser
, IID_PPV_ARG(IShellBrowser
, &m_shellBrowser
));
519 //// *** IContextMenuCB method ***
520 STDMETHODIMP
CFindFolder::CallBack(IShellFolder
*psf
, HWND hwndOwner
, IDataObject
*pdtobj
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
524 case DFM_MERGECONTEXTMENU
:
526 QCMINFO
*pqcminfo
= (QCMINFO
*) lParam
;
527 _InsertMenuItemW(pqcminfo
->hmenu
, pqcminfo
->indexMenu
++, TRUE
, pqcminfo
->idCmdFirst
++, MFT_SEPARATOR
, NULL
, 0);
528 _InsertMenuItemW(pqcminfo
->hmenu
, pqcminfo
->indexMenu
++, TRUE
, pqcminfo
->idCmdFirst
++, MFT_STRING
, L
"Open Containing Folder", MFS_ENABLED
);
529 _InsertMenuItemW(pqcminfo
->hmenu
, pqcminfo
->indexMenu
++, TRUE
, pqcminfo
->idCmdFirst
++, MFT_SEPARATOR
, NULL
, 0);
532 case DFM_INVOKECOMMAND
:
533 case DFM_INVOKECOMMANDEX
:
538 PCUITEMID_CHILD
*apidl
;
540 HRESULT hr
= m_shellFolderView
->GetSelectedObjects(&apidl
, &cidl
);
541 if (FAILED_UNEXPECTEDLY(hr
))
544 for (UINT i
= 0; i
< cidl
; i
++)
546 CComHeapPtr
<ITEMIDLIST
> pidl
;
548 hr
= SHILCreateFromPathW((LPCWSTR
) apidl
[i
]->mkid
.abID
, &pidl
, &attrs
);
551 SHOpenFolderAndSelectItems(NULL
, 1, &pidl
, 0);
557 case DFM_GETDEFSTATICID
:
560 return Shell_DefaultContextMenuCallBack(m_pisfInner
, pdtobj
);
563 //// *** IPersistFolder2 methods ***
564 STDMETHODIMP
CFindFolder::GetCurFolder(LPITEMIDLIST
*pidl
)
566 *pidl
= ILClone(m_pidl
);
570 // *** IPersistFolder methods ***
571 STDMETHODIMP
CFindFolder::Initialize(LPCITEMIDLIST pidl
)
573 m_pidl
= ILClone(pidl
);
575 return E_OUTOFMEMORY
;
577 return SHELL32_CoCreateInitSF(m_pidl
,
580 &CLSID_ShellFSFolder
,
581 IID_PPV_ARG(IShellFolder2
, &m_pisfInner
));
584 // *** IPersist methods ***
585 STDMETHODIMP
CFindFolder::GetClassID(CLSID
*pClassId
)
587 if (pClassId
== NULL
)
589 memcpy(pClassId
, &CLSID_FindFolder
, sizeof(CLSID
));