diff --git a/mods/explorer-details-better-file-sizes.wh.cpp b/mods/explorer-details-better-file-sizes.wh.cpp index 62c2f98ae..a6ecc8b0b 100644 --- a/mods/explorer-details-better-file-sizes.wh.cpp +++ b/mods/explorer-details-better-file-sizes.wh.cpp @@ -2,7 +2,7 @@ // @id explorer-details-better-file-sizes // @name Better file sizes in Explorer details // @description Optional improvements: show folder sizes, use MB/GB for large files (by default, all sizes are shown in KBs), use IEC terms (such as KiB instead of KB) -// @version 1.2.1 +// @version 1.3 // @author m417z // @github https://github.com/m417z // @twitter https://twitter.com/m417z @@ -114,7 +114,9 @@ KiB?](https://devblogs.microsoft.com/oldnewthing/20090611-00/?p=17933). #include #include #include +#include #include +#include #include @@ -147,897 +149,328 @@ auto hookRefCountScope() { &g_hookRefCount, [](auto hookRefCount) { (*hookRefCount)--; }}; } -// Partial version of the Everything SDK to be able to query folder sizes. -// https://www.voidtools.com/support/everything/sdk/ +// The partial version of the Everything SDK below and some of the code for +// querying Everything are based on code from SizeES: A Plugin for Fast, +// Persistent FolderSizes in x2 via Everything Search. +// https://forum.zabkat.com/viewtopic.php?t=12326 + #pragma region everything_sdk // clang-format off -// -// Copyright (C) 2016 David Carpenter -// -// Permission is hereby granted, free of charge, -// to any person obtaining a copy of this software -// and associated documentation files (the "Software"), -// to deal in the Software without restriction, -// including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, -// subject to the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -#define EVERYTHING_ERROR_MEMORY 1 // out of memory. -#define EVERYTHING_ERROR_IPC 2 // Everything search client is not running -#define EVERYTHING_ERROR_REGISTERCLASSEX 3 // unable to register window class. -#define EVERYTHING_ERROR_CREATEWINDOW 4 // unable to create listening window -#define EVERYTHING_ERROR_INVALIDINDEX 6 // invalid index -#define EVERYTHING_ERROR_INVALIDCALL 7 // invalid call -#define EVERYTHING_ERROR_INVALIDREQUEST 8 // invalid request data, request data first. - -#define EVERYTHING_SORT_NAME_ASCENDING 1 - -#define EVERYTHING_REQUEST_FILE_NAME 0x00000001 -#define EVERYTHING_REQUEST_PATH 0x00000002 -#define EVERYTHING_REQUEST_SIZE 0x00000010 - -#ifndef EVERYTHINGAPI -#define EVERYTHINGAPI //__stdcall -#endif - -#ifndef EVERYTHINGUSERAPI -#define EVERYTHINGUSERAPI //__declspec(dllimport) -#endif - -// find the everything IPC window -#define EVERYTHING_IPC_WNDCLASSW L"EVERYTHING_TASKBAR_NOTIFICATION" +// Version 1.5 -// search flags for querys -#define EVERYTHING_IPC_MATCHCASE 0x00000001 // match case -#define EVERYTHING_IPC_MATCHWHOLEWORD 0x00000002 // match whole word -#define EVERYTHING_IPC_MATCHPATH 0x00000004 // include paths in search -#define EVERYTHING_IPC_REGEX 0x00000008 // enable regex +#define FT_NOMOREFIELDS 0 +#define FT_NUMERIC_32 1 +#define FT_NUMERIC_64 2 +#define FT_NUMERIC_FLOATING 3 +#define FT_DATE 4 +#define FT_TIME 5 +#define FT_BOOLEAN 6 +#define FT_MULTIPLECHOICE 7 +#define FT_STRING 8 +#define FT_FULLTEXT 9 +#define FT_DATETIME 10 +#define FT_STRINGW 11 -typedef struct EVERYTHING_IPC_COMMAND_LINE -{ - DWORD show_command; // MUST be one of the SW_* ShowWindow() commands +#define FT_NOSUCHFIELD -1 // Error, invalid field number given +#define FT_FILEERROR -2 // File i/o error +#define FT_FIELDEMPTY -3 // Field valid, but empty +#define FT_NOTSUPPORTED -5 // Function not supported - // null terminated variable sized command line text in UTF-8. - BYTE command_line_text[1]; +typedef struct { + int size; + DWORD PluginInterfaceVersionLow; + DWORD PluginInterfaceVersionHi; + char DefaultIniName[MAX_PATH]; +} ContentDefaultParamStruct; -}EVERYTHING_IPC_COMMAND_LINE; +int __stdcall ContentGetDetectString(LPSTR DetectString, int maxlen); +int __stdcall ContentGetSupportedField(int FieldIndex, LPSTR FieldName, [[maybe_unused]] LPSTR Units, [[maybe_unused]] int maxlen); +int __stdcall ContentGetValueW(LPWSTR FileName, int FieldIndex, [[maybe_unused]] int UnitIndex, LPVOID FieldValue, [[maybe_unused]] int maxlen, [[maybe_unused]] int flags); -// the WM_COPYDATA message for a query. -#define EVERYTHING_IPC_COPYDATAQUERYA 1 -#define EVERYTHING_IPC_COPYDATAQUERYW 2 +void __stdcall ContentSetDefaultParams([[maybe_unused]] ContentDefaultParamStruct *dps); +int __stdcall ContentGetSupportedFieldFlags(int FieldIndex); -// all results -#define EVERYTHING_IPC_ALLRESULTS 0xFFFFFFFF // all results - -#pragma pack (push,1) - -typedef struct EVERYTHING_IPC_QUERYW -{ - // the window that will receive the new results. - // only 32bits are required to store a window handle. (even on x64) - DWORD reply_hwnd; +// Severely reduced Everything_IPC.h, Copyright (C) 2022 David Carpenter (this file only) https://www.voidtools.com/Everything-SDK.zip - // the value to set the dwData member in the COPYDATASTRUCT struct - // sent by Everything when the query is complete. - DWORD reply_copydata_message; +#define EVERYTHING_WM_IPC (WM_USER) - // search flags (see EVERYTHING_IPC_MATCHCASE | EVERYTHING_IPC_MATCHWHOLEWORD | EVERYTHING_IPC_MATCHPATH) - DWORD search_flags; +#define EVERYTHING_IPC_GET_MAJOR_VERSION 0 +#define EVERYTHING_IPC_GET_MINOR_VERSION 1 +#define EVERYTHING_IPC_GET_REVISION 2 +#define EVERYTHING_IPC_GET_BUILD_NUMBER 3 - // only return results after 'offset' results (0 to return from the first result) - // useful for scrollable lists - DWORD offset; +#define EVERYTHING_IPC_WNDCLASSW L"EVERYTHING_TASKBAR_NOTIFICATION" +#define EVERYTHING_IPC_WNDCLASSW_15A L"EVERYTHING_TASKBAR_NOTIFICATION_(1.5a)" - // the number of results to return - // zero to return no results - // EVERYTHING_IPC_ALLRESULTS to return ALL results - DWORD max_results; +#define EVERYTHING_IPC_IS_FILE_INFO_INDEXED 411 +#define EVERYTHING_IPC_FILE_INFO_FOLDER_SIZE 2 - // null terminated string. variable lengthed search string buffer. - WCHAR search_string[1]; +#define EVERYTHING_IPC_MATCHCASE 0x00000001 +#define EVERYTHING_IPC_MATCHDIACRITICS 0x00000010 +#define EVERYTHING_IPC_COPYDATA_QUERY2W 18 +#define EVERYTHING_IPC_SORT_NAME_ASCENDING 1 -}EVERYTHING_IPC_QUERYW; +#define EVERYTHING_IPC_QUERY2_REQUEST_SIZE 0x00000010 -// ASCII version -typedef struct EVERYTHING_IPC_QUERYA -{ - // the window that will receive the new results. - // only 32bits are required to store a window handle. (even on x64) +typedef struct { DWORD reply_hwnd; - - // the value to set the dwData member in the COPYDATASTRUCT struct - // sent by Everything when the query is complete. DWORD reply_copydata_message; - - // search flags (see EVERYTHING_IPC_MATCHCASE | EVERYTHING_IPC_MATCHWHOLEWORD | EVERYTHING_IPC_MATCHPATH) DWORD search_flags; - - // only return results after 'offset' results (0 to return from the first result) - // useful for scrollable lists DWORD offset; - - // the number of results to return - // zero to return no results - // EVERYTHING_IPC_ALLRESULTS to return ALL results DWORD max_results; - - // null terminated string. variable lengthed search string buffer. - CHAR search_string[1]; - -}EVERYTHING_IPC_QUERYA; - -typedef struct EVERYTHING_IPC_ITEMW -{ - // item flags - DWORD flags; - - // The offset of the filename from the beginning of the list structure. - // (wchar_t *)((char *)everything_list + everythinglist->name_offset) - DWORD filename_offset; - - // The offset of the filename from the beginning of the list structure. - // (wchar_t *)((char *)everything_list + everythinglist->path_offset) - DWORD path_offset; - -}EVERYTHING_IPC_ITEMW; - -typedef struct EVERYTHING_IPC_ITEMA -{ - // item flags - DWORD flags; - - // The offset of the filename from the beginning of the list structure. - // (char *)((char *)everything_list + everythinglist->name_offset) - DWORD filename_offset; - - // The offset of the filename from the beginning of the list structure. - // (char *)((char *)everything_list + everythinglist->path_offset) - DWORD path_offset; - -}EVERYTHING_IPC_ITEMA; - -typedef struct EVERYTHING_IPC_LISTW -{ - // the total number of folders found. - DWORD totfolders; - - // the total number of files found. - DWORD totfiles; - - // totfolders + totfiles - DWORD totitems; - - // the number of folders available. - DWORD numfolders; - - // the number of files available. - DWORD numfiles; - - // the number of items available. - DWORD numitems; - - // index offset of the first result in the item list. - DWORD offset; - - // variable lengthed item list. - // use numitems to determine the actual number of items available. - EVERYTHING_IPC_ITEMW items[1]; - -}EVERYTHING_IPC_LISTW; - -typedef struct EVERYTHING_IPC_LISTA -{ - // the total number of folders found. - DWORD totfolders; - - // the total number of files found. - DWORD totfiles; - - // totfolders + totfiles - DWORD totitems; - - // the number of folders available. - DWORD numfolders; - - // the number of files available. - DWORD numfiles; - - // the number of items available. - DWORD numitems; - - // index offset of the first result in the item list. - DWORD offset; - - // variable lengthed item list. - // use numitems to determine the actual number of items available. - EVERYTHING_IPC_ITEMA items[1]; - -}EVERYTHING_IPC_LISTA; - -#pragma pack (pop) - -#define EVERYTHING_IPC_WNDCLASS EVERYTHING_IPC_WNDCLASSW - -// the WM_COPYDATA message for a query. -// requires Everything 1.4.1 -#define EVERYTHING_IPC_COPYDATA_QUERY2A 17 -#define EVERYTHING_IPC_COPYDATA_QUERY2W 18 - -#pragma pack (push,1) - -// ASCII version -typedef struct EVERYTHING_IPC_QUERY2 -{ - // the window that will receive the new results. - // only 32bits are required to store a window handle. (even on x64) - DWORD reply_hwnd; - - // the value to set the dwData member in the COPYDATASTRUCT struct - // sent by Everything when the query is complete. - DWORD reply_copydata_message; - - // search flags (see EVERYTHING_IPC_MATCHCASE | EVERYTHING_IPC_MATCHWHOLEWORD | EVERYTHING_IPC_MATCHPATH) - DWORD search_flags; - - // only return results after 'offset' results (0 to return from the first result) - // useful for scrollable lists - DWORD offset; - - // the number of results to return - // zero to return no results - // EVERYTHING_IPC_ALLRESULTS to return ALL results - DWORD max_results; - - // request types. - // one or more of EVERYTHING_IPC_QUERY2_REQUEST_* types. DWORD request_flags; - - // sort type, set to one of EVERYTHING_IPC_SORT_* types. - // set to EVERYTHING_IPC_SORT_NAME_ASCENDING for the best performance (there will never be a performance hit when sorting by name ascending). - // Other sorts will also be instant if the corresponding fast sort is enabled from Tools -> Options -> Indexes. DWORD sort_type; +} EVERYTHING_IPC_QUERY2; - // followed by null terminated search. - // TCHAR search_string[1]; - -}EVERYTHING_IPC_QUERY2; - -typedef struct EVERYTHING_IPC_ITEM2 -{ - // item flags one of (EVERYTHING_IPC_FOLDER|EVERYTHING_IPC_DRIVE|EVERYTHING_IPC_ROOT) - DWORD flags; - - // offset from the start of the EVERYTHING_IPC_LIST2 struct to the data content - DWORD data_offset; - -}EVERYTHING_IPC_ITEM2; - -typedef struct EVERYTHING_IPC_LIST2 -{ - // number of items found. +typedef struct { DWORD totitems; - - // the number of items available. DWORD numitems; - - // index offset of the first result in the item list. DWORD offset; - - // valid request types. DWORD request_flags; - - // this sort type. - // one of EVERYTHING_IPC_SORT_* types. - // maybe different to requested sort type. DWORD sort_type; +} EVERYTHING_IPC_LIST2; - // items follow. - // EVERYTHING_IPC_ITEM2 items[numitems] - - // item data follows. - -}EVERYTHING_IPC_LIST2; - -#pragma pack (pop) - -#pragma pack (push,1) - -typedef struct EVERYTHING_IPC_RUN_HISTORY -{ - DWORD run_count; - - // null terminated ansi/wchar filename follows. - // TCHAR filename[]; - -}EVERYTHING_IPC_RUN_HISTORY; - -#pragma pack (pop) - -// return copydata code -#define _EVERYTHING_COPYDATA_QUERYREPLY 0 - -#define _EVERYTHING_MSGFLT_ALLOW 1 - -typedef struct _EVERYTHING_tagCHANGEFILTERSTRUCT -{ - DWORD cbSize; - DWORD ExtStatus; -}_EVERYTHING_CHANGEFILTERSTRUCT, * _EVERYTHING_PCHANGEFILTERSTRUCT; - -static void* _Everything_Alloc(DWORD size); -static void _Everything_Free(void* ptr); -static void _Everything_Lock(void); -static void _Everything_Unlock(void); -static DWORD _Everything_StringLengthA(LPCSTR start); -static DWORD _Everything_StringLengthW(LPCWSTR start); -static BOOL _Everything_ShouldUseVersion2(void); -static BOOL _Everything_SendIPCQuery(void); -static BOOL _Everything_SendIPCQuery2(HWND everything_hwnd); -static void _Everything_FreeLists(void); -static BOOL _Everything_IsValidResultIndex(DWORD dwIndex); -static void* _Everything_GetRequestData(DWORD dwIndex, DWORD dwRequestType); -static void _Everything_ChangeWindowMessageFilter(HWND hwnd); -static BOOL _Everything_GetResultRequestData(DWORD dwIndex, DWORD dwRequestType, void* data, int size); -static DWORD _Everything_GetNumResults(void); - -// internal state -static thread_local BOOL _Everything_MatchPath = FALSE; -static thread_local BOOL _Everything_MatchCase = FALSE; -static thread_local BOOL _Everything_MatchWholeWord = FALSE; -static thread_local BOOL _Everything_Regex = FALSE; -static thread_local DWORD _Everything_LastError = FALSE; -static thread_local DWORD _Everything_Max = EVERYTHING_IPC_ALLRESULTS; -static thread_local DWORD _Everything_Offset = 0; -static thread_local DWORD _Everything_Sort = EVERYTHING_SORT_NAME_ASCENDING; -static thread_local DWORD _Everything_RequestFlags = EVERYTHING_REQUEST_PATH | EVERYTHING_REQUEST_FILE_NAME; -static thread_local BOOL _Everything_IsUnicodeQuery = FALSE; -static thread_local DWORD _Everything_QueryVersion = 0; -static thread_local BOOL _Everything_IsUnicodeSearch = FALSE; -static thread_local void* _Everything_Search = NULL; // wchar or char -static thread_local EVERYTHING_IPC_LIST2* _Everything_List2 = NULL; -static thread_local void* _Everything_List = NULL; // EVERYTHING_IPC_LISTW or EVERYTHING_IPC_LISTA -static thread_local HWND _Everything_ReplyWindow = 0; -static thread_local DWORD _Everything_ReplyID = 0; -static thread_local BOOL(WINAPI* _Everything_pChangeWindowMessageFilterEx)(HWND hWnd, UINT message, DWORD action, _EVERYTHING_PCHANGEFILTERSTRUCT pChangeFilterStruct) = 0; -static thread_local HANDLE _Everything_user32_hdll = NULL; -static thread_local BOOL _Everything_GotChangeWindowMessageFilterEx = FALSE; - -static void _Everything_Lock(void) -{ - // Unnecessary since everything is thread-local. -} - -static void _Everything_Unlock(void) -{ - // Unnecessary since everything is thread-local. -} - -// avoid other libs -static DWORD _Everything_StringLengthA(LPCSTR start) -{ - LPCSTR s; - - s = start; - - while (*s) - { - s++; - } - - return (DWORD)(s - start); -} +typedef struct { + DWORD flags; + DWORD data_offset; +} EVERYTHING_IPC_ITEM2; -static DWORD _Everything_StringLengthW(LPCWSTR start) -{ - LPCWSTR s; +// clang-format on +#pragma endregion // everything_sdk - s = start; +constexpr DWORD kGsTimeoutIPC = 1000; - while (*s) - { - s++; - } +#define GS_SEARCH_PREFIX L"folder:wfn:\"" +#define GS_SEARCH_SUFFIX L"\"" - return (DWORD)(s - start); -} +std::atomic g_gsReceiverWnd; -void EVERYTHINGAPI Everything_SetMax(DWORD dwMax) -{ - _Everything_Lock(); +struct { + // Auto-reset event signalled when a reply to a search query is received. + std::atomic hEvent; + DWORD dwID; + bool bResult; + int64_t liSize; +} g_gsReply; + +std::mutex g_gsReplyMutex; +std::mutex g_gsReplyCopyDataMutex; +std::atomic g_gsReplyCounter; + +enum : unsigned { + ES_QUERY_OK, + ES_QUERY_NO_MEMORY, + ES_QUERY_NO_ES_IPC, + ES_QUERY_NO_PLUGIN_IPC, + ES_QUERY_TIMEOUT, + ES_QUERY_REPLY_TIMEOUT, + ES_QUERY_NO_RESULT, +}; +PCWSTR g_gsQueryStatus[] = { + L"", L"No Memory", L"No ES IPC", L"No Plugin IPC", + L"Query Timeout", L"Reply Timeout", L"No Result", +}; - _Everything_Max = dwMax; +HANDLE g_everything4Wh_Thread; - _Everything_Unlock(); -} +unsigned Everything4Wh_GetFileSize(PCWSTR folderPath, int64_t* size) { + *size = 0; -EVERYTHINGUSERAPI void EVERYTHINGAPI Everything_SetRequestFlags(DWORD dwRequestFlags) -{ - _Everything_Lock(); + HWND hReceiverWnd = g_gsReceiverWnd; - _Everything_RequestFlags = dwRequestFlags; + if (!hReceiverWnd) { + return ES_QUERY_NO_PLUGIN_IPC; + } - _Everything_Unlock(); -} + HWND hEverything = FindWindow(EVERYTHING_IPC_WNDCLASSW, nullptr); -// get the search length -static DWORD _Everything_GetSearchLengthW(void) -{ - if (_Everything_Search) - { - if (_Everything_IsUnicodeSearch) - { - return _Everything_StringLengthW((LPCWSTR)_Everything_Search); - } - else - { - return MultiByteToWideChar(CP_ACP, 0, (LPCSTR)_Everything_Search, -1, 0, 0); - } - } - - return 0; -} - -// get the search length -static DWORD _Everything_GetSearchLengthA(void) -{ - if (_Everything_Search) - { - if (_Everything_IsUnicodeSearch) - { - return WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)_Everything_Search, -1, 0, 0, 0, 0); - } - else - { - return _Everything_StringLengthA((LPCSTR)_Everything_Search); - } - } - - return 0; -} + if (!hEverything) { + hEverything = FindWindow(EVERYTHING_IPC_WNDCLASSW_15A, nullptr); + } -// get the search -static void _Everything_GetSearchTextW(LPWSTR wbuf) -{ - DWORD wlen; + if (!hEverything) { + return ES_QUERY_NO_ES_IPC; + } - if (_Everything_Search) - { - wlen = _Everything_GetSearchLengthW(); + // Prevent querying from within the Everything process to avoid deadlocks. + DWORD dwEverythingProcessId = 0; + GetWindowThreadProcessId(hEverything, &dwEverythingProcessId); + if (dwEverythingProcessId == GetCurrentProcessId()) { + return ES_QUERY_NO_ES_IPC; + } - if (_Everything_IsUnicodeSearch) - { - CopyMemory(wbuf, _Everything_Search, (wlen + 1) * sizeof(WCHAR)); + DWORD dwSize = sizeof(EVERYTHING_IPC_QUERY2) + + sizeof(GS_SEARCH_PREFIX GS_SEARCH_SUFFIX) + + (wcslen(folderPath) * sizeof(WCHAR)); + std::vector queryBuffer(dwSize, 0); + EVERYTHING_IPC_QUERY2* pQuery = (EVERYTHING_IPC_QUERY2*)queryBuffer.data(); - return; - } - else - { - MultiByteToWideChar(CP_ACP, 0, (LPCSTR)_Everything_Search, -1, wbuf, wlen + 1); + pQuery->reply_hwnd = (DWORD)(DWORD_PTR)hReceiverWnd; + pQuery->reply_copydata_message = ++g_gsReplyCounter; + // Always consider Pokémon distinct from Pokemon. + pQuery->search_flags = + EVERYTHING_IPC_MATCHCASE | EVERYTHING_IPC_MATCHDIACRITICS; + pQuery->offset = 0; + pQuery->max_results = 1; + pQuery->request_flags = EVERYTHING_IPC_QUERY2_REQUEST_SIZE; + pQuery->sort_type = EVERYTHING_IPC_SORT_NAME_ASCENDING; - return; - } - } + swprintf_s((LPWSTR)(pQuery + 1), + (dwSize - sizeof(*pQuery)) / sizeof(wchar_t), L"%s%s%s", + GS_SEARCH_PREFIX, folderPath, GS_SEARCH_SUFFIX); - *wbuf = 0; -} + COPYDATASTRUCT cds = { + .dwData = EVERYTHING_IPC_COPYDATA_QUERY2W, + .cbData = dwSize, + .lpData = pQuery, + }; -// get the search -static void _Everything_GetSearchTextA(LPSTR buf) -{ - DWORD len; + std::lock_guard guard(g_gsReplyMutex); - if (_Everything_Search) - { - len = _Everything_GetSearchLengthA(); + { + std::lock_guard copyDataGuard(g_gsReplyCopyDataMutex); + g_gsReply.dwID = pQuery->reply_copydata_message; + } - if (_Everything_IsUnicodeSearch) - { - WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)_Everything_Search, -1, buf, len + 1, 0, 0); + unsigned result; - return; - } - else - { - CopyMemory(buf, _Everything_Search, len + 1); + ResetEvent(g_gsReply.hEvent); - return; - } - } + LRESULT lrPending = SendMessageTimeout( + hEverything, WM_COPYDATA, (WPARAM)hReceiverWnd, (LPARAM)&cds, + SMTO_BLOCK | SMTO_ABORTIFHUNG, kGsTimeoutIPC, nullptr); + if (lrPending) { + // Wait on ReceiverWndProc (below) to signal that the reply was + // processed. + DWORD waitResult = WaitForSingleObject(g_gsReply.hEvent, kGsTimeoutIPC); + if (waitResult != WAIT_OBJECT_0) { + result = ES_QUERY_REPLY_TIMEOUT; + } else if (!g_gsReply.bResult) { + result = ES_QUERY_NO_RESULT; + } else { + *size = g_gsReply.liSize; + result = ES_QUERY_OK; + } + } else { + result = ES_QUERY_TIMEOUT; + } - *buf = 0; -} + { + std::lock_guard copyDataGuard(g_gsReplyCopyDataMutex); + g_gsReply.dwID = 0; + } -static BOOL _Everything_SendIPCQuery2(HWND everything_hwnd) -{ - BOOL ret; - DWORD size; - EVERYTHING_IPC_QUERY2* query; - - // try version 2. - - if (_Everything_IsUnicodeQuery) - { - // unicode - size = sizeof(EVERYTHING_IPC_QUERY2) + ((_Everything_GetSearchLengthW() + 1) * sizeof(WCHAR)); - } - else - { - // ansi - size = sizeof(EVERYTHING_IPC_QUERY2) + ((_Everything_GetSearchLengthA() + 1) * sizeof(char)); - } - - // alloc - query = (EVERYTHING_IPC_QUERY2*)_Everything_Alloc(size); - - if (query) - { - COPYDATASTRUCT cds; - - query->max_results = _Everything_Max; - query->offset = _Everything_Offset; - query->reply_copydata_message = _Everything_ReplyID; - query->search_flags = (_Everything_Regex ? EVERYTHING_IPC_REGEX : 0) | (_Everything_MatchCase ? EVERYTHING_IPC_MATCHCASE : 0) | (_Everything_MatchWholeWord ? EVERYTHING_IPC_MATCHWHOLEWORD : 0) | (_Everything_MatchPath ? EVERYTHING_IPC_MATCHPATH : 0); - query->reply_hwnd = (DWORD)(DWORD_PTR)_Everything_ReplyWindow; - query->sort_type = (DWORD)_Everything_Sort; - query->request_flags = (DWORD)_Everything_RequestFlags; - - if (_Everything_IsUnicodeQuery) - { - _Everything_GetSearchTextW((LPWSTR)(query + 1)); - } - else - { - _Everything_GetSearchTextA((LPSTR)(query + 1)); - } - - cds.cbData = size; - cds.dwData = _Everything_IsUnicodeQuery ? EVERYTHING_IPC_COPYDATA_QUERY2W : EVERYTHING_IPC_COPYDATA_QUERY2A; - cds.lpData = query; - - if (SendMessage(everything_hwnd, WM_COPYDATA, (WPARAM)_Everything_ReplyWindow, (LPARAM)&cds)) - { - // successful. - ret = TRUE; - } - else - { - // no ipc - _Everything_LastError = EVERYTHING_ERROR_IPC; - - ret = FALSE; - } - - // get result from window. - _Everything_Free(query); - } - else - { - _Everything_LastError = EVERYTHING_ERROR_MEMORY; - - ret = FALSE; - } - - return ret; + return result; } -static BOOL _Everything_ShouldUseVersion2(void) -{ - if (_Everything_RequestFlags != (EVERYTHING_REQUEST_PATH | EVERYTHING_REQUEST_FILE_NAME)) - { - return TRUE; - } +LRESULT CALLBACK Everything4Wh_ReceiverWndProc(HWND hWnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) { + switch (uMsg) { + case WM_COPYDATA: { + std::lock_guard guard(g_gsReplyCopyDataMutex); - if (_Everything_Sort != EVERYTHING_SORT_NAME_ASCENDING) - { - return TRUE; - } + COPYDATASTRUCT* pcds = (COPYDATASTRUCT*)lParam; - // just use version 1 - return FALSE; -} + if (pcds->dwData == g_gsReply.dwID) { + EVERYTHING_IPC_LIST2* list = + (EVERYTHING_IPC_LIST2*)pcds->lpData; -static BOOL _Everything_SendIPCQuery(void) -{ - HWND everything_hwnd; - BOOL ret; - - // find the everything ipc window. - everything_hwnd = FindWindow(EVERYTHING_IPC_WNDCLASS, 0); - - if (!everything_hwnd) { - everything_hwnd = FindWindow(L"EVERYTHING_TASKBAR_NOTIFICATION_(1.5a)", 0); - } - - if (everything_hwnd) - { - _Everything_QueryVersion = 2; - - // try version 2 first (if we specified some non-version 1 request flags or sort) - if ((_Everything_ShouldUseVersion2()) && (_Everything_SendIPCQuery2(everything_hwnd))) - { - // successful. - ret = TRUE; - } - else - { - DWORD len; - DWORD size; - void* query; - - // try version 1. - - if (_Everything_IsUnicodeQuery) - { - // unicode - len = _Everything_GetSearchLengthW(); - - size = sizeof(EVERYTHING_IPC_QUERYW) - sizeof(WCHAR) + len * sizeof(WCHAR) + sizeof(WCHAR); - } - else - { - // ansi - len = _Everything_GetSearchLengthA(); - - size = sizeof(EVERYTHING_IPC_QUERYA) - sizeof(char) + (len * sizeof(char)) + sizeof(char); - } - - // alloc - query = _Everything_Alloc(size); - - if (query) - { - COPYDATASTRUCT cds; - - if (_Everything_IsUnicodeQuery) - { - ((EVERYTHING_IPC_QUERYW*)query)->max_results = _Everything_Max; - ((EVERYTHING_IPC_QUERYW*)query)->offset = _Everything_Offset; - ((EVERYTHING_IPC_QUERYW*)query)->reply_copydata_message = _Everything_ReplyID; - ((EVERYTHING_IPC_QUERYW*)query)->search_flags = (_Everything_Regex ? EVERYTHING_IPC_REGEX : 0) | (_Everything_MatchCase ? EVERYTHING_IPC_MATCHCASE : 0) | (_Everything_MatchWholeWord ? EVERYTHING_IPC_MATCHWHOLEWORD : 0) | (_Everything_MatchPath ? EVERYTHING_IPC_MATCHPATH : 0); - ((EVERYTHING_IPC_QUERYW*)query)->reply_hwnd = (DWORD)(DWORD_PTR)_Everything_ReplyWindow; - - _Everything_GetSearchTextW(((EVERYTHING_IPC_QUERYW*)query)->search_string); - } - else - { - ((EVERYTHING_IPC_QUERYA*)query)->max_results = _Everything_Max; - ((EVERYTHING_IPC_QUERYA*)query)->offset = _Everything_Offset; - ((EVERYTHING_IPC_QUERYA*)query)->reply_copydata_message = _Everything_ReplyID; - ((EVERYTHING_IPC_QUERYA*)query)->search_flags = (_Everything_Regex ? EVERYTHING_IPC_REGEX : 0) | (_Everything_MatchCase ? EVERYTHING_IPC_MATCHCASE : 0) | (_Everything_MatchWholeWord ? EVERYTHING_IPC_MATCHWHOLEWORD : 0) | (_Everything_MatchPath ? EVERYTHING_IPC_MATCHPATH : 0); - ((EVERYTHING_IPC_QUERYA*)query)->reply_hwnd = (DWORD)(DWORD_PTR)_Everything_ReplyWindow; - - _Everything_GetSearchTextA(((EVERYTHING_IPC_QUERYA*)query)->search_string); - } - - cds.cbData = size; - cds.dwData = _Everything_IsUnicodeQuery ? EVERYTHING_IPC_COPYDATAQUERYW : EVERYTHING_IPC_COPYDATAQUERYA; - cds.lpData = query; - - _Everything_QueryVersion = 1; - - if (SendMessage(everything_hwnd, WM_COPYDATA, (WPARAM)_Everything_ReplyWindow, (LPARAM)&cds)) - { - // successful. - ret = TRUE; - } - else - { - // no ipc - _Everything_LastError = EVERYTHING_ERROR_IPC; - - ret = FALSE; - } - - // get result from window. - _Everything_Free(query); - } - else - { - _Everything_LastError = EVERYTHING_ERROR_MEMORY; - - ret = FALSE; - } - } - } - else - { - _Everything_LastError = EVERYTHING_ERROR_IPC; - - ret = FALSE; - } - - return ret; -} + if (list->numitems) { + g_gsReply.liSize = + *(int64_t*)(((BYTE*)(list + 1)) + + sizeof(EVERYTHING_IPC_ITEM2)); + g_gsReply.bResult = true; + } else { + g_gsReply.liSize = 0; + g_gsReply.bResult = false; + } -static DWORD _Everything_GetNumResults(void) -{ - DWORD ret; - - if (_Everything_List) - { - if (_Everything_IsUnicodeQuery) - { - ret = ((EVERYTHING_IPC_LISTW*)_Everything_List)->numitems; - } - else - { - ret = ((EVERYTHING_IPC_LISTA*)_Everything_List)->numitems; - } - } - else - if (_Everything_List2) - { - ret = _Everything_List2->numitems; - } - else - { - _Everything_LastError = EVERYTHING_ERROR_INVALIDCALL; - - ret = 0; - } - - return ret; -} + SetEvent(g_gsReply.hEvent); + } -static void* _Everything_Alloc(DWORD size) -{ - return HeapAlloc(GetProcessHeap(), 0, size); -} + return TRUE; + } + } -static void _Everything_Free(void* ptr) -{ - HeapFree(GetProcessHeap(), 0, ptr); + return DefWindowProc(hWnd, uMsg, wParam, lParam); } -static void _Everything_FreeLists(void) -{ - if (_Everything_List) - { - _Everything_Free(_Everything_List); - - _Everything_List = 0; - } - - if (_Everything_List2) - { - _Everything_Free(_Everything_List2); +HMODULE GetCurrentModuleHandle() { + HMODULE module; + if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | + GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + L"", &module)) { + return nullptr; + } - _Everything_List2 = 0; - } + return module; } -static BOOL _Everything_IsValidResultIndex(DWORD dwIndex) -{ - if (dwIndex < 0) - { - return FALSE; - } - - if (dwIndex >= _Everything_GetNumResults()) - { - return FALSE; - } +DWORD WINAPI Everything4Wh_Thread(void* parameter) { + constexpr WCHAR kClassName[] = "WindhawkEverythingReceiver_" WH_MOD_ID; - return TRUE; -} + HANDLE hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr); + if (!hEvent) { + Wh_Log(L"CreateEvent failed"); + return 1; + } -// assumes _Everything_List2 and dwIndex are valid. -static void* _Everything_GetRequestData(DWORD dwIndex, DWORD dwRequestType) -{ - char* p; - EVERYTHING_IPC_ITEM2* items; + WNDCLASSEXW wc = { + .cbSize = sizeof(WNDCLASSEXW), + .lpfnWndProc = Everything4Wh_ReceiverWndProc, + .hInstance = GetCurrentModuleHandle(), + .lpszClassName = kClassName, + }; - items = (EVERYTHING_IPC_ITEM2*)(_Everything_List2 + 1); + if (!RegisterClassEx(&wc)) { + Wh_Log(L"RegisterClassEx failed"); + CloseHandle(hEvent); + return 1; + } - p = ((char*)_Everything_List2) + items[dwIndex].data_offset; + HWND hReceiverWnd = + CreateWindowEx(0, wc.lpszClassName, nullptr, 0, 0, 0, 0, 0, + HWND_MESSAGE, nullptr, wc.hInstance, nullptr); + if (!hReceiverWnd) { + Wh_Log(L"CreateWindowEx failed"); + UnregisterClass(wc.lpszClassName, wc.hInstance); + CloseHandle(hEvent); + return 1; + } - if (_Everything_List2->request_flags & EVERYTHING_REQUEST_SIZE) - { - if (dwRequestType == EVERYTHING_REQUEST_SIZE) - { - return p; - } + g_gsReply.hEvent = hEvent; + g_gsReceiverWnd = hReceiverWnd; - p += sizeof(LARGE_INTEGER); - } + Wh_Log(L"hReceiverWnd=%08X", (DWORD)(ULONG_PTR)hReceiverWnd); - return NULL; -} + BOOL bRet; + MSG msg; + while ((bRet = GetMessage(&msg, nullptr, 0, 0)) != 0) { + if (bRet == -1) { + msg.wParam = 0; + break; + } -static void _Everything_ChangeWindowMessageFilter(HWND hwnd) -{ - if (!_Everything_GotChangeWindowMessageFilterEx) - { - // allow the everything window to send a reply. - _Everything_user32_hdll = LoadLibraryW(L"user32.dll"); - - if (_Everything_user32_hdll) - { - _Everything_pChangeWindowMessageFilterEx = (BOOL(WINAPI*)(HWND hWnd, UINT message, DWORD action, _EVERYTHING_PCHANGEFILTERSTRUCT pChangeFilterStruct))GetProcAddress((HMODULE)_Everything_user32_hdll, "ChangeWindowMessageFilterEx"); - } - - _Everything_GotChangeWindowMessageFilterEx = 1; - } - - if (_Everything_GotChangeWindowMessageFilterEx) - { - if (_Everything_pChangeWindowMessageFilterEx) - { - _Everything_pChangeWindowMessageFilterEx(hwnd, WM_COPYDATA, _EVERYTHING_MSGFLT_ALLOW, 0); - } - } -} + if (msg.hwnd == nullptr && msg.message == WM_APP) { + g_gsReceiverWnd = nullptr; + DestroyWindow(hReceiverWnd); + PostQuitMessage(0); + continue; + } -static BOOL _Everything_GetResultRequestData(DWORD dwIndex, DWORD dwRequestType, void* data, int size) -{ - BOOL ret; - - _Everything_Lock(); - - if (_Everything_List2) - { - if (_Everything_IsValidResultIndex(dwIndex)) - { - void* request_data; - - request_data = _Everything_GetRequestData(dwIndex, dwRequestType); - if (request_data) - { - CopyMemory(data, request_data, size); - - ret = TRUE; - } - else - { - _Everything_LastError = EVERYTHING_ERROR_INVALIDREQUEST; - - ret = FALSE; - } - } - else - { - _Everything_LastError = EVERYTHING_ERROR_INVALIDINDEX; - - ret = FALSE; - } - } - else - { - _Everything_LastError = EVERYTHING_ERROR_INVALIDCALL; - - ret = FALSE; - } - - _Everything_Unlock(); - - return ret; -} + TranslateMessage(&msg); + DispatchMessage(&msg); + } -BOOL EVERYTHINGAPI Everything_GetResultSize(DWORD dwIndex, LARGE_INTEGER* lpSize) -{ - return _Everything_GetResultRequestData(dwIndex, EVERYTHING_REQUEST_SIZE, lpSize, sizeof(LARGE_INTEGER)); + UnregisterClass(wc.lpszClassName, wc.hInstance); + g_gsReply.hEvent = nullptr; + CloseHandle(hEvent); + return 0; } -// clang-format on -#pragma endregion // everything_sdk - -constexpr WCHAR kEverything4Wh_className[] = "EVERYTHING_WINDHAWK_" WH_MOD_ID; - LPCITEMIDLIST PIDLNext(LPCITEMIDLIST pidl) { return reinterpret_cast(reinterpret_cast(pidl) + pidl->mkid.cb); @@ -1063,175 +496,10 @@ std::vector PIDLToVector(const ITEMIDLIST* pidl) { return std::vector(ptr, ptr + size); } -thread_local winrt::com_ptr g_everything4Wh_cacheShellFolder; +thread_local winrt::com_ptr g_cacheShellFolder; thread_local std::map, std::optional> - g_everything4Wh_cache; -thread_local DWORD g_everything4Wh_cacheUsedTickCount; - -// custom window proc -LRESULT WINAPI Everything4Wh_window_proc(HWND hwnd, - UINT msg, - WPARAM wParam, - LPARAM lParam) { - switch (msg) { - case WM_COPYDATA: { - COPYDATASTRUCT* cds = (COPYDATASTRUCT*)lParam; - - switch (cds->dwData) { - case _EVERYTHING_COPYDATA_QUERYREPLY: - - if (_Everything_QueryVersion == 2) { - _Everything_FreeLists(); - - _Everything_List2 = - (EVERYTHING_IPC_LIST2*)_Everything_Alloc( - cds->cbData); - - if (_Everything_List2) { - CopyMemory(_Everything_List2, cds->lpData, - cds->cbData); - } else { - _Everything_LastError = EVERYTHING_ERROR_MEMORY; - } - - PostMessage(hwnd, WM_APP, 0, 0); - - return TRUE; - } else if (_Everything_QueryVersion == 1) { - _Everything_FreeLists(); - - _Everything_List = _Everything_Alloc(cds->cbData); - - if (_Everything_List) { - CopyMemory(_Everything_List, cds->lpData, - cds->cbData); - } else { - _Everything_LastError = EVERYTHING_ERROR_MEMORY; - } - - PostMessage(hwnd, WM_APP, 0, 0); - - return TRUE; - } - - break; - } - - break; - } - } - - return DefWindowProc(hwnd, msg, wParam, lParam); -} - -bool Everything4Wh_InitForQuery() { - if (_Everything_ReplyWindow) { - return true; - } - - WNDCLASSEX wcex; - - ZeroMemory(&wcex, sizeof(WNDCLASSEX)); - wcex.cbSize = sizeof(WNDCLASSEX); - - if (!GetClassInfoEx(GetModuleHandle(0), kEverything4Wh_className, &wcex)) { - ZeroMemory(&wcex, sizeof(WNDCLASSEX)); - wcex.cbSize = sizeof(WNDCLASSEX); - wcex.hInstance = GetModuleHandle(0); - wcex.lpfnWndProc = Everything4Wh_window_proc; - wcex.lpszClassName = kEverything4Wh_className; - - if (!RegisterClassEx(&wcex)) { - _Everything_LastError = EVERYTHING_ERROR_REGISTERCLASSEX; - return false; - } - } - - HWND hwnd = CreateWindow(kEverything4Wh_className, L"", 0, 0, 0, 0, 0, 0, 0, - GetModuleHandle(0), 0); - if (!hwnd) { - _Everything_LastError = EVERYTHING_ERROR_CREATEWINDOW; - return false; - } - - _Everything_ChangeWindowMessageFilter(hwnd); - - _Everything_ReplyWindow = hwnd; - _Everything_ReplyID = _EVERYTHING_COPYDATA_QUERYREPLY; - return true; -} - -bool Everything4Wh_QueryAndWait() { - HWND hwnd = _Everything_ReplyWindow; - MSG msg; - int ret; - bool succeeded = false; - - _Everything_IsUnicodeQuery = TRUE; - - if (_Everything_SendIPCQuery()) { - // message pump - loop: - - WaitMessage(); - - // update windows - while (PeekMessage(&msg, hwnd, 0, 0, 0)) { - ret = (DWORD)GetMessage(&msg, hwnd, 0, 0); - if (ret == -1 || !ret) { - goto exit; - } - if (msg.message == WM_APP) { - succeeded = true; - goto exit; - } - - // let windows handle it. - TranslateMessage(&msg); - DispatchMessage(&msg); - } - - goto loop; - } - -exit: - - // get result from window. - DestroyWindow(hwnd); - _Everything_ReplyWindow = nullptr; - - return succeeded; -} - -void Everything4Wh_Cleanup() { - UnregisterClass(kEverything4Wh_className, GetModuleHandle(0)); -} - -thread_local WCHAR - g_Everything4Wh_SearchBuffer[sizeof("path:wholefilename:\"\"") - 1 + - MAX_PATH]; - -bool Everything4Wh_GetFileSize(WCHAR path[MAX_PATH], LARGE_INTEGER* size) { - if (!Everything4Wh_InitForQuery()) { - return false; - } - - swprintf_s(g_Everything4Wh_SearchBuffer, L"path:wholefilename:\"%s\"", - path); - - _Everything_Search = g_Everything4Wh_SearchBuffer; - _Everything_IsUnicodeSearch = 1; - Everything_SetRequestFlags(EVERYTHING_REQUEST_SIZE); - Everything_SetMax(1); - - if (!Everything4Wh_QueryAndWait()) { - return false; - } - - bool succeeded = Everything_GetResultSize(0, size); - _Everything_FreeLists(); - return succeeded; -} + g_cacheShellFolderSizes; +thread_local DWORD g_cacheShellFolderLastUsedTickCount; constexpr GUID KStorage = {0xB725F130, 0x47EF, @@ -1400,14 +668,14 @@ HRESULT WINAPI CFSFolder__GetSize_Hook(void* pCFSFolder, return S_OK; } - if (shellFolder2 != g_everything4Wh_cacheShellFolder || - GetTickCount() - g_everything4Wh_cacheUsedTickCount > 1000) { - g_everything4Wh_cache.clear(); + if (shellFolder2 != g_cacheShellFolder || + GetTickCount() - g_cacheShellFolderLastUsedTickCount > 1000) { + g_cacheShellFolderSizes.clear(); } - g_everything4Wh_cacheShellFolder = shellFolder2; + g_cacheShellFolder = shellFolder2; - auto [cacheIt, cacheMissing] = g_everything4Wh_cache.try_emplace( + auto [cacheIt, cacheMissing] = g_cacheShellFolderSizes.try_emplace( PIDLToVector(itemidChild), std::nullopt); if (cacheMissing) { @@ -1420,11 +688,13 @@ HRESULT WINAPI CFSFolder__GetSize_Hook(void* pCFSFolder, CalculateFolderSizes::everything) { WCHAR path[MAX_PATH]; if (GetFolderPathFromIShellFolder(childFolder.get(), path)) { - LARGE_INTEGER size; - if (Everything4Wh_GetFileSize(path, &size)) { - cacheIt->second = size.QuadPart; + int64_t size; + unsigned result = Everything4Wh_GetFileSize(path, &size); + if (result == ES_QUERY_OK) { + cacheIt->second = size; } else { - Wh_Log(L"Failed to get size of %s", path); + Wh_Log(L"Failed to get size: %s for %s", + g_gsQueryStatus[result], path); } } else { Wh_Log(L"Failed to get path"); @@ -1436,7 +706,7 @@ HRESULT WINAPI CFSFolder__GetSize_Hook(void* pCFSFolder, Wh_Log(L"Using cached size"); } - g_everything4Wh_cacheUsedTickCount = GetTickCount(); + g_cacheShellFolderLastUsedTickCount = GetTickCount(); std::optional folderSize = cacheIt->second; if (folderSize) { @@ -1613,11 +883,21 @@ BOOL Wh_ModInit() { return TRUE; } +void Wh_ModAfterInit() { + if (g_settings.calculateFolderSizes == CalculateFolderSizes::everything) { + g_everything4Wh_Thread = + CreateThread(nullptr, 0, Everything4Wh_Thread, nullptr, 0, nullptr); + } +} + void Wh_ModUninit() { Wh_Log(L">"); - if (g_settings.calculateFolderSizes == CalculateFolderSizes::everything) { - Everything4Wh_Cleanup(); + if (g_everything4Wh_Thread) { + PostThreadMessage(GetThreadId(g_everything4Wh_Thread), WM_APP, 0, 0); + WaitForSingleObject(g_everything4Wh_Thread, INFINITE); + CloseHandle(g_everything4Wh_Thread); + g_everything4Wh_Thread = nullptr; } while (g_hookRefCount > 0) {