diff --git a/src/vscode-windhawk-ui/apps/vscode-windhawk-ui/src/app/app.css b/src/vscode-windhawk-ui/apps/vscode-windhawk-ui/src/app/app.css index 553639e..c9f1c17 100644 --- a/src/vscode-windhawk-ui/apps/vscode-windhawk-ui/src/app/app.css +++ b/src/vscode-windhawk-ui/apps/vscode-windhawk-ui/src/app/app.css @@ -313,7 +313,8 @@ mark { content: ''; } .anticon { - display: inline-block; + display: inline-flex; + align-items: center; color: inherit; font-style: normal; line-height: 0; @@ -5775,7 +5776,7 @@ a.ant-btn-sm { margin-left: 0; } .ant-picker-status-error.ant-picker, -.ant-picker-status-error.ant-picker:not([disabled]):hover { +.ant-picker-status-error.ant-picker:not(.ant-picker-disabled):hover { background-color: transparent; border-color: #a61d24; } @@ -5790,7 +5791,7 @@ a.ant-btn-sm { background: #b33b3d; } .ant-picker-status-warning.ant-picker, -.ant-picker-status-warning.ant-picker:not([disabled]):hover { +.ant-picker-status-warning.ant-picker:not(.ant-picker-disabled):hover { background-color: transparent; border-color: #d89614; } @@ -6149,7 +6150,6 @@ textarea.ant-picker-input > input { .ant-picker-range-arrow { position: absolute; z-index: 1; - display: none; width: 11.3137085px; height: 11.3137085px; margin-left: 16.5px; @@ -14038,6 +14038,7 @@ textarea.ant-input-affix-wrapper { padding: 0; } .ant-input-affix-wrapper::before { + display: inline-block; width: 0; visibility: hidden; content: '\a0'; @@ -14472,9 +14473,8 @@ textarea.ant-input { vertical-align: top; border-radius: 0; } -.ant-input-group.ant-input-group-compact > .ant-input-affix-wrapper { - display: inline-flex; -} +.ant-input-group.ant-input-group-compact > .ant-input-affix-wrapper, +.ant-input-group.ant-input-group-compact > .ant-input-number-affix-wrapper, .ant-input-group.ant-input-group-compact > .ant-picker-range { display: inline-flex; } @@ -14950,6 +14950,7 @@ textarea.ant-input-number-affix-wrapper { padding: 0; } .ant-input-number-affix-wrapper::before { + display: inline-block; width: 0; visibility: hidden; content: '\a0'; @@ -15348,9 +15349,8 @@ textarea.ant-input-number { vertical-align: top; border-radius: 0; } -.ant-input-number-group.ant-input-number-group-compact > .ant-input-number-affix-wrapper { - display: inline-flex; -} +.ant-input-number-group.ant-input-number-group-compact > .ant-input-number-affix-wrapper, +.ant-input-number-group.ant-input-number-group-compact > .ant-input-number-number-affix-wrapper, .ant-input-number-group.ant-input-number-group-compact > .ant-picker-range { display: inline-flex; } @@ -15508,7 +15508,8 @@ textarea.ant-input-number { } .ant-input-number-handler-up-inner, .ant-input-number-handler-down-inner { - display: inline-block; + display: inline-flex; + align-items: center; color: inherit; font-style: normal; line-height: 0; @@ -18880,6 +18881,7 @@ textarea.ant-pagination-options-quick-jumper input { top: 0; left: 0; z-index: 1030; + max-width: 100vw; font-weight: normal; white-space: normal; text-align: left; @@ -18941,6 +18943,8 @@ textarea.ant-pagination-options-quick-jumper input { border-bottom: 1px solid #303030; } .ant-popover-inner-content { + width: max-content; + max-width: 100%; padding: 12px 16px; color: rgba(255, 255, 255, 0.85); } @@ -20012,7 +20016,7 @@ span.ant-radio + * { .ant-select-single .ant-select-selector .ant-select-selection-placeholder { padding: 0; line-height: 30px; - transition: all 0.3s; + transition: all 0.3s, visibility 0s; } .ant-select-single .ant-select-selector .ant-select-selection-item { position: relative; @@ -20140,6 +20144,7 @@ span.ant-radio + * { width: 0; margin: 2px 0; line-height: 24px; + visibility: hidden; content: '\a0'; } .ant-select-multiple.ant-select-show-arrow .ant-select-selector, @@ -20188,7 +20193,8 @@ span.ant-radio + * { text-rendering: optimizelegibility; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; - display: inline-block; + display: inline-flex; + align-items: center; color: rgba(255, 255, 255, 0.45); font-weight: bold; font-size: 10px; @@ -20286,10 +20292,6 @@ span.ant-radio + * { .ant-select-multiple.ant-select-sm .ant-select-selection-search { margin-inline-start: 3px; } -.ant-select-multiple.ant-select-lg .ant-select-selection-item { - height: 32px; - line-height: 32px; -} .ant-select-disabled .ant-select-selection-item-remove { display: none; } @@ -20428,7 +20430,7 @@ span.ant-radio + * { } } .ant-select-arrow { - display: inline-block; + display: inline-flex; color: inherit; font-style: normal; line-height: 0; @@ -21247,6 +21249,7 @@ span.ant-radio + * { position: absolute; display: none; color: #177ddc; + font-size: 0; text-align: center; vertical-align: middle; opacity: 0; @@ -21281,6 +21284,7 @@ span.ant-radio + * { top: 50%; width: 100%; padding-top: 5px; + font-size: 14px; text-shadow: 0 1px 2px #141414; } .ant-spin-nested-loading > div > .ant-spin.ant-spin-show-text .ant-spin-dot { @@ -26874,6 +26878,10 @@ body.windhawk-no-pointer-events .windhawk-popup-content-no-select { color: #fff; background-color: #177ddc; } +.rc-virtual-list-scrollbar-thumb { + border-radius: 0 !important; + background: rgba(121, 121, 121, 0.4) !important; +} .ant-dropdown-menu-item-active:not(.ant-dropdown-menu-item-disabled) { color: #fff; background-color: #1f1f1f; diff --git a/src/vscode-windhawk-ui/apps/vscode-windhawk-ui/src/app/app.less b/src/vscode-windhawk-ui/apps/vscode-windhawk-ui/src/app/app.less index 6c4a9d8..b39cc52 100644 --- a/src/vscode-windhawk-ui/apps/vscode-windhawk-ui/src/app/app.less +++ b/src/vscode-windhawk-ui/apps/vscode-windhawk-ui/src/app/app.less @@ -69,6 +69,11 @@ body.windhawk-no-pointer-events { } } +.rc-virtual-list-scrollbar-thumb { + border-radius: 0 !important; + background: rgba(121, 121, 121, 0.4) !important; +} + // An ugly patch providing dark theme for dropdown menu elements. .@{dropdown-prefix-cls} { &-menu { diff --git a/src/vscode-windhawk-ui/apps/vscode-windhawk-ui/src/app/components/ReactMarkdownCustom.tsx b/src/vscode-windhawk-ui/apps/vscode-windhawk-ui/src/app/components/ReactMarkdownCustom.tsx index 6f5cca4..da54022 100644 --- a/src/vscode-windhawk-ui/apps/vscode-windhawk-ui/src/app/components/ReactMarkdownCustom.tsx +++ b/src/vscode-windhawk-ui/apps/vscode-windhawk-ui/src/app/components/ReactMarkdownCustom.tsx @@ -7,7 +7,9 @@ const ReactMarkdownWithStyle = styled(ReactMarkdown)` // Word-wrap long lines. overflow-wrap: break-word; + // Table style. // https://github.com/micromark/micromark-extension-gfm-table#css + table { border-spacing: 0; border-collapse: collapse; @@ -19,27 +21,10 @@ const ReactMarkdownWithStyle = styled(ReactMarkdown)` overflow: auto; } - tr { - background-color: var(--color-canvas-default); - border-top: 1px solid var(--color-border-muted); - } - - tr:nth-child(2n) { - background-color: var(--color-canvas-subtle); - } - td, th { padding: 6px 13px; - border: 1px solid var(--color-border-default); - } - - th { - font-weight: 600; - } - - table img { - background-color: transparent; + border: 1px solid #434343; } `; diff --git a/src/vscode-windhawk-ui/apps/vscode-windhawk-ui/src/app/panel/mockData.ts b/src/vscode-windhawk-ui/apps/vscode-windhawk-ui/src/app/panel/mockData.ts index a28d5c4..6342e83 100644 --- a/src/vscode-windhawk-ui/apps/vscode-windhawk-ui/src/app/panel/mockData.ts +++ b/src/vscode-windhawk-ui/apps/vscode-windhawk-ui/src/app/panel/mockData.ts @@ -149,7 +149,15 @@ export const mockInstalledModSourceData = !useMockData : { source: '// Mock local source...\n', metadata: mockModMetadata, - readme: '# Mock readme...\n', + readme: `# Mock readme... + +| Month | Savings | +| -------- | ------- | +| January | $250 | +| February | $80 | +| March | $420 | + +More text...`, initialSettings: [ { key: 'mock-setting', @@ -157,6 +165,23 @@ export const mockInstalledModSourceData = !useMockData name: 'Mock Setting Name', description: 'Mock setting description', }, + { + key: 'mock-setting-dropdown', + value: 'a', + name: 'Mock Setting Dropdown Name', + description: 'Mock setting dropdown description', + options: [ + { a: 'a option' }, + { b: 'b option' }, + { c: 'c option' }, + { d: 'd option' }, + { e: 'e option' }, + { f: 'f option' }, + { g: 'g option' }, + { h: 'h option' }, + { i: 'i option' }, + ], + }, { key: 'mock-setting-array', value: ['a', 'b', 'c'], @@ -186,6 +211,7 @@ export const mockModSettings = !useMockData ? null : { 'mock-setting': 'mock-setting-value', + 'mock-setting-dropdown': 'mock-setting-value', 'mock-setting-array[0]': 'a', 'mock-setting-array[1]': 'b', 'mock-setting-array[2]': 'c', diff --git a/src/vscode-windhawk-ui/apps/vscode-windhawk-ui/src/main.css b/src/vscode-windhawk-ui/apps/vscode-windhawk-ui/src/main.css index 6163e93..d036acf 100644 --- a/src/vscode-windhawk-ui/apps/vscode-windhawk-ui/src/main.css +++ b/src/vscode-windhawk-ui/apps/vscode-windhawk-ui/src/main.css @@ -8,23 +8,3 @@ body { them, we aren't using them anyway */ overflow: hidden; } - -/* https://github.com/micromark/micromark-extension-gfm-table#css */ - -/* Light theme. */ -:root { - --color-canvas-default: #ffffff; - --color-canvas-subtle: #f6f8fa; - --color-border-default: #d0d7de; - --color-border-muted: hsla(210, 18%, 87%, 1); -} - -/* Dark theme. */ -@media (prefers-color-scheme: dark) { - :root { - --color-canvas-default: #141414; - --color-canvas-subtle: #141414; - --color-border-default: #303030; - --color-border-muted: #21262d; - } -} diff --git a/src/vscode-windhawk-ui/package-lock.json b/src/vscode-windhawk-ui/package-lock.json index 0f849f7..bc9c2f4 100644 --- a/src/vscode-windhawk-ui/package-lock.json +++ b/src/vscode-windhawk-ui/package-lock.json @@ -1,12 +1,12 @@ { "name": "vscode-windhawk-ui-nx", - "version": "1.5.0", + "version": "1.5.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "vscode-windhawk-ui-nx", - "version": "1.5.0", + "version": "1.5.1", "dependencies": { "@fortawesome/fontawesome-svg-core": "^1.2.36", "@fortawesome/free-brands-svg-icons": "^5.15.4", diff --git a/src/vscode-windhawk-ui/package.json b/src/vscode-windhawk-ui/package.json index 99ee557..f74bdcc 100644 --- a/src/vscode-windhawk-ui/package.json +++ b/src/vscode-windhawk-ui/package.json @@ -1,6 +1,6 @@ { "name": "vscode-windhawk-ui-nx", - "version": "1.5.0", + "version": "1.5.1", "scripts": { "start": "nx serve", "watch": "nx build --configuration=development --watch", diff --git a/src/vscode-windhawk/package-lock.json b/src/vscode-windhawk/package-lock.json index f31fb39..3f5b80e 100644 --- a/src/vscode-windhawk/package-lock.json +++ b/src/vscode-windhawk/package-lock.json @@ -1,12 +1,12 @@ { "name": "windhawk", - "version": "1.5.0", + "version": "1.5.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "windhawk", - "version": "1.5.0", + "version": "1.5.1", "dependencies": { "fs-ext": "^2.0.0", "ini-win": "^3.0.3", diff --git a/src/vscode-windhawk/package.json b/src/vscode-windhawk/package.json index 7a8df50..c03592d 100644 --- a/src/vscode-windhawk/package.json +++ b/src/vscode-windhawk/package.json @@ -2,7 +2,7 @@ "name": "windhawk", "displayName": "Windhawk", "description": "Part of the Windhawk Windows customization tool", - "version": "1.5.0", + "version": "1.5.1", "icon": "assets/main-icon.png", "publisher": "m417z", "engines": { diff --git a/src/windhawk/app/functions.cpp b/src/windhawk/app/functions.cpp index 5b8b16a..209deae 100644 --- a/src/windhawk/app/functions.cpp +++ b/src/windhawk/app/functions.cpp @@ -233,6 +233,27 @@ std::vector SplitStringToViews(std::wstring_view s, return std::vector(view.begin(), view.end()); } +// https://stackoverflow.com/a/29752943 +std::wstring ReplaceAll(std::wstring_view source, + std::wstring_view from, + std::wstring_view to) { + std::wstring newString; + + size_t lastPos = 0; + size_t findPos; + + while ((findPos = source.find(from, lastPos)) != source.npos) { + newString.append(source, lastPos, findPos - lastPos); + newString += to; + lastPos = findPos + from.length(); + } + + // Care for the rest after last occurrence. + newString += source.substr(lastPos); + + return newString; +} + UINT GetDpiForWindowWithFallback(HWND hWnd) { using GetDpiForWindow_t = UINT(WINAPI*)(HWND hwnd); static GetDpiForWindow_t pGetDpiForWindow = []() { diff --git a/src/windhawk/app/functions.h b/src/windhawk/app/functions.h index d302180..69f79a4 100644 --- a/src/windhawk/app/functions.h +++ b/src/windhawk/app/functions.h @@ -11,6 +11,9 @@ PCWSTR LoadStrFromRsrc(UINT uStrId); std::vector SplitString(std::wstring_view s, WCHAR delim); std::vector SplitStringToViews(std::wstring_view s, WCHAR delim); +std::wstring ReplaceAll(std::wstring_view source, + std::wstring_view from, + std::wstring_view to); UINT GetDpiForWindowWithFallback(HWND hWnd); int GetSystemMetricsForDpiWithFallback(int nIndex, UINT dpi); int GetSystemMetricsForWindow(HWND hWnd, int nIndex); diff --git a/src/windhawk/app/storage_manager.cpp b/src/windhawk/app/storage_manager.cpp index 492c346..637cf61 100644 --- a/src/windhawk/app/storage_manager.cpp +++ b/src/windhawk/app/storage_manager.cpp @@ -1,10 +1,11 @@ #include "stdafx.h" +#include "functions.h " #include "storage_manager.h" namespace { -std::filesystem::path pathFromStorage( +std::filesystem::path PathFromStorage( const PortableSettings& storage, PCWSTR valueName, const std::filesystem::path& baseFolderPath) { @@ -17,27 +18,10 @@ std::filesystem::path pathFromStorage( SYSTEM_INFO siSystemInfo; GetNativeSystemInfo(&siSystemInfo); if (siSystemInfo.wProcessorArchitecture != PROCESSOR_ARCHITECTURE_INTEL) { - // Replace %ProgramFiles% with %ProgramW6432% to get the native Program - // Files path regardless of the current process architecture. - constexpr WCHAR kEnvVar[] = L"%ProgramFiles%"; - constexpr size_t kEnvVarLength = ARRAYSIZE(kEnvVar) - 1; - - if (storedPath.length() >= kEnvVarLength) { - constexpr WCHAR kEnvVarReplacement[] = L"%ProgramW6432%"; - constexpr size_t kEnvVarReplacementLength = - ARRAYSIZE(kEnvVarReplacement) - 1; - - for (size_t i = 0; i < storedPath.length() - kEnvVarLength + 1;) { - if (_wcsnicmp(storedPath.c_str() + i, kEnvVar, kEnvVarLength) == - 0) { - storedPath.replace(i, kEnvVarLength, kEnvVarReplacement, - kEnvVarReplacementLength); - i += kEnvVarReplacementLength; - } else { - i++; - } - } - } + // Get the native Program Files path regardless of the current + // process architecture. + storedPath = Functions::ReplaceAll(storedPath, L"%ProgramFiles%", + L"%ProgramW6432%"); } #endif // _WIN64 @@ -133,10 +117,10 @@ StorageManager::StorageManager() { auto storage = IniFileSettings(iniFilePath.c_str(), L"Storage", false); - enginePath = pathFromStorage(storage, L"EnginePath", folderPath); - uiPath = pathFromStorage(storage, L"UIPath", folderPath); - compilerPath = pathFromStorage(storage, L"CompilerPath", folderPath); - appDataPath = pathFromStorage(storage, L"AppDataPath", folderPath); + enginePath = PathFromStorage(storage, L"EnginePath", folderPath); + uiPath = PathFromStorage(storage, L"UIPath", folderPath); + compilerPath = PathFromStorage(storage, L"CompilerPath", folderPath); + appDataPath = PathFromStorage(storage, L"AppDataPath", folderPath); if (!std::filesystem::is_directory(appDataPath)) { std::error_code ec; diff --git a/src/windhawk/app/task_manager_dlg.cpp b/src/windhawk/app/task_manager_dlg.cpp index b3bc3b3..69d96f2 100644 --- a/src/windhawk/app/task_manager_dlg.cpp +++ b/src/windhawk/app/task_manager_dlg.cpp @@ -19,6 +19,7 @@ struct ListItemData { DWORD processId = 0; ULONGLONG creationTime = 0; bool isFrozen = false; + wil::unique_process_handle executionRequiredRequestProcess; wil::unique_handle executionRequiredRequest; }; @@ -541,15 +542,21 @@ void CTaskManagerDlg::AddItemToList(int itemIndex, m_taskListSort.AddItem(itemIndex, 2, std::to_wstring(processId).c_str()); m_taskListSort.AddItem(itemIndex, 3, LocalizeStatus(status).c_str()); + // The process handle must be kept alive while the request is active. + // Otherwise, a BSOD might occur in Windows 10. + wil::unique_process_handle executionRequiredRequestProcess; wil::unique_handle executionRequiredRequest; if (Functions::IsWindowsVersionOrGreaterWithBuildNumber(10, 0, 0)) { - wil::unique_process_handle process( + executionRequiredRequestProcess.reset( OpenProcess(PROCESS_SET_LIMITED_INFORMATION, FALSE, processId)); - if (process) { + if (executionRequiredRequestProcess) { HRESULT hr = Functions::CreateExecutionRequiredRequest( - process.get(), executionRequiredRequest.put()); - if (FAILED(hr)) { + executionRequiredRequestProcess.get(), + executionRequiredRequest.put()); + if (FAILED(hr) || !executionRequiredRequest) { LOG(L"Failed to create execution required request: %08X", hr); + executionRequiredRequest.reset(); + executionRequiredRequestProcess.reset(); } } } @@ -560,6 +567,8 @@ void CTaskManagerDlg::AddItemToList(int itemIndex, .processId = processId, .creationTime = wil::filetime::to_int64(creationTime), .isFrozen = isFrozen, + .executionRequiredRequestProcess = + std::move(executionRequiredRequestProcess), .executionRequiredRequest = std::move(executionRequiredRequest), }; m_taskListSort.SetItemData(itemIndex, diff --git a/src/windhawk/engine/functions.cpp b/src/windhawk/engine/functions.cpp index 3a484c9..23617be 100644 --- a/src/windhawk/engine/functions.cpp +++ b/src/windhawk/engine/functions.cpp @@ -99,6 +99,27 @@ std::vector SplitStringToViews(std::wstring_view s, return std::vector(view.begin(), view.end()); } +// https://stackoverflow.com/a/29752943 +std::wstring ReplaceAll(std::wstring_view source, + std::wstring_view from, + std::wstring_view to) { + std::wstring newString; + + size_t lastPos = 0; + size_t findPos; + + while ((findPos = source.find(from, lastPos)) != source.npos) { + newString.append(source, lastPos, findPos - lastPos); + newString += to; + lastPos = findPos + from.length(); + } + + // Care for the rest after last occurrence. + newString += source.substr(lastPos); + + return newString; +} + bool DoesPathMatchPattern(std::wstring_view path, std::wstring_view pattern, bool explicitOnly) { @@ -142,30 +163,10 @@ bool DoesPathMatchPattern(std::wstring_view path, GetNativeSystemInfo(&siSystemInfo); if (siSystemInfo.wProcessorArchitecture != PROCESSOR_ARCHITECTURE_INTEL) { - // Replace %ProgramFiles% with %ProgramW6432% to get the native - // Program Files path regardless of the current process - // architecture. - constexpr WCHAR kEnvVar[] = L"%ProgramFiles%"; - constexpr size_t kEnvVarLength = ARRAYSIZE(kEnvVar) - 1; - - if (patternPart.length() >= kEnvVarLength) { - constexpr WCHAR kEnvVarReplacement[] = L"%ProgramW6432%"; - constexpr size_t kEnvVarReplacementLength = - ARRAYSIZE(kEnvVarReplacement) - 1; - - for (size_t i = 0; - i < patternPart.length() - kEnvVarLength + 1;) { - if (_wcsnicmp(patternPart.c_str() + i, kEnvVar, - kEnvVarLength) == 0) { - patternPart.replace(i, kEnvVarLength, - kEnvVarReplacement, - kEnvVarReplacementLength); - i += kEnvVarReplacementLength; - } else { - i++; - } - } - } + // Get the native Program Files path regardless of the current + // process architecture. + patternPart = + ReplaceAll(patternPart, L"%ProgramFiles%", L"%ProgramW6432%"); } #endif // _WIN64 diff --git a/src/windhawk/engine/functions.h b/src/windhawk/engine/functions.h index b61d5c0..803af24 100644 --- a/src/windhawk/engine/functions.h +++ b/src/windhawk/engine/functions.h @@ -6,6 +6,9 @@ bool wcsmatch(PCWSTR pat, size_t plen, PCWSTR str, size_t slen); std::vector SplitString(std::wstring_view s, WCHAR delim); std::vector SplitStringToViews(std::wstring_view s, WCHAR delim); +std::wstring ReplaceAll(std::wstring_view source, + std::wstring_view from, + std::wstring_view to); bool DoesPathMatchPattern(std::wstring_view path, std::wstring_view pattern, bool explicitOnly = false); diff --git a/src/windhawk/engine/mod.cpp b/src/windhawk/engine/mod.cpp index c34dd4a..b75f88d 100644 --- a/src/windhawk/engine/mod.cpp +++ b/src/windhawk/engine/mod.cpp @@ -1307,6 +1307,10 @@ BOOL LoadedMod::HookSymbols(HMODULE module, const WH_HOOK_SYMBOLS_OPTIONS* options) { auto modDebugLoggingScope = MOD_DEBUG_LOGGING_SCOPE(); + if (symbolHooksCount == 0) { + return TRUE; + } + try { constexpr WCHAR kCacheVer = L'1'; constexpr WCHAR kCacheSep = L'#'; @@ -1474,79 +1478,76 @@ BOOL LoadedMod::HookSymbols(HMODULE module, newSystemCacheStr += L'-'; newSystemCacheStr += imageSize; - auto resolveSymbolsFromCache = - [&kCacheVer, symbolHooks, symbolHooksCount, &symbolResolved, - &onSymbolResolved, &newSystemCacheStr, - module](std::vector& cacheParts) { - // In the new format, cacheParts[1] and cacheParts[2] are - // ignored and act like comments. - if (cacheParts.size() < 3 || - cacheParts[0] != std::wstring_view(&kCacheVer, 1)) { - return false; + auto resolveSymbolsFromCache = [&kCacheVer, symbolHooks, + symbolHooksCount, &symbolResolved, + &onSymbolResolved, &newSystemCacheStr, + module](std::vector& + cacheParts) { + // In the new format, cacheParts[1] and cacheParts[2] are + // ignored and act like comments. + if (cacheParts.size() < 3 || + cacheParts[0] != std::wstring_view(&kCacheVer, 1)) { + return false; + } + + for (size_t i = 3; i + 1 < cacheParts.size(); i += 2) { + const auto& symbol = cacheParts[i]; + const auto& address = cacheParts[i + 1]; + if (address.length() == 0) { + continue; } - for (size_t i = 3; i + 1 < cacheParts.size(); i += 2) { - const auto& symbol = cacheParts[i]; - const auto& address = cacheParts[i + 1]; - if (address.length() == 0) { - continue; - } + void* addressPtr = + (void*)(std::stoull(std::wstring(address), nullptr, 10) + + (ULONG_PTR)module); - void* addressPtr = - (void*)(std::stoull(std::wstring(address), nullptr, - 10) + - (ULONG_PTR)module); + onSymbolResolved(symbol, addressPtr); + } - onSymbolResolved(symbol, addressPtr); + for (size_t i = 0; i < symbolHooksCount; i++) { + if (symbolResolved[i] || !symbolHooks[i].optional) { + continue; } - for (size_t i = 0; i < symbolHooksCount; i++) { - if (symbolResolved[i] || !symbolHooks[i].optional) { + size_t noAddressMatchCount = 0; + for (size_t j = 3; j + 1 < cacheParts.size(); j += 2) { + const auto& symbol = cacheParts[j]; + const auto& address = cacheParts[j + 1]; + if (address.length() != 0) { continue; } - size_t noAddressMatchCount = 0; - for (size_t j = 3; j + 1 < cacheParts.size(); j += 2) { - const auto& symbol = cacheParts[j]; - const auto& address = cacheParts[j + 1]; - if (address.length() != 0) { - continue; - } - - for (size_t s = 0; s < symbolHooks[i].symbolsCount; - s++) { - auto hookSymbol = std::wstring_view( - symbolHooks[i].symbols[s].string, - symbolHooks[i].symbols[s].length); - if (hookSymbol == symbol) { - noAddressMatchCount++; - break; - } + for (size_t s = 0; s < symbolHooks[i].symbolsCount; s++) { + auto hookSymbol = + std::wstring_view(symbolHooks[i].symbols[s].string, + symbolHooks[i].symbols[s].length); + if (hookSymbol == symbol) { + noAddressMatchCount++; + break; } } + } - if (noAddressMatchCount == symbolHooks[i].symbolsCount) { - VERBOSE( - L"Optional symbol %d doesn't exist (from cache)", + if (noAddressMatchCount == symbolHooks[i].symbolsCount) { + VERBOSE(L"Optional symbol %d doesn't exist (from cache)", i); - symbolResolved[i] = true; + symbolResolved[i] = true; - for (size_t s = 0; s < symbolHooks[i].symbolsCount; - s++) { - auto hookSymbol = std::wstring_view( - symbolHooks[i].symbols[s].string, - symbolHooks[i].symbols[s].length); - newSystemCacheStr += kCacheSep; - newSystemCacheStr += hookSymbol; - newSystemCacheStr += kCacheSep; - } + for (size_t s = 0; s < symbolHooks[i].symbolsCount; s++) { + auto hookSymbol = + std::wstring_view(symbolHooks[i].symbols[s].string, + symbolHooks[i].symbols[s].length); + newSystemCacheStr += kCacheSep; + newSystemCacheStr += hookSymbol; + newSystemCacheStr += kCacheSep; } } + } - return std::all_of(symbolResolved.begin(), symbolResolved.end(), - [](bool b) { return b; }); - }; + return std::all_of(symbolResolved.begin(), symbolResolved.end(), + [](bool b) { return b; }); + }; if (resolveSymbolsFromCache(cacheParts)) { return TRUE; @@ -1603,6 +1604,16 @@ BOOL LoadedMod::HookSymbols(HMODULE module, return TRUE; } } + } else { + VERBOSE(L"Couldn't contact the online cache server"); + VERBOSE( + L"In case of a firewall, you can open cmd manually and run " + L"the following command, then disable and re-enable the " + L"mod:"); + VERBOSE( + LR"(for /f "delims=" %%I in ('curl -f %s') do @reg add "HKLM\SOFTWARE\Windhawk\Engine\ModsWritable\%s\SymbolCache" /t REG_SZ /v %s /d "%%I" /f)", + onlineCacheUrl.c_str(), m_modName.c_str(), + cacheStrKey.c_str()); } VERBOSE(L"Couldn't resolve all symbols from online cache"); diff --git a/src/windhawk/engine/mods_api.h b/src/windhawk/engine/mods_api.h index 10545f2..c27d994 100644 --- a/src/windhawk/engine/mods_api.h +++ b/src/windhawk/engine/mods_api.h @@ -172,6 +172,7 @@ inline BOOL Wh_SetBinaryValue(PCWSTR valueName, /** * @brief Deletes a value from the mod's local storage. + * @since Windhawk v1.5 * @param valueName The name of the value to delete. * @return A boolean value indicating whether the function succeeded. */ diff --git a/src/windhawk/engine/storage_manager.cpp b/src/windhawk/engine/storage_manager.cpp index 8cbff2e..9a9bcfc 100644 --- a/src/windhawk/engine/storage_manager.cpp +++ b/src/windhawk/engine/storage_manager.cpp @@ -8,7 +8,7 @@ extern HINSTANCE g_hDllInst; namespace { -std::filesystem::path pathFromStorage( +std::filesystem::path PathFromStorage( const PortableSettings& storage, PCWSTR valueName, const std::filesystem::path& baseFolderPath) { @@ -21,27 +21,10 @@ std::filesystem::path pathFromStorage( SYSTEM_INFO siSystemInfo; GetNativeSystemInfo(&siSystemInfo); if (siSystemInfo.wProcessorArchitecture != PROCESSOR_ARCHITECTURE_INTEL) { - // Replace %ProgramFiles% with %ProgramW6432% to get the native Program - // Files path regardless of the current process architecture. - constexpr WCHAR kEnvVar[] = L"%ProgramFiles%"; - constexpr size_t kEnvVarLength = ARRAYSIZE(kEnvVar) - 1; - - if (storedPath.length() >= kEnvVarLength) { - constexpr WCHAR kEnvVarReplacement[] = L"%ProgramW6432%"; - constexpr size_t kEnvVarReplacementLength = - ARRAYSIZE(kEnvVarReplacement) - 1; - - for (size_t i = 0; i < storedPath.length() - kEnvVarLength + 1;) { - if (_wcsnicmp(storedPath.c_str() + i, kEnvVar, kEnvVarLength) == - 0) { - storedPath.replace(i, kEnvVarLength, kEnvVarReplacement, - kEnvVarReplacementLength); - i += kEnvVarReplacementLength; - } else { - i++; - } - } - } + // Get the native Program Files path regardless of the current process + // architecture. + storedPath = Functions::ReplaceAll(storedPath, L"%ProgramFiles%", + L"%ProgramW6432%"); } #endif // _WIN64 @@ -53,28 +36,12 @@ std::filesystem::path pathFromStorage( // replace it manually. if (GetEnvironmentVariable(L"ProgramData", nullptr, 0) == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) { - constexpr WCHAR kEnvVar[] = L"%ProgramData%"; - constexpr size_t kEnvVarLength = ARRAYSIZE(kEnvVar) - 1; - - if (expandedPath.length() >= kEnvVarLength) { - wil::unique_cotaskmem_string programData; - HRESULT hr = SHGetKnownFolderPath(FOLDERID_ProgramData, 0, nullptr, - &programData); - if (SUCCEEDED(hr)) { - size_t programDataLength = wcslen(programData.get()); - for (size_t i = 0; - i < expandedPath.length() - kEnvVarLength + 1;) { - if (_wcsnicmp(expandedPath.c_str() + i, kEnvVar, - kEnvVarLength) == 0) { - expandedPath.replace(i, kEnvVarLength, - programData.get(), - programDataLength); - i += programDataLength; - } else { - i++; - } - } - } + wil::unique_cotaskmem_string programData; + HRESULT hr = SHGetKnownFolderPath(FOLDERID_ProgramData, 0, nullptr, + &programData); + if (SUCCEEDED(hr)) { + expandedPath = Functions::ReplaceAll(expandedPath, L"%ProgramData%", + programData.get()); } } @@ -269,28 +236,19 @@ std::filesystem::path StorageManager::GetSymbolsPath() { } StorageManager::StorageManager() { - std::filesystem::path iterPath = + std::filesystem::path dllPath = wil::GetModuleFileName(g_hDllInst); - std::filesystem::path iniFilePath; - bool found = false; - - while (!found && iterPath.has_relative_path()) { - iterPath = iterPath.parent_path(); + std::filesystem::path iniFileFolder = dllPath.parent_path().parent_path(); + std::filesystem::path iniFilePath = iniFileFolder / L"engine.ini"; - iniFilePath = iterPath / L"engine.ini"; - found = std::filesystem::is_regular_file(iniFilePath); - } - - if (!found) { + if (!std::filesystem::is_regular_file(iniFilePath)) { throw std::runtime_error("engine.ini not found"); } - std::filesystem::path iniFileFolder = std::move(iterPath); - auto storage = IniFileSettings(iniFilePath.c_str(), L"Storage", false); - appDataPath = pathFromStorage(storage, L"AppDataPath", iniFileFolder); + appDataPath = PathFromStorage(storage, L"AppDataPath", iniFileFolder); if (!std::filesystem::is_directory(appDataPath)) { std::error_code ec; diff --git a/src/windhawk/shared/version.h b/src/windhawk/shared/version.h index db20d74..f51ba97 100644 --- a/src/windhawk/shared/version.h +++ b/src/windhawk/shared/version.h @@ -3,7 +3,7 @@ // Version #define VERSION_MAJOR 1 #define VERSION_MINOR 5 -#define VERSION_REVISION 0 +#define VERSION_REVISION 1 #define VERSION_BUILD 0 // etc.