From 2f3db8d9a3d153855a6adafa1ea8dede7a29756c Mon Sep 17 00:00:00 2001 From: Brock Mammen Date: Sat, 3 Aug 2019 12:07:59 -0500 Subject: [PATCH] [SHELLFIND] Add search functionality --- dll/win32/browseui/shellfind/CFindFolder.cpp | 230 ++++++++++++++++--- dll/win32/browseui/shellfind/CFindFolder.h | 13 +- dll/win32/browseui/shellfind/CSearchBar.cpp | 17 +- dll/win32/browseui/shellfind/CSearchBar.h | 3 + 4 files changed, 215 insertions(+), 48 deletions(-) diff --git a/dll/win32/browseui/shellfind/CFindFolder.cpp b/dll/win32/browseui/shellfind/CFindFolder.cpp index 4e986bebeb1..535c1af09d0 100644 --- a/dll/win32/browseui/shellfind/CFindFolder.cpp +++ b/dll/win32/browseui/shellfind/CFindFolder.cpp @@ -37,10 +37,18 @@ CFindFolder::CFindFolder() : { } -static LPITEMIDLIST _ILCreate(LPCWSTR lpszPath, LPCITEMIDLIST lpcFindDataPidl) +static LPITEMIDLIST _ILCreate(LPCWSTR lpszPath) { + CComHeapPtr lpFSPidl(ILCreateFromPathW(lpszPath)); + if (!(LPITEMIDLIST)lpFSPidl) + { + ERR("Failed to create pidl from path\n"); + return 0; + } + LPITEMIDLIST lpLastFSPidl = ILFindLastID(lpFSPidl); + int pathLen = (wcslen(lpszPath) + 1) * sizeof(WCHAR); - int cbData = sizeof(WORD) + pathLen + lpcFindDataPidl->mkid.cb; + int cbData = sizeof(WORD) + pathLen + lpLastFSPidl->mkid.cb; LPITEMIDLIST pidl = (LPITEMIDLIST) SHAlloc(cbData + sizeof(WORD)); if (!pidl) return NULL; @@ -52,8 +60,8 @@ static LPITEMIDLIST _ILCreate(LPCWSTR lpszPath, LPCITEMIDLIST lpcFindDataPidl) memcpy(p, lpszPath, pathLen); p += pathLen; - memcpy(p, lpcFindDataPidl, lpcFindDataPidl->mkid.cb); - p += lpcFindDataPidl->mkid.cb; + memcpy(p, lpLastFSPidl, lpLastFSPidl->mkid.cb); + p += lpLastFSPidl->mkid.cb; *((WORD *) p) = 0; @@ -75,53 +83,199 @@ static LPCITEMIDLIST _ILGetFSPidl(LPCITEMIDLIST pidl) + ((wcslen((LPCWSTR) pidl->mkid.abID) + 1) * sizeof(WCHAR))); } -LRESULT CFindFolder::AddItem(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) +struct _SearchData +{ + HWND hwnd; + HANDLE hStopEvent; + SearchStart *pSearchParams; +}; + +static LPCSTR WINAPI StrStrNA(LPCSTR lpFirst, LPCSTR lpSrch, UINT cchMax) +{ + UINT i; + int len; + + if (!lpFirst || !lpSrch || !*lpSrch || !cchMax) + return NULL; + + len = strlen(lpSrch); + + for (i = cchMax; *lpFirst && (i > 0); i--, lpFirst++) + { + if (!strncmp(lpFirst, lpSrch, len)) + return (LPCSTR)lpFirst; + } + + return NULL; +} + +static UINT SearchFile(LPCWSTR lpFilePath, _SearchData *pSearchData) +{ + HANDLE hFile = CreateFileW(lpFilePath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); + if (hFile == INVALID_HANDLE_VALUE) + return 0; + + DWORD size = GetFileSize(hFile, NULL); + HANDLE hFileMap = CreateFileMappingW(hFile, NULL, PAGE_READONLY, 0, 0, NULL); + CloseHandle(hFile); + if (hFileMap == INVALID_HANDLE_VALUE) + return 0; + + LPBYTE lpFileContent = (LPBYTE) MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 0); + CloseHandle(hFileMap); + if (!lpFileContent) + return 0; + + UINT uMatches = 0; + if ((size >= 2) && (lpFileContent[0] == 0xFF) && (lpFileContent[1] == 0xFE)) + { + // UTF16 LE + LPCWSTR lpSearchPos = (LPCWSTR) lpFileContent; + DWORD dwCharsRemaining = size / sizeof(WCHAR); + const LPCWSTR lpSearchEnd = (LPCWSTR) lpFileContent + dwCharsRemaining; + const LPCWSTR lpszQuery = pSearchData->pSearchParams->szQuery; + const size_t queryLen = wcslen(lpszQuery); + while ((lpSearchPos = StrStrNW(lpSearchPos, lpszQuery, dwCharsRemaining)) + && lpSearchPos < lpSearchEnd) + { + uMatches++; + lpSearchPos += queryLen; + dwCharsRemaining -= queryLen; + } + } + else + { + DWORD len = WideCharToMultiByte(CP_ACP, 0, pSearchData->pSearchParams->szQuery, -1, NULL, 0, NULL, NULL); + const LPSTR lpszQuery = new CHAR[len]; + WideCharToMultiByte(CP_ACP, 0, pSearchData->pSearchParams->szQuery, -1, lpszQuery, len, NULL, NULL); + LPCSTR lpSearchPos = (LPCSTR) lpFileContent; + DWORD dwCharsRemaining = size; + const LPCSTR lpSearchEnd = (LPCSTR) lpFileContent + dwCharsRemaining; + const size_t queryLen = len; + while ((lpSearchPos = StrStrNA(lpSearchPos, lpszQuery, dwCharsRemaining)) + && lpSearchPos < lpSearchEnd) + { + uMatches++; + lpSearchPos += queryLen; + dwCharsRemaining -= queryLen; + } + } + + UnmapViewOfFile(lpFileContent); + + return uMatches; +} + +static VOID RecursiveFind(LPCWSTR lpPath, _SearchData *pSearchData) +{ + if (WaitForSingleObject(pSearchData->hStopEvent, 0) != WAIT_TIMEOUT) + return; + + WCHAR szPath[MAX_PATH]; + WIN32_FIND_DATAW FindData; + HANDLE hFindFile; + BOOL bMoreFiles = TRUE; + + PathCombineW(szPath, lpPath, L"*.*"); + + for (hFindFile = FindFirstFileW(szPath, &FindData); + bMoreFiles && hFindFile != INVALID_HANDLE_VALUE; + bMoreFiles = FindNextFileW(hFindFile, &FindData)) + { + if (!wcscmp(FindData.cFileName, L".") || !wcscmp(FindData.cFileName, L"..")) + continue; + + PathCombineW(szPath, lpPath, FindData.cFileName); + + if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + CStringW* status = new CStringW(); + status->Format(L"Searching '%s'", FindData.cFileName); + PostMessageW(pSearchData->hwnd, WM_SEARCH_UPDATE_STATUS, 0, (LPARAM) status); + + RecursiveFind(szPath, pSearchData); + } + else if (pSearchData->szFileName.IsEmpty() || PathMatchSpecW(FindData.cFileName, pSearchData->szFileName)) + { + DbgPrint("Searching file: '%S'\n", szPath); + UINT uMatches = SearchFile(szPath, pSearchData); + if (uMatches) + { + ::PostMessageW(pSearchData->hwnd, WM_SEARCH_ADD_RESULT, 0, (LPARAM) StrDupW(szPath)); + } + } + } + + if (hFindFile != INVALID_HANDLE_VALUE) + FindClose(hFindFile); +} + +static DWORD WINAPI _SearchThreadProc(LPVOID lpParameter) +{ + _SearchData *data = static_cast<_SearchData*>(lpParameter); + + SearchStart* params = (SearchStart *) data->pSearchParams; + + RecursiveFind(params->szPath, data); + + SHFree(params); + SHFree(lpParameter); + + return 0; +} + +LRESULT CFindFolder::StartSearch(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) { if (!lParam) return 0; - HRESULT hr; - LPWSTR path = (LPWSTR) lParam; - - CComPtr pShellFolder; - hr = SHGetDesktopFolder(&pShellFolder); - if (FAILED_UNEXPECTEDLY(hr)) - { - LocalFree(path); - return hr; - } - - CComHeapPtr lpFSPidl; - DWORD pchEaten; - hr = pShellFolder->ParseDisplayName(NULL, NULL, path, &pchEaten, &lpFSPidl, NULL); - if (FAILED_UNEXPECTEDLY(hr)) - { - LocalFree(path); - return hr; - } - - LPITEMIDLIST lpLastFSPidl = ILFindLastID(lpFSPidl); - CComHeapPtr lpSearchPidl(_ILCreate(path, lpLastFSPidl)); - LocalFree(path); - if (!lpSearchPidl) - { - return E_OUTOFMEMORY; - } - + // Clear all previous search results UINT uItemIndex; - hr = m_shellFolderView->AddObject(lpSearchPidl, &uItemIndex); + m_shellFolderView->RemoveObject(NULL, &uItemIndex); - return hr; + _SearchData* pSearchData = new _SearchData(); + pSearchData->pFindFolder = this; + pSearchData->hwnd = m_hWnd; + if (m_hStopEvent) + SetEvent(m_hStopEvent); + pSearchData->hStopEvent = m_hStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + pSearchData->pSearchParams = (SearchStart *) lParam; + + if (!SHCreateThread(_SearchThreadProc, pSearchData, NULL, NULL)) + { + SHFree(pSearchData->pSearchParams); + SHFree(pSearchData); + return HRESULT_FROM_WIN32(GetLastError()); + } + + return S_OK; +} + +LRESULT CFindFolder::AddResult(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) +{ + if (!lParam) + return 0; + + CComHeapPtr lpPath((LPWSTR) lParam); + + CComHeapPtr lpSearchPidl(_ILCreate(lpPath)); + if (lpSearchPidl) + { + UINT uItemIndex; + m_shellFolderView->AddObject(lpSearchPidl, &uItemIndex); + } + + return 0; } LRESULT CFindFolder::UpdateStatus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) { - LPWSTR status = (LPWSTR) lParam; + CStringW *status = (CStringW *) lParam; if (m_shellBrowser) { - m_shellBrowser->SetStatusTextSB(status); + m_shellBrowser->SetStatusTextSB(status->GetBuffer()); } - LocalFree(status); + delete status; return S_OK; } diff --git a/dll/win32/browseui/shellfind/CFindFolder.h b/dll/win32/browseui/shellfind/CFindFolder.h index 236d353bc4d..1768d47f2c4 100644 --- a/dll/win32/browseui/shellfind/CFindFolder.h +++ b/dll/win32/browseui/shellfind/CFindFolder.h @@ -66,6 +66,7 @@ private: CComPtr m_pisfInner; CComPtr m_shellFolderView; CComPtr m_shellBrowser; + HANDLE m_hStopEvent; //// *** IPersistFolder2 methods *** STDMETHODIMP GetCurFolder(LPITEMIDLIST *pidl); @@ -78,11 +79,16 @@ private: // *** IPersist methods *** STDMETHODIMP GetClassID(CLSID *pClassId); - LRESULT AddItem(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled); + // *** Message handlers *** + LRESULT StartSearch(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled); + + LRESULT AddResult(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled); LRESULT UpdateStatus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled); public: + CFindFolder(); + DECLARE_REGISTRY_RESOURCEID(IDR_FINDFOLDER) DECLARE_NOT_AGGREGATABLE(CFindFolder) @@ -90,8 +96,9 @@ public: DECLARE_PROTECT_FINAL_CONSTRUCT() BEGIN_MSG_MAP(CFindFolder) - MESSAGE_HANDLER(SWM_ADD_ITEM, AddItem) - MESSAGE_HANDLER(SWM_UPDATE_STATUS, UpdateStatus) + MESSAGE_HANDLER(WM_SEARCH_START, StartSearch) + MESSAGE_HANDLER(WM_SEARCH_ADD_RESULT, AddResult) + MESSAGE_HANDLER(WM_SEARCH_UPDATE_STATUS, UpdateStatus) END_MSG_MAP() BEGIN_COM_MAP(CFindFolder) diff --git a/dll/win32/browseui/shellfind/CSearchBar.cpp b/dll/win32/browseui/shellfind/CSearchBar.cpp index 49d6f87b846..d09c4784f97 100644 --- a/dll/win32/browseui/shellfind/CSearchBar.cpp +++ b/dll/win32/browseui/shellfind/CSearchBar.cpp @@ -46,34 +46,35 @@ void CSearchBar::InitializeSearchBar() m_hWnd, NULL, _AtlBaseModule.GetModuleInstance(), NULL); - CreateWindowExW(0, WC_STATIC, L"A &word or phrase in the file:", + CreateWindowExW(0, WC_STATIC, L"All or part &of the file name:", WS_CHILD | WS_VISIBLE, 10, 50, 500, 20, m_hWnd, NULL, _AtlBaseModule.GetModuleInstance(), NULL); - CreateWindowExW(WS_EX_CLIENTEDGE, WC_EDITW, NULL, + m_fileName = CreateWindowExW(WS_EX_CLIENTEDGE, WC_EDITW, NULL, WS_BORDER | WS_CHILD | WS_VISIBLE, 10, 70, 100, 20, m_hWnd, NULL, _AtlBaseModule.GetModuleInstance(), NULL); - CreateWindowExW(0, WC_STATIC, L"&Look in:", + CreateWindowExW(0, WC_STATIC, L"A &word or phrase in the file:", WS_CHILD | WS_VISIBLE, 10, 100, 500, 20, m_hWnd, NULL, _AtlBaseModule.GetModuleInstance(), NULL); - CreateWindowExW(WS_EX_CLIENTEDGE, WC_EDITW, NULL, + m_query = CreateWindowExW(WS_EX_CLIENTEDGE, WC_EDITW, NULL, WS_BORDER | WS_CHILD | WS_VISIBLE, 10, 120, 100, 20, m_hWnd, NULL, _AtlBaseModule.GetModuleInstance(), NULL); + Edit_LimitText(m_query, MAX_PATH); CreateWindowExW(0, WC_STATIC, L"&Look in:", WS_CHILD | WS_VISIBLE, 10, 150, 500, 20, m_hWnd, NULL, _AtlBaseModule.GetModuleInstance(), NULL); - CreateWindowExW(WS_EX_CLIENTEDGE, WC_EDITW, NULL, + m_path = CreateWindowExW(WS_EX_CLIENTEDGE, WC_EDITW, NULL, WS_BORDER | WS_CHILD | WS_VISIBLE, 10, 180, 100, 20, m_hWnd, NULL, @@ -198,8 +199,10 @@ LRESULT CSearchBar::OnSearchButtonClicked(WORD wNotifyCode, WORD wID, HWND hWndC return hr; } - GetSearchResultsFolder(&pShellBrowser, &hwnd, NULL); - if (hwnd) + hr = GetSearchResultsFolder(*pShellBrowser, &hwnd, NULL); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + ::PostMessageW(hwnd, WM_SEARCH_START, 0, (LPARAM) StrDupW(L"Starting search...")); return S_OK; diff --git a/dll/win32/browseui/shellfind/CSearchBar.h b/dll/win32/browseui/shellfind/CSearchBar.h index fd84b0c58f1..0613a317ab8 100644 --- a/dll/win32/browseui/shellfind/CSearchBar.h +++ b/dll/win32/browseui/shellfind/CSearchBar.h @@ -41,6 +41,9 @@ private: CComPtr pSite; BOOL fVisible; BOOL bFocused; + HWND m_fileName; + HWND m_query; + HWND m_path; void InitializeSearchBar(); HRESULT GetSearchResultsFolder(IShellBrowser **ppShellBrowser, HWND *pHwnd, IShellFolder **ppShellFolder);