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::StopSearch(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
)
273 SetEvent(m_hStopEvent
);
279 LRESULT
CFindFolder::AddResult(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
)
284 CComHeapPtr
<WCHAR
> lpPath((LPWSTR
) lParam
);
286 CComHeapPtr
<ITEMIDLIST
> lpSearchPidl(_ILCreate(lpPath
));
290 m_shellFolderView
->AddObject(lpSearchPidl
, &uItemIndex
);
296 LRESULT
CFindFolder::UpdateStatus(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
)
298 CStringW
*status
= (CStringW
*) lParam
;
301 m_shellBrowser
->SetStatusTextSB(status
->GetBuffer());
308 // *** IShellFolder2 methods ***
309 STDMETHODIMP
CFindFolder::GetDefaultSearchGUID(GUID
*pguid
)
315 STDMETHODIMP
CFindFolder::EnumSearches(IEnumExtraSearch
**ppenum
)
321 STDMETHODIMP
CFindFolder::GetDefaultColumn(DWORD
, ULONG
*pSort
, ULONG
*pDisplay
)
330 STDMETHODIMP
CFindFolder::GetDefaultColumnState(UINT iColumn
, DWORD
*pcsFlags
)
334 if (iColumn
>= _countof(g_ColumnDefs
))
335 return m_pisfInner
->GetDefaultColumnState(iColumn
- _countof(g_ColumnDefs
) + 1, pcsFlags
);
336 *pcsFlags
= g_ColumnDefs
[iColumn
].dwDefaultState
;
340 STDMETHODIMP
CFindFolder::GetDetailsEx(PCUITEMID_CHILD pidl
, const SHCOLUMNID
*pscid
, VARIANT
*pv
)
342 return m_pisfInner
->GetDetailsEx(pidl
, pscid
, pv
);
345 STDMETHODIMP
CFindFolder::GetDetailsOf(PCUITEMID_CHILD pidl
, UINT iColumn
, SHELLDETAILS
*pDetails
)
347 if (iColumn
>= _countof(g_ColumnDefs
))
348 return m_pisfInner
->GetDetailsOf(_ILGetFSPidl(pidl
), iColumn
- _countof(g_ColumnDefs
) + 1, pDetails
);
350 pDetails
->cxChar
= g_ColumnDefs
[iColumn
].cxChar
;
351 pDetails
->fmt
= g_ColumnDefs
[iColumn
].fmt
;
354 return SHSetStrRet(&pDetails
->str
, g_ColumnDefs
[iColumn
].wzColumnName
);
358 WCHAR path
[MAX_PATH
];
359 wcscpy(path
, _ILGetPath(pidl
));
360 PathRemoveFileSpecW(path
);
361 return SHSetStrRet(&pDetails
->str
, path
);
364 return GetDisplayNameOf(pidl
, SHGDN_NORMAL
, &pDetails
->str
);
367 STDMETHODIMP
CFindFolder::MapColumnToSCID(UINT iColumn
, SHCOLUMNID
*pscid
)
373 // *** IShellFolder methods ***
374 STDMETHODIMP
CFindFolder::ParseDisplayName(HWND hwndOwner
, LPBC pbc
, LPOLESTR lpszDisplayName
, ULONG
*pchEaten
,
375 PIDLIST_RELATIVE
*ppidl
, ULONG
*pdwAttributes
)
381 STDMETHODIMP
CFindFolder::EnumObjects(HWND hwndOwner
, DWORD dwFlags
, LPENUMIDLIST
*ppEnumIDList
)
383 *ppEnumIDList
= NULL
;
387 STDMETHODIMP
CFindFolder::BindToObject(PCUIDLIST_RELATIVE pidl
, LPBC pbcReserved
, REFIID riid
, LPVOID
*ppvOut
)
393 STDMETHODIMP
CFindFolder::BindToStorage(PCUIDLIST_RELATIVE pidl
, LPBC pbcReserved
, REFIID riid
, LPVOID
*ppvOut
)
399 STDMETHODIMP
CFindFolder::CompareIDs(LPARAM lParam
, PCUIDLIST_RELATIVE pidl1
, PCUIDLIST_RELATIVE pidl2
)
401 return m_pisfInner
->CompareIDs(lParam
, _ILGetFSPidl(pidl1
), _ILGetFSPidl(pidl2
));
404 STDMETHODIMP
CFindFolder::CreateViewObject(HWND hwndOwner
, REFIID riid
, LPVOID
*ppvOut
)
406 if (riid
== IID_IShellView
)
408 SFV_CREATE sfvparams
= {};
409 sfvparams
.cbSize
= sizeof(SFV_CREATE
);
410 sfvparams
.pshf
= this;
411 sfvparams
.psfvcb
= this;
412 HRESULT hr
= SHCreateShellFolderView(&sfvparams
, (IShellView
**) ppvOut
);
413 if (FAILED_UNEXPECTEDLY(hr
))
418 return ((IShellView
* ) * ppvOut
)->QueryInterface(IID_PPV_ARG(IShellFolderView
, &m_shellFolderView
));
420 return E_NOINTERFACE
;
423 STDMETHODIMP
CFindFolder::GetAttributesOf(UINT cidl
, PCUITEMID_CHILD_ARRAY apidl
, DWORD
*rgfInOut
)
425 CComHeapPtr
<PCITEMID_CHILD
> aFSPidl
;
426 aFSPidl
.Allocate(cidl
);
427 for (UINT i
= 0; i
< cidl
; i
++)
429 aFSPidl
[i
] = _ILGetFSPidl(apidl
[i
]);
432 return m_pisfInner
->GetAttributesOf(cidl
, aFSPidl
, rgfInOut
);
435 STDMETHODIMP
CFindFolder::GetUIObjectOf(HWND hwndOwner
, UINT cidl
, PCUITEMID_CHILD_ARRAY apidl
, REFIID riid
,
436 UINT
*prgfInOut
, LPVOID
*ppvOut
)
438 if (riid
== IID_IDataObject
&& cidl
== 1)
440 WCHAR path
[MAX_PATH
];
441 wcscpy(path
, (LPCWSTR
) apidl
[0]->mkid
.abID
);
442 PathRemoveFileSpecW(path
);
443 CComHeapPtr
<ITEMIDLIST
> rootPidl(ILCreateFromPathW(path
));
445 return E_OUTOFMEMORY
;
446 PCITEMID_CHILD aFSPidl
[1];
447 aFSPidl
[0] = _ILGetFSPidl(apidl
[0]);
448 return IDataObject_Constructor(hwndOwner
, rootPidl
, aFSPidl
, cidl
, (IDataObject
**) ppvOut
);
453 return m_pisfInner
->GetUIObjectOf(hwndOwner
, cidl
, apidl
, riid
, prgfInOut
, ppvOut
);
456 PCITEMID_CHILD
*aFSPidl
= new PCITEMID_CHILD
[cidl
];
457 for (UINT i
= 0; i
< cidl
; i
++)
459 aFSPidl
[i
] = _ILGetFSPidl(apidl
[i
]);
462 if (riid
== IID_IContextMenu
)
466 AddFSClassKeysToArray(aFSPidl
[0], hKeys
, &cKeys
);
469 dcm
.hwnd
= hwndOwner
;
471 dcm
.pidlFolder
= m_pidl
;
477 dcm
.punkAssociationInfo
= NULL
;
478 HRESULT hr
= SHCreateDefaultContextMenu(&dcm
, riid
, ppvOut
);
484 HRESULT hr
= m_pisfInner
->GetUIObjectOf(hwndOwner
, cidl
, aFSPidl
, riid
, prgfInOut
, ppvOut
);
490 STDMETHODIMP
CFindFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl
, DWORD dwFlags
, LPSTRRET pName
)
492 return m_pisfInner
->GetDisplayNameOf(_ILGetFSPidl(pidl
), dwFlags
, pName
);
495 STDMETHODIMP
CFindFolder::SetNameOf(HWND hwndOwner
, PCUITEMID_CHILD pidl
, LPCOLESTR lpName
, DWORD dwFlags
,
496 PITEMID_CHILD
*pPidlOut
)
502 //// *** IShellFolderViewCB method ***
503 STDMETHODIMP
CFindFolder::MessageSFVCB(UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
507 case SFVM_DEFVIEWMODE
:
509 FOLDERVIEWMODE
*pViewMode
= (FOLDERVIEWMODE
*) lParam
;
510 *pViewMode
= FVM_DETAILS
;
513 case SFVM_WINDOWCREATED
:
515 SubclassWindow((HWND
) wParam
);
517 CComPtr
<IServiceProvider
> pServiceProvider
;
518 HRESULT hr
= m_shellFolderView
->QueryInterface(IID_PPV_ARG(IServiceProvider
, &pServiceProvider
));
519 if (FAILED_UNEXPECTEDLY(hr
))
523 return pServiceProvider
->QueryService(SID_SShellBrowser
, IID_PPV_ARG(IShellBrowser
, &m_shellBrowser
));
529 //// *** IContextMenuCB method ***
530 STDMETHODIMP
CFindFolder::CallBack(IShellFolder
*psf
, HWND hwndOwner
, IDataObject
*pdtobj
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
534 case DFM_MERGECONTEXTMENU
:
536 QCMINFO
*pqcminfo
= (QCMINFO
*) lParam
;
537 _InsertMenuItemW(pqcminfo
->hmenu
, pqcminfo
->indexMenu
++, TRUE
, pqcminfo
->idCmdFirst
++, MFT_SEPARATOR
, NULL
, 0);
538 _InsertMenuItemW(pqcminfo
->hmenu
, pqcminfo
->indexMenu
++, TRUE
, pqcminfo
->idCmdFirst
++, MFT_STRING
, L
"Open Containing Folder", MFS_ENABLED
);
539 _InsertMenuItemW(pqcminfo
->hmenu
, pqcminfo
->indexMenu
++, TRUE
, pqcminfo
->idCmdFirst
++, MFT_SEPARATOR
, NULL
, 0);
542 case DFM_INVOKECOMMAND
:
543 case DFM_INVOKECOMMANDEX
:
548 PCUITEMID_CHILD
*apidl
;
550 HRESULT hr
= m_shellFolderView
->GetSelectedObjects(&apidl
, &cidl
);
551 if (FAILED_UNEXPECTEDLY(hr
))
554 for (UINT i
= 0; i
< cidl
; i
++)
556 CComHeapPtr
<ITEMIDLIST
> pidl
;
558 hr
= SHILCreateFromPathW((LPCWSTR
) apidl
[i
]->mkid
.abID
, &pidl
, &attrs
);
561 SHOpenFolderAndSelectItems(NULL
, 1, &pidl
, 0);
567 case DFM_GETDEFSTATICID
:
570 return Shell_DefaultContextMenuCallBack(m_pisfInner
, pdtobj
);
573 //// *** IPersistFolder2 methods ***
574 STDMETHODIMP
CFindFolder::GetCurFolder(LPITEMIDLIST
*pidl
)
576 *pidl
= ILClone(m_pidl
);
580 // *** IPersistFolder methods ***
581 STDMETHODIMP
CFindFolder::Initialize(LPCITEMIDLIST pidl
)
583 m_pidl
= ILClone(pidl
);
585 return E_OUTOFMEMORY
;
587 return SHELL32_CoCreateInitSF(m_pidl
,
590 &CLSID_ShellFSFolder
,
591 IID_PPV_ARG(IShellFolder2
, &m_pisfInner
));
594 // *** IPersist methods ***
595 STDMETHODIMP
CFindFolder::GetClassID(CLSID
*pClassId
)
597 if (pClassId
== NULL
)
599 memcpy(pClassId
, &CLSID_FindFolder
, sizeof(CLSID
));