From 1e62db06cc972d776be64ab8a0276348a8132b06 Mon Sep 17 00:00:00 2001 From: Roland Pihlakas Date: Tue, 3 Sep 2024 13:07:09 +0300 Subject: [PATCH 01/52] Updated the readme instruction for triggering early / system start of Windhawk service, which is necessary to ensure that classic theme is properly applied before any programs including Taskbar run. The mod code is same, only readme instruction has changed. Also added one more classic theme related mod to the recommended mods list in the readme. (#917) * Updated the readme instruction for triggering early / system start of Windhawk service, which is necessary to ensure that classic theme is properly applied before any programs including Taskbar run. The mod code is same, only readme instruction has changed. Also added one more classic theme related mod to the recommended mods list in the readme. * Added instruction for making a service dependent on Windhawk --- ...-enable-with-extended-compatibility.wh.cpp | 98 +++++++++++++++---- 1 file changed, 78 insertions(+), 20 deletions(-) diff --git a/mods/classic-theme-enable-with-extended-compatibility.wh.cpp b/mods/classic-theme-enable-with-extended-compatibility.wh.cpp index 919ca41e..fc78a6d4 100644 --- a/mods/classic-theme-enable-with-extended-compatibility.wh.cpp +++ b/mods/classic-theme-enable-with-extended-compatibility.wh.cpp @@ -1,8 +1,8 @@ // ==WindhawkMod== // @id classic-theme-enable-with-extended-compatibility // @name Classic Theme Enable with extended compatibility -// @description Enables classic theme. Supports RDP sessions and is compatible with early / system start of Windhawk. -// @version 1.2 +// @description Enables classic theme. Supports Remote Desktop sessions and is compatible with early / system start of Windhawk. +// @version 1.2.1 // @author Roland Pihlakas // @github https://github.com/levitation // @homepage https://www.simplify.ee/ @@ -27,9 +27,13 @@ /* # Classic Theme Enable with extended compatibility -Enables classic theme in Windows 10 and 11. **This mod version adds support for RDP sessions and compatibility with early / system start of Windhawk.** +Enables classic theme in Windows 10 and 11. **This mod version adds support for Remote Desktop sessions and compatibility with early / system start of Windhawk.** -[Click here](#6-how-to-configure-system-start-of-windhawk) if you want to see instructions for configuring system start of Windhawk. More technical details about the update can be found at the end of this document. +[Click here](#6-how-to-configure-system-start-of-windhawk) if you want to see instructions for configuring system start of Windhawk. + +If you already used Classic Theme Enable mod earlier then you do not need to read the ["Instructions for setting up the classic theme"](#instructions-for-setting-up-the-classic-theme) section below in order to use this mod. You can just replace the previous mod with the current version, and you will get Remote Desktop support and early / system start compatibility. + +More technical details about the update can be found at the end of this document. ## A screenshot @@ -45,7 +49,7 @@ There are few programs that have minor visual glitches. Various Windhawk mods de The most important problematic program is Taskbar. Fortunately there are a couple of programs and a number of Windhawk mods, each fixing a different problem of classic theme in Taskbar. All these are mentioned together with links in the instructions below. -The only totally problematic program uncompatible with classic theme is Task Manager. There exists alternative software which is able to handle Ctrl-Alt-Del as well, also mentioned below. +The only totally problematic program incompatible with classic theme is Task Manager. There exists alternative software which is able to handle Ctrl-Alt-Del as well, also mentioned below. Other programs have been running fine, I have been using classic theme for about a few months. Right now my systems look entirely classic (except for programs that have their own built-in themes). I am quite intensive user using many different programs. @@ -63,6 +67,8 @@ In summary, there are certain additional steps you need to do in order for your # Instructions for setting up the classic theme +If you already used Classic Theme Enable mod earlier then you do not need to read this section in order to use this mod. You can just replace the previous mod with the current version, and you will get Remote Desktop support and early / system start compatibility. + Note, upon first start, the mod affects only programs started after enabling the mod. Therefore your system might look weird here and there during performing the following installation steps and until you reboot. **After you have finished the configuration steps below, you may want to restart your system.** @@ -160,8 +166,9 @@ I recommend installing the following classic theme related mods in order to get * [Classic theme transparency fix](https://windhawk.net/mods/classic-theme-transparency-fix) * [Classic UWP Fix](https://windhawk.net/mods/classic-uwp-fix) * [Clientedge Everywhere](https://windhawk.net/mods/clientedge-in-apps) +* [Disable Immersive Context Menus](https://windhawk.net/mods/disable-immersive-context-menus) * [Disable rounded corners in Windows 11](https://windhawk.net/mods/disable-rounded-corners) (Win 11 only) -* [DWM Ghost Mods](https://windhawk.net/mods/dwm-ghost-mods) +* [DWM Ghost Mods](https://windhawk.net/mods/dwm-ghost-mods) - Under this mod's Settings enable `"Use classic theme"` option, then click `"Save settings"` button. * [Fix browsers for Windows Classic theme](https://windhawk.net/mods/classic-browser-fix) * [Fix Classic Theme Maximized Windows](https://windhawk.net/mods/classic-maximized-windows-fix) * [Win32 Tray Clock Experience](https://windhawk.net/mods/win32-tray-clock-experience) (Win 10 only) @@ -180,19 +187,64 @@ Starting Windhawk early improves the probability that classic theme is enabled b *In contrast, when Windhawk is activated normally then there is increased chance that the Taskbar process starts before classic theme is enabled - then the Taskbar would not have classic appearance and the user needs to restart the Taskbar manually later in order to apply classic theme to Taskbar.* -Steps to enable system start of Windhawk: + +### A safe method + +Steps to configure system start of Windhawk service: 1. Start Task Scheduler -2. Open "Task Scheduler Library" section -3. Find the row titled "WindhawkRunUITask", open it by double clicking -4. Go to Triggers -5. Click "New..." button -6. Select Begin the task: "At startup" -7. OK -8. Click "New..." button -9. Select Begin the task: "At log on" -10. OK -11. OK +2. Left click on "Task Scheduler Library" in the tree +3. Then right click on it +4. Choose "Create Task..." +5. Enter to "Name" field: "Start Windhawk service" (without quotes) +6. Under "Security options" choose "Run whether user is logged on or not" +7. Go to "Triggers" section +8. Press "New..." button +9. Under "Begin the task:" choose "At startup" +10. Press OK +11. Go to "Actions" section +12. Press "New..." button +13. Enter to "Program/script" field: "net start windhawk" (without quotes) +14. Press OK +15. A popup appears with a question "It appears as though arguments have been included ..." +16. Press "Yes" button +17. Press OK +18. Enter an username and password with admin permissions +19. Press OK + +If this is not yet sufficient to get classic theme enabled by the right time during system boot, then there is one more thing you can try: + +1. Open the Settings app +2. Search "Sign-in options" +3. Click "Sign-in options" in the results list. Click "Show all results" to see all search results if necessary. +4. Turn off the option which reads: + * In Windows 10: "Use my sign-in info to automatically finish setting up my device after an update or restart". + * In Windows 11: "Use my sign-in info to automatically finish setting up after an update". + + The title of this option may vary across operation system versions. +5. Each time you boot your computer and the password prompt appears, wait a little before you log in. + + +### A more effective, but somewhat less safe method + +If you are not happy with the results from the above instructions then there is a final method that should provide you the timely start of Windhawk 100% of time. This method guarantees that Windhawk will start even earlier. + +This method sets Plug and Play service dependent on Windhawk service. + +But there is a slight risk related to this method. If an antivirus removes Windhawk then your computer will not be able to detect hardware changes. It will probably still boot though and you will still be able to log in, but use this method at your own risk and responsibility. + +Import the following registry file: + +``` +Windows Registry Editor Version 5.00 +[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\PlugPlay] +; This is a REG_MULTI_SZ type with a value "Windhawk". This registry entry would not work with a REG_SZ type. +"DependOnService"=hex(7):57,00,69,00,6e,00,64,00,68,00,61,00,77,00,6b,00,00,00,00,00 +``` + +It does not matter whether Plug and Play service is configured to Manual start or Automatic start. It will start immediately at the system boot regardless. By default, it is configured as Manual start and you can keep it like that. + +If you ever uninstall Windhawk or your antivirus removes Windhawk, then remove/rename this `"DependOnService"` registry value from `HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\PlugPlay` @@ -223,7 +275,7 @@ If this is the first time you installed classic theme, then you may want to rebo # Troubleshooting -In case the window borders become too thick or other dimensions of window elements become different than you prefer, then look under to the registry key\ +In case the window borders become too thick or other dimensions of window elements become different than you prefer, then look under the registry key\ `HKEY_CURRENT_USER\Control Panel\Desktop\WindowMetrics` You may want to import again the registry file provided in chapter ["Needed registry changes"](#1-needed-registry-changes), point (3) "Import the following reg file:". @@ -252,10 +304,16 @@ The mod injects only into the process winlogon.exe. ## Detailed description of the compatibility updates -This mod has the following two capabilities built on top of previous classic theme mod [Enable Classic Theme by handle method by @Anixx](https://windhawk.net/mods/classic-theme-enable): Improved support for RDP sessions and code for handling early mod load, including during system start. +This mod has the following two capabilities built on top of previous classic theme mod [Enable Classic Theme by handle method by @Anixx](https://windhawk.net/mods/classic-theme-enable): Improved support for Remote Desktop sessions and code for handling early mod load, including during system start. 1) If Windhawk loads too early during system startup with the original mod, then the classic theme initialisation would fail. At the same time, starting Windhawk early (during system startup, not during user login) will improve the chances that the classic theme is applied as soon as possible and no programs need to be restarted later to get classic theme applied. In order for the classic theme enable to succeed in these conditions, the mod needs to check for conditions, and if needed, wait a bit in case the system is not yet ready to apply classic theme. -2) With the original mod the RDP sessions often disconnected during connecting. This happened even if the session was already logged in and had classic theme already applied, but was currently in disconnected state. Each new RDP connection gets its own winlogon.exe process. The mod needs to wait for the session "active" state in case it is modding RDP session related winlogon.exe processes. +2) With the original mod the Remote Desktop sessions often disconnected during connecting. This happened even if the session was already logged in and had classic theme already applied, but was currently in disconnected state. Each new Remote Desktop connection gets its own winlogon.exe process. The mod needs to wait for the session "active" state in case it is modding Remote Desktop session related winlogon.exe processes. + + + +## Acknowledgements + +I would like to mention @Anixx who is the author of the previous Classic Theme Enable mod. The current mod is built upon that work. */ // ==/WindhawkModReadme== From 15b8878e02343f715b24376701b5f15b5dfb2d33 Mon Sep 17 00:00:00 2001 From: Anixx Date: Wed, 4 Sep 2024 13:47:10 +0300 Subject: [PATCH 02/52] Create win10-taskbar-on-win11-24h2.wh.cpp (#923) * Create win10-taskbar-on-win11-24h2.wh.cpp * Update win10-taskbar-on-win11-24h2.wh.cpp * Update win10-taskbar-on-win11-24h2.wh.cpp * Update win10-taskbar-on-win11-24h2.wh.cpp --- mods/win10-taskbar-on-win11-24h2.wh.cpp | 162 ++++++++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 mods/win10-taskbar-on-win11-24h2.wh.cpp diff --git a/mods/win10-taskbar-on-win11-24h2.wh.cpp b/mods/win10-taskbar-on-win11-24h2.wh.cpp new file mode 100644 index 00000000..85d32c5c --- /dev/null +++ b/mods/win10-taskbar-on-win11-24h2.wh.cpp @@ -0,0 +1,162 @@ +// ==WindhawkMod== +// @id win10-taskbar-on-win11-24h2 +// @name Enables Win10 taskbar on Win11 24H2 +// @description Enables Windows 10 taskbar on Windows 11 version 24H2, Windows Server 2025 and Windows 11 IoT Enterprise LTSC 2024 +// @version 0.1 +// @architecture x86-64 +// @author Anixx +// @github https://github.com/Anixx +// @include userinit.exe +// @compilerOptions -lurlmon +// ==/WindhawkMod== + +// ==WindhawkModReadme== +/* +Enables Windows 10 taskbar on Windows 11 version 24H2, Windows Server 2025 and Windows 11 IoT Enterprise LTSC 2024. +If you are on Windows 11 version 21H2 to 23H2, you should not use this mod, but rather install the mod "Windows 10 taskbar on +Windows 11". +Important! Before enabling this mod, install the mod "Fake Explorer path". +Since this mod downloads the Windows 10 taskbar from Microsoft's symbols server and stores it in the Windhawk data directory, +it won't work in the portable version of Windhawk. +If you are using Classic theme, you should also install mods "Classic Theme Explorer Lite" and "Non Immersive Taskbar Context Menu". +The mod "Eradicate immersive menus" will not work. +You also can use 7+ Taskbar Tweaker. +Explorer Patcher by default will have no effect, ask at EP forums for support. +To customize the clock and tray area, launch legacy tray setup dialog: +`explorer shell:::{05d7b0f4-2121-4eff-bf6b-ed3f69b894d9}` +*/ +// ==/WindhawkModReadme== + +#include +#include +#include +#include +#include +#include + +typedef LONG (WINAPI *REGQUERYVALUEEXW)(HKEY hKey, LPCWSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData); + +REGQUERYVALUEEXW pOriginalRegQueryValueExW; + +LONG WINAPI RegQueryValueExWHook(HKEY hKey, LPCWSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData) +{ + + Wh_Log("Queried:%s",lpValueName); + if (lstrcmpiW(lpValueName, L"Shell") == 0) + { + + // Define the directory and file paths as wide strings + const wchar_t* dirPath = L"%ProgramData%\\Windhawk\\Engine\\ModsWritable\\LegacyStore"; + const wchar_t* filePath = L"%ProgramData%\\Windhawk\\Engine\\ModsWritable\\LegacyStore\\explorer.exe"; + const wchar_t* downloadUrl = L"https://msdl.microsoft.com/download/symbols/explorer.exe/7AC6EEC3442000/explorer.exe"; + + // Expand environment variables + wchar_t expandedDirPath[MAX_PATH]; + ExpandEnvironmentStringsW(dirPath, expandedDirPath, MAX_PATH); + + wchar_t expandedFilePath[MAX_PATH]; + ExpandEnvironmentStringsW(filePath, expandedFilePath, MAX_PATH); + + // Check if the directory exists + DWORD dirAttributes = GetFileAttributesW(expandedDirPath); + if (dirAttributes == INVALID_FILE_ATTRIBUTES || !(dirAttributes & FILE_ATTRIBUTE_DIRECTORY)) { + // Create the directory + if (CreateDirectoryW(expandedDirPath, NULL)) { + Wh_Log(L"Directory created successfully: %s\n", expandedDirPath); + } else { + Wh_Log("Failed to create directory: %ld\n", GetLastError()); + } + } else { + Wh_Log(L"Directory already exists: %s\n", expandedDirPath); + } + + // Check if the file exists + DWORD fileAttributes = GetFileAttributesW(expandedFilePath); + if (fileAttributes == INVALID_FILE_ATTRIBUTES) { + // Download the file + HRESULT hr = URLDownloadToFileW(NULL, downloadUrl, expandedFilePath, 0, NULL); + if (SUCCEEDED(hr)) { + Wh_Log(L"File downloaded successfully: %s\n", expandedFilePath); + } else { + Wh_Log("Failed to download file: %ld\n", GetLastError()); + + } + } else { + Wh_Log(L"File already exists: %s\n", expandedFilePath); + } + + wchar_t localeName[LOCALE_NAME_MAX_LENGTH]; + if (GetUserDefaultLocaleName(localeName, LOCALE_NAME_MAX_LENGTH) == 0) { + Wh_Log("Failed to get the current locale: %ld\n", GetLastError()); + + } + + // Create the full path for the locale directory/link + std::wstring localeDirPath = std::wstring(expandedDirPath) + L"\\" + localeName; + + + // Expand environment variable for %systemroot% + + wchar_t systemRoot[MAX_PATH]; + ExpandEnvironmentStringsW(L"%systemroot%", systemRoot, MAX_PATH); + + // Check if the directory or symlink already exists + + if (GetFileAttributesW(localeDirPath.c_str()) != INVALID_FILE_ATTRIBUTES) { + wprintf(L"Locale directory or symlink already exists: %s\n", localeDirPath.c_str()); + } else { + // Instead of creating a symbolic link, create a directory + if (CreateDirectoryW(localeDirPath.c_str(), NULL)) { + Wh_Log(L"Directory created successfully: %s\n", localeDirPath.c_str()); + } else { + Wh_Log("Failed to create directory: %ld\n", GetLastError()); + } + } + + // Define the file to be copied + std::wstring sourceFilePath = std::wstring(systemRoot) + L"\\" + localeName + L"\\explorer.exe.mui"; + std::wstring destinationFilePath = localeDirPath + L"\\explorer.exe.mui"; + + // Copy the file + if (CopyFileW(sourceFilePath.c_str(), destinationFilePath.c_str(), FALSE)) { + Wh_Log(L"File copied successfully: %s -> %s\n", sourceFilePath.c_str(), destinationFilePath.c_str()); + } else { + Wh_Log("Failed to copy file: %ld\n", GetLastError()); + } + + + if (lpType) + *lpType = REG_SZ; + + if (lpData && lpcbData && *lpcbData >= (wcslen(expandedFilePath) + 1) * sizeof(wchar_t)) + { + // Copy the expandedFilePath to the lpData buffer + wcscpy((wchar_t*)lpData, expandedFilePath); + *lpcbData = (wcslen(expandedFilePath) + 1) * sizeof(wchar_t); // Include the null terminator + } + else if (lpcbData) + { + // If buffer is too small, indicate required buffer size + *lpcbData = (wcslen(expandedFilePath) + 1) * sizeof(wchar_t); + return ERROR_MORE_DATA; + } + + return ERROR_SUCCESS; + } + + return pOriginalRegQueryValueExW(hKey, lpValueName, lpReserved, lpType, lpData, lpcbData); +} + +BOOL Wh_ModInit(void) +{ + Wh_Log(L"Init"); + + // Hook the RegQueryValueExW function + Wh_SetFunctionHook( + (void*)GetProcAddress(LoadLibrary(L"kernelbase.dll"), "RegQueryValueExW"), + (void*)RegQueryValueExWHook, + (void**)&pOriginalRegQueryValueExW + ); + + return TRUE; +} From fc6e85e377c3948bcf182bd71fe74f5f0afea3b5 Mon Sep 17 00:00:00 2001 From: Roland Pihlakas Date: Thu, 5 Sep 2024 09:26:42 +0300 Subject: [PATCH 03/52] Fixed a small resource leak related to the "Show desktop" button (#924) * Fixed a small resource leak related to the "Show desktop" button --- mods/classic-taskbar-background-fix.wh.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mods/classic-taskbar-background-fix.wh.cpp b/mods/classic-taskbar-background-fix.wh.cpp index 2ef1dda1..e746a820 100644 --- a/mods/classic-taskbar-background-fix.wh.cpp +++ b/mods/classic-taskbar-background-fix.wh.cpp @@ -2,7 +2,7 @@ // @id classic-taskbar-background-fix // @name Classic Taskbar background fix // @description Fixes Taskbar background in classic theme by replacing black background with a classic button face colour -// @version 1.0.2 +// @version 1.0.3 // @author Roland Pihlakas // @github https://github.com/levitation // @homepage https://www.simplify.ee/ @@ -759,6 +759,7 @@ bool DrawThemeParentBackgroundInternal(HWND hwnd, HDC hdc) { } else { FillRect(hdc, &rect, brush); + DeleteObject(brush); SetLastError(originalError); //reset the error code so that the hooked API does not appear to have errored in case any helper code above caused an error code to be set return true; From 2bc5971618b258d5d262c1327fba3491bdf9798b Mon Sep 17 00:00:00 2001 From: Michael Maltsev <4129781+m417z@users.noreply.github.com> Date: Fri, 6 Sep 2024 20:31:45 +0300 Subject: [PATCH 04/52] Customize Windows 11 notifications placement v1.0 (#928) --- mods/notifications-placement.wh.cpp | 472 ++++++++++++++++++++++++++++ 1 file changed, 472 insertions(+) create mode 100644 mods/notifications-placement.wh.cpp diff --git a/mods/notifications-placement.wh.cpp b/mods/notifications-placement.wh.cpp new file mode 100644 index 00000000..4cdc4708 --- /dev/null +++ b/mods/notifications-placement.wh.cpp @@ -0,0 +1,472 @@ +// ==WindhawkMod== +// @id notifications-placement +// @name Customize Windows 11 notifications placement +// @description Move notifications to another monitor or another corner of the screen +// @version 1.0 +// @author m417z +// @github https://github.com/m417z +// @twitter https://twitter.com/m417z +// @homepage https://m417z.com/ +// @include explorer.exe +// @include ShellExperienceHost.exe +// @architecture x86-64 +// @compilerOptions -DWINVER=0x0A00 -lshcore +// ==/WindhawkMod== + +// Source code is published under The GNU General Public License v3.0. +// +// For bug reports and feature requests, please open an issue here: +// https://github.com/ramensoftware/windhawk-mods/issues +// +// For pull requests, development takes place here: +// https://github.com/m417z/my-windhawk-mods + +// ==WindhawkModReadme== +/* +# Customize Windows 11 notifications placement + +Move notifications to another monitor or another corner of the screen. + +Only Windows 11 is supported. + +![Screenshot](https://i.imgur.com/4PxMvLg.png) +*/ +// ==/WindhawkModReadme== + +// ==WindhawkModSettings== +/* +- monitor: 1 + $name: Monitor + $description: >- + The monitor number that notifications will appear on +- horizontalPlacement: right + $name: Horizontal placement on the screen + $options: + - right: Right + - left: Left + - center: Center +- horizontalDistanceFromScreenEdge: 0 + $name: Distance from the right/left side of the screen +- verticalPlacement: bottom + $name: Vertical placement on the screen + $options: + - bottom: Bottom + - top: Top + - center: Center +- verticalDistanceFromScreenEdge: 0 + $name: Distance from the bottom/top side of the screen +*/ +// ==/WindhawkModSettings== + +#include +#include +#include + +std::atomic g_unloading; + +enum class HorizontalPlacement { + right, + left, + center, +}; + +enum class VerticalPlacement { + bottom, + top, + center, +}; + +struct { + int monitor; + HorizontalPlacement horizontalPlacement; + int horizontalDistanceFromScreenEdge; + VerticalPlacement verticalPlacement; + int verticalDistanceFromScreenEdge; +} g_settings; + +WINUSERAPI UINT WINAPI GetDpiForWindow(HWND hwnd); +typedef enum MONITOR_DPI_TYPE { + MDT_EFFECTIVE_DPI = 0, + MDT_ANGULAR_DPI = 1, + MDT_RAW_DPI = 2, + MDT_DEFAULT = MDT_EFFECTIVE_DPI +} MONITOR_DPI_TYPE; +STDAPI GetDpiForMonitor(HMONITOR hmonitor, + MONITOR_DPI_TYPE dpiType, + UINT* dpiX, + UINT* dpiY); + +HMONITOR GetMonitorById(int monitorId) { + HMONITOR monitorResult = nullptr; + int currentMonitorId = 0; + + auto monitorEnumProc = [&monitorResult, ¤tMonitorId, + monitorId](HMONITOR hMonitor) -> BOOL { + if (currentMonitorId == monitorId) { + monitorResult = hMonitor; + return FALSE; + } + currentMonitorId++; + return TRUE; + }; + + EnumDisplayMonitors( + nullptr, nullptr, + [](HMONITOR hMonitor, HDC hdc, LPRECT lprcMonitor, + LPARAM dwData) -> BOOL { + auto& proc = *reinterpret_cast(dwData); + return proc(hMonitor); + }, + reinterpret_cast(&monitorEnumProc)); + + return monitorResult; +} + +bool GetMonitorWorkArea(HMONITOR monitor, RECT* rc) { + MONITORINFO monitorInfo{ + .cbSize = sizeof(MONITORINFO), + }; + return GetMonitorInfo(monitor, &monitorInfo) && + CopyRect(rc, &monitorInfo.rcWork); +} + +std::wstring GetProcessFileName(DWORD dwProcessId) { + HANDLE hProcess = + OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, dwProcessId); + if (!hProcess) { + return std::wstring{}; + } + + WCHAR processPath[MAX_PATH]; + + DWORD dwSize = ARRAYSIZE(processPath); + if (!QueryFullProcessImageName(hProcess, 0, processPath, &dwSize)) { + CloseHandle(hProcess); + return std::wstring{}; + } + + CloseHandle(hProcess); + + PCWSTR processFileNameUpper = wcsrchr(processPath, L'\\'); + if (!processFileNameUpper) { + return std::wstring{}; + } + + processFileNameUpper++; + return processFileNameUpper; +} + +bool IsTargetCoreWindow(HWND hWnd) { + DWORD processId = 0; + if (!hWnd || !GetWindowThreadProcessId(hWnd, &processId)) { + return false; + } + + if (_wcsicmp(GetProcessFileName(processId).c_str(), + L"ShellExperienceHost.exe") != 0) { + return false; + } + + WCHAR szClassName[32]; + if (GetClassName(hWnd, szClassName, ARRAYSIZE(szClassName)) == 0 || + _wcsicmp(szClassName, L"Windows.UI.Core.CoreWindow") != 0) { + return false; + } + + WCHAR szWindowText[32]; + if (GetWindowText(hWnd, szWindowText, ARRAYSIZE(szWindowText)) == 0 || + wcscmp(szWindowText, L"New notification") != 0) { + return false; + } + + return true; +} + +std::vector GetCoreWindows() { + struct ENUM_WINDOWS_PARAM { + std::vector* hWnds; + }; + + std::vector hWnds; + ENUM_WINDOWS_PARAM param = {&hWnds}; + EnumWindows( + [](HWND hWnd, LPARAM lParam) WINAPI -> BOOL { + ENUM_WINDOWS_PARAM& param = *(ENUM_WINDOWS_PARAM*)lParam; + + if (IsTargetCoreWindow(hWnd)) { + param.hWnds->push_back(hWnd); + } + + return TRUE; + }, + (LPARAM)¶m); + + return hWnds; +} + +void AdjustCoreWindowPos(int* x, int* y, int* cx, int* cy) { + Wh_Log(L"Before: %dx%d %dx%d", *x, *y, *cx, *cy); + + HMONITOR primaryMonitor = + MonitorFromPoint({0, 0}, MONITOR_DEFAULTTONEAREST); + + HMONITOR srcMonitor = MonitorFromPoint({*x + *cx / 2, *y + *cy * 2}, + MONITOR_DEFAULTTONEAREST); + + UINT srcMonitorDpiX = 96; + UINT srcMonitorDpiY = 96; + GetDpiForMonitor(srcMonitor, MDT_DEFAULT, &srcMonitorDpiX, &srcMonitorDpiY); + + RECT srcMonitorWorkArea; + if (!GetMonitorWorkArea(srcMonitor, &srcMonitorWorkArea)) { + return; + } + + HMONITOR destMonitor = !g_unloading && g_settings.monitor >= 1 + ? GetMonitorById(g_settings.monitor - 1) + : nullptr; + if (!destMonitor) { + destMonitor = primaryMonitor; + } + + RECT destMonitorWorkArea; + int horizontalDistanceFromScreenEdge = 0; + int verticalDistanceFromScreenEdge = 0; + + Wh_Log(L"Monitor %p->%p", srcMonitor, destMonitor); + + if (destMonitor != srcMonitor) { + UINT destMonitorDpiX = 96; + UINT destMonitorDpiY = 96; + GetDpiForMonitor(destMonitor, MDT_DEFAULT, &destMonitorDpiX, + &destMonitorDpiY); + + if (!GetMonitorWorkArea(destMonitor, &destMonitorWorkArea)) { + return; + } + + *cx = MulDiv(*cx, destMonitorDpiX, srcMonitorDpiX); + if (*y + *cy == srcMonitorWorkArea.bottom) { + *y = destMonitorWorkArea.bottom - + MulDiv(*cy, destMonitorDpiY, srcMonitorDpiY); + *cy = MulDiv(*cy, destMonitorDpiY, srcMonitorDpiY); + } else { + *cy = MulDiv(*cy, destMonitorDpiY, srcMonitorDpiY); + } + + if (*y == destMonitorWorkArea.top && + *y + *cy > destMonitorWorkArea.bottom) { + *cy = destMonitorWorkArea.bottom - destMonitorWorkArea.top; + } + + if (!g_unloading) { + horizontalDistanceFromScreenEdge = + MulDiv(g_settings.horizontalDistanceFromScreenEdge, + destMonitorDpiX, 96); + verticalDistanceFromScreenEdge = MulDiv( + g_settings.verticalDistanceFromScreenEdge, destMonitorDpiY, 96); + } + } else { + CopyRect(&destMonitorWorkArea, &srcMonitorWorkArea); + + if (!g_unloading) { + horizontalDistanceFromScreenEdge = + MulDiv(g_settings.horizontalDistanceFromScreenEdge, + srcMonitorDpiX, 96); + verticalDistanceFromScreenEdge = MulDiv( + g_settings.verticalDistanceFromScreenEdge, srcMonitorDpiY, 96); + } + } + + if (destMonitor != primaryMonitor) { + UINT destMonitorDpiX = 96; + UINT destMonitorDpiY = 96; + GetDpiForMonitor(destMonitor, MDT_DEFAULT, &destMonitorDpiX, + &destMonitorDpiY); + + UINT primaryMonitorDpiX = 96; + UINT primaryMonitorDpiY = 96; + GetDpiForMonitor(primaryMonitor, MDT_DEFAULT, &primaryMonitorDpiX, + &primaryMonitorDpiY); + + *cx = MulDiv(*cx, destMonitorDpiX, primaryMonitorDpiX); + // *cy = MulDiv(*cy, destMonitorDpiY, primaryMonitorDpiY); + } + + switch (g_unloading ? HorizontalPlacement::right + : g_settings.horizontalPlacement) { + case HorizontalPlacement::right: + *x = destMonitorWorkArea.right - *cx - + horizontalDistanceFromScreenEdge; + break; + + case HorizontalPlacement::left: + *x = destMonitorWorkArea.left + horizontalDistanceFromScreenEdge; + break; + + case HorizontalPlacement::center: + *x = destMonitorWorkArea.left + + (destMonitorWorkArea.right - destMonitorWorkArea.left - *cx) / + 2 + + horizontalDistanceFromScreenEdge; + break; + } + + switch (g_unloading ? VerticalPlacement::bottom + : g_settings.verticalPlacement) { + case VerticalPlacement::bottom: + *y = destMonitorWorkArea.bottom - *cy - + verticalDistanceFromScreenEdge; + break; + + case VerticalPlacement::top: + *y = destMonitorWorkArea.top + verticalDistanceFromScreenEdge; + break; + + case VerticalPlacement::center: + *y = destMonitorWorkArea.top + + (destMonitorWorkArea.bottom - destMonitorWorkArea.top - *cy) / + 2 + + verticalDistanceFromScreenEdge; + break; + } + + Wh_Log(L"After: %dx%d %dx%d", *x, *y, *cx, *cy); +} + +using SetWindowPos_t = decltype(&SetWindowPos); +SetWindowPos_t SetWindowPos_Original; +BOOL WINAPI SetWindowPos_Hook(HWND hWnd, + HWND hWndInsertAfter, + int X, + int Y, + int cx, + int cy, + UINT uFlags) { + auto original = [&]() { + return SetWindowPos_Original(hWnd, hWndInsertAfter, X, Y, cx, cy, + uFlags); + }; + + if (!IsTargetCoreWindow(hWnd)) { + return original(); + } + + Wh_Log(L"%08X %08X", (DWORD)(ULONG_PTR)hWnd, uFlags); + + RECT rc{}; + GetWindowRect(hWnd, &rc); + + // Skip if no size or empty size. + if ((uFlags & SWP_NOSIZE) || cx == 0 || cy == 0) { + Wh_Log(L"Skipping"); + uFlags |= SWP_NOMOVE | SWP_NOSIZE; + return original(); + } + + if (uFlags & SWP_NOMOVE) { + uFlags &= ~SWP_NOMOVE; + X = rc.left; + Y = rc.top; + } + + if (uFlags & SWP_NOSIZE) { + uFlags &= ~SWP_NOSIZE; + cx = rc.right - rc.left; + cy = rc.bottom - rc.top; + } + + AdjustCoreWindowPos(&X, &Y, &cx, &cy); + + return SetWindowPos_Original(hWnd, hWndInsertAfter, X, Y, cx, cy, uFlags); +} + +void ApplySettings() { + if (!GetModuleHandle(L"ShellExperienceHost.exe")) { + return; + } + + for (HWND hCoreWnd : GetCoreWindows()) { + Wh_Log(L"Adjusting core window %08X", (DWORD)(ULONG_PTR)hCoreWnd); + + RECT rc; + if (!GetWindowRect(hCoreWnd, &rc)) { + continue; + } + + int x = rc.left; + int y = rc.top; + int cx = rc.right - rc.left; + int cy = rc.bottom - rc.top; + + AdjustCoreWindowPos(&x, &y, &cx, &cy); + + SetWindowPos_Original(hCoreWnd, nullptr, x, y, cx, cy, + SWP_NOZORDER | SWP_NOACTIVATE); + } +} + +void LoadSettings() { + g_settings.monitor = Wh_GetIntSetting(L"monitor"); + + PCWSTR horizontalPlacement = Wh_GetStringSetting(L"horizontalPlacement"); + g_settings.horizontalPlacement = HorizontalPlacement::right; + if (wcscmp(horizontalPlacement, L"left") == 0) { + g_settings.horizontalPlacement = HorizontalPlacement::left; + } else if (wcscmp(horizontalPlacement, L"center") == 0) { + g_settings.horizontalPlacement = HorizontalPlacement::center; + } + Wh_FreeStringSetting(horizontalPlacement); + + g_settings.horizontalDistanceFromScreenEdge = + Wh_GetIntSetting(L"horizontalDistanceFromScreenEdge"); + + PCWSTR verticalPlacement = Wh_GetStringSetting(L"verticalPlacement"); + g_settings.verticalPlacement = VerticalPlacement::bottom; + if (wcscmp(verticalPlacement, L"top") == 0) { + g_settings.verticalPlacement = VerticalPlacement::top; + } else if (wcscmp(verticalPlacement, L"center") == 0) { + g_settings.verticalPlacement = VerticalPlacement::center; + } + Wh_FreeStringSetting(verticalPlacement); + + g_settings.verticalDistanceFromScreenEdge = + Wh_GetIntSetting(L"verticalDistanceFromScreenEdge"); +} + +BOOL Wh_ModInit() { + Wh_Log(L">"); + + LoadSettings(); + + Wh_SetFunctionHook((void*)SetWindowPos, (void*)SetWindowPos_Hook, + (void**)&SetWindowPos_Original); + + return TRUE; +} + +void Wh_ModAfterInit() { + Wh_Log(L">"); + + ApplySettings(); +} + +void Wh_ModBeforeUninit() { + Wh_Log(L">"); + + g_unloading = true; + + ApplySettings(); +} + +void Wh_ModUninit() { + Wh_Log(L">"); +} + +void Wh_ModSettingsChanged() { + Wh_Log(L">"); + + LoadSettings(); + + ApplySettings(); +} From 7292f722244cd19dc93c4438270bcc600b4bf2ec Mon Sep 17 00:00:00 2001 From: CatmanFan <30674270+CatmanFan@users.noreply.github.com> Date: Sat, 7 Sep 2024 01:32:30 +0100 Subject: [PATCH 05/52] Accent Color Sync v1.32 (#925) * Replaced DWM Colorization Parameters calculation formula with [ALTaleX's method](https://github.com/ALTaleX531/dwm_colorization_calculator). * Compatibility check now returns the actual Windows version instead of the version returned from Explorer. This fixes the issue of the mod not being compatible with [explorer7](https://github.com/Erizur/explorer7-releases/). * Updated readme. --- mods/accent-color-sync.wh.cpp | 803 ++++++++++++++++++++++------------ 1 file changed, 518 insertions(+), 285 deletions(-) diff --git a/mods/accent-color-sync.wh.cpp b/mods/accent-color-sync.wh.cpp index baa4131d..e0e40b1c 100644 --- a/mods/accent-color-sync.wh.cpp +++ b/mods/accent-color-sync.wh.cpp @@ -4,34 +4,43 @@ // @description Synchronises OpenGlass and Control Panel color settings // @description:fr-FR Synchronisation des couleurs d'OpenGlass et du Panneau de configuration // @description:es-ES Sincroniza los colores de OpenGlass y del Panel de control -// @version 1.31 +// @version 1.4 // @author CatmanFan / Mr._Lechkar // @github https://github.com/CatmanFan // @include explorer.exe // @architecture x86 // @architecture x86-64 -// @compilerOptions -lcomdlg32 +// @compilerOptions -lcomdlg32 -lversion // ==/WindhawkMod== // ==WindhawkModReadme== /* Brings back the functionality of the 'Color intensity' slider to Windows 10 using OpenGlass. The mod synchronises OpenGlass's Aero settings with the slider value. -## ⚠️ Requirements -**This mod will not function properly without [kfh83](https://github.com/kfh83)'s OpenGlass-legacy fork installed.** This is because the mod writes values to the registry's DWM section that are used only by this specific software. This requires the GlassType value in the registry to be set to 0x01 (Aero), and it will be automatically changed as such if it isn't. +---- + +## ⚠️ To use this mod, you will need [kfh83](https://github.com/kfh83)'s OpenGlass-legacy fork. To get this mod to function fully, perform the following steps: 1. Install the OpenGlass-legacy fork; this can be done by compiling the source code from **[the official repo](https://github.com/ALTaleX531/OpenGlass/tree/legacy)**, or by getting an existing binary version from **[this GitHub post](https://github.com/ALTaleX531/OpenGlass/pull/14#issue-2415161314)**. * If you are updating this mod from version 1.0, it is required to disable or uninstall any other existing DWM shader software (such as regular OpenGlass or DWMBlurGlass). -2. Afterwards, go to *HKCU\SOFTWARE\Microsoft\Windows\DWM* in the registry and add any one of the three DWORD values of *og_ColorizationColorBalance*, *og_ColorizationAfterglowBalance* and *og_ColorizationBlurBalance* before applying this mod. These will be handled automatically. +2. Afterwards, go to *HKCU\SOFTWARE\Microsoft\Windows\DWM* in the registry and add any one of the three DWORD values of **og_ColorizationColorBalance**, **og_ColorizationAfterglowBalance** and **og_ColorizationBlurBalance** before enabling this mod. These registry values will be handled automatically. + +If you are updating this mod from version 1.3, it is recommended to also enable the *Sync with DWM* option from the mod's settings, although this can have some minor bugs (see below). -If you are updating this mod from version 1.3, it is recommended to also enable the *Sync with DWM* option from the mod's settings, although this can have some minor bugs (see below). You may need to try changing the accent color manually if changes do not automatically take effect. +You may need to try changing the accent color manually if changes do not automatically take effect. -## Current known bugs -* **Sync with DWM** option: In the Control Panel, the theme preview's color icon's opacity does not always reflect the value set by the color intensity slider if it is changed while the *Sync with DWM* mod setting is enabled, unless after the theme preview is regenerated by changing the color RGB values or the desktop background. -* When toggling transparency, because the GlassType value used by OpenGlass is changed, the taskbar and windows visually glitch after DWM is refreshed. A hotfix is currently implemented which logs out the current session if this setting is toggled. +---- + +### Known bugs: +* ***Sync with DWM* option enabled:** + * When changing the color intensity in the Control Panel without changing the color itself, the theme preview icon used for the color does not change unless after the theme preview is regenerated by changing the color RGB values or the desktop background. * Actually closing the Personalization window does not produce the same behaviour as clicking "Cancel" (i.e. the RGB color is changed but the OpenGlass opacity stays the same). -* When changing the intensity slider, there is a very rare chance of DWM crashing. +* When using [Explorer7](https://github.com/Erizur/explorer7-releases/), the Start menu may not immediately change in opacity and requires a restart of explorer.exe. This may be fixed with schm1dt's glass POC mod downloadable [here](https://winclassic.net/post/24274). + +### Special credits: +* [OjasK](https://github.com/ojask) for his DirectUI reverse-engineering work, part of which was referenced in the making of this mod. +* [ALTaleX](https://github.com/ALTaleX531) for the incredible work on OpenGlass, and on porting the Windows 7 DWM colorization functionality to open-source code with [this formula](https://github.com/ALTaleX531/dwm_colorization_calculator). */ // ==/WindhawkModReadme== @@ -47,39 +56,42 @@ If you are updating this mod from version 1.3, it is recommended to also enable */ // ==/WindhawkModSettings== +#include #if _WIN64 -#define THISCALL __cdecl +#define THISCALL __cdecl #define STHISCALL L"__cdecl" -#define STDCALL __cdecl +#define STDCALL __cdecl #define SSTDCALL L"__cdecl" #define STRTOID_NAME L"StrToID" #else -#define THISCALL __thiscall +#define THISCALL __thiscall #define STHISCALL L"__thiscall" -#define STDCALL __stdcall +#define STDCALL __stdcall #define SSTDCALL L"__stdcall" #define STRTOID_NAME L"_StrToID@4" #endif +#include +#include +#include +#include #include #include -#include -#include -#include -#include -#include +#include -struct -{ +struct { int opacity; bool boolTransparency; bool boolSyncDWM; } settings; +enum class WinVersion { None, Unsupported, Win10, Win11 }; +WinVersion winVer; + const std::wstring dwmKey = L"SOFTWARE\\Microsoft\\Windows\\DWM"; const std::wstring opacityValue = L"og_Opacity"; -#pragma region ## Registry functions ## +#pragma region ----- Registry functions ----- /** * Reads a string value from a registry key within HKCU. * @@ -96,13 +108,11 @@ std::wstring read_SZ(std::wstring sk, std::wstring v, std::wstring defaultValue) HKEY hKey; LONG openRes = RegOpenKeyEx(HKEY_CURRENT_USER, subkey, 0, KEY_READ, &hKey); - if (openRes == ERROR_SUCCESS) - { + if (openRes == ERROR_SUCCESS) { LONG setRes = RegQueryValueEx(hKey, value, 0, NULL, (LPBYTE)&szBuffer, &size); RegCloseKey(hKey); - if (setRes == ERROR_SUCCESS) - { + if (setRes == ERROR_SUCCESS) { defaultValue = szBuffer; } } @@ -126,20 +136,16 @@ DWORD read_DWORD(std::wstring sk, std::wstring v) HKEY hKey; LONG openRes = RegOpenKeyEx(HKEY_CURRENT_USER, subkey, 0, KEY_READ, &hKey); - if (openRes != ERROR_SUCCESS) - { + if (openRes != ERROR_SUCCESS) { return NULL; } LONG setRes = RegQueryValueEx(hKey, value, 0, NULL, reinterpret_cast(&data), &size); RegCloseKey(hKey); - if (setRes == ERROR_SUCCESS) - { + if (setRes == ERROR_SUCCESS) { return data; - } - else - { + } else { return NULL; } } @@ -162,13 +168,10 @@ BOOL exists_DWORD(std::wstring sk, std::wstring v) LONG openRes = RegOpenKeyEx(HKEY_CURRENT_USER, subkey, 0, KEY_ALL_ACCESS, &hKey); LONG setRes = RegQueryValueEx(hKey, value, 0, NULL, reinterpret_cast(&data), &size); RegCloseKey(hKey); - - if (openRes != ERROR_SUCCESS || setRes != ERROR_SUCCESS) - { + + if (openRes != ERROR_SUCCESS || setRes != ERROR_SUCCESS) { return FALSE; - } - else - { + } else { return TRUE; } } @@ -185,12 +188,9 @@ BOOL exists_Key(std::wstring sk) HKEY hKey; LONG openRes = RegOpenKeyEx(HKEY_CURRENT_USER, subkey, 0, KEY_ALL_ACCESS, &hKey); - if (openRes != ERROR_SUCCESS) - { + if (openRes != ERROR_SUCCESS) { return FALSE; - } - else - { + } else { return TRUE; } } @@ -210,8 +210,7 @@ BOOL set_DWORD(std::wstring sk, std::wstring v, unsigned long data) HKEY hKey; LONG openRes = RegOpenKeyEx(HKEY_CURRENT_USER, subkey, 0, KEY_ALL_ACCESS, &hKey); - if (openRes != ERROR_SUCCESS) - { + if (openRes != ERROR_SUCCESS) { Wh_Log(L"Failed to open registry key"); return FALSE; } @@ -219,190 +218,367 @@ BOOL set_DWORD(std::wstring sk, std::wstring v, unsigned long data) LONG setRes = data < 0 ? RegDeleteValue(hKey, value) : RegSetValueEx(hKey, value, 0, REG_DWORD, (const BYTE*)&data, sizeof(data)); RegCloseKey(hKey); - if (setRes == ERROR_SUCCESS) - { + if (setRes == ERROR_SUCCESS) { return TRUE; - } - else - { + } else { Wh_Log(L"Failed writing to registry"); return FALSE; } } #pragma endregion -#pragma region ## Windows7 opacity ## -int getOpacityFromBlur(int bB, bool returnOutOf100 = TRUE) -{ - bool found = FALSE; - if (bB == 28) found = TRUE; - int x = 0; +#pragma region ----- DWM colorization calculator ----- +// ---------------------------------------------------------------------------- +// Original code is by ALTaleX !! (converted from Python) +// https://github.com/ALTaleX531/dwm_colorization_calculator/blob/main/main.py +// ---------------------------------------------------------------------------- - x = (bB - 103.6842f) / -0.526316f; - if (x >= 26 && x < 102) found = TRUE; +class hsb { +public: + float hue; // 0-360 + float saturation; // 0-1 + float brightness; // 0-1 - x = (bB - 76.093f) / -0.255814f; - if (x >= 102 && x < 188) found = TRUE; + hsb(float hue, float saturation, float brightness) + : hue(hue) + , saturation(saturation) + , brightness(brightness) + { + } + + void log() { Wh_Log(L"[H: %d, S: %d, B: %d]", hue, saturation, brightness); } +}; - x = (bB - 131.25f) / -0.535714f; - if (x >= 189 && x <= 217) found = TRUE; +// Just the plain ARGB value +// ************************************** +class argb { +public: + int value; - if (found) + argb(int value) + : value(value) { - if (returnOutOf100) x = (x - 26.0) / (217.0 - 26.0) * 100.0; - return x; } - return 0; -} -void getColorBalances(int sliderPosition, int &primaryBalance, int &secondaryBalance, int &blurBalance) -{ - int pB = 0, sB = 0, bB = 0; - int x = sliderPosition; - - //primary - if (x >= 26 && x < 103) - pB = 5; - else if (x >= 103 && x < 188) - pB = 0.776471*x - 74.9765f + 1.5f; - else if (x == 188) - pB = 71; - else if (x >= 189 && x <= 217) - pB = 0.535714*x - 31.25f + 1.5f; - - //secondary - if (x >= 26 && x < 102) - sB = 0.526316*x - 8.68421f; - else if (x >= 102 && x < 189) - sB = -0.517241*x + 97.7586f; - else if(x >= 189 && x <= 217) - sB = 0; - - //blur - if (x >= 26 && x < 102) - bB = -0.526316*x + 103.6842f; - else if (x >= 102 && x < 188) - bB = -0.255814f*x + 76.093f; - else if (x == 188) - bB = 28; - else if (x >= 189 && x <= 217) - bB = -0.535714f*x + 131.25f; - - primaryBalance = pB; - secondaryBalance = sB; - blurBalance = bB; -} + int get_a() const { return (value & 0xFF000000) >> 24; } + int get_r() const { return (value & 0x00FF0000) >> 16; } + int get_g() const { return (value & 0x0000FF00) >> 8; } + int get_b() const { return (value & 0x000000FF); } + + static int from_argb_channel(float a, float r, float g, float b) { return ((static_cast(a * 255.0 + 0.5) & 0xFF) << 24) | ((static_cast(r * 255.0 + 0.5) & 0xFF) << 16) | ((static_cast(g * 255.0 + 0.5) & 0xFF) << 8) | ((static_cast(b * 255.0 + 0.5) & 0xFF)); } +}; + +// Colorization color, which permits ARGB color to be converted to DWM parameters +// ************************************** +class colorization_color : public argb { +public: + colorization_color(int value) + : argb(value) + { + } + + static colorization_color from_hsb(const hsb& color, float intensity) + { + float r, g, b; + + if (color.saturation == 0.0) { + r = g = b = color.brightness; + } else { + float value = color.hue / 60.0; + float difference = value - static_cast(value); + + if (color.hue > 0.0 && color.hue <= 60.0) { + g = (1.0 - (1.0 - difference) * color.saturation) * color.brightness; + b = (1.0 - color.saturation) * color.brightness; + r = color.brightness; + } else if (color.hue > 60.0 && color.hue <= 120.0) { + r = (1.0 - difference * color.saturation) * color.brightness; + g = color.brightness; + b = (1.0 - color.saturation) * color.brightness; + } else if (color.hue > 120.0 && color.hue <= 180.0) { + r = (1.0 - color.saturation) * color.brightness; + g = color.brightness; + b = (1.0 - (1.0 - difference) * color.saturation) * color.brightness; + } else if (color.hue > 180.0 && color.hue <= 240.0) { + r = (1.0 - color.saturation) * color.brightness; + g = (1.0 - difference * color.saturation) * color.brightness; + b = color.brightness; + } else if (color.hue > 240.0 && color.hue <= 300.0) { + r = (1.0 - (1.0 - difference) * color.saturation) * color.brightness; + g = (1.0 - color.saturation) * color.brightness; + b = color.brightness; + } else /* if (color.hue > 300.0 && color.hue <= 360.0) */ { + r = color.brightness; + g = (1.0 - color.saturation) * color.brightness; + b = (1.0 - difference * color.saturation) * color.brightness; + } + } + + return colorization_color(argb::from_argb_channel(intensity, r, g, b)); + } + + float get_intensity() const { return static_cast(get_a()) / 255.0; } + + hsb get_hsb_color() const + { + float r = static_cast(get_r()) / 255.0; + float g = static_cast(get_g()) / 255.0; + float b = static_cast(get_b()) / 255.0; + + float brightness = std::max({ r, g, b }); + float darkness = std::min({ r, g, b }); + float range_value = brightness - darkness; + float saturation, value; + + if (brightness == 0.0 || range_value == 0.0) { + saturation = 0.0; + value = 0.0; + } else { + saturation = range_value / brightness; + if (r == brightness) { + value = (g - b) / range_value; + } else if (g == brightness) { + value = (b - r) / range_value + 2.0; + } else { + value = (r - g) / range_value + 4.0; + } + } + + float hue = value * 60.0; + if (hue < 0.0) + hue += 360.0; + + return hsb(hue, saturation, brightness); + } +}; + +// Now we get to the good stuff +// ************************************** +class dwm_colorization_parameters { +public: + argb color; + argb afterglow; + int color_balance; + int afterglow_balance; + int blur_balance; + int glass_reflection_intensity; + bool opaque_blend; + + dwm_colorization_parameters(argb color = argb(0x6b74b8fc), argb afterglow = argb(0x6b74b8fc), int color_balance = 8, int afterglow_balance = 43, int blur_balance = 49, int glass_reflection_intensity = 50, bool opaque_blend = false) + : color(color) + , afterglow(afterglow) + , color_balance(color_balance) + , afterglow_balance(afterglow_balance) + , blur_balance(blur_balance) + , glass_reflection_intensity(glass_reflection_intensity) + , opaque_blend(opaque_blend) + { + } + + void log() + { + Wh_Log(L"[Color: %8x]", color); + Wh_Log(L"[Afterglow: %8x]", afterglow); + Wh_Log(L"[Balance (color): %d]", color_balance); + Wh_Log(L"[Balance (afterglow): %d]", afterglow_balance); + Wh_Log(L"[Balance (blur): %d]", blur_balance); + Wh_Log(L"{Glass reflection intensity: %d}", glass_reflection_intensity); + Wh_Log(L"{Opaque blend: %d}", opaque_blend); + } + + DWORD convert_colorization_parameters_to_argb() + { + int balance = 0; + + if (opaque_blend) { + balance = color_balance; + } else if (blur_balance < 50) { + if (blur_balance <= 23) { + balance = color_balance + 25; + } else { + balance = 95 - afterglow_balance; + } + } else { + balance = 100 - blur_balance; + } + + return color.value & (0xFFFFFF | ((static_cast((static_cast(balance - 10) * 0.75 / 100.0 + 0.1) * 255.0 + 0.5) & 0xFF) << 24)); + } + + colorization_color to_colorization_color() + { + int argb_color = 0; + + if ((color.value & 0xFF000000) == 0xFF000000) { + argb_color = convert_colorization_parameters_to_argb(); + } else { + argb_color = color.value; + } + + return colorization_color(argb_color); + } + + colorization_color calculate_dwm_color() + { + float afterglow_balance = static_cast(this->afterglow_balance) / 100.0; + float color_balance = static_cast(this->color_balance) / 100.0; + + float color_r = static_cast(color.get_r()) / 255.0; + float color_g = static_cast(color.get_g()) / 255.0; + float color_b = static_cast(color.get_b()) / 255.0; + + float afterglow_r = static_cast(afterglow.get_r()) / 255.0; + float afterglow_g = static_cast(afterglow.get_g()) / 255.0; + float afterglow_b = static_cast(afterglow.get_b()) / 255.0; + + float result_a = std::max(0.0f, (1.0f - afterglow_balance) - (static_cast(blur_balance) / 100.0f)); + float brightness = (color_g * 0.7152 + color_r * 0.2126 + color_b * 0.0722) * afterglow_balance * color_balance; + + float result_r = afterglow_r * brightness + color_r * color_balance; + float result_g = afterglow_g * brightness + color_g * color_balance; + float result_b = afterglow_b * brightness + color_b * color_balance; + + return colorization_color(argb::from_argb_channel(result_a, result_r, result_g, result_b)); + } +}; #pragma endregion +#pragma region ----- DWM colorization calculation: Functions ----- +colorization_color old = NULL; +colorization_color current = NULL; +hsb HSB = current.get_hsb_color(); +dwm_colorization_parameters dwmSettings; +DWORD modified; + int intensitySliderMin = 10; int intensitySliderMax = 85; int valueTo100(int x) { return x - intensitySliderMin / (intensitySliderMax - intensitySliderMin) * 100; } int valueFrom100(int x) { return (x / 100) * (intensitySliderMax - intensitySliderMin) + intensitySliderMin; } -int valueTo255(int x) { return round(2.5497*x + 0.0449); } -int getOpacityFromColor(DWORD color, bool rounded) +#pragma endregion + +DWORD readColor() { - std::stringstream ssA; - ssA << std::hex << std::setfill('0') << std::setw(8) << color; - unsigned long x = std::stoul(ssA.str().substr(0, 2), nullptr, 16); - return rounded ? round((x - 0.0732) / 2.5488) : x; + const std::wstring value = L"ColorizationColor"; + + if (!exists_DWORD(dwmKey, value)) + return 0xFF000000; + else + return read_DWORD(dwmKey, value); } -/** - * Writes the OpenGlass opacity value to DWM's color and afterglow values in registry. - */ -void SyncOpacity() +void loadColorValues(DWORD input) { - if (!settings.boolSyncDWM) return; - if (settings.opacity < 0 || settings.opacity > 100) - { - settings.opacity = getOpacityFromBlur(0x31); - set_DWORD(dwmKey, opacityValue, settings.opacity); + current = colorization_color(input); + HSB = current.get_hsb_color(); +} + +// ---------------------------------------------------------------------------- +// Taken from "to_dwm_colorization_parameters" function (again, by ALTaleX): +// https://github.com/ALTaleX531/dwm_colorization_calculator/blob/main/main.py#L110 +// ---------------------------------------------------------------------------- +void calculateIntensity(DWORD self) +{ + int balance = int((float((self >> 24) & 0xFF) / 255.0f - 0.1f) / 0.75f * 100.0f + 10.0f); + dwmSettings = dwm_colorization_parameters(self, self, 0, 0, 0, 50, !settings.boolTransparency); + + if (dwmSettings.opaque_blend) { + dwmSettings.afterglow_balance = 10; + dwmSettings.color_balance = balance - dwmSettings.afterglow_balance; + dwmSettings.blur_balance = 100 - balance; } - const std::wstring value = L"ColorizationColor"; - if (!exists_DWORD(dwmKey, value)) return; - DWORD x = read_DWORD(dwmKey, value); - int sysOpacity = getOpacityFromColor(x, false); - - int ogOpacity = valueTo255(settings.opacity); - - // Hotfixes to force match with Windows 7 accent colors, since the calculation above is janky - if (ogOpacity == 0x69 || ogOpacity == 0x6a) ogOpacity = 0x6b; - if (ogOpacity == 0xa6 || ogOpacity == 0xa7) ogOpacity = 0xa8; - if (ogOpacity == 0x67) ogOpacity = 0x66; - if (ogOpacity == 0x6e || ogOpacity == 0x6f) ogOpacity = 0x70; - if (x == 0x52fadc0e) x = 0x54fadc0e; - if (x == 0x54fadc0e) ogOpacity = 0x54; - - if (sysOpacity != ogOpacity) - { - std::stringstream ssA; - ssA << std::hex << std::setfill('0') << std::setw(8) << x; - std::stringstream ssB; - ssB << std::hex << std::setfill('0') << std::setw(2) << ogOpacity; - DWORD newColor = std::stoul("0x" + ssB.str() + ssA.str().substr(2), nullptr, 16); + else if (balance < 50) { + dwmSettings.color_balance = 5; + dwmSettings.blur_balance = 100 - balance; + dwmSettings.afterglow_balance = (100 - dwmSettings.color_balance) - dwmSettings.blur_balance; + } + + else if (balance >= 95) { + dwmSettings.afterglow_balance = 0; + dwmSettings.color_balance = balance - 25; + dwmSettings.blur_balance = 100 - dwmSettings.color_balance; + } - set_DWORD(dwmKey, L"ColorizationColor", newColor); - set_DWORD(dwmKey, L"ColorizationAfterglow", newColor); + else { + dwmSettings.afterglow_balance = 95 - balance; + dwmSettings.blur_balance = 50 - ((balance - 50) >> 1); + dwmSettings.color_balance = 100 - dwmSettings.afterglow_balance - dwmSettings.blur_balance; } } int opacity = -1; +/** + * Writes the OpenGlass opacity value to DWM's color and afterglow values in registry. + */ +void bruteforceOpacity() +{ + if (!settings.boolSyncDWM) + return; + + argb dwm(readColor()); + argb og(dwmSettings.to_colorization_color().value); + if (dwm.value == 0x52fadc0e) dwm.value = 0x54fadc0e; // Sun + if (dwm.value == og.value) return; + int opacity = og.get_a(); + + // Hotfixes to match with Windows 7 accent colors + if (opacity == 0x69 || opacity == 0x6a) + opacity = 0x6b; + if (opacity == 0xa6 || opacity == 0xa7) + opacity = 0xa8; + if (opacity == 0x67) + opacity = 0x66; + if (opacity == 0x6e || opacity == 0x6f) + opacity = 0x70; + if (dwm.value == 0x54fadc0e) + opacity = 0x54; + + DWORD New = (dwm.value & 0x00ffffff) | (opacity << 24); + set_DWORD(dwmKey, L"ColorizationColor", New); + set_DWORD(dwmKey, L"ColorizationAfterglow", New); + + dwmSettings.color = dwmSettings.afterglow = New; +} + /** * Calculates the color, afterglow and blur intensity values from an integer out of 100, then writes them to the registry for use with OpenGlass. - * @param sync Determines whether to also sync opacity. + * @param bruteforce Determines whether to also simultaneously bruteforce the opacity value to DWM. */ -void WriteNewColor(bool sync) +void writeColorizationBalance(bool bruteforce = FALSE) { - if (!settings.boolTransparency) - opacity = 100; - else - { - if (opacity < 0 || opacity > 100) - opacity = settings.opacity; - if (opacity < 0 || opacity > 100) - { - settings.opacity = opacity = getOpacityFromBlur(0x31); - } - } + if (settings.opacity < 0 || settings.opacity > 100) + settings.opacity = 42; // 40.7853080838; + if (opacity < 0 || opacity > 100) + opacity = settings.opacity; set_DWORD(dwmKey, opacityValue, opacity); + float alpha = opacity / 100.0f; + // ********************************************* - // Create Aero intensity values + // Min: 26, max: 217 + // NOTE: Changing the ColorizationColor and Afterglow also affects the intensity slider // ********************************************* - int colour = 100; - int afterglow = 0; - int blur = 0; - - if (settings.boolTransparency) - { - getColorBalances - ( - valueTo255(opacity), - colour, - afterglow, - blur - ); - - // Changing the ColorizationColor and Afterglow also affects the intensity slider - } - + loadColorValues((current.value & 0x00FFFFFF) | ((static_cast(alpha * 255.0 + 0.5) & 0xFF) << 24)); + calculateIntensity(current.value); + // ********************************************* // Actually do the registry editing // ********************************************* - set_DWORD(dwmKey, L"og_ColorizationColorBalance", colour); - set_DWORD(dwmKey, L"og_ColorizationAfterglowBalance", afterglow); - set_DWORD(dwmKey, L"og_ColorizationBlurBalance", blur); + set_DWORD(dwmKey, L"og_ColorizationColorBalance", dwmSettings.color_balance); + set_DWORD(dwmKey, L"og_ColorizationAfterglowBalance", dwmSettings.afterglow_balance); + set_DWORD(dwmKey, L"og_ColorizationBlurBalance", dwmSettings.blur_balance); // Other registry values - set_DWORD(dwmKey, L"GlassOpacity", settings.boolTransparency ? 0 : 100); - set_DWORD(dwmKey, L"GlassType", settings.boolTransparency ? 1 : 0); + set_DWORD(dwmKey, L"GlassOpacity", 0); // settings.boolTransparency ? 0 : 100); + set_DWORD(dwmKey, L"GlassType", 1); // settings.boolTransparency ? 1 : 0); + + if (bruteforce) + bruteforceOpacity(); - if (sync) SyncOpacity(); PostMessage(FindWindow(TEXT("dwm"), nullptr), WM_DWMCOLORIZATIONCOLORCHANGED, 0, 0); } -#pragma region ## DirectUI hooks ## +#pragma region ----- DirectUI hooks ----- typedef ATOM WINAPI (*StrToId_T)(unsigned const short*); StrToId_T StrToID; @@ -411,73 +587,105 @@ intptr_t intensitySlider = 0; intptr_t okButton = 0; intptr_t cancelButton = 0; -typedef unsigned short(*THISCALL Element_GetID_T)(class Element*, void*); +typedef unsigned short (*THISCALL Element_GetID_T)(class Element*, void*); Element_GetID_T Element_GetID; -typedef void(*THISCALL Element_OnPropertyChanged_T)(class Element*, class PropertyInfo const *,int,class Value *,class Value *); -Element_OnPropertyChanged_T Element_OnPropertyChanged; - -void THISCALL Element_OnPropertyChanged_hook(class Element* This, class PropertyInfo const *prop, int integer, class Value *valueA, class Value *valueB) +void (*THISCALL Element_OnPropertyChanged)(class Element*, class PropertyInfo const*, int, class Value*, class Value*); +void THISCALL Element_OnPropertyChanged_hook(class Element* This, class PropertyInfo const* prop, int integer, class Value* valueA, class Value* valueB) { Element_OnPropertyChanged(This, prop, integer, valueA, valueB); + intptr_t ptr = reinterpret_cast(This); + if (ptr <= 0) return; ATOM id = Element_GetID(This, &This); + if (intensitySlider != id && id == StrToID((unsigned const short*)L"IntensitySlider")) - { - intensitySlider = reinterpret_cast(This); - } - else if (okButton != id && id == StrToID((unsigned const short*)L"OkButton")) - { - okButton = reinterpret_cast(This); - SyncOpacity(); + intensitySlider = ptr; + + // ********************************************* + // The bruteforcing here has to be done so that the default alpha color value is overwritten + // ********************************************* + else if (okButton != id && id == StrToID((unsigned const short*)L"OkButton")) { + okButton = ptr; + bruteforceOpacity(); } - else if (cancelButton != id && id == StrToID((unsigned const short*)L"CancelButton")) - { - cancelButton = reinterpret_cast(This); - SyncOpacity(); + else if (cancelButton != id && id == StrToID((unsigned const short*)L"CancelButton")) { + cancelButton = ptr; + bruteforceOpacity(); } } long (*THISCALL CCTrackBar_SetThumbPosition)(class CCTrackBar*, int); long THISCALL CCTrackBar_SetThumbPosition_hook(class CCTrackBar* This, int value) { - intptr_t current = reinterpret_cast(This); + intptr_t ptr = reinterpret_cast(This); // Track bar value - if (current > 0 && current == intensitySlider) - { + if (ptr > 0 && ptr == intensitySlider) { opacity = value; - WriteNewColor(false); + writeColorizationBalance(); } return CCTrackBar_SetThumbPosition(This, value); } +/** + * ------------------------- + * Function for permanent hooking of opacity to DWM when OK/Cancel is clicked + * ------------------------- + */ typedef void (*THISCALL CCPushButton_OnSelectedPropertyChanged_T)(class CCPushButton*, void*); CCPushButton_OnSelectedPropertyChanged_T CCPushButton_OnSelectedPropertyChanged; void THISCALL CCPushButton_OnSelectedPropertyChanged_hook(class CCPushButton* This, void* that) { - intptr_t current = reinterpret_cast(This); + intptr_t ptr = reinterpret_cast(This); // OK button - if (current > 0 && current == okButton) - { + if (ptr > 0 && ptr == okButton) { settings.opacity = opacity; - SyncOpacity(); + bruteforceOpacity(); } // Cancel button - else if (current > 0 && current == cancelButton) - { + else if (ptr > 0 && ptr == cancelButton) { opacity = settings.opacity; - WriteNewColor(true); - if (intensitySlider > 0) CCTrackBar_SetThumbPosition(reinterpret_cast(intensitySlider), opacity); + loadColorValues(old.value); + writeColorizationBalance(TRUE); + + if (intensitySlider > 0) + CCTrackBar_SetThumbPosition(reinterpret_cast(intensitySlider), opacity); } CCPushButton_OnSelectedPropertyChanged(This, that); } #pragma endregion +#pragma region ##ThemeUI hooks## + +enum DWMPGLASSATTRIBUTE : INT { + DWMPGA_TRANSPARENCY_ALLOWED = 0x0, + DWMPGA_TRANSPARENCY_DISALLOWED = 0x1, + DWMPGA_NO_GLASS = 0x2, + DWMPGA_LAST = 0x3, +}; + +/** + * ------------------------- + * Function for setting OpenGlass opacity when the theme is changed from the Control Panel + * ------------------------- + */ +long (*STDCALL SetDwmColorizationColor)(unsigned long, enum DWMPGLASSATTRIBUTE, int); +long STDCALL SetDwmColorizationColor_hook(unsigned long color, enum DWMPGLASSATTRIBUTE attribute, int integer) +{ + settings.opacity = opacity = round(argb(color).get_a() / 255.0 * 100.0); + old = colorization_color(color); + loadColorValues(old.value); + writeColorizationBalance(); + + return SetDwmColorizationColor(color, attribute, integer); +} +#pragma endregion + WindhawkUtils::SYMBOL_HOOK dui70dll_hooks[] = { { {STRTOID_NAME}, @@ -508,27 +716,6 @@ WindhawkUtils::SYMBOL_HOOK dui70dll_hooks[] = { } }; -#pragma region ## ThemeUI hooks ## -enum DWMPGLASSATTRIBUTE : INT -{ - DWMPGA_TRANSPARENCY_ALLOWED = 0x0, - DWMPGA_TRANSPARENCY_DISALLOWED = 0x1, - DWMPGA_NO_GLASS = 0x2, - DWMPGA_LAST = 0x3, -}; - -long (*STDCALL SetDwmColorizationColor)(unsigned long, enum DWMPGLASSATTRIBUTE,int); -long STDCALL SetDwmColorizationColor_hook(unsigned long color, enum DWMPGLASSATTRIBUTE attribute,int integer) -{ - if (settings.boolSyncDWM) - { - settings.opacity = opacity = getOpacityFromColor(color, true); - WriteNewColor(false); - } - return SetDwmColorizationColor(color, attribute, integer); -} -#pragma endregion - WindhawkUtils::SYMBOL_HOOK themeuidll_hooks[] = { { {L"long " SSTDCALL " SetDwmColorizationColor(unsigned long,enum DWMPGLASSATTRIBUTE,int)"}, @@ -537,42 +724,88 @@ WindhawkUtils::SYMBOL_HOOK themeuidll_hooks[] = { } }; +typedef NTSTATUS(NTAPI* RtlGetVersion_t)(PRTL_OSVERSIONINFOW); +RtlGetVersion_t RtlGetVersion; +WinVersion getWinVer() +{ + HMODULE hNtDll = LoadLibraryW(L"ntdll.dll"); + if (!hNtDll) + return WinVersion::None; + + RtlGetVersion = (RtlGetVersion_t)GetProcAddress(hNtDll, "RtlGetVersion"); + if (!RtlGetVersion) + return WinVersion::None; + + RTL_OSVERSIONINFOW osv = { sizeof(RTL_OSVERSIONINFOW) }; + if (RtlGetVersion(&osv) == 0x00) { + WORD major = osv.dwMajorVersion; + WORD minor = osv.dwMinorVersion; + WORD build = osv.dwBuildNumber; + Wh_Log(L"Current OS version: %u.%u (build %u)", major, minor, build); + + switch (major) { + case 10: + if (build < 22000) + return WinVersion::Win10; + else + return WinVersion::Win11; + break; + + default: + return WinVersion::Unsupported; + } + } + + return WinVersion::None; +} + +BOOL isOpenGlassInstalled(bool strict = FALSE) +{ + return strict ? exists_DWORD(dwmKey, L"og_ColorizationColorBalance") && exists_DWORD(dwmKey, L"og_ColorizationAfterglowBalance") && exists_DWORD(dwmKey, L"og_ColorizationBlurBalance") + : exists_DWORD(dwmKey, L"og_ColorizationColorBalance") || exists_DWORD(dwmKey, L"og_ColorizationAfterglowBalance") || exists_DWORD(dwmKey, L"og_ColorizationBlurBalance"); +} + BOOL LoadSettings() { bool regSetup = FALSE; - if (!exists_DWORD(dwmKey, L"og_ColorizationColorBalance") - && !exists_DWORD(dwmKey, L"og_ColorizationAfterglowBalance") - && !exists_DWORD(dwmKey, L"og_ColorizationBlurBalance")) - { - Wh_Log(L"OpenGlass Legacy is not detected, cannot continue"); + // ********************************************* + // Check for OpenGlass installation + // ********************************************* + if (!isOpenGlassInstalled()) { + Wh_Log(L"HALT: OpenGlass Legacy was not detected"); return FALSE; - } - else if (exists_DWORD(dwmKey, L"og_ColorizationColorBalance") - && exists_DWORD(dwmKey, L"og_ColorizationAfterglowBalance") - && exists_DWORD(dwmKey, L"og_ColorizationBlurBalance")) - { - regSetup = FALSE; - } - else - { - regSetup = TRUE; + } else { + regSetup = !isOpenGlassInstalled(TRUE); } - if (!exists_DWORD(dwmKey, opacityValue)) regSetup = TRUE; - else settings.opacity = read_DWORD(dwmKey, opacityValue); + // ********************************************* + // Check/load from opacity DWORD in registry + // ********************************************* + if (!exists_DWORD(dwmKey, opacityValue)) + regSetup = TRUE; + else + settings.opacity = read_DWORD(dwmKey, opacityValue); - if (regSetup) - { + // ********************************************* + // Setup if those checks were not fully met + // ********************************************* + if (regSetup) { Wh_Log(L"Setting up registry"); + + settings.opacity = 42; set_DWORD(dwmKey, L"og_ColorizationColorBalance", 0x08); set_DWORD(dwmKey, L"og_ColorizationAfterglowBalance", 0x2b); set_DWORD(dwmKey, L"og_ColorizationBlurBalance", 0x31); - settings.opacity = getOpacityFromBlur(0x31); - if (!set_DWORD(dwmKey, opacityValue, settings.opacity)) return FALSE; - if (!exists_DWORD(dwmKey, opacityValue)) return FALSE; + if (!set_DWORD(dwmKey, opacityValue, settings.opacity)) + return FALSE; + if (!exists_DWORD(dwmKey, opacityValue)) + return FALSE; } + + loadColorValues(readColor()); + old = current; settings.boolTransparency = TRUE; settings.boolSyncDWM = Wh_GetIntSetting(L"syncDWM"); @@ -580,63 +813,63 @@ BOOL LoadSettings() return TRUE; } -BOOL Wh_ModSettingsChanged(BOOL* bReload) { +BOOL Wh_ModSettingsChanged(BOOL* bReload) +{ Wh_Log(L"Settings changed"); *bReload = TRUE; return TRUE; } -BOOL Wh_ModInit() { - if (!IsWindows10OrGreater()) - { - Wh_Log(L"Cannot run on Windows 8.1 or earlier"); +BOOL Wh_ModInit() +{ + std::wstring username = read_SZ(L"Volatile Environment", L"USERNAME", L"???"); + if (username == L"???") { + Wh_Log(L"HALT: Local username not detected"); return FALSE; } - std::wstring username = read_SZ(L"Volatile Environment", L"USERNAME", L"???"); - if (username == L"???") - { - Wh_Log(L"Local username not detected, unloading"); + winVer = getWinVer(); + if (winVer == WinVersion::None) { + Wh_Log(L"HALT: Invalid OS version"); return FALSE; } + switch (winVer) { + case WinVersion::Unsupported: + Wh_Log(L"HALT: Cannot run on Windows 8.1 or earlier"); + return FALSE; - if (!LoadSettings()) - { - Wh_Log(L"Failed to load settings"); + default: + break; + } + + if (!LoadSettings()) { + Wh_Log(L"HALT: Failed to load settings"); return FALSE; } HMODULE hDui = LoadLibraryW(L"dui70.dll"); - if (hDui) - { - if (!WindhawkUtils::HookSymbols(hDui, dui70dll_hooks, ARRAYSIZE(dui70dll_hooks))) - { - Wh_Log(L"Failed to hook symbols from dui70.dll"); + if (hDui) { + if (!WindhawkUtils::HookSymbols(hDui, dui70dll_hooks, ARRAYSIZE(dui70dll_hooks))) { + Wh_Log(L"HALT: Failed to hook symbols from dui70.dll"); return FALSE; } - } - else - { - Wh_Log(L"Failed to load dui70.dll"); + } else { + Wh_Log(L"HALT: Failed to load dui70.dll"); return FALSE; } - + HMODULE hThemeUi = LoadLibraryW(L"themeui.dll"); - if (hThemeUi) - { - if (!WindhawkUtils::HookSymbols(hThemeUi, themeuidll_hooks, ARRAYSIZE(themeuidll_hooks))) - { - Wh_Log(L"Failed to hook symbols from themeui.dll"); + if (hThemeUi) { + if (!WindhawkUtils::HookSymbols(hThemeUi, themeuidll_hooks, ARRAYSIZE(themeuidll_hooks))) { + Wh_Log(L"HALT: Failed to hook symbols from themeui.dll"); return FALSE; } - } - else - { - Wh_Log(L"Failed to load themeui.dll"); + } else { + Wh_Log(L"HALT: Failed to load themeui.dll"); return FALSE; } - WriteNewColor(true); + writeColorizationBalance(); return TRUE; } \ No newline at end of file From 64075c826be2989a7d02742caa12efe29c40bd1f Mon Sep 17 00:00:00 2001 From: tetawaves <130876208+tetawaves@users.noreply.github.com> Date: Sat, 7 Sep 2024 09:51:55 +0200 Subject: [PATCH 06/52] UIFILE Override v1.0.2 (#933) * Check executable name instead of full path for explorer hooking --- mods/uifile-override.wh.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mods/uifile-override.wh.cpp b/mods/uifile-override.wh.cpp index 8ee48682..68857c3b 100644 --- a/mods/uifile-override.wh.cpp +++ b/mods/uifile-override.wh.cpp @@ -2,7 +2,7 @@ // @id uifile-override // @name UIFILE Override // @description Override UIFILE resources -// @version 1.0.1 +// @version 1.0.2 // @author xalejandro // @github https://github.com/tetawaves // @include * @@ -174,7 +174,7 @@ BOOL Wh_ModInit() { Wh_Log(L"Failed to load dui70.dll"); } - if (lstrcmpiW(L"C:\\Windows\\explorer.exe", exePath)) { + if (lstrcmpiW(L"explorer.exe", PathFindFileNameW(exePath))) { return TRUE; } From bf5b0746e2cb9d22e109b664ca3a63c430199f18 Mon Sep 17 00:00:00 2001 From: Michael Maltsev <4129781+m417z@users.noreply.github.com> Date: Sat, 7 Sep 2024 14:41:08 +0300 Subject: [PATCH 07/52] Windows 11 Notification Center Styler v1.1.2 (#935) * Added a new theme: Unified. * Added a workaround for hanging the notification center process on Windows 10. Although the available themes and example styles are designed for Windows 11, it should now be possible to use the mod on Windows 10 too. --- ...ndows-11-notification-center-styler.wh.cpp | 112 +++++++++++++----- 1 file changed, 81 insertions(+), 31 deletions(-) diff --git a/mods/windows-11-notification-center-styler.wh.cpp b/mods/windows-11-notification-center-styler.wh.cpp index 671471d9..08ca6606 100644 --- a/mods/windows-11-notification-center-styler.wh.cpp +++ b/mods/windows-11-notification-center-styler.wh.cpp @@ -2,7 +2,7 @@ // @id windows-11-notification-center-styler // @name Windows 11 Notification Center Styler // @description Customize the Notification Center with themes contributed by others or create your own -// @version 1.1.1 +// @version 1.1.2 // @author m417z // @github https://github.com/m417z // @twitter https://twitter.com/m417z @@ -41,35 +41,9 @@ mod and can be selected in the settings: \ TranslucentShell](https://github.com/ramensoftware/windows-11-notification-center-styling-guide/blob/main/Themes/TranslucentShell/README.md) -## Examples - -### Hide the focus assist section -**Target**: `ActionCenter.FocusSessionControl` \ -**Style**: `Height=0` - -### Square the corners of the notification center -**Target**: `Windows.UI.Xaml.Controls.Grid#NotificationCenterGrid` \ -**Style**: `CornerRadius=0` - -### Square the corners of the calendar -**Target**: `Windows.UI.Xaml.Controls.Grid#CalendarCenterGrid` \ -**Style**: `CornerRadius=0` - -### Square the corners of the quick action center -**Target**: `Windows.UI.Xaml.Controls.Grid#ControlCenterRegion` \ -**Style**: `CornerRadius=0` - -### Calendar and notification titlebars: titles on the right, buttons on the left -**Target**: `Windows.UI.Xaml.Controls.Grid#RootContent` \ -**Style**: `FlowDirection=1` - -### Add accelerator key (ALT+X) to clear all notifications -**Target**: `Windows.UI.Xaml.Controls.Button#ClearAll` \ -**Style**: `AccessKey=x` - -### Add accelerator key (ALT+E) to expand/collapse the calendar -**Target**: `Windows.UI.Xaml.Controls.Button#ExpandCollapseButton` \ -**Style**: `AccessKey=e` +[![Unified](https://raw.githubusercontent.com/ramensoftware/windows-11-notification-center-styling-guide/main/Themes/Unified/screenshot-small.png) +\ +Unified](https://github.com/ramensoftware/windows-11-notification-center-styling-guide/blob/main/Themes/Unified/README.md) ## Advanced styling @@ -116,6 +90,36 @@ specified as following: `Style@VisualState=Value`, in which case the style will only apply when the visual state group specified in the target matches the specified visual state. +A couple of practical examples: + +#### Hide the focus assist section +**Target**: `ActionCenter.FocusSessionControl` \ +**Style**: `Height=0` + +#### Square the corners of the notification center +**Target**: `Windows.UI.Xaml.Controls.Grid#NotificationCenterGrid` \ +**Style**: `CornerRadius=0` + +#### Square the corners of the calendar +**Target**: `Windows.UI.Xaml.Controls.Grid#CalendarCenterGrid` \ +**Style**: `CornerRadius=0` + +#### Square the corners of the quick action center +**Target**: `Windows.UI.Xaml.Controls.Grid#ControlCenterRegion` \ +**Style**: `CornerRadius=0` + +#### Calendar and notification titlebars: titles on the right, buttons on the left +**Target**: `Windows.UI.Xaml.Controls.Grid#RootContent` \ +**Style**: `FlowDirection=1` + +#### Add accelerator key (ALT+X) to clear all notifications +**Target**: `Windows.UI.Xaml.Controls.Button#ClearAll` \ +**Style**: `AccessKey=x` + +#### Add accelerator key (ALT+E) to expand/collapse the calendar +**Target**: `Windows.UI.Xaml.Controls.Button#ExpandCollapseButton` \ +**Style**: `AccessKey=e` + ### Resource variables Some variables, such as size and padding for various controls, are defined as @@ -140,6 +144,7 @@ code from the **TranslucentTB** project. $options: - "": None - TranslucentShell: TranslucentShell + - Unified: Unified - controlStyles: - - target: "" $name: Target @@ -238,6 +243,31 @@ const Theme g_themeTranslucentShell = {{ L"CornerRadius=15"}}, }}; +const Theme g_themeUnified = {{ + ThemeTargetStyles{L"ActionCenter.FocusSessionControl", { + L"Height=0"}}, + ThemeTargetStyles{L"Windows.UI.Xaml.Controls.Grid#ControlCenterRegion", { + L"CornerRadius=0"}}, + ThemeTargetStyles{L"Windows.UI.Xaml.Controls.Grid#CalendarCenterGrid", { + L"CornerRadius=0", + L"Margin=0,0,0,12", + L"BorderThickness=1,0,1,1"}}, + ThemeTargetStyles{L"Windows.UI.Xaml.Controls.Grid#NotificationCenterGrid", { + L"CornerRadius=0", + L"BorderThickness=1,1,1,0"}}, + ThemeTargetStyles{L"Windows.UI.Xaml.Controls.CalendarViewDayItem", { + L"CornerRadius=0", + L"Margin=1,1,1,1"}}, + ThemeTargetStyles{L"Windows.UI.Xaml.Controls.CalendarViewDayItem > Windows.UI.Xaml.Controls.Border", { + L"CornerRadius=3"}}, + ThemeTargetStyles{L"Windows.UI.Xaml.Controls.Grid#MediaTransportControlsRegion", { + L"CornerRadius=0", + L"BorderThickness=1,1,1,0", + L"Margin=0,0,0,0"}}, + ThemeTargetStyles{L"QuickActions.ControlCenter.FrameWithContentChanged#L2Frame", { + L"CornerRadius=0"}}, +}}; + // clang-format on std::atomic g_initialized; @@ -333,7 +363,25 @@ VisualTreeWatcher::VisualTreeWatcher(winrt::com_ptr site) : m_XamlDiagnostics(site.as()) { Wh_Log(L"Constructing VisualTreeWatcher"); - winrt::check_hresult(m_XamlDiagnostics.as()->AdviseVisualTreeChange(this)); + // winrt::check_hresult(m_XamlDiagnostics.as()->AdviseVisualTreeChange(this)); + + // Calling AdviseVisualTreeChange from the current thread causes the app to + // hang on Windows 10 in Advising::RunOnUIThread. Creating a new thread and + // calling it from there fixes it. + HANDLE thread = CreateThread( + nullptr, 0, + [](LPVOID lpParam) -> DWORD { + auto watcher = reinterpret_cast(lpParam); + HRESULT hr = watcher->m_XamlDiagnostics.as()->AdviseVisualTreeChange(watcher); + if (FAILED(hr)) { + Wh_Log(L"Error %08X", hr); + } + return 0; + }, + this, 0, nullptr); + if (thread) { + CloseHandle(thread); + } } VisualTreeWatcher::~VisualTreeWatcher() @@ -1584,6 +1632,8 @@ void ProcessAllStylesFromSettings() { const Theme* theme = nullptr; if (wcscmp(themeName, L"TranslucentShell") == 0) { theme = &g_themeTranslucentShell; + } else if (wcscmp(themeName, L"Unified") == 0) { + theme = &g_themeUnified; } Wh_FreeStringSetting(themeName); From ce129c4525faef315041ebdba6cf363fd64fe991 Mon Sep 17 00:00:00 2001 From: aubrey <44238627+aubymori@users.noreply.github.com> Date: Sat, 7 Sep 2024 10:47:10 -0500 Subject: [PATCH 08/52] Classic Clock Button Behavior 1.0.0 (#936) --- mods/classic-clock-button-behavior.wh.cpp | 90 +++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 mods/classic-clock-button-behavior.wh.cpp diff --git a/mods/classic-clock-button-behavior.wh.cpp b/mods/classic-clock-button-behavior.wh.cpp new file mode 100644 index 00000000..648a9331 --- /dev/null +++ b/mods/classic-clock-button-behavior.wh.cpp @@ -0,0 +1,90 @@ +// ==WindhawkMod== +// @id classic-clock-button-behavior +// @name Classic Clock Button Behavior +// @description Makes the tray clock open the Date and Time CPL on double click +// @version 1.0.0 +// @author aubymori +// @github https://github.com/aubymori +// @include explorer.exe +// @architecture x86-64 +// ==/WindhawkMod== + +// ==WindhawkModReadme== +/* +# Classic Clock Button Behavior +In Windows XP and before, the tray clock did nothing on single click, and opened +the Date and Time CPL on double click. This mod makes the Windows 10 tray clock +button do that. +*/ +// ==/WindhawkModReadme== + +#include +#include + +constexpr UINT_PTR DOUBLE_CLICK_TIMER_ID = 5500; + +bool g_bDoubleClicked = false; + +void CALLBACK TimerProc(HWND hWnd, UINT uMsg, UINT_PTR nIDEvent, DWORD dwTime) +{ + g_bDoubleClicked = false; + KillTimer(hWnd, nIDEvent); +} + +#define ClockButton_Window(pThis) *((HWND *)pThis + 1) + +HRESULT (*ClockButton_v_OnClick_orig)(void *, UINT); +HRESULT ClockButton_v_OnClick_hook( + void *pThis, + UINT uClickDevice +) +{ + if (g_bDoubleClicked) + { + g_bDoubleClicked = false; + ShellExecuteW( + ClockButton_Window(pThis), + NULL, + L"timedate.cpl", + NULL, + NULL, + SW_SHOWNORMAL + ); + } + else + { + g_bDoubleClicked = true; + SetTimer( + NULL, + DOUBLE_CLICK_TIMER_ID, + GetDoubleClickTime(), + TimerProc + ); + } + return S_OK; +} + +const WindhawkUtils::SYMBOL_HOOK explorerExeHooks[] = { + { + { + L"protected: virtual long __cdecl ClockButton::v_OnClick(enum ClickDevice)" + }, + &ClockButton_v_OnClick_orig, + ClockButton_v_OnClick_hook, + false + } +}; + +BOOL Wh_ModInit(void) +{ + if (!WindhawkUtils::HookSymbols( + GetModuleHandleW(NULL), + explorerExeHooks, + ARRAYSIZE(explorerExeHooks) + )) + { + Wh_Log(L"Failed to hook one or more symbol functions in explorer.exe"); + return FALSE; + } + return TRUE; +} \ No newline at end of file From 2914b84cba2528701e51fcb904fe379e9bfd982d Mon Sep 17 00:00:00 2001 From: Michael Maltsev <4129781+m417z@users.noreply.github.com> Date: Sun, 8 Sep 2024 10:08:16 +0300 Subject: [PATCH 09/52] Customize Windows notifications placement v1.0.1: (#938) * The mod was tested on Windows 10 and was found to be working correctly. The mod name and description were updated accordingly. * Made the mod work on non-English Windows versions. --- mods/notifications-placement.wh.cpp | 61 ++++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 6 deletions(-) diff --git a/mods/notifications-placement.wh.cpp b/mods/notifications-placement.wh.cpp index 4cdc4708..7acfd666 100644 --- a/mods/notifications-placement.wh.cpp +++ b/mods/notifications-placement.wh.cpp @@ -1,8 +1,8 @@ // ==WindhawkMod== // @id notifications-placement -// @name Customize Windows 11 notifications placement +// @name Customize Windows notifications placement // @description Move notifications to another monitor or another corner of the screen -// @version 1.0 +// @version 1.0.1 // @author m417z // @github https://github.com/m417z // @twitter https://twitter.com/m417z @@ -23,11 +23,11 @@ // ==WindhawkModReadme== /* -# Customize Windows 11 notifications placement +# Customize Windows notifications placement Move notifications to another monitor or another corner of the screen. -Only Windows 11 is supported. +Only Windows 10 64-bit and Windows 11 are supported. ![Screenshot](https://i.imgur.com/4PxMvLg.png) */ @@ -60,6 +60,7 @@ Only Windows 11 is supported. #include #include +#include #include std::atomic g_unloading; @@ -173,9 +174,57 @@ bool IsTargetCoreWindow(HWND hWnd) { return false; } - WCHAR szWindowText[32]; + // The window title is locale-dependent, and unfortunately I didn't find a + // simpler way to identify the target window. + // String source: Windows.UI.ShellCommon..pri + // String resource: \ActionCenter\AC_ToastCenter_Title + // The strings were collected from here: + // https://github.com/m417z/windows-language-files + static const std::unordered_set newNotificationStrings = { + L"Jakinarazpen berria", + L"Jauns paziņojums", + L"Naujas pranešimas", + L"Neue Benachrichtigung", + L"New notification", + L"Nieuwe melding", + L"Notificació nova", + L"Notificación nueva", + L"Notificare nouă", + L"Nouvelle notification", + L"Nova notificação", + L"Nova notificación", + L"Nova obavijesti", + L"Nové oznámení", + L"Nové oznámenie", + L"Novo obaveštenje", + L"Novo obvestilo", + L"Nowe powiadomienie", + L"Nueva notificación", + L"Nuova notifica", + L"Ny meddelelse", + L"Ny varsling", + L"Nytt meddelande", + L"Pemberitahuan baru", + L"Thông báo mới", + L"Új értesítés", + L"Uus teatis", + L"Uusi ilmoitus", + L"Yeni bildirim", + L"Νέα ειδοποίηση", + L"Нове сповіщення", + L"Ново известие", + L"Новое уведомление", + L"הודעה חדשה", + L"\u200f\u200fإعلام جديد", + L"การแจ้งให้ทราบใหม่", + L"새 알림", + L"新しい通知", + L"新通知", + }; + + WCHAR szWindowText[256]; if (GetWindowText(hWnd, szWindowText, ARRAYSIZE(szWindowText)) == 0 || - wcscmp(szWindowText, L"New notification") != 0) { + !newNotificationStrings.contains(szWindowText)) { return false; } From 40c94993b0e85ef8ed053f5b5b0e06b16380e42d Mon Sep 17 00:00:00 2001 From: Toru the Red Fox Date: Sun, 8 Sep 2024 11:42:30 +0100 Subject: [PATCH 10/52] Logon, Logoff & Shutdown Sounds Restored - v1.0.0 (#937) * Create logon-logoff-shutdown-sounds.wh.cpp * Hopefully make it now work on older versions * x86 fix and follow hook naming convention --- mods/logon-logoff-shutdown-sounds.wh.cpp | 586 +++++++++++++++++++++++ 1 file changed, 586 insertions(+) create mode 100644 mods/logon-logoff-shutdown-sounds.wh.cpp diff --git a/mods/logon-logoff-shutdown-sounds.wh.cpp b/mods/logon-logoff-shutdown-sounds.wh.cpp new file mode 100644 index 00000000..e3140b9d --- /dev/null +++ b/mods/logon-logoff-shutdown-sounds.wh.cpp @@ -0,0 +1,586 @@ +// ==WindhawkMod== +// @id logon-logoff-shutdown-sounds +// @name Logon, Logoff & Shutdown Sounds Restored +// @description Restores the logon, logoff and shutdown sounds from earlier versions of Windows +// @version 1.0.0 +// @author Toru the Red Fox +// @github https://github.com/TorutheRedFox +// @twitter https://twitter.com/TorutheRedFox +// @include explorer.exe +// @compilerOptions -lcomdlg32 -lshlwapi -lwinmm -luser32 +// ==/WindhawkMod== + +// ==WindhawkModReadme== +/* +# Logon Sounds Restored +Restores the logon, logoff and shutdown sounds from earlier versions of Windows, simple as. + +It is recommended to use [these reg files](https://www.howtogeek.com/wp-content/uploads/2016/09/Shutdown-Logoff-Logon-Sound-Hacks.zip) to restore the sound events to the Sound control panel applet. +*/ +// ==/WindhawkModReadme== + +// ==WindhawkModSettings== +/* + +- xpmode: false + $name: XP Startup behavior + $description: Plays the startup sound on logon. Resuming will still play the logon sound. It is recommended to untick the box to play the startup sound in the control panel to prevent it from playing before you log on. + +*/ +// ==/WindhawkModSettings== + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// settings +struct { + bool bXpMode; +} settings; + +// the various global handles used + +HMODULE g_hShlwapi; +HWND g_hwSound; +HANDLE g_hSoundThread; +HMODULE g_hExplorer; + +// NOTE: sligtly different versions of this exist in... +// SHPlaySound() -> shell32 +// IEPlaySound() -> shdocvw/browseui + +STDAPI_(void) ExplorerPlaySound(LPCTSTR pszSound) +{ + // note, we have access only to global system sounds here as we use "Apps\.Default" + TCHAR szKey[256]; + wsprintf(szKey, TEXT("AppEvents\\Schemes\\Apps\\.Default\\%s\\.current"), pszSound); + + TCHAR szFileName[MAX_PATH]; + szFileName[0] = 0; + LONG cbSize = sizeof(szFileName); + + // test for an empty string, PlaySound will play the Default Sound if we + // give it a sound it cannot find... + + if ((RegQueryValue(HKEY_CURRENT_USER, szKey, szFileName, &cbSize) == ERROR_SUCCESS) + && szFileName[0]) + { + // flags are relevant, we try to not stomp currently playing sounds + PlaySound(szFileName, NULL, SND_FILENAME | SND_ASYNC | SND_NODEFAULT | SND_NOSTOP); + } +} + +typedef LRESULT WINAPI (* _OnSessionChange_t)(void* _this, WPARAM wParam, LPARAM lParam); +_OnSessionChange_t _OnSessionChange_orig; +LRESULT WINAPI _OnSessionChange_hook(void* _this, WPARAM wParam, LPARAM lParam) +{ + if ((WTS_SESSION_LOCK == wParam) || (WTS_SESSION_UNLOCK == wParam)) + { + if (wParam == WTS_SESSION_LOCK) + { + ExplorerPlaySound(TEXT("WindowsLogoff")); + } + else if (wParam == WTS_SESSION_UNLOCK) + { + ExplorerPlaySound(TEXT("WindowsLogon")); + } + } + + return _OnSessionChange_orig(_this, wParam, lParam); +} + +#ifndef SND_SYSTEM // because this is sometimes missing??? +#define SND_SYSTEM 0x00200000 +#endif + +DWORD WINAPI PlaySoundFileThreadProc( LPVOID lpParam ) +{ + PlaySoundW((WCHAR*)lpParam, 0, SND_NODEFAULT | SND_MEMORY | SND_SYSTEM); + LocalFree(lpParam); + return 0; +} + +DWORD WINAPI GetLastErrorError() +{ + DWORD hr = GetLastError(); + if ( hr == S_OK ) + return 1; + return hr; +} + +HRESULT WINAPI HRESULTFromLastErrorError() +{ + HRESULT hr = GetLastErrorError(); + if ( FAILED(hr) ) + return HRESULT_FROM_WIN32((USHORT)hr); + return hr; +} + +HRESULT WINAPI PlaySoundFile(HANDLE* hCurrentThread, LPCWSTR lpszFileName) { + DWORD szSoundFile; // eax + signed int hr = 0; // esi + DWORD NumberOfBytesRead; // [esp+8h] [ebp-Ch] BYREF + HANDLE hFile; // [esp+Ch] [ebp-8h] + LPVOID lpSndBuf = 0; // [esp+10h] [ebp-4h] + + hFile = CreateFile( + lpszFileName, + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, + NULL + ); + + if (hFile != INVALID_HANDLE_VALUE) { + szSoundFile = GetFileSize(hFile, 0); + hr = E_OUTOFMEMORY; + if (szSoundFile != INVALID_FILE_SIZE && szSoundFile >= 0 && szSoundFile < 0x400000) { + lpSndBuf = LocalAlloc(0, szSoundFile); + if (lpSndBuf && ReadFile(hFile, lpSndBuf, szSoundFile, &NumberOfBytesRead, 0)) { + hr = szSoundFile != NumberOfBytesRead ? HRESULT_FROM_WIN32(ERROR_IO_PENDING) : 0; + } else { + hr = HRESULTFromLastErrorError(); + } + } + CloseHandle(hFile); + } else { + hr = HRESULTFromLastErrorError(); + } + + if (SUCCEEDED(hr)) { + HANDLE hPlaySndThread = CreateThread(0, 0, PlaySoundFileThreadProc, lpSndBuf, 0, 0); + if (hPlaySndThread) { + if (hCurrentThread) + *hCurrentThread = hPlaySndThread; + else + CloseHandle(hPlaySndThread); + return hr; + } + hr = HRESULTFromLastErrorError(); + } + + if (lpSndBuf) + LocalFree(lpSndBuf); + + return hr; +} + +typedef enum _ELOGONLOGOFFSOUNDTYPE +{ + ST_LOGON, + ST_LOGOFF, + ST_EXIT, + ST_START, +} ELOGONLOGOFFSOUNDTYPE; + +HRESULT PlayLogonLogoffSound(HANDLE* hThread, ELOGONLOGOFFSOUNDTYPE enSoundType) +{ + const wchar_t *szSoundType; + HRESULT hr; + DWORD pcbData[4]; + WCHAR szSubKey[264]; + WCHAR pvData[264]; + + switch (enSoundType) { + case ST_LOGON: + szSoundType = TEXT("WindowsLogon"); + break; + case ST_LOGOFF: + szSoundType = TEXT("WindowsLogoff"); + break; + case ST_EXIT: + szSoundType = TEXT("SystemExit"); + break; + case ST_START: + szSoundType = TEXT("SystemStart"); + break; + } + + pcbData[0] = 520; + + hr = wsprintf(szSubKey, TEXT("AppEvents\\Schemes\\Apps\\.Default\\%ws\\.Current"), szSoundType); + + if (SUCCEEDED(hr)) + { + hr = RegGetValue(HKEY_CURRENT_USER, szSubKey, 0, 2u, 0, pvData, pcbData); + if (hr == S_OK) + return PlaySoundFile(hThread, pvData); + else + return HRESULT_FROM_WIN32((USHORT)hr); + } + return hr; +} + +LRESULT SHDefWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + if (IsWindowUnicode(hwnd)) + { + return DefWindowProcW(hwnd, uMsg, wParam, lParam); + } + else + { + return DefWindowProcA(hwnd, uMsg, wParam, lParam); + } +} + +STDAPI_(BOOL) SHRegisterClassW(const WNDCLASSW* pwc) +{ + WNDCLASSW wc; + if (!GetClassInfo(pwc->hInstance, pwc->lpszClassName, &wc)) + { + return RegisterClass(pwc); + } + return TRUE; +} + +HWND WINAPI SHCreateWorkerWindowW(WNDPROC pfnWndProc, HWND hwndParent, DWORD dwExStyle, DWORD dwFlags, HMENU hmenu, void * p) +{ + WNDCLASSW wc = {0}; + + wc.lpfnWndProc = DefWindowProcW; + wc.cbWndExtra = sizeof(void *); + wc.hInstance = g_hShlwapi; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1); + wc.lpszClassName = L"WorkerW"; + //dwExStyle |= IsBiDiLocalizedSystem() ? /*dwExStyleRTLMirrorWnd*/0L : 0L; + + SHRegisterClassW(&wc); + + HWND hwnd = CreateWindowExW(dwExStyle, L"WorkerW", NULL, dwFlags, + 0, 0, 0, 0, hwndParent, + (HMENU)hmenu, g_hShlwapi, NULL); + if (hwnd) + { + SetWindowLongPtr(hwnd, 0, (LPARAM)(p)); + + // Note: Must explicitly use W version to avoid charset thunks + if (pfnWndProc) + SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONG_PTR)pfnWndProc); + + } + + return hwnd; +} + +class CSoundWnd { + public: + WINAPI CSoundWnd(); + LONG m_refCount; + HWND m_hwndSound; + HANDLE m_thread; + BOOL WINAPI Init(); + static DWORD CALLBACK s_ThreadProc(void* lpParam); + LONG WINAPI Release(); + static DWORD CALLBACK s_CreateWindow(void* lpParam); + static LRESULT CALLBACK s_WndProc(HWND hWnd, + UINT msg, + WPARAM wParam, + LPARAM lParam); + LRESULT WINAPI v_WndProc(HWND hWnd, UINT mst, WPARAM wParam, LPARAM lParam); +}; + +WINAPI CSoundWnd::CSoundWnd() + : m_refCount(1) + , m_hwndSound(nullptr) + , m_thread(nullptr) +{ +} + +BOOL WINAPI CSoundWnd::Init() +{ + if (!SHCreateThread(s_ThreadProc, this, CTF_COINIT_STA | CTF_PROCESS_REF, s_CreateWindow)) { + DWORD dwLastError = GetLastError(); + LPWSTR pszMessageBuffer = nullptr; + + //Ask Win32 to give us the string version of that message ID. + //The parameters we pass in, tell Win32 to create the buffer that holds the message for us (because we don't yet know how long the message string will be). + FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dwLastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&pszMessageBuffer, 0, NULL); + + Wh_Log(TEXT("CSoundWnd::Init Failed to create thread %s"), pszMessageBuffer); + } + g_hwSound = this->m_hwndSound; + SetProcessShutdownParameters(0x4FF, NULL); + return this->m_hwndSound != NULL; +} + +DWORD CALLBACK CSoundWnd::s_CreateWindow(void* lpParam) +{ + InterlockedIncrement(&((CSoundWnd*)lpParam)->m_refCount); + + ((CSoundWnd*)lpParam)->m_hwndSound = SHCreateWorkerWindowW(s_WndProc, 0, 0, 0, 0, lpParam); + return 0; +} + +__inline void * GetWindowPtr0(HWND hWnd) { + return (void *)GetWindowLongPtrA(hWnd, 0); +} + +DWORD CALLBACK CSoundWnd::s_ThreadProc(void* lpParam) +{ + struct tagMSG Msg; // [esp+8h] [ebp-1Ch] BYREF + CSoundWnd* _this = static_cast(lpParam); + + if ( _this->m_hwndSound ) + { + while ( GetMessageW(&Msg, 0, 0, 0) ) + { + TranslateMessage(&Msg); + DispatchMessageW(&Msg); + } + } + _this->Release(); + return 0; +} + +LRESULT CALLBACK CSoundWnd::s_WndProc(HWND hWnd, + UINT msg, + WPARAM wParam, + LPARAM lParam) { + CSoundWnd* soundWnd = (CSoundWnd*)GetWindowPtr0(hWnd); + if (soundWnd) + return soundWnd->v_WndProc(hWnd, msg, wParam, lParam); + else + return SHDefWindowProc(hWnd, msg, wParam, lParam); +} + +LRESULT WINAPI CSoundWnd::v_WndProc(HWND hWnd, + UINT msg, + WPARAM wParam, + LPARAM lParam) { + WCHAR szShutdownReason[256]; + + switch (msg) { + case WM_QUERYENDSESSION: + if ((lParam & ENDSESSION_CRITICAL) == FALSE) { + LoadString(GetModuleHandle(NULL), 0x2DBu, szShutdownReason, ARRAYSIZE(szShutdownReason)); + ShutdownBlockReasonCreate(this->m_hwndSound, szShutdownReason); + PlayLogonLogoffSound(&this->m_thread, (lParam & ENDSESSION_LOGOFF) != 0 ? ST_LOGOFF : ST_EXIT); + if (this->m_thread) { + g_hSoundThread = this->m_thread; + WaitForSingleObject(this->m_thread, INFINITE); + CloseHandle(this->m_thread); + } + } + return 1; + case WM_ENDSESSION: + if (wParam && (lParam & ENDSESSION_CRITICAL) == FALSE && this->m_thread) { + WaitForSingleObject(this->m_thread, INFINITE); + CloseHandle(this->m_thread); + } + DestroyWindow(this->m_hwndSound); + return SHDefWindowProc(hWnd, msg, wParam, lParam); + case WM_NCDESTROY: + SetWindowLong(hWnd, 0, 0); + g_hwSound = NULL; + this->m_hwndSound = NULL; + PostQuitMessage(0); + return 0; + default: + return SHDefWindowProc(hWnd, msg, wParam, lParam); + } +} + +LONG WINAPI CSoundWnd::Release() +{ + LONG lRefCount = InterlockedDecrement(&this->m_refCount); + if ( !lRefCount ) + operator delete((void *)this); + return lRefCount; +} + +BOOL WINAPI InitSoundWindow() +{ + BOOL bSuccess = FALSE; + CSoundWnd* soundWnd = new CSoundWnd(); + if (soundWnd) + { + bSuccess = soundWnd->Init(); + soundWnd->Release(); + } + return bSuccess; +} + +DWORD WINAPI GetCurrentSessionId() +{ + DWORD pSessionId; + DWORD CurrentProcessId = GetCurrentProcessId(); + ProcessIdToSessionId(CurrentProcessId, &pSessionId); + return pSessionId; +} + +HRESULT WINAPI SHCreateSessionKey(REGSAM samDesired, PHKEY phKey) +{ + HRESULT hr = S_OK; + static WCHAR wszSessionKey[256]; + LONG Error; + + if (DWORD dwSessionId = GetCurrentSessionId()) + { + swprintf(wszSessionKey, + L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\SessionInfo\\%d", + dwSessionId); + } + else + hr = HRESULT_FROM_WIN32(GetLastError()); + + if(SUCCEEDED(hr)) + { + Error = RegCreateKeyExW(HKEY_LOCAL_MACHINE, wszSessionKey, 0, NULL, + REG_OPTION_VOLATILE, samDesired, NULL, phKey, NULL); + if (Error != ERROR_SUCCESS) + hr = HRESULT_FROM_WIN32(Error); + } + + return hr; +} + +// a three state boolean for bools that need initialization +typedef enum +{ + TRIBIT_UNDEFINED = 0, + TRIBIT_TRUE, + TRIBIT_FALSE, +} TRIBIT; + +TRIBIT g_tbPlayedStartupSound; +DWORD g_dwCurProcId; + +BOOL WINAPI HasPerLogonActionBeenDone(LPCWSTR lpSubKey, TRIBIT* out_tbDone) +{ + DWORD dwDisposition; + HKEY hKey; + HKEY phkResult; + + *out_tbDone = TRIBIT_TRUE; + if ( (int)SHCreateSessionKey(131078, &hKey) >= 0 ) + { + if ( !RegCreateKeyExW(hKey, lpSubKey, 0, 0, 1u, 0x20006u, 0, &phkResult, &dwDisposition) ) + { + RegCloseKey(phkResult); + if ( dwDisposition == 1 ) + *out_tbDone = TRIBIT_FALSE; + } + RegCloseKey(hKey); + } + return *out_tbDone == TRIBIT_TRUE; +} + +BOOL WINAPI HasLogonSoundBeenPlayed() +{ + return HasPerLogonActionBeenDone(L"LogonSoundHasBeenPlayed", &g_tbPlayedStartupSound); +} + +BOOL WINAPI IsDesktopProcess() { + DWORD dwShellProcId = NULL; + DWORD dwCurProcId = GetCurrentProcessId(); + HWND hShellWindow = FindWindow(TEXT("Shell_TrayWnd"), NULL); + if (!hShellWindow) // no desktop present yet, assume it's main explorer starting up + { + g_dwCurProcId = dwCurProcId; + return TRUE; + } + GetWindowThreadProcessId(hShellWindow, &dwShellProcId); + if (dwCurProcId == dwShellProcId || !hShellWindow) { + if (dwCurProcId != g_dwCurProcId) // because the dll handle is shared, the boolean needs to be reset when the explorer process changes + { + g_tbPlayedStartupSound = TRIBIT_UNDEFINED; + g_dwCurProcId = dwCurProcId; + } + return TRUE; + } else { + g_dwCurProcId = dwCurProcId; + return FALSE; + } +} + +// Load the settings for the mod +void LoadSettings(void) +{ + settings.bXpMode = (BOOL)Wh_GetIntSetting(L"xpmode"); +} + +// The mod is being initialized, load settings, hook functions, and do other +// initialization stuff if required. +BOOL Wh_ModInit() { + Wh_Log(L"Init"); + + LoadSettings(); + + g_hExplorer = GetModuleHandleW(NULL); + if (!g_hExplorer) + Wh_Log(L"Failed to get Explorer's handle"); + + g_hShlwapi = LoadLibrary(TEXT("shlwapi.dll")); + + WindhawkUtils::SYMBOL_HOOK explorerExeHooks[] = { + { + { + #ifdef _WIN64 + L"protected: __int64 __cdecl CTray::_OnSessionChange(unsigned __int64,__int64)" + #else + L"protected: long __thiscall CTray::_OnSessionChange(unsigned int,long)" + #endif + }, + (void **)&_OnSessionChange_orig, + (void *)_OnSessionChange_hook, + false + } + }; + + if (!WindhawkUtils::HookSymbols( + g_hExplorer, + explorerExeHooks, + ARRAYSIZE(explorerExeHooks) + )) + { + Wh_Log(L"Failed to hook _OnSessionChange"); + return FALSE; + } + + if (IsDesktopProcess()) { + InitSoundWindow(); + SetProcessShutdownParameters(0x4FF, NULL); + if (!HasLogonSoundBeenPlayed()) + PlayLogonLogoffSound(NULL, settings.bXpMode ? ST_START : ST_LOGON); + else + Wh_Log(TEXT("Logon sound already played in this session.")); + } + + return TRUE; +} + +void Wh_ModSettingsChanged(void) +{ + LoadSettings(); +} + +VOID Wh_ModUninit() +{ + Wh_Log(TEXT("Exiting")); + + if (g_hSoundThread) // make sure DLL stays alive until we finish playing + WaitForSingleObject(g_hSoundThread, INFINITE); + + if (g_hwSound) { + PostMessage(g_hwSound, WM_CLOSE, NULL, NULL); + g_hwSound = NULL; + } + + if (g_hExplorer) { + CloseHandle(g_hExplorer); + } + + if (g_hShlwapi) { + CloseHandle(g_hShlwapi); + } +} From c17ce99483d75133beaed805c705e10872660059 Mon Sep 17 00:00:00 2001 From: Michael Maltsev <4129781+m417z@users.noreply.github.com> Date: Sun, 8 Sep 2024 13:51:15 +0300 Subject: [PATCH 11/52] Update VERIFIED_TWITTER_ACCOUNTS in pr_validation.py --- .github/pr_validation.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/pr_validation.py b/.github/pr_validation.py index a33c449d..92c4c94f 100644 --- a/.github/pr_validation.py +++ b/.github/pr_validation.py @@ -25,6 +25,8 @@ 'https://twitter.com/learn_more': 'https://github.com/learn-more', 'https://twitter.com/realgam3': 'https://github.com/realgam3', 'https://twitter.com/teknixstuff': 'https://github.com/teknixstuff', + 'https://twitter.com/kawaipure': 'https://github.com/kawapure', + 'https://twitter.com/TorutheRedFox': 'https://github.com/TorutheRedFox', } MOD_METADATA_PARAMS = { From f00161757c50921c354688dd50fd0ef968fe36ac Mon Sep 17 00:00:00 2001 From: aubrey <44238627+aubymori@users.noreply.github.com> Date: Sun, 8 Sep 2024 13:09:57 -0500 Subject: [PATCH 12/52] Old This PC Commands 1.0.1: Codebase cleanup (#941) Codebase cleanup - Remove unnecessary `#include` and `DEFINE_GUID` statements - Rely on public `IOpenControlPanel` interface instead of finding `COpenControlPanel::Open` from symbols - Move symbol hook definitions and update the name to match modern convention - Properly free memory in `OpenCplPage` --- mods/old-this-pc-commands.wh.cpp | 119 ++++++++++++++----------------- 1 file changed, 52 insertions(+), 67 deletions(-) diff --git a/mods/old-this-pc-commands.wh.cpp b/mods/old-this-pc-commands.wh.cpp index 3a74b077..49680132 100644 --- a/mods/old-this-pc-commands.wh.cpp +++ b/mods/old-this-pc-commands.wh.cpp @@ -2,7 +2,7 @@ // @id old-this-pc-commands // @name Old This PC Commands // @description Makes "Open Settings", "System properties", etc. in This PC use Control Panel instead of Settings -// @version 1.0.0 +// @version 1.0.1 // @author aubymori // @github https://github.com/aubymori // @include explorer.exe @@ -28,40 +28,33 @@ to `ExplorerFrame.dll` takes place to remove the redirect. #include #include -#include #include -#include -DEFINE_GUID(IID_IUnknown, 0x00000000, 0x0000, 0x0000, 0xc0,0x00, 0x00,0x00,0x00,0x00,0x00,0x46); -DEFINE_GUID(IID_OpenControlPanel, 0xD11AD862, 0x66DE, 0x4DF4, 0xBF,0x6C, 0x1F,0x56,0x21,0x99,0x6A,0xF1); -DEFINE_GUID(IID_IServiceProvider, 0x6D5140C1, 0x7436, 0x11CE, 0x80,0x34, 0x00,0xAA,0x00,0x60,0x09,0xFA); -DEFINE_GUID(SID_STopLevelBrowser, 0x4c96be40, 0x915C, 0x11CF, 0x99,0xD3, 0x00,0xAA,0x00,0x4A,0xE8,0x37); - -HRESULT (*COpenControlPanel_Open)(void *pThis, LPCWSTR lpPage, LPCWSTR lpSubPage, IUnknown *pu); - -HRESULT OpenCplPage(IUnknown *pu, LPCWSTR lpPage, LPCWSTR lpSubPage) +HRESULT OpenCplPage(IUnknown *punk, LPCWSTR lpPage, LPCWSTR lpSubPage) { IObjectWithSite *pows; - HRESULT hr = pu->QueryInterface(IID_IObjectWithSite, (void **)&pows); + HRESULT hr = punk->QueryInterface(IID_IObjectWithSite, (void **)&pows); if (SUCCEEDED(hr)) { - IUnknown *pu2; - hr = pows->GetSite(IID_IUnknown, (void **)&pu2); + IUnknown *punk2; + hr = pows->GetSite(IID_PPV_ARGS(&punk2)); if (SUCCEEDED(hr)) { - void *pocp; + IOpenControlPanel *pocp = nullptr; hr = CoCreateInstance( CLSID_OpenControlPanel, NULL, CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER, - IID_OpenControlPanel, - &pocp + IID_PPV_ARGS(&pocp) ); if (SUCCEEDED(hr)) { - hr = COpenControlPanel_Open(pocp, lpPage, lpSubPage, pu2); + hr = pocp->Open(lpPage, lpSubPage, punk2); + pocp->Release(); } + punk2->Release(); } + pows->Release(); } return hr; } @@ -71,52 +64,79 @@ typedef HRESULT (*CDrivesViewCallback__OnEventPunkSite)(IUnknown *, IUnknown *, CDrivesViewCallback__OnEventPunkSite CDrivesViewCallback__OnOpenSystemSettingsPunkSite_orig; HRESULT CDrivesViewCallback__OnOpenSystemSettingsPunkSite_hook( - IUnknown *pu, - IUnknown *pu2, + IUnknown *punk, + IUnknown *punkRibbon, IShellItemArray *psia, IBindCtx *pbctx ) { - HRESULT hr = OpenCplPage(pu, NULL, NULL); + HRESULT hr = OpenCplPage(punk, NULL, NULL); if (!SUCCEEDED(hr)) { - hr = OpenCplPage(pu2, NULL, NULL); + hr = OpenCplPage(punkRibbon, NULL, NULL); } return hr; } CDrivesViewCallback__OnEventPunkSite CDrivesViewCallback__OnSystemPropertiesPunkSite_orig; HRESULT CDrivesViewCallback__OnSystemPropertiesPunkSite_hook( - IUnknown *pu, - IUnknown *pu2, + IUnknown *punk, + IUnknown *punkRibbon, IShellItemArray *psia, IBindCtx *pbctx ) { - HRESULT hr = OpenCplPage(pu, L"Microsoft.System", NULL); + HRESULT hr = OpenCplPage(punk, L"Microsoft.System", NULL); if (!SUCCEEDED(hr)) { - hr = OpenCplPage(pu2, L"Microsoft.System", NULL); + hr = OpenCplPage(punkRibbon, L"Microsoft.System", NULL); } return hr; } CDrivesViewCallback__OnEventPunkSite CDrivesViewCallback__OnAddRemoveProgramsPunkSite_orig; HRESULT CDrivesViewCallback__OnAddRemoveProgramsPunkSite_hook( - IUnknown *pu, - IUnknown *pu2, + IUnknown *punk, + IUnknown *punkRibbon, IShellItemArray *psia, IBindCtx *pbctx ) { - HRESULT hr = OpenCplPage(pu, L"Microsoft.ProgramsAndFeatures", NULL); + HRESULT hr = OpenCplPage(punk, L"Microsoft.ProgramsAndFeatures", NULL); if (!SUCCEEDED(hr)) { - hr = OpenCplPage(pu2, L"Microsoft.ProgramsAndFeatures", NULL); + hr = OpenCplPage(punkRibbon, L"Microsoft.ProgramsAndFeatures", NULL); } return hr; } +const WindhawkUtils::SYMBOL_HOOK shell32DllHooks[] = { + { + { + L"public: static long __cdecl CDrivesViewCallback::_OnOpenSystemSettingsPunkSite(struct IUnknown *,struct IUnknown *,struct IShellItemArray *,struct IBindCtx *)" + }, + &CDrivesViewCallback__OnOpenSystemSettingsPunkSite_orig, + CDrivesViewCallback__OnOpenSystemSettingsPunkSite_hook, + false + }, + { + { + L"public: static long __cdecl CDrivesViewCallback::_OnSystemPropertiesPunkSite(struct IUnknown *,struct IUnknown *,struct IShellItemArray *,struct IBindCtx *)" + }, + &CDrivesViewCallback__OnSystemPropertiesPunkSite_orig, + CDrivesViewCallback__OnSystemPropertiesPunkSite_hook, + false + }, + { + { + L"public: static long __cdecl CDrivesViewCallback::_OnAddRemoveProgramsPunkSite(struct IUnknown *,struct IUnknown *,struct IShellItemArray *,struct IBindCtx *)" + }, + &CDrivesViewCallback__OnAddRemoveProgramsPunkSite_orig, + CDrivesViewCallback__OnAddRemoveProgramsPunkSite_hook, + false + } +}; + BOOL Wh_ModInit(void) { HMODULE hShell32 = LoadLibraryW(L"shell32.dll"); @@ -126,45 +146,10 @@ BOOL Wh_ModInit(void) return FALSE; } - const WindhawkUtils::SYMBOL_HOOK hooks[] = { - { - { - L"public: virtual long __cdecl COpenControlPanel::Open(unsigned short const *,unsigned short const *,struct IUnknown *)" - }, - &COpenControlPanel_Open, - nullptr, - false - }, - { - { - L"public: static long __cdecl CDrivesViewCallback::_OnOpenSystemSettingsPunkSite(struct IUnknown *,struct IUnknown *,struct IShellItemArray *,struct IBindCtx *)" - }, - &CDrivesViewCallback__OnOpenSystemSettingsPunkSite_orig, - CDrivesViewCallback__OnOpenSystemSettingsPunkSite_hook, - false - }, - { - { - L"public: static long __cdecl CDrivesViewCallback::_OnSystemPropertiesPunkSite(struct IUnknown *,struct IUnknown *,struct IShellItemArray *,struct IBindCtx *)" - }, - &CDrivesViewCallback__OnSystemPropertiesPunkSite_orig, - CDrivesViewCallback__OnSystemPropertiesPunkSite_hook, - false - }, - { - { - L"public: static long __cdecl CDrivesViewCallback::_OnAddRemoveProgramsPunkSite(struct IUnknown *,struct IUnknown *,struct IShellItemArray *,struct IBindCtx *)" - }, - &CDrivesViewCallback__OnAddRemoveProgramsPunkSite_orig, - CDrivesViewCallback__OnAddRemoveProgramsPunkSite_hook, - false - } - }; - if (!WindhawkUtils::HookSymbols( hShell32, - hooks, - ARRAYSIZE(hooks) + shell32DllHooks, + ARRAYSIZE(shell32DllHooks) )) { Wh_Log(L"Failed to hook one or more symbol functions in shell32.dll"); From d2ebc37c2e5b3b8c2cda9d391238b7ac3879fd63 Mon Sep 17 00:00:00 2001 From: Michael Maltsev <4129781+m417z@users.noreply.github.com> Date: Mon, 9 Sep 2024 21:32:03 +0300 Subject: [PATCH 13/52] Cycle taskbar buttons with mouse wheel v1.1.5 (#945) * Added support for Windows 11 version 24H2. --- mods/taskbar-wheel-cycle.wh.cpp | 182 +++++++++++++++++--------------- 1 file changed, 95 insertions(+), 87 deletions(-) diff --git a/mods/taskbar-wheel-cycle.wh.cpp b/mods/taskbar-wheel-cycle.wh.cpp index b2a099f8..a2e25a51 100644 --- a/mods/taskbar-wheel-cycle.wh.cpp +++ b/mods/taskbar-wheel-cycle.wh.cpp @@ -2,7 +2,7 @@ // @id taskbar-wheel-cycle // @name Cycle taskbar buttons with mouse wheel // @description Use the mouse wheel while hovering over the taskbar to cycle between taskbar buttons (Windows 11 only) -// @version 1.1.4 +// @version 1.1.5 // @author m417z // @github https://github.com/m417z // @twitter https://twitter.com/m417z @@ -108,6 +108,15 @@ enum { kHotkeyIdRight, }; +using CTaskBtnGroup_GetGroupType_t = int(WINAPI*)(void* pThis); +CTaskBtnGroup_GetGroupType_t CTaskBtnGroup_GetGroupType; + +using CTaskBtnGroup_GetNumItems_t = int(WINAPI*)(void* pThis); +CTaskBtnGroup_GetNumItems_t CTaskBtnGroup_GetNumItems; + +using CTaskBtnGroup_GetTaskItem_t = void*(WINAPI*)(void* pThis, int index); +CTaskBtnGroup_GetTaskItem_t CTaskBtnGroup_GetTaskItem; + using CWindowTaskItem_GetWindow_t = HWND(WINAPI*)(PVOID pThis); CWindowTaskItem_GetWindow_t CWindowTaskItem_GetWindow_Original; @@ -225,36 +234,31 @@ BOOL IsMinimizedTaskItem(LONG_PTR* task_item) { BOOL TaskbarScrollRight(int button_groups_count, LONG_PTR** button_groups, int* p_button_group_index, - int* p_button_index, - int* p_buttons_count, - LONG_PTR*** p_buttons) { + int* p_button_index) { int button_group_index = *p_button_group_index; int button_index = *p_button_index; - int buttons_count = *p_buttons_count; - LONG_PTR** buttons = *p_buttons; - LONG_PTR* plp; int button_group_type; - if (button_group_index == -1 || ++button_index >= buttons_count) { + int buttons_count = + button_group_index == -1 + ? 0 + : CTaskBtnGroup_GetNumItems(button_groups[button_group_index]); + if (++button_index >= buttons_count) { do { button_group_index++; - if (button_group_index >= button_groups_count) + if (button_group_index >= button_groups_count) { return FALSE; + } - button_group_type = (int)button_groups[button_group_index][8]; + button_group_type = + CTaskBtnGroup_GetGroupType(button_groups[button_group_index]); } while (button_group_type != 1 && button_group_type != 3); - plp = (LONG_PTR*)button_groups[button_group_index][7]; - buttons_count = (int)plp[0]; - buttons = (LONG_PTR**)plp[1]; - button_index = 0; } *p_button_group_index = button_group_index; *p_button_index = button_index; - *p_buttons_count = buttons_count; - *p_buttons = buttons; return TRUE; } @@ -262,38 +266,32 @@ BOOL TaskbarScrollRight(int button_groups_count, BOOL TaskbarScrollLeft(int button_groups_count, LONG_PTR** button_groups, int* p_button_group_index, - int* p_button_index, - int* p_buttons_count, - LONG_PTR*** p_buttons) { + int* p_button_index) { int button_group_index = *p_button_group_index; int button_index = *p_button_index; - int buttons_count = *p_buttons_count; - LONG_PTR** buttons = *p_buttons; - LONG_PTR* plp; int button_group_type; if (button_group_index == -1 || --button_index < 0) { - if (button_group_index == -1) + if (button_group_index == -1) { button_group_index = button_groups_count; + } do { button_group_index--; - if (button_group_index < 0) + if (button_group_index < 0) { return FALSE; + } - button_group_type = (int)button_groups[button_group_index][8]; + button_group_type = + CTaskBtnGroup_GetGroupType(button_groups[button_group_index]); } while (button_group_type != 1 && button_group_type != 3); - plp = (LONG_PTR*)button_groups[button_group_index][7]; - buttons_count = (int)plp[0]; - buttons = (LONG_PTR**)plp[1]; - + int buttons_count = + CTaskBtnGroup_GetNumItems(button_groups[button_group_index]); button_index = buttons_count - 1; } *p_button_group_index = button_group_index; - *p_buttons_count = buttons_count; - *p_buttons = buttons; *p_button_index = button_index; return TRUE; @@ -309,20 +307,11 @@ LONG_PTR* TaskbarScrollHelper(int button_groups_count, int button_group_index, button_index; BOOL bRotateRight; int prev_button_group_index, prev_button_index; - LONG_PTR* plp; - int buttons_count; - LONG_PTR** buttons; BOOL bScrollSucceeded; button_group_index = button_group_index_active; button_index = button_index_active; - if (button_group_index != -1) { - plp = (LONG_PTR*)button_groups[button_group_index][7]; - buttons_count = (int)plp[0]; - buttons = (LONG_PTR**)plp[1]; - } - bRotateRight = TRUE; if (nRotates < 0) { bRotateRight = FALSE; @@ -334,31 +323,33 @@ LONG_PTR* TaskbarScrollHelper(int button_groups_count, while (nRotates--) { if (bRotateRight) { - bScrollSucceeded = TaskbarScrollRight( - button_groups_count, button_groups, &button_group_index, - &button_index, &buttons_count, &buttons); + bScrollSucceeded = + TaskbarScrollRight(button_groups_count, button_groups, + &button_group_index, &button_index); while (bScrollSucceeded && bSkipMinimized && - IsMinimizedTaskItem((LONG_PTR*)buttons[button_index][4])) { - bScrollSucceeded = TaskbarScrollRight( - button_groups_count, button_groups, &button_group_index, - &button_index, &buttons_count, &buttons); + IsMinimizedTaskItem((LONG_PTR*)CTaskBtnGroup_GetTaskItem( + button_groups[button_group_index], button_index))) { + bScrollSucceeded = + TaskbarScrollRight(button_groups_count, button_groups, + &button_group_index, &button_index); } } else { - bScrollSucceeded = TaskbarScrollLeft( - button_groups_count, button_groups, &button_group_index, - &button_index, &buttons_count, &buttons); + bScrollSucceeded = + TaskbarScrollLeft(button_groups_count, button_groups, + &button_group_index, &button_index); while (bScrollSucceeded && bSkipMinimized && - IsMinimizedTaskItem((LONG_PTR*)buttons[button_index][4])) { - bScrollSucceeded = TaskbarScrollLeft( - button_groups_count, button_groups, &button_group_index, - &button_index, &buttons_count, &buttons); + IsMinimizedTaskItem((LONG_PTR*)CTaskBtnGroup_GetTaskItem( + button_groups[button_group_index], button_index))) { + bScrollSucceeded = + TaskbarScrollLeft(button_groups_count, button_groups, + &button_group_index, &button_index); } } if (!bScrollSucceeded) { // If no results were found in the whole taskbar if (prev_button_group_index == -1) { - return NULL; + return nullptr; } if (bWarpAround) { @@ -371,10 +362,6 @@ LONG_PTR* TaskbarScrollHelper(int button_groups_count, button_group_index = prev_button_group_index; button_index = prev_button_index; - plp = (LONG_PTR*)button_groups[button_group_index][7]; - buttons_count = (int)plp[0]; - buttons = (LONG_PTR**)plp[1]; - break; } } @@ -384,10 +371,12 @@ LONG_PTR* TaskbarScrollHelper(int button_groups_count, } if (button_group_index == button_group_index_active && - button_index == button_index_active) - return NULL; + button_index == button_index_active) { + return nullptr; + } - return (LONG_PTR*)buttons[button_index][4]; + return (LONG_PTR*)CTaskBtnGroup_GetTaskItem( + button_groups[button_group_index], button_index); } LONG_PTR* TaskbarScroll(LONG_PTR lpMMTaskListLongPtr, @@ -395,44 +384,42 @@ LONG_PTR* TaskbarScroll(LONG_PTR lpMMTaskListLongPtr, BOOL bSkipMinimized, BOOL bWarpAround, LONG_PTR* src_task_item) { - LONG_PTR* button_group_active; - int button_group_index_active, button_index_active; - LONG_PTR* plp; - int button_groups_count; - LONG_PTR** button_groups; - int button_group_type; - int buttons_count; - LONG_PTR** buttons; - int i, j; + if (nRotates == 0) { + return nullptr; + } - if (nRotates == 0) - return NULL; + LONG_PTR* plp = + (LONG_PTR*)*EV_MM_TASKLIST_BUTTON_GROUPS_HDPA(lpMMTaskListLongPtr); + if (!plp) { + return nullptr; + } - plp = (LONG_PTR*)*EV_MM_TASKLIST_BUTTON_GROUPS_HDPA(lpMMTaskListLongPtr); - if (!plp) - return NULL; + int button_groups_count = (int)plp[0]; + LONG_PTR** button_groups = (LONG_PTR**)plp[1]; - button_groups_count = (int)plp[0]; - button_groups = (LONG_PTR**)plp[1]; + int button_group_index_active, button_index_active; if (src_task_item) { + int i; for (i = 0; i < button_groups_count; i++) { - button_group_type = (int)button_groups[i][8]; + int button_group_type = + CTaskBtnGroup_GetGroupType(button_groups[i]); if (button_group_type == 1 || button_group_type == 3) { - plp = (LONG_PTR*)button_groups[i][7]; - buttons_count = (int)plp[0]; - buttons = (LONG_PTR**)plp[1]; + int buttons_count = CTaskBtnGroup_GetNumItems(button_groups[i]); + int j; for (j = 0; j < buttons_count; j++) { - if ((LONG_PTR*)buttons[j][4] == src_task_item) { + if ((LONG_PTR*)CTaskBtnGroup_GetTaskItem( + button_groups[i], j) == src_task_item) { button_group_index_active = i; button_index_active = j; break; } } - if (j < buttons_count) + if (j < buttons_count) { break; + } } } @@ -441,12 +428,13 @@ LONG_PTR* TaskbarScroll(LONG_PTR lpMMTaskListLongPtr, button_index_active = -1; } } else { - button_group_active = + LONG_PTR* button_group_active = *EV_MM_TASKLIST_ACTIVE_BUTTON_GROUP(lpMMTaskListLongPtr); button_index_active = *EV_MM_TASKLIST_ACTIVE_BUTTON_INDEX(lpMMTaskListLongPtr); if (button_group_active && button_index_active >= 0) { + int i; for (i = 0; i < button_groups_count; i++) { if (button_groups[i] == button_group_active) { button_group_index_active = i; @@ -454,8 +442,9 @@ LONG_PTR* TaskbarScroll(LONG_PTR lpMMTaskListLongPtr, } } - if (i == button_groups_count) - return NULL; + if (i == button_groups_count) { + return nullptr; + } } else { button_group_index_active = -1; button_index_active = -1; @@ -1236,7 +1225,14 @@ bool HookSymbols(HMODULE module, if (noAddressMatchCount == symbolHooks[i].symbols.size()) { Wh_Log(L"Optional symbol %d doesn't exist (from cache)", i); + symbolResolved[i] = true; + + for (auto hookSymbol : symbolHooks[i].symbols) { + newSystemCacheStr += cacheSep; + newSystemCacheStr += hookSymbol; + newSystemCacheStr += cacheSep; + } } } @@ -1513,6 +1509,18 @@ BOOL HookTaskbarDllSymbols() { } SYMBOL_HOOK taskbarDllHooks[] = { + { + {LR"(public: virtual enum eTBGROUPTYPE __cdecl CTaskBtnGroup::GetGroupType(void))"}, + (void**)&CTaskBtnGroup_GetGroupType, + }, + { + {LR"(public: virtual int __cdecl CTaskBtnGroup::GetNumItems(void))"}, + (void**)&CTaskBtnGroup_GetNumItems, + }, + { + {LR"(public: virtual struct ITaskItem * __cdecl CTaskBtnGroup::GetTaskItem(int))"}, + (void**)&CTaskBtnGroup_GetTaskItem, + }, { {LR"(public: virtual struct HWND__ * __cdecl CWindowTaskItem::GetWindow(void))"}, (void**)&CWindowTaskItem_GetWindow_Original, From 59083d3667a6d5170f10f51bc2c112e5f32d5e1d Mon Sep 17 00:00:00 2001 From: Michael Maltsev <4129781+m417z@users.noreply.github.com> Date: Mon, 9 Sep 2024 21:37:50 +0300 Subject: [PATCH 14/52] Try to fix GitHub Actions deployment --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 4f4b9f02..98c98bf7 100644 --- a/package.json +++ b/package.json @@ -1,4 +1,5 @@ { + "type": "module", "devDependencies": { "@types/node": "^17.0.13", "@types/showdown": "^2.0.0" From 769a313ba347d2af37cb4e80255546d58d94c041 Mon Sep 17 00:00:00 2001 From: Michael Maltsev <4129781+m417z@users.noreply.github.com> Date: Mon, 9 Sep 2024 21:41:47 +0300 Subject: [PATCH 15/52] Try to fix GitHub Actions deployment (2) --- .github/workflows/deploy.yml | 4 +- package-lock.json | 713 ++++++++++++++++++++++++++++++++++- package.json | 4 +- 3 files changed, 715 insertions(+), 6 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index ea434ce7..8c06344e 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -22,10 +22,8 @@ jobs: fetch-depth: 0 - name: Install dependencies run: npm install - - name: Install global dependencies - run: npm install -g ts-node - name: Prepare static content - run: ts-node deploy.ts + run: npx tsx deploy.ts - name: Deploy uses: peaceiris/actions-gh-pages@v4 with: diff --git a/package-lock.json b/package-lock.json index 3d58260d..41ff0760 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,392 @@ }, "devDependencies": { "@types/node": "^17.0.13", - "@types/showdown": "^2.0.0" + "@types/showdown": "^2.0.0", + "tsx": "^4.19.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", + "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", + "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", + "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", + "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", + "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", + "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", + "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", + "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", + "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", + "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", + "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", + "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", + "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", + "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", + "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", + "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", + "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", + "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", + "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", + "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", + "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", + "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", + "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", + "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" } }, "node_modules/@types/node": { @@ -33,6 +418,45 @@ "node": "^12.20.0 || >=14" } }, + "node_modules/esbuild": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", + "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.23.1", + "@esbuild/android-arm": "0.23.1", + "@esbuild/android-arm64": "0.23.1", + "@esbuild/android-x64": "0.23.1", + "@esbuild/darwin-arm64": "0.23.1", + "@esbuild/darwin-x64": "0.23.1", + "@esbuild/freebsd-arm64": "0.23.1", + "@esbuild/freebsd-x64": "0.23.1", + "@esbuild/linux-arm": "0.23.1", + "@esbuild/linux-arm64": "0.23.1", + "@esbuild/linux-ia32": "0.23.1", + "@esbuild/linux-loong64": "0.23.1", + "@esbuild/linux-mips64el": "0.23.1", + "@esbuild/linux-ppc64": "0.23.1", + "@esbuild/linux-riscv64": "0.23.1", + "@esbuild/linux-s390x": "0.23.1", + "@esbuild/linux-x64": "0.23.1", + "@esbuild/netbsd-x64": "0.23.1", + "@esbuild/openbsd-arm64": "0.23.1", + "@esbuild/openbsd-x64": "0.23.1", + "@esbuild/sunos-x64": "0.23.1", + "@esbuild/win32-arm64": "0.23.1", + "@esbuild/win32-ia32": "0.23.1", + "@esbuild/win32-x64": "0.23.1" + } + }, "node_modules/feed": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/feed/-/feed-4.2.2.tgz", @@ -44,6 +468,41 @@ "node": ">=0.4.0" } }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-tsconfig": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.0.tgz", + "integrity": "sha512-Pgba6TExTZ0FJAn1qkJAjIeKoDJ3CsI2ChuLohJnZl/tTU8MVrq3b+2t5UOPfRa4RMsorClBjJALkJUMjG1PAw==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, "node_modules/sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", @@ -64,6 +523,25 @@ "url": "https://www.paypal.me/tiviesantos" } }, + "node_modules/tsx": { + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.0.tgz", + "integrity": "sha512-bV30kM7bsLZKZIOCHeMNVMJ32/LuJzLVajkQI/qf92J2Qr08ueLQvW00PUZGiuLPP760UINwupgUj8qrSCPUKg==", + "dev": true, + "dependencies": { + "esbuild": "~0.23.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, "node_modules/xml-js": { "version": "1.6.11", "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", @@ -77,6 +555,174 @@ } }, "dependencies": { + "@esbuild/aix-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", + "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", + "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", + "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "dev": true, + "optional": true + }, + "@esbuild/android-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", + "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", + "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", + "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", + "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", + "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", + "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", + "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", + "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-loong64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", + "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "dev": true, + "optional": true + }, + "@esbuild/linux-mips64el": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", + "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", + "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "dev": true, + "optional": true + }, + "@esbuild/linux-riscv64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", + "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-s390x": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", + "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "dev": true, + "optional": true + }, + "@esbuild/linux-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", + "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "dev": true, + "optional": true + }, + "@esbuild/netbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", + "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", + "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", + "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "dev": true, + "optional": true + }, + "@esbuild/sunos-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", + "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "dev": true, + "optional": true + }, + "@esbuild/win32-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", + "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "dev": true, + "optional": true + }, + "@esbuild/win32-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", + "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "dev": true, + "optional": true + }, + "@esbuild/win32-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", + "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "dev": true, + "optional": true + }, "@types/node": { "version": "17.0.13", "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.13.tgz", @@ -94,6 +740,38 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==" }, + "esbuild": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", + "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "dev": true, + "requires": { + "@esbuild/aix-ppc64": "0.23.1", + "@esbuild/android-arm": "0.23.1", + "@esbuild/android-arm64": "0.23.1", + "@esbuild/android-x64": "0.23.1", + "@esbuild/darwin-arm64": "0.23.1", + "@esbuild/darwin-x64": "0.23.1", + "@esbuild/freebsd-arm64": "0.23.1", + "@esbuild/freebsd-x64": "0.23.1", + "@esbuild/linux-arm": "0.23.1", + "@esbuild/linux-arm64": "0.23.1", + "@esbuild/linux-ia32": "0.23.1", + "@esbuild/linux-loong64": "0.23.1", + "@esbuild/linux-mips64el": "0.23.1", + "@esbuild/linux-ppc64": "0.23.1", + "@esbuild/linux-riscv64": "0.23.1", + "@esbuild/linux-s390x": "0.23.1", + "@esbuild/linux-x64": "0.23.1", + "@esbuild/netbsd-x64": "0.23.1", + "@esbuild/openbsd-arm64": "0.23.1", + "@esbuild/openbsd-x64": "0.23.1", + "@esbuild/sunos-x64": "0.23.1", + "@esbuild/win32-arm64": "0.23.1", + "@esbuild/win32-ia32": "0.23.1", + "@esbuild/win32-x64": "0.23.1" + } + }, "feed": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/feed/-/feed-4.2.2.tgz", @@ -102,6 +780,28 @@ "xml-js": "^1.6.11" } }, + "fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "optional": true + }, + "get-tsconfig": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.0.tgz", + "integrity": "sha512-Pgba6TExTZ0FJAn1qkJAjIeKoDJ3CsI2ChuLohJnZl/tTU8MVrq3b+2t5UOPfRa4RMsorClBjJALkJUMjG1PAw==", + "dev": true, + "requires": { + "resolve-pkg-maps": "^1.0.0" + } + }, + "resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true + }, "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", @@ -115,6 +815,17 @@ "commander": "^9.0.0" } }, + "tsx": { + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.0.tgz", + "integrity": "sha512-bV30kM7bsLZKZIOCHeMNVMJ32/LuJzLVajkQI/qf92J2Qr08ueLQvW00PUZGiuLPP760UINwupgUj8qrSCPUKg==", + "dev": true, + "requires": { + "esbuild": "~0.23.0", + "fsevents": "~2.3.3", + "get-tsconfig": "^4.7.5" + } + }, "xml-js": { "version": "1.6.11", "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", diff --git a/package.json b/package.json index 98c98bf7..31ee6fab 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { - "type": "module", "devDependencies": { "@types/node": "^17.0.13", - "@types/showdown": "^2.0.0" + "@types/showdown": "^2.0.0", + "tsx": "^4.19.0" }, "dependencies": { "feed": "^4.2.2", From 89b7248389967e412f6de6213e320adbfee9ad89 Mon Sep 17 00:00:00 2001 From: Michael Maltsev <4129781+m417z@users.noreply.github.com> Date: Mon, 9 Sep 2024 22:23:13 +0300 Subject: [PATCH 16/52] Virtual Desktop Preserve Taskbar Order v1.0.4: (#946) * Added support for Windows 11 version 24H2. --- mods/virtual-desktop-taskbar-order.wh.cpp | 61 +++++++++++++++-------- 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/mods/virtual-desktop-taskbar-order.wh.cpp b/mods/virtual-desktop-taskbar-order.wh.cpp index 7fbbd025..babbd7a0 100644 --- a/mods/virtual-desktop-taskbar-order.wh.cpp +++ b/mods/virtual-desktop-taskbar-order.wh.cpp @@ -2,7 +2,7 @@ // @id virtual-desktop-taskbar-order // @name Virtual Desktop Preserve Taskbar Order // @description The order on the taskbar isn't preserved between virtual desktop switches, this mod fixes it -// @version 1.0.3 +// @version 1.0.4 // @author m417z // @github https://github.com/m417z // @twitter https://twitter.com/m417z @@ -127,6 +127,15 @@ size_t* EV_APP_VIEW_MGR_APP_ARRAY_SIZE(LONG_PTR lp) { #pragma endregion // offsets +using CTaskBtnGroup_GetGroup_t = void*(WINAPI*)(void* pThis); +CTaskBtnGroup_GetGroup_t CTaskBtnGroup_GetGroup; + +using CTaskBtnGroup_GetNumItems_t = int(WINAPI*)(void* pThis); +CTaskBtnGroup_GetNumItems_t CTaskBtnGroup_GetNumItems; + +using CTaskBtnGroup_GetTaskItem_t = void*(WINAPI*)(void* pThis, int index); +CTaskBtnGroup_GetTaskItem_t CTaskBtnGroup_GetTaskItem; + using CTaskGroup_DoesWindowMatch_t = HRESULT(WINAPI*)(LPVOID pThis, HWND hCompareWnd, @@ -283,32 +292,28 @@ void OnButtonGroupInserted(LONG_PTR lpTaskSwLongPtr, LONG_PTR** button_groups = (LONG_PTR**)plp[1]; LONG_PTR* button_group = button_groups[nButtonGroupIndex]; - plp = (LONG_PTR*)button_group[7]; - if (!plp) - return; - - int buttons_count = (int)plp[0]; - LONG_PTR** buttons = (LONG_PTR**)plp[1]; - if (buttons_count == 0) + int buttons_count = CTaskBtnGroup_GetNumItems(button_group); + if (buttons_count == 0) { return; + } - LONG_PTR* task_group = (LONG_PTR*)button_group[4]; - plp = (LONG_PTR*)task_group[4]; - if (!plp) + LONG_PTR* task_group = (LONG_PTR*)CTaskBtnGroup_GetGroup(button_group); + if (!task_group) { return; + } - int task_items_count = (int)plp[0]; - if (task_items_count == 0) + LONG_PTR* first_task_item = + (LONG_PTR*)CTaskBtnGroup_GetTaskItem(button_group, 0); + if (!first_task_item) { return; - - LONG_PTR** task_items = (LONG_PTR**)plp[1]; + } plp = *(LONG_PTR**)task_group; void** ppTaskGroupRelease = (void**)&plp[2]; PointerRedirectionAdd(ppTaskGroupRelease, (void*)TaskGroupReleaseHook, &prTaskGroupRelease); - plp = *(LONG_PTR**)task_items[0]; + plp = *(LONG_PTR**)first_task_item; void** ppTaskItemRelease = (void**)&plp[2]; PointerRedirectionAdd(ppTaskItemRelease, (void*)TaskItemReleaseHook, &prTaskItemRelease); @@ -352,8 +357,9 @@ void OnButtonGroupInserted(LONG_PTR lpTaskSwLongPtr, break; } - if (!g_taskGroupVirtualDesktopReleased) + if (!g_taskGroupVirtualDesktopReleased) { continue; + } if (g_taskGroupVirtualDesktopReleased != task_group) { if (nRightNeighbourItemIndex == nArraySize) { @@ -361,7 +367,7 @@ void OnButtonGroupInserted(LONG_PTR lpTaskSwLongPtr, j++) { LONG_PTR* check_button_group = button_groups[j]; LONG_PTR* check_task_group = - (LONG_PTR*)check_button_group[4]; + (LONG_PTR*)CTaskBtnGroup_GetGroup(check_button_group); if (g_taskGroupVirtualDesktopReleased == check_task_group) { // The current item in lpArray is from the same group // of at least one of the items in button_groups to the @@ -375,12 +381,13 @@ void OnButtonGroupInserted(LONG_PTR lpTaskSwLongPtr, continue; } - if (!g_taskItemVirtualDesktopReleased) + if (!g_taskItemVirtualDesktopReleased) { continue; + } for (int j = 0; j < buttons_count; j++) { - LONG_PTR* button = buttons[j]; - LONG_PTR* task_item = (LONG_PTR*)button[4]; + LONG_PTR* task_item = + (LONG_PTR*)CTaskBtnGroup_GetTaskItem(button_group, j); if (g_taskItemVirtualDesktopReleased == task_item) { // The current item in lpArray matches one of the @@ -943,6 +950,18 @@ BOOL Wh_ModInit() { } SYMBOL_HOOK taskbarDllHooks[] = { + { + {LR"(public: virtual struct ITaskGroup * __cdecl CTaskBtnGroup::GetGroup(void))"}, + (void**)&CTaskBtnGroup_GetGroup, + }, + { + {LR"(public: virtual int __cdecl CTaskBtnGroup::GetNumItems(void))"}, + (void**)&CTaskBtnGroup_GetNumItems, + }, + { + {LR"(public: virtual struct ITaskItem * __cdecl CTaskBtnGroup::GetTaskItem(int))"}, + (void**)&CTaskBtnGroup_GetTaskItem, + }, { {LR"(public: virtual long __cdecl CTaskGroup::DoesWindowMatch(struct HWND__ *,struct _ITEMIDLIST_ABSOLUTE const *,unsigned short const *,enum WINDOWMATCHCONFIDENCE *,struct ITaskItem * *))"}, (void**)&CTaskGroup_DoesWindowMatch_Original, From 93793152290b764989cd5f1f1ae4f0dd156bee36 Mon Sep 17 00:00:00 2001 From: Olivia <149018134+OliviaIsTyping@users.noreply.github.com> Date: Tue, 10 Sep 2024 23:38:25 -0600 Subject: [PATCH 17/52] FixStrips WH Port - v1.0 (#948) * Add files via upload Add fixstrips.wh.cpp * Update and rename fixstrips.wh.cpp to mods/fix-strips.wh.cpp * Move credits mention to description * update README for more clarity on mod functionality --------- Co-authored-by: Tech Stuff <103606018+teknixstuff@users.noreply.github.com> --- mods/fix-strips.wh.cpp | 71 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 mods/fix-strips.wh.cpp diff --git a/mods/fix-strips.wh.cpp b/mods/fix-strips.wh.cpp new file mode 100644 index 00000000..e9d0240f --- /dev/null +++ b/mods/fix-strips.wh.cpp @@ -0,0 +1,71 @@ +// ==WindhawkMod== +// @id fix-strips +// @name FixStrips WH Port +// @description Port of the fixstrips AHK script that fixes some classic theme issues with Explorer7 +// @version 1.0 +// @author OliveIsTyping +// @github https://github.com/OliviaIsTyping +// @include explorer.exe +// @compilerOptions -lcomdlg32 +// ==/WindhawkMod== + +// ==WindhawkModReadme== +/* +## NOTE: + +This mod fixes the presence of the DWM window frame around the taskbar when using classic theme with [Explorer7](https://winclassic.net/thread/2588/explorer7-windows-explorer-10-11). You may need to restart explorer.exe when enabling the mod to see the changes take effect. + + +## Before +![Before](https://raw.githubusercontent.com/OliviaIsTyping/images/main/fixstrips-before.png) + +## After +![After](https://raw.githubusercontent.com/OliviaIsTyping/images/main/fixstrips-after.png) + +Credits to [@Anixx](https://github.com/Anixx) for the original AHK Script +*/ +// ==/WindhawkModReadme== + +// ==WindhawkModSettings== + +// ==/WindhawkModSettings== +#include + +using CreateWindowExW_t = decltype(&CreateWindowExW); +CreateWindowExW_t CreateWindowExW_Orig; +HWND WINAPI CreateWindowExW_Hook(DWORD dwExStyle,LPCWSTR lpClassName,LPCWSTR lpWindowName,DWORD dwStyle,int X,int Y,int nWidth,int nHeight,HWND hWndParent, + HMENU hMenu,HINSTANCE hInstance,LPVOID lpParam) { + + wchar_t wszClassName[200]; + ZeroMemory(wszClassName, 200); + //Add WS_DLGFRAME to Shell_TrayWnd + if ((((ULONG_PTR)lpClassName & ~(ULONG_PTR)0xffff) != 0) && !wcscmp(lpClassName, L"Shell_TrayWnd")) + { + dwStyle |= WS_DLGFRAME; + } + HWND hWnd = CreateWindowExW_Orig(dwExStyle,lpClassName,lpWindowName,dwStyle,X,Y,nWidth,nHeight,hWndParent,hMenu,hInstance,lpParam); + + //Remove WS_DLGFRAME from Shell_TrayWnd (dont ask why it works but it does :D) + if ((((ULONG_PTR)lpClassName & ~(ULONG_PTR)0xffff) != 0) && !wcscmp(lpClassName, L"Shell_TrayWnd")) + { + SetWindowLongPtrW(hWnd,GWL_STYLE,GetWindowLongPtrW(hWnd, GWL_STYLE) & ~WS_DLGFRAME); + } + + return hWnd; +} + + +// The mod is being initialized, load settings, hook functions, and do other initialization stuff if required. +BOOL Wh_ModInit() { + Wh_Log(L"Init"); + + Wh_SetFunctionHook((void*)CreateWindowExW, + (void*)CreateWindowExW_Hook, + (void**)&CreateWindowExW_Orig); + return TRUE; +} + +// The mod is being unloaded, free all allocated resources. +void Wh_ModUninit() { + Wh_Log(L"Uninit"); +} From e3bf280ec5e53bc81d83312320394e70bc7b9899 Mon Sep 17 00:00:00 2001 From: Toru the Red Fox Date: Fri, 13 Sep 2024 08:50:01 +0100 Subject: [PATCH 18/52] Logon, Logoff & Shutdown Sounds Restored - v1.0.1 (#949) * Fix bug where the logon sound needs user to have write access to HKLM in order to play * State that it's likely redundant with explorer7 --- mods/logon-logoff-shutdown-sounds.wh.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/mods/logon-logoff-shutdown-sounds.wh.cpp b/mods/logon-logoff-shutdown-sounds.wh.cpp index e3140b9d..b4082460 100644 --- a/mods/logon-logoff-shutdown-sounds.wh.cpp +++ b/mods/logon-logoff-shutdown-sounds.wh.cpp @@ -2,7 +2,7 @@ // @id logon-logoff-shutdown-sounds // @name Logon, Logoff & Shutdown Sounds Restored // @description Restores the logon, logoff and shutdown sounds from earlier versions of Windows -// @version 1.0.0 +// @version 1.0.1 // @author Toru the Red Fox // @github https://github.com/TorutheRedFox // @twitter https://twitter.com/TorutheRedFox @@ -16,6 +16,8 @@ Restores the logon, logoff and shutdown sounds from earlier versions of Windows, simple as. It is recommended to use [these reg files](https://www.howtogeek.com/wp-content/uploads/2016/09/Shutdown-Logoff-Logon-Sound-Hacks.zip) to restore the sound events to the Sound control panel applet. + +Note: Likely redundant with explorer7 due to 7's explorer having this code in it already. */ // ==/WindhawkModReadme== @@ -37,6 +39,7 @@ It is recommended to use [these reg files](https://www.howtogeek.com/wp-content/ #include #include #include +#include #include // settings @@ -435,7 +438,7 @@ HRESULT WINAPI SHCreateSessionKey(REGSAM samDesired, PHKEY phKey) if(SUCCEEDED(hr)) { - Error = RegCreateKeyExW(HKEY_LOCAL_MACHINE, wszSessionKey, 0, NULL, + Error = RegCreateKeyEx(HKEY_CURRENT_USER, wszSessionKey, 0, NULL, REG_OPTION_VOLATILE, samDesired, NULL, phKey, NULL); if (Error != ERROR_SUCCESS) hr = HRESULT_FROM_WIN32(Error); @@ -462,12 +465,12 @@ BOOL WINAPI HasPerLogonActionBeenDone(LPCWSTR lpSubKey, TRIBIT* out_tbDone) HKEY phkResult; *out_tbDone = TRIBIT_TRUE; - if ( (int)SHCreateSessionKey(131078, &hKey) >= 0 ) + if ( SUCCEEDED(SHCreateSessionKey(KEY_WRITE, &hKey)) ) { - if ( !RegCreateKeyExW(hKey, lpSubKey, 0, 0, 1u, 0x20006u, 0, &phkResult, &dwDisposition) ) + if ( RegCreateKeyExW(hKey, lpSubKey, NULL, NULL, REG_OPTION_VOLATILE, KEY_WRITE, NULL, &phkResult, &dwDisposition) == ERROR_SUCCESS ) { RegCloseKey(phkResult); - if ( dwDisposition == 1 ) + if ( dwDisposition == REG_CREATED_NEW_KEY ) *out_tbDone = TRIBIT_FALSE; } RegCloseKey(hKey); From 2588c1bd9fb279ee7a7b982ee8a9e8405459299c Mon Sep 17 00:00:00 2001 From: Michael Maltsev <4129781+m417z@users.noreply.github.com> Date: Fri, 13 Sep 2024 15:02:09 +0300 Subject: [PATCH 19/52] Cycle through taskbar windows on click v1.0 (#950) --- mods/taskbar-left-click-cycle.wh.cpp | 228 +++++++++++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100644 mods/taskbar-left-click-cycle.wh.cpp diff --git a/mods/taskbar-left-click-cycle.wh.cpp b/mods/taskbar-left-click-cycle.wh.cpp new file mode 100644 index 00000000..cae73326 --- /dev/null +++ b/mods/taskbar-left-click-cycle.wh.cpp @@ -0,0 +1,228 @@ +// ==WindhawkMod== +// @id taskbar-left-click-cycle +// @name Cycle through taskbar windows on click +// @description Makes clicking on combined taskbar items cycle through windows instead of opening thumbnail previews +// @version 1.0 +// @author m417z +// @github https://github.com/m417z +// @twitter https://twitter.com/m417z +// @homepage https://m417z.com/ +// @include explorer.exe +// @architecture x86-64 +// @compilerOptions -lversion +// ==/WindhawkMod== + +// Source code is published under The GNU General Public License v3.0. +// +// For bug reports and feature requests, please open an issue here: +// https://github.com/ramensoftware/windhawk-mods/issues +// +// For pull requests, development takes place here: +// https://github.com/m417z/my-windhawk-mods + +// ==WindhawkModReadme== +/* +# Cycle through taskbar windows on click + +Makes clicking on combined taskbar items cycle through windows instead of +opening thumbnail previews. It's still possible to open thumbnail previews by +holding the Ctrl key while clicking. + +Only Windows 10 64-bit and Windows 11 are supported. For other Windows versions +check out [7+ Taskbar Tweaker](https://tweaker.ramensoftware.com/). + +**Note:** To customize the old taskbar on Windows 11 (if using ExplorerPatcher +or a similar tool), enable the relevant option in the mod's settings. + +![Demonstration](https://i.imgur.com/ecYYtGU.gif) +*/ +// ==/WindhawkModReadme== + +// ==WindhawkModSettings== +/* +- oldTaskbarOnWin11: false + $name: Customize the old taskbar on Windows 11 + $description: >- + Enable this option to customize the old taskbar on Windows 11 (if using + ExplorerPatcher or a similar tool). +*/ +// ==/WindhawkModSettings== + +#include + +struct { + bool oldTaskbarOnWin11; +} g_settings; + +enum class WinVersion { + Unsupported, + Win10, + Win11, +}; + +WinVersion g_winVersion; + +using CTaskBtnGroup_GetGroupType_t = int(WINAPI*)(PVOID pThis); +CTaskBtnGroup_GetGroupType_t CTaskBtnGroup_GetGroupType_Original; + +using CTaskListWnd__HandleClick_t = void(WINAPI*)(PVOID pThis, + PVOID taskBtnGroup, + int taskItemIndex, + int clickAction, + int param4, + int param5); +CTaskListWnd__HandleClick_t CTaskListWnd__HandleClick_Original; +void WINAPI CTaskListWnd__HandleClick_Hook(PVOID pThis, + PVOID taskBtnGroup, + int taskItemIndex, + int clickAction, + int param4, + int param5) { + Wh_Log(L"> %d", clickAction); + + auto original = [&]() { + CTaskListWnd__HandleClick_Original(pThis, taskBtnGroup, taskItemIndex, + clickAction, param4, param5); + }; + + constexpr int kClick = 0; + constexpr int kShiftClick = 4; + + if (clickAction != kClick && clickAction != kShiftClick) { + return original(); + } + + // Group types: + // 1 - Single item or multiple uncombined items + // 2 - Pinned item + // 3 - Multiple combined items + int groupType = CTaskBtnGroup_GetGroupType_Original(taskBtnGroup); + if (groupType != 3) { + return original(); + } + + CTaskListWnd__HandleClick_Original( + pThis, taskBtnGroup, taskItemIndex, + clickAction == kClick ? kShiftClick : kClick, param4, param5); +} + +VS_FIXEDFILEINFO* GetModuleVersionInfo(HMODULE hModule, UINT* puPtrLen) { + void* pFixedFileInfo = nullptr; + UINT uPtrLen = 0; + + HRSRC hResource = + FindResource(hModule, MAKEINTRESOURCE(VS_VERSION_INFO), RT_VERSION); + if (hResource) { + HGLOBAL hGlobal = LoadResource(hModule, hResource); + if (hGlobal) { + void* pData = LockResource(hGlobal); + if (pData) { + if (!VerQueryValue(pData, L"\\", &pFixedFileInfo, &uPtrLen) || + uPtrLen == 0) { + pFixedFileInfo = nullptr; + uPtrLen = 0; + } + } + } + } + + if (puPtrLen) { + *puPtrLen = uPtrLen; + } + + return (VS_FIXEDFILEINFO*)pFixedFileInfo; +} + +WinVersion GetExplorerVersion() { + VS_FIXEDFILEINFO* fixedFileInfo = GetModuleVersionInfo(nullptr, nullptr); + if (!fixedFileInfo) { + return WinVersion::Unsupported; + } + + WORD major = HIWORD(fixedFileInfo->dwFileVersionMS); + WORD minor = LOWORD(fixedFileInfo->dwFileVersionMS); + WORD build = HIWORD(fixedFileInfo->dwFileVersionLS); + WORD qfe = LOWORD(fixedFileInfo->dwFileVersionLS); + + Wh_Log(L"Version: %u.%u.%u.%u", major, minor, build, qfe); + + switch (major) { + case 10: + if (build < 22000) { + return WinVersion::Win10; + } else { + return WinVersion::Win11; + } + break; + } + + return WinVersion::Unsupported; +} + +void LoadSettings() { + g_settings.oldTaskbarOnWin11 = Wh_GetIntSetting(L"oldTaskbarOnWin11"); +} + +BOOL Wh_ModInit() { + Wh_Log(L">"); + + LoadSettings(); + + g_winVersion = GetExplorerVersion(); + if (g_winVersion == WinVersion::Unsupported) { + Wh_Log(L"Unsupported Windows version"); + return FALSE; + } + + if (g_winVersion >= WinVersion::Win11 && g_settings.oldTaskbarOnWin11) { + g_winVersion = WinVersion::Win10; + } + + // Taskbar.dll, explorer.exe + WindhawkUtils::SYMBOL_HOOK symbolHooks[] = { + { + { + LR"(public: virtual enum eTBGROUPTYPE __cdecl CTaskBtnGroup::GetGroupType(void))", + LR"(public: virtual enum eTBGROUPTYPE __cdecl CTaskBtnGroup::GetGroupType(void) __ptr64)", + }, + (void**)&CTaskBtnGroup_GetGroupType_Original, + }, + { + { + LR"(protected: void __cdecl CTaskListWnd::_HandleClick(struct ITaskBtnGroup *,int,enum CTaskListWnd::eCLICKACTION,int,int))", + LR"(protected: void __cdecl CTaskListWnd::_HandleClick(struct ITaskBtnGroup * __ptr64,int,enum CTaskListWnd::eCLICKACTION,int,int) __ptr64)", + }, + (void**)&CTaskListWnd__HandleClick_Original, + (void*)CTaskListWnd__HandleClick_Hook, + }, + }; + + HMODULE module; + if (g_winVersion <= WinVersion::Win10) { + module = GetModuleHandle(nullptr); + } else { + module = LoadLibrary(L"taskbar.dll"); + if (!module) { + Wh_Log(L"Couldn't load taskbar.dll"); + return FALSE; + } + } + + if (!HookSymbols(module, symbolHooks, ARRAYSIZE(symbolHooks))) { + return FALSE; + } + + return TRUE; +} + +BOOL Wh_ModSettingsChanged(BOOL* bReload) { + Wh_Log(L">"); + + bool prevOldTaskbarOnWin11 = g_settings.oldTaskbarOnWin11; + + LoadSettings(); + + *bReload = g_settings.oldTaskbarOnWin11 != prevOldTaskbarOnWin11; + + return TRUE; +} From e7a03d627c3bc66756e9748a987d484863153397 Mon Sep 17 00:00:00 2001 From: aubrey <44238627+aubymori@users.noreply.github.com> Date: Mon, 16 Sep 2024 15:55:40 -0500 Subject: [PATCH 20/52] Message Box Fix 2.0.0 (#954) - Restructure code to match modern conventions - Remove option to fix the font and make it always fix it; there's not much use to having it disabled. - Revamp "Remove background" option - Renamed to "Classic style" - Now shows message boxes with Windows XP positioning and scaling - May break with per-monitor DPI, have not tested. Regardless, I don't think anyone using a Windows XP/2000 theme would want per-monitor DPI. --- mods/msg-box-font-fix.wh.cpp | 1329 ++++++++++++++++++++++++++++------ 1 file changed, 1111 insertions(+), 218 deletions(-) diff --git a/mods/msg-box-font-fix.wh.cpp b/mods/msg-box-font-fix.wh.cpp index 43cf0f59..1c1793c3 100644 --- a/mods/msg-box-font-fix.wh.cpp +++ b/mods/msg-box-font-fix.wh.cpp @@ -1,27 +1,24 @@ // ==WindhawkMod== // @id msg-box-font-fix // @name Message Box Fix -// @description Fixes the MessageBox font size and background -// @version 1.5.0 +// @description Fixes the MessageBox font size and optionally make them like Windows XP +// @version 2.0.0 // @author aubymori // @github https://github.com/aubymori // @include * -// @compilerOptions -luser32 -lgdi32 -lcomctl32 +// @compilerOptions -luser32 -lgdi32 -lcomctl32 -DWINVER=0x0A00 // ==/WindhawkMod== // ==WindhawkModReadme== /* # Message Box Fix -Starting with Windows Vista, message boxes render the "Window" color in their upper half. - Starting with Windows 10 1709, message boxes render their font size 1pt less than the -user-defined size.\* You cannot just set this size higher, as many applications still query -it, and will show up with bigger fonts. +user-defined size. You cannot just set this size higher, as many applications still query +the font with the user-defined size, and will show up with bigger fonts. Also, starting with +Windows Vista, message boxes got a visual overhaul. This mod fixes both of those things. -**This mod will only work on Windhawk v1.4 and greater.** - **Before:** ![Before](https://raw.githubusercontent.com/aubymori/images/main/message-box-font-fix-before.png) @@ -31,289 +28,1185 @@ This mod fixes both of those things. ![After](https://raw.githubusercontent.com/aubymori/images/main/message-box-font-fix-after.png) ![After (classic)](https://raw.githubusercontent.com/aubymori/images/main/message-box-fix-after-classic.png) - -*\*Microsoft changed the way the font size was calculator for Per-Monitor V2 DPI awareness. It ALWAYS gets -1pt below the font size, even when on a higher DPI. This is because Microsoft decided to do some weird math -instead of just using `SystemParametersInfoForDpi` like a normal person.* */ // ==/WindhawkModReadme== // ==WindhawkModSettings== /* -- font: true - $name: Fix font size - $description: Fix the message box font being too small - background: false - $name: Remove "Window" background - $description: Remove the "Window" color from the background, much like XP and before + $name: Classic style + $description: Make message boxes look like Windows XP and before */ // ==/WindhawkModSettings== #include -struct { - BOOL font; - BOOL background; -} settings; +HMODULE g_hUser32 = NULL; +bool g_bClassic = false; /* Only available in Windows 10 version 1607 and greater. */ -typedef UINT (WINAPI *GetDpiForWindow_t)(HWND); -GetDpiForWindow_t GetDpiForWindow; +WINUSERAPI UINT WINAPI GetDpiForWindow(HWND hWnd); +WINUSERAPI BOOL WINAPI SystemParametersInfoForDpi(UINT uiAction, UINT uiParam, PVOID pvParam, UINT fWinIni, UINT dpi); -typedef BOOL (WINAPI *SystemParametersInfoForDpi_t)(UINT, UINT, PVOID, UINT, UINT); -SystemParametersInfoForDpi_t SystemParametersInfoForDpi; +/* Imported from win32u.dll */ +typedef DWORD (NTAPI *NtUserCallOneParam_t)(DWORD Param, DWORD Routine); +NtUserCallOneParam_t NtUserCallOneParam = nullptr; -/* Message box text windows that have been - subclassed for background removal. - - See WM_PAINT on MsgBoxTextSubclassProc. */ -std::vector g_subclassed; +typedef DWORD (NTAPI *NtUserCallHwnd_t)(HWND Wnd, DWORD Routine); +NtUserCallHwnd_t NtUserCallHwnd = nullptr; -typedef HFONT (__fastcall *GetMessageBoxFontForDpi_t)(UINT); -GetMessageBoxFontForDpi_t GetMessageBoxFontForDpi_orig; +HFONT (__fastcall *GetMessageBoxFontForDpi_orig)(UINT); HFONT __fastcall GetMessageBoxFontForDpi_hook( UINT nDpi ) { - if (!settings.font) + NONCLIENTMETRICSW ncm; + ncm.cbSize = sizeof(NONCLIENTMETRICSW); + if (SystemParametersInfoForDpi( + SPI_GETNONCLIENTMETRICS, + sizeof(NONCLIENTMETRICSW), + &ncm, + 0, + nDpi + )) { - return GetMessageBoxFontForDpi_orig( - nDpi - ); + return CreateFontIndirectW(&ncm.lfMessageFont); } + return NULL; +} + +#pragma region "Classic style" + +typedef struct _MSGBOXDATA { + // BEGIN MSGBOXPARAMS + UINT cbSize; + HWND hwndOwner; + HINSTANCE hInstance; + LPCWSTR lpszText; + LPCWSTR lpszCaption; + DWORD dwStyle; + LPCWSTR lpszIcon; + DWORD_PTR dwContextHelpId; + MSGBOXCALLBACK lpfnMsgBoxCallback; + DWORD dwLanguageId; + // END MSGBOXPARAMS + HWND HWNDOwner; + DWORD dwPadding; + WORD wLanguageId; + const INT *pidButton; + LPCWSTR *ppszButtonText; + DWORD cButtons; + UINT DefButton; + UINT CancelId; + DWORD dwTimeout; + HWND *phwndList; + DWORD dwReserved[20]; +} MSGBOXDATA, *PMSGBOXDATA, *LPMSGBOXDATA; + +typedef struct _TEB64 { + UCHAR ignored[0x0800]; + ULONG_PTR Win32ClientInfo[0x3E]; +} TEB64, *PTEB64; + +typedef struct _DESKTOPINFO +{ + DWORD ignored[12]; + int cntMBox; +} DESKTOPINFO, *PDESKTOPINFO; + +typedef struct _CLIENTINFO64 { + DWORD64 CI_flags; + DWORD64 cSpins; + DWORD dwExpWinVer; + DWORD dwCompatFlags; + DWORD dwCompatFlags2; + DWORD dwTIFlags; + PDESKTOPINFO pDeskInfo; +} CLIENTINFO64, *PCLIENTINFO64; + +inline PCLIENTINFO64 GetClientInfo() +{ + PTEB64 teb = (PTEB64)NtCurrentTeb(); +// This breaks on real 32-bit Windows, it's WOW64 specific. +// But literally nobody uses that anymore, so who cares. +#ifndef _WIN64 + teb = *(PTEB64 *)((LPBYTE)teb + 0x0F70); +#endif + return (PCLIENTINFO64)teb->Win32ClientInfo; +} + +static CONST WCHAR szEmpty[] = L""; + +// IDs and such +#define MAX_RES_STRING 256 +#define UNICODE_RLM 0x200F // RIGHT-TO-LEFT MARK (RLM) +#define IDUSERICON 20 +#define BUTTONCODE 0x80 +#define STATICCODE 0x82 +#define SFI_PLAYEVENTSOUND 57 +#define SFI_SETMSGBOX 89 + +// Macros +#define MAXUSHORT (0xFFFF) +#define IS_PTR(p) ((((ULONG_PTR)(p)) & ~MAXUSHORT) != 0) +#define PTR_TO_ID(p) ((USHORT)(((ULONG_PTR)(p)) & MAXUSHORT)) +#define SYSMET(i) GetSystemMetrics( SM_##i ) +#define XPixFromXDU(x, cxChar) MulDiv(x, cxChar, 4) +#define YPixFromYDU(y, cyChar) MulDiv(y, cyChar, 8) +#define XDUFromXPix(x, cxChar) MulDiv(x, 4, cxChar) +#define YDUFromYPix(y, cyChar) MulDiv(y, 8, cyChar) +#define NextWordBoundary(p) ((PBYTE)(p) + ((ULONG_PTR)(p) & 1)) +#define NextDWordBoundary(p) ((PBYTE)(p) + ((ULONG_PTR)(-(LONG_PTR)(p)) & 3)) +#define USERGLOBALLOCK(h, p) p = (LPWSTR)GlobalLock((HANDLE)(h)) +#define USERGLOBALUNLOCK(h) GlobalUnlock((HANDLE)(h)) +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#define min(a,b) (((a) < (b)) ? (a) : (b)) + +// Dialog unit dimensions +#define DU_OUTERMARGIN 7 +#define DU_INNERMARGIN 10 +#define DU_BTNGAP 4 // D.U. of space between buttons +#define DU_BTNHEIGHT 14 // D.U. of button height +#define DU_BTNWIDTH 50 // D.U. of minimum button width in a message box + +// Custom struct and function because we can't use gpsi +typedef struct tagMSGBOXMETRICS +{ + WORD cxMsgFontChar; + WORD cyMsgFontChar; + WORD wMaxBtnSize; + HFONT hCaptionFont; + HFONT hMessageFont; +} MSGBOXMETRICS, *LPMSGBOXMETRICS; +MSGBOXMETRICS mbm; + +BOOL GetMessageBoxMetrics(LPMSGBOXMETRICS pmbm) +{ + if (!pmbm) + return FALSE; + + ZeroMemory(pmbm, sizeof(MSGBOXMETRICS)); + BOOL succeeded = TRUE; + HDC hDesktopDC = GetDC(NULL); + HDC hMemDC = CreateCompatibleDC(hDesktopDC); + + HFONT hf = GetMessageBoxFontForDpi_hook(GetDeviceCaps(hMemDC, LOGPIXELSX)); + SelectObject(hMemDC, hf); NONCLIENTMETRICSW ncm; ncm.cbSize = sizeof(NONCLIENTMETRICSW); - - BOOL bResult; - if (SystemParametersInfoForDpi) + SystemParametersInfoForDpi( + SPI_GETNONCLIENTMETRICS, + sizeof(ncm), + &ncm, + 0, + GetDeviceCaps(hMemDC, LOGPIXELSX) + ); + + TEXTMETRICW tm; + succeeded = GetTextMetricsW(hMemDC, &tm); + if (succeeded) { - bResult = SystemParametersInfoForDpi( - SPI_GETNONCLIENTMETRICS, - sizeof(NONCLIENTMETRICSW), - &ncm, - 0, - nDpi - ); + pmbm->cxMsgFontChar = tm.tmAveCharWidth; + pmbm->cyMsgFontChar = tm.tmHeight; + pmbm->wMaxBtnSize = XPixFromXDU(DU_BTNWIDTH, tm.tmAveCharWidth); + pmbm->hCaptionFont = CreateFontIndirectW(&ncm.lfCaptionFont); + pmbm->hMessageFont = hf; } - else + + DeleteDC(hMemDC); + ReleaseDC(NULL, hDesktopDC); + return succeeded; +} + +UINT MB_GetIconOrdNum(UINT rgBits) +{ + switch (rgBits & MB_ICONMASK) { + case MB_USERICON: + case MB_ICONHAND: + return PtrToUlong(IDI_HAND); + + case MB_ICONQUESTION: + return PtrToUlong(IDI_QUESTION); + + case MB_ICONEXCLAMATION: + return PtrToUlong(IDI_EXCLAMATION); + + case MB_ICONASTERISK: + return PtrToUlong(IDI_ASTERISK); + } + + return 0; +} + + +UINT MB_FindDlgTemplateSize(LPMSGBOXDATA lpmb) +{ + ULONG_PTR cbLen; + UINT cbT; + UINT i; + UINT wCount; + + wCount = lpmb->cButtons; + + /* Start with dialog header's size */ + cbLen = (ULONG_PTR)NextWordBoundary(sizeof(DLGTEMPLATE) + sizeof(WCHAR)); + cbLen = (ULONG_PTR)NextWordBoundary(cbLen + sizeof(WCHAR)); + cbLen += wcslen(lpmb->lpszCaption) * sizeof(WCHAR) + sizeof(WCHAR); + cbLen += sizeof(WORD); // Font height + cbLen = (ULONG_PTR)NextDWordBoundary(cbLen); + + /* Check if an icon is present */ + if (lpmb->dwStyle & MB_ICONMASK) + cbLen += (ULONG_PTR)NextDWordBoundary(sizeof(DLGITEMTEMPLATE) + 7 * sizeof(WCHAR)); + + /* Find the number of buttons in the msg box */ + for (i = 0; i < wCount; i++) { + cbLen = (ULONG_PTR)NextWordBoundary(cbLen + sizeof(DLGITEMTEMPLATE) + + (2 * sizeof(WCHAR))); + cbT = (wcslen(lpmb->ppszButtonText[i]) + 1) * sizeof(WCHAR); + cbLen = (ULONG_PTR)NextWordBoundary(cbLen + cbT); + cbLen += sizeof(WCHAR); + cbLen = (ULONG_PTR)NextDWordBoundary(cbLen); + } + + /* Add in the space required for the text message (if there is one) */ + if (lpmb->lpszText != NULL) { + cbLen = (ULONG_PTR)NextWordBoundary(cbLen + sizeof(DLGITEMTEMPLATE) + + (2 * sizeof(WCHAR))); + cbT = (wcslen(lpmb->lpszText) + 1) * sizeof(WCHAR); + cbLen = (ULONG_PTR)NextWordBoundary(cbLen + cbT); + cbLen += sizeof(WCHAR); + cbLen = (ULONG_PTR)NextDWordBoundary(cbLen); + } + + return (UINT)cbLen; +} + +LPBYTE MB_UpdateDlgHdr( + LPDLGTEMPLATE lpDlgTmp, + long lStyle, + long lExtendedStyle, + BYTE bItemCount, + int iX, + int iY, + int iCX, + int iCY, + LPWSTR lpszCaption, + int cchCaptionLen) +{ + LPTSTR lpStr; + RECT rc; + + rc.left = iX + SYSMET(CXFIXEDFRAME) + SYSMET(CXPADDEDBORDER); + rc.top = iY + SYSMET(CYFIXEDFRAME) + SYSMET(CXPADDEDBORDER); + rc.right = iX + iCX - SYSMET(CXFIXEDFRAME) - SYSMET(CXPADDEDBORDER); + rc.bottom = iY + iCY - SYSMET(CYFIXEDFRAME) - SYSMET(CXPADDEDBORDER); + rc.top += SYSMET(CYCAPTION); + + lpDlgTmp->style = lStyle; + lpDlgTmp->dwExtendedStyle = lExtendedStyle; + lpDlgTmp->cdit = bItemCount; + lpDlgTmp->x = XDUFromXPix(rc.left, mbm.cxMsgFontChar); + lpDlgTmp->y = YDUFromYPix(rc.top, mbm.cyMsgFontChar); + lpDlgTmp->cx = XDUFromXPix(rc.right - rc.left, mbm.cxMsgFontChar); + lpDlgTmp->cy = YDUFromYPix(rc.bottom - rc.top, mbm.cyMsgFontChar); + + /* + * Move pointer to variable length fields. No menu resource for + * message box, a zero window class (means dialog box class). + */ + lpStr = (LPWSTR)(lpDlgTmp + 1); + *lpStr++ = 0; // Menu + lpStr = (LPWSTR)NextWordBoundary(lpStr); + *lpStr++ = 0; // Class + lpStr = (LPWSTR)NextWordBoundary(lpStr); + + /* + * NOTE: iCaptionLen may be less than the length of the Caption string; + * So, DO NOT USE lstrcpy(); + */ + RtlCopyMemory(lpStr, lpszCaption, cchCaptionLen * sizeof(WCHAR)); + lpStr += cchCaptionLen; + *lpStr++ = TEXT('\0'); + + /* Font height of 0x7FFF means use the message box font */ + *lpStr++ = 0x7FFF; + + return NextDWordBoundary(lpStr); +} + +LPBYTE MB_UpdateDlgItem( + LPDLGITEMTEMPLATE lpDlgItem, + int iCtrlId, + long lStyle, + long lExtendedStyle, + int iX, + int iY, + int iCX, + int iCY, + LPWSTR lpszText, + UINT cchTextLen, + int iControlClass) +{ + LPWSTR lpStr; + BOOL fIsOrdNum; + + + lpDlgItem->x = XDUFromXPix(iX, mbm.cxMsgFontChar); + lpDlgItem->y = YDUFromYPix(iY, mbm.cyMsgFontChar); + lpDlgItem->cx = XDUFromXPix(iCX, mbm.cxMsgFontChar); + lpDlgItem->cy = YDUFromYPix(iCY, mbm.cyMsgFontChar); + lpDlgItem->id = (WORD)iCtrlId; + lpDlgItem->style = lStyle; + lpDlgItem->dwExtendedStyle = lExtendedStyle; + + if (iControlClass == STATICCODE && + (((lStyle & 0x0F) == SS_LEFT) || ((lStyle & 0x0F) == SS_RIGHT))) { - bResult = SystemParametersInfoW( - SPI_GETNONCLIENTMETRICS, - sizeof(NONCLIENTMETRICSW), - &ncm, - 0 - ); + lpDlgItem->cx++; + lpDlgItem->cy++; } - if (bResult) + lpStr = (LPWSTR)(lpDlgItem + 1); + + *lpStr++ = 0xFFFF; + *lpStr++ = (BYTE)iControlClass; + lpStr = (LPWSTR)NextWordBoundary(lpStr); + + fIsOrdNum = ((*lpszText == 0xFFFF) && (cchTextLen == sizeof(DWORD) / sizeof(WCHAR))); + + RtlCopyMemory(lpStr, lpszText, cchTextLen * sizeof(WCHAR)); + lpStr = lpStr + cchTextLen; + if (!fIsOrdNum) { - return CreateFontIndirectW(&ncm.lfMessageFont); + *lpStr = TEXT('\0'); + lpStr = (LPWSTR)NextWordBoundary(lpStr + 1); } - return NULL; + + *lpStr++ = 0; + + return NextDWordBoundary(lpStr); } -LRESULT CALLBACK MsgBoxTextSubclassProc( - HWND hWnd, - UINT uMsg, - WPARAM wParam, - LPARAM lParam, - DWORD_PTR dwRefData -) +LPBYTE MB_AddPushButtons( + LPDLGITEMTEMPLATE lpDlgTmp, + LPMSGBOXDATA lpmb, + UINT wLEdge, + UINT wBEdge) { - if (settings.background) - { - switch (uMsg) - { - /* I literally could not find any style or anything that could - remove the background on this, so I just paint it myself. - Sorry. */ - case WM_PAINT: - { - PAINTSTRUCT ps; - HDC hDC = BeginPaint(hWnd, &ps); - - RECT rcClient; - GetClientRect(hWnd, &rcClient); - - int len = GetWindowTextLengthW(hWnd) + 1; - LPWSTR szText = new WCHAR[len]; - GetWindowTextW(hWnd, szText, len); - - HFONT hfMsg; - if (settings.font || !GetDpiForWindow) - { - hfMsg = GetMessageBoxFontForDpi_hook( - GetDpiForWindow ? GetDpiForWindow(hWnd) : 96 - ); - } - else - { - hfMsg = GetMessageBoxFontForDpi_orig(GetDpiForWindow(hWnd)); - } - HFONT hfOld = (HFONT)SelectObject(hDC, hfMsg); - - SetBkMode(hDC, TRANSPARENT); - SetTextColor(hDC, GetSysColor(COLOR_BTNTEXT)); - - DRAWTEXTPARAMS dtp = { sizeof(DRAWTEXTPARAMS) }; - dtp.uiLengthDrawn = wcslen(szText); - - DrawTextExW( - hDC, - szText, - -1, - &rcClient, - DT_LEFT | DT_WORDBREAK | DT_EDITCONTROL, - &dtp - ); - - SelectObject(hDC, hfOld); - DeleteObject(hfMsg); - delete[] szText; - EndPaint(hWnd, &ps); - return 0; + UINT wYValue; + UINT i; + UINT wHeight; + UINT wCount = lpmb->cButtons; + + wHeight = YPixFromYDU(DU_BTNHEIGHT, mbm.cyMsgFontChar); + + wYValue = wBEdge - wHeight; // Y coord for push buttons + + for (i = 0; i < wCount; i++) { + + lpDlgTmp = (LPDLGITEMTEMPLATE)MB_UpdateDlgItem( + lpDlgTmp, /* Ptr to template */ + lpmb->pidButton[i], /* Control Id */ + WS_TABSTOP | WS_CHILD | WS_VISIBLE | (i == 0 ? WS_GROUP : 0) | + ((UINT)i == lpmb->DefButton ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON), + 0, + wLEdge, /* X coord */ + wYValue, /* Y coord */ + mbm.wMaxBtnSize, /* CX */ + wHeight, /* CY */ + (LPWSTR)lpmb->ppszButtonText[i], /* String for button */ + (UINT)wcslen(lpmb->ppszButtonText[i]),/* Length */ + BUTTONCODE); + + /* Get the X coord for the next push button */ + wLEdge += mbm.wMaxBtnSize + XPixFromXDU(DU_BTNGAP, mbm.cxMsgFontChar); + } + + return (LPBYTE)lpDlgTmp; +} + +void MB_CopyToClipboard(HWND hwndDlg) +{ + LPCWSTR lpszRead; + LPWSTR lpszAll, lpszWrite; + HANDLE hData; + static CONST WCHAR szLine[] = L"---------------------------\r\n"; + UINT cBufSize, i, cWrote; + LPMSGBOXDATA lpmb; + + if (!(lpmb = (LPMSGBOXDATA)GetWindowLongPtr(hwndDlg, GWLP_USERDATA))) { + return; + } + + if (!OpenClipboard(hwndDlg)) { + return; + } + + /* + * Calculate the buffer size: + * - the message text can be all \n, that will become \r\n + * - there are a few extra \r\n (that's why 8) + */ + cBufSize = (lpmb->lpszCaption ? wcslen(lpmb->lpszCaption) : 0) + + (lpmb->lpszText ? 2 * wcslen(lpmb->lpszText) : 0) + + 4 * sizeof(szLine) + + lpmb->cButtons * mbm.wMaxBtnSize + + 8; + + cBufSize *= sizeof(WCHAR); + + if (!(hData = GlobalAlloc(LHND, (LONG)(cBufSize)))) { + goto CloseClip; + } + + USERGLOBALLOCK(hData, lpszAll); + + cWrote = wsprintf(lpszAll, L"%s%s\r\n%s", + szLine, + lpmb->lpszCaption ? lpmb->lpszCaption : L"", + szLine); + + lpszWrite = lpszAll + cWrote; + lpszRead = lpmb->lpszText; + /* + * Change \n to \r\n in the text + */ + for (i = 0; *lpszRead; i++) { + + if (*lpszRead == L'\n') + *lpszWrite++ = L'\r'; + + *lpszWrite++ = *lpszRead++; + } + + cWrote = wsprintf(lpszWrite, L"\r\n%s", szLine); + lpszWrite += cWrote; + + /* + * Remove & from the button texts + */ + for (i = 0; i < lpmb->cButtons; i++) { + + lpszRead = lpmb->ppszButtonText[i]; + while (*lpszRead) { + if (*lpszRead != L'&') { + *lpszWrite++ = *lpszRead; } - case WM_DESTROY: - g_subclassed.erase(std::remove_if( - g_subclassed.begin(), - g_subclassed.end(), - [hWnd](HWND hw) - { - return hw == hWnd; - } - )); - return 0; + lpszRead++; } + *lpszWrite++ = L' '; + *lpszWrite++ = L' '; + *lpszWrite++ = L' '; } + wsprintf(lpszWrite, L"\r\n%s\0", szLine); + + USERGLOBALUNLOCK(hData); + + EmptyClipboard(); + + SetClipboardData(CF_UNICODETEXT, hData); + +CloseClip: + CloseClipboard(); - return DefSubclassProc(hWnd, uMsg, wParam, lParam); } -typedef INT_PTR (CALLBACK *MB_DlgProc_t)(HWND, UINT, WPARAM, LPARAM); -MB_DlgProc_t MB_DlgProc_orig; -INT_PTR CALLBACK MB_DlgProc_hook( - HWND hWnd, - UINT uMsg, +INT_PTR CALLBACK MB_DlgProc( + HWND hwndDlg, + UINT wMsg, WPARAM wParam, - LPARAM lParam -) + LPARAM lParam) { - if (settings.background) + HWND hwndT; + int iCount; + LPMSGBOXDATA lpmb; + HWND hwndOwner; + PVOID lpfnCallback; + BOOL bTimedOut = FALSE; + + switch (wMsg) { + case WM_CTLCOLORDLG: + case WM_CTLCOLORSTATIC: + return DefWindowProcW(hwndDlg, WM_CTLCOLORMSGBOX, + wParam, lParam); + + case WM_TIMER: + if (!bTimedOut) { + bTimedOut = TRUE; + EndDialog(hwndDlg, IDTIMEOUT); + } + break; + + case WM_NCDESTROY: + if ((lpmb = (LPMSGBOXDATA)GetWindowLongPtr(hwndDlg, GWLP_USERDATA))) { + if (lpmb->dwTimeout != INFINITE) { + KillTimer(hwndDlg, 0); + lpmb->dwTimeout = INFINITE; + } + } + return DefWindowProcW(hwndDlg, wMsg, + wParam, lParam); + + + case WM_INITDIALOG: { - switch (uMsg) - { - case WM_CTLCOLORDLG: - case WM_CTLCOLORSTATIC: - return (INT_PTR)GetSysColorBrush(COLOR_3DFACE); - /* The static window used for the text must be subclassed - to completely override painting */ - case WM_INITDIALOG: - { - HWND hTxt = GetDlgItem(hWnd, 0xFFFF); - if (WindhawkUtils::SetWindowSubclassFromAnyThread(hTxt, MsgBoxTextSubclassProc, NULL)) - { - g_subclassed.push_back(hTxt); - } - - return MB_DlgProc_orig(hWnd, uMsg, wParam, lParam); + lpmb = (LPMSGBOXDATA)lParam; + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (ULONG_PTR)lParam); + + NtUserCallHwnd(hwndDlg, SFI_SETMSGBOX); + + if (lpmb->dwStyle & MB_TOPMOST) { + SetWindowPos(hwndDlg, + HWND_TOPMOST, + 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE); + } + + if (lpmb->dwStyle & MB_USERICON) { + SendDlgItemMessage(hwndDlg, IDUSERICON, STM_SETICON, (WPARAM)(lpmb->lpszIcon), 0); + iCount = ALERT_SYSTEM_WARNING; + } + else { + /* + * Generate an alert notification + */ + switch (lpmb->dwStyle & MB_ICONMASK) { + case MB_ICONWARNING: + iCount = ALERT_SYSTEM_WARNING; + break; + + case MB_ICONQUESTION: + iCount = ALERT_SYSTEM_QUERY; + break; + + case MB_ICONERROR: + iCount = ALERT_SYSTEM_ERROR; + break; + + case MB_ICONINFORMATION: + default: + iCount = ALERT_SYSTEM_INFORMATIONAL; + break; } - case WM_PAINT: + } + + NotifyWinEvent(EVENT_SYSTEM_ALERT, hwndDlg, OBJID_ALERT, iCount); + + hwndT = GetWindow(hwndDlg, GW_CHILD); + iCount = lpmb->DefButton; + while (iCount--) + hwndT = GetWindow(hwndT, GW_HWNDNEXT); + + SetFocus(hwndT); + + hwndT = hwndDlg; + + if (lpmb->CancelId == 0) { + HMENU hMenu; + + if (hMenu = GetSystemMenu(hwndDlg, FALSE)) { + DeleteMenu(hMenu, SC_CLOSE, (UINT)MF_BYCOMMAND); + } + } + + if ((lpmb->dwStyle & MB_TYPEMASK) == MB_OK) { + hwndDlg = GetDlgItem(hwndDlg, IDOK); + + if (hwndDlg != NULL) { + SetWindowLongPtr(hwndDlg, GWLP_ID, IDCANCEL); + } + } + + if (lpmb->dwTimeout != INFINITE) { + if (SetTimer(hwndT, 0, lpmb->dwTimeout, NULL) == 0) { + /* + * Couldn't create the timer, so "clear" out the timeout value + * for future reference. + */ + lpmb->dwTimeout = INFINITE; + } + } + + // Hack because XP's positioning code is broken on 10 + // This centers it properly + HMONITOR hm = MonitorFromWindow(hwndT, MONITOR_DEFAULTTOPRIMARY); + MONITORINFO mi; + mi.cbSize = sizeof(mi); + GetMonitorInfoW(hm, &mi); + RECT rc; + GetWindowRect(hwndT, &rc); + int x = (mi.rcWork.right - mi.rcWork.left) / 2; + x -= (rc.right - rc.left) / 2; + int y = (mi.rcWork.bottom - mi.rcWork.top) / 2; + y -= (rc.bottom - rc.top) /2; + + // Tile effect + int cntMBox = GetClientInfo()->pDeskInfo->cntMBox; + x += cntMBox * SYSMET(CXSIZE); + y += cntMBox * SYSMET(CYSIZE); + + // Make sure it stays in the screen + if ((x + (rc.right - rc.left)) > mi.rcWork.right) + { + x = mi.rcWork.right - SYSMET(CXEDGE) - (rc.right - rc.left); + } + + if (y + (rc.bottom - rc.top) > mi.rcWork.bottom) + { + y = mi.rcWork.bottom - SYSMET(CYEDGE) - (rc.bottom - rc.top); + if (y < mi.rcWork.top) { - PAINTSTRUCT ps; - BeginPaint(hWnd, &ps); - EndPaint(hWnd, &ps); - return TRUE; + y = mi.rcMonitor.bottom - SYSMET(CYEDGE) - (rc.bottom - rc.top); } } + + SetWindowPos( + hwndT, + NULL, + x, y, + 0, 0, + SWP_NOZORDER | SWP_NOSIZE + ); + + return FALSE; + } + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + case IDCANCEL: + // + // Check if a control exists with the given ID; This + // check is needed because DlgManager returns IDCANCEL + // blindly when ESC is pressed even if a button with + // IDCANCEL is not present. + // + if (!GetDlgItem(hwndDlg, LOWORD(wParam))) + return FALSE; + + + // else FALL THRO....This is intentional. + case IDABORT: + case IDIGNORE: + case IDNO: + case IDRETRY: + case IDYES: + case IDTRYAGAIN: + case IDCONTINUE: + EndDialog(hwndDlg, LOWORD(wParam)); + break; + + default: + return FALSE; + break; + } + break; + + case WM_COPY: + MB_CopyToClipboard(hwndDlg); + break; + + default: + return FALSE; + } + + return TRUE; +} + +int SoftModalMessageBox_XP( + LPMSGBOXDATA lpmb) +{ + LPBYTE lpDlgTmp; + int cyIcon, cxIcon; + int cxButtons; + int cxMBMax; + int cxText, cyText, xText; + int cxBox, cyBox; + int cxFoo, cxCaption; + int xMB, yMB; + HDC hdc; + DWORD wIconOrdNum; + DWORD wCaptionLen; + DWORD wTextLen; + WORD OrdNum[2]; // Must be an array or WORDs + RECT rc; + RECT rcWork; + HCURSOR hcurOld; + DWORD dwStyleMsg, dwStyleText; + DWORD dwExStyleMsg = 0; + DWORD dwStyleDlg; + HWND hwndOwner; + LPWSTR lpsz; + int iRetVal = 0; + HICON hIcon; + HGLOBAL hTemplate = NULL; + HGLOBAL hCaption = NULL; + HGLOBAL hText = NULL; + HINSTANCE hInstMsg = lpmb->hInstance; + SIZE size; + HFONT hFontOld = NULL; + int cntMBox; + HMONITOR hMonitor; + MONITORINFO mi; + + GetMessageBoxMetrics(&mbm); + + dwStyleMsg = lpmb->dwStyle; + + if (dwStyleMsg & MB_RIGHT) { + dwExStyleMsg |= WS_EX_RIGHT; + } + + if (!IS_PTR(lpmb->lpszCaption)) { + /* + * Won't ever be NULL because MessageBox sticks "Error!" on error. + */ + if (hInstMsg && (hCaption = LocalAlloc(LPTR, MAX_RES_STRING * sizeof(WCHAR)))) { + lpsz = (LPWSTR)hCaption; + LoadString(hInstMsg, PTR_TO_ID(lpmb->lpszCaption), lpsz, MAX_RES_STRING); + } + else { + lpsz = NULL; + } + + lpmb->lpszCaption = lpsz ? lpsz : szEmpty; + } + + if (!IS_PTR(lpmb->lpszText)) { + // NULL not allowed + if (hInstMsg && (hText = LocalAlloc(LPTR, MAX_RES_STRING * sizeof(WCHAR)))) { + lpsz = (LPWSTR)hText; + LoadString(hInstMsg, PTR_TO_ID(lpmb->lpszText), lpsz, MAX_RES_STRING); + } + else { + lpsz = NULL; + } + + lpmb->lpszText = lpsz ? lpsz : szEmpty; + } + + if ((dwStyleMsg & MB_RTLREADING) || + (lpmb->lpszText != NULL && (lpmb->lpszText[0] == UNICODE_RLM) && + (lpmb->lpszText[1] == UNICODE_RLM))) { + // + // Set Mirroring so that MessageBox and its child controls + // get mirrored. Otherwise, the message box and its child controls + // are Left-To-Right. + // + dwExStyleMsg |= WS_EX_LAYOUTRTL; + + // + // And turn off any conflicting flags. + // + dwExStyleMsg &= ~WS_EX_RIGHT; + if (dwStyleMsg & MB_RTLREADING) { + dwStyleMsg &= ~MB_RTLREADING; + dwStyleMsg ^= MB_RIGHT; + } + } + + if ((dwStyleMsg & MB_ICONMASK) == MB_USERICON) + hIcon = LoadIcon(hInstMsg, lpmb->lpszIcon); + else + hIcon = NULL; + + // For compatibility reasons, we still allow the message box to come up. + hwndOwner = lpmb->hwndOwner; + +getthedc: + // Check if we're out of cache DCs until robustness... + if (!(hdc = GetDCEx(NULL, NULL, DCX_WINDOW | DCX_CACHE))) { + + /* + * The above call might fail for TIF_RESTRICTED processes + * so check for the DC from the owner window + */ + if (!(hdc = GetDCEx(hwndOwner, NULL, DCX_WINDOW | DCX_CACHE))) + goto SMB_Exit; + } + + // Figure out the types and dimensions of buttons + cxButtons = (lpmb->cButtons * mbm.wMaxBtnSize) + ((lpmb->cButtons - 1) * XPixFromXDU(DU_BTNGAP, mbm.cxMsgFontChar)); + + // Ditto for the icon, if there is one. If not, cxIcon & cyIcon are 0. + + if (wIconOrdNum = MB_GetIconOrdNum(dwStyleMsg)) { + cxIcon = SYSMET(CXICON) + XPixFromXDU(DU_INNERMARGIN, mbm.cxMsgFontChar); + cyIcon = SYSMET(CYICON); + } + else + cxIcon = cyIcon = 0; + + hFontOld = (HFONT)SelectObject(hdc, mbm.hCaptionFont); + + // Find the max between the caption text and the buttons + wCaptionLen = wcslen(lpmb->lpszCaption); + GetTextExtentPointW(hdc, lpmb->lpszCaption, wCaptionLen, &size); + cxCaption = size.cx + 2 * SYSMET(CXSIZE); + + // + // The max width of the message box is 5/8 of the work area for most + // countries. We will then try 6/8 and 7/8 if it won't fit. Then + // we will use whole screen. + // + hMonitor = MonitorFromWindow(hwndOwner, MONITOR_DEFAULTTOPRIMARY); + mi.cbSize = sizeof(MONITORINFO); + GetMonitorInfoW(hMonitor, &mi); + CopyRect(&rcWork, &mi.rcWork); + cxMBMax = MulDiv(rcWork.right - rcWork.left, 5, 8); + + cxFoo = 2 * XPixFromXDU(DU_OUTERMARGIN, mbm.cxMsgFontChar); + + SelectObject(hdc, mbm.hMessageFont); + + // + // If the text doesn't fit in 5/8, try 7/8 of the screen + // +ReSize: + // + // The message box is as big as needed to hold the caption/text/buttons, + // but not bigger than the maximum width. + // + + cxBox = cxMBMax - 2 * SYSMET(CXFIXEDFRAME); + + // Ask DrawText for the right cx and cy + rc.left = 0; + rc.top = 0; + rc.right = cxBox - cxFoo - cxIcon; + rc.bottom = rcWork.bottom - rcWork.top; + cyText = DrawTextExW(hdc, (LPWSTR)lpmb->lpszText, -1, &rc, + DT_CALCRECT | DT_WORDBREAK | DT_EXPANDTABS | + DT_NOPREFIX | DT_EXTERNALLEADING | DT_EDITCONTROL, NULL); + // + // Make sure we have enough width to hold the buttons, in addition to + // the icon+text. Always force the buttons. If they don't fit, it's + // because the working area is small. + // + // + // The buttons are centered underneath the icon/text. + // + cxText = rc.right - rc.left + cxIcon + cxFoo; + cxBox = min(cxBox, max(cxText, cxCaption)); + cxBox = max(cxBox, cxButtons + cxFoo); + cxText = cxBox - cxFoo - cxIcon; + + // + // Now we know the text width for sure. Really calculate how high the + // text will be. + // + rc.left = 0; + rc.top = 0; + rc.right = cxText; + rc.bottom = rcWork.bottom - rcWork.top; + cyText = DrawTextExW(hdc, (LPWSTR)lpmb->lpszText, -1, &rc, DT_CALCRECT | DT_WORDBREAK + | DT_EXPANDTABS | DT_NOPREFIX | DT_EXTERNALLEADING | DT_EDITCONTROL, NULL); + + // Find the window size. + cxBox += 2 * SYSMET(CXFIXEDFRAME); + cyBox = 2 * SYSMET(CYFIXEDFRAME) + SYSMET(CYCAPTION) + YPixFromYDU(2 * DU_OUTERMARGIN + + DU_INNERMARGIN + DU_BTNHEIGHT, mbm.cyMsgFontChar); + + cyBox += max(cyIcon, cyText); + + // + // If the message box doesn't fit on the working area, we'll try wider + // sizes successively: 6/8 of work then 7/8 of screen. + // + if (cyBox > rcWork.bottom - rcWork.top) { + int cxTemp; + + cxTemp = MulDiv(rcWork.right - rcWork.left, 6, 8); + + if (cxMBMax == MulDiv(rcWork.right - rcWork.left, 5, 8)) { + cxMBMax = cxTemp; + goto ReSize; + } + else if (cxMBMax == cxTemp) { + // then let's try with rcMonitor + CopyRect(&rcWork, &mi.rcMonitor); + cxMBMax = MulDiv(rcWork.right - rcWork.left, 7, 8); + goto ReSize; + } + } + + if (hFontOld) { + SelectObject(hdc, hFontOld); + } + ReleaseDC(NULL, hdc); + + // Find the window position + cntMBox = GetClientInfo()->pDeskInfo->cntMBox; + + xMB = (rcWork.left + rcWork.right - cxBox) / 2 + (cntMBox * SYSMET(CXSIZE)); + xMB = max(xMB, rcWork.left); + yMB = (rcWork.top + rcWork.bottom - cyBox) / 2 + (cntMBox * SYSMET(CYSIZE)); + yMB = max(yMB, rcWork.top); + + // + // Bottom, right justify if we're going off the screen--but leave a + // little gap. + // + if (xMB + cxBox > rcWork.right) { + xMB = rcWork.right - SYSMET(CXEDGE) - cxBox; + } + + // + // Pin to the working area. If it won't fit, then pin to the screen + // height. Bottom justify it at least if too big even for that, so + // that the buttons are visible. + // + if (yMB + cyBox > rcWork.bottom) { + yMB = rcWork.bottom - SYSMET(CYEDGE) - cyBox; + if (yMB < rcWork.top) { + yMB = mi.rcMonitor.bottom - SYSMET(CYEDGE) - cyBox; + } + } + + wTextLen = wcslen(lpmb->lpszText); + + // Find out the memory required for the Dlg template and try to alloc it + hTemplate = LocalAlloc(LPTR, MB_FindDlgTemplateSize(lpmb)); + if (!hTemplate) { + goto SMB_Exit; + } + + lpDlgTmp = (LPBYTE)hTemplate; + + // + // Setup the dialog style for the message box + // + dwStyleDlg = WS_POPUPWINDOW | WS_CAPTION | DS_ABSALIGN | DS_NOIDLEMSG | + DS_SETFONT | DS_3DLOOK; + + if ((dwStyleMsg & MB_MODEMASK) == MB_SYSTEMMODAL) { + dwStyleDlg |= DS_SYSMODAL | DS_SETFOREGROUND; + } + else { + dwStyleDlg |= DS_MODALFRAME | WS_SYSMENU; + } + + if (dwStyleMsg & MB_SETFOREGROUND) { + dwStyleDlg |= DS_SETFOREGROUND; + } + + // Add the Header of the Dlg Template + // BOGUS !!! don't ADD bools + lpDlgTmp = MB_UpdateDlgHdr((LPDLGTEMPLATE)lpDlgTmp, dwStyleDlg, dwExStyleMsg, + (BYTE)(lpmb->cButtons + (wIconOrdNum != 0) + (lpmb->lpszText != NULL)), + xMB, yMB, cxBox, cyBox, (LPWSTR)lpmb->lpszCaption, wCaptionLen); + + // + // Center the buttons + // + + cxFoo = (cxBox - 2 * SYSMET(CXFIXEDFRAME) - cxButtons) / 2; + + lpDlgTmp = MB_AddPushButtons((LPDLGITEMTEMPLATE)lpDlgTmp, lpmb, cxFoo, + cyBox - SYSMET(CYCAPTION) - (2 * SYSMET(CYFIXEDFRAME)) - + YPixFromYDU(DU_OUTERMARGIN, mbm.cyMsgFontChar)); + + // Add Icon, if any, to the Dlg template + // + // The icon is always top justified. If the text is shorter than the + // height of the icon, we center it. Otherwise the text will start at + // the top. + // + if (wIconOrdNum) { + OrdNum[0] = 0xFFFF; // To indicate that an Ordinal number follows + OrdNum[1] = (WORD)wIconOrdNum; + + lpDlgTmp = MB_UpdateDlgItem((LPDLGITEMTEMPLATE)lpDlgTmp, IDUSERICON, // Control Id + SS_ICON | WS_GROUP | WS_CHILD | WS_VISIBLE, 0, + XPixFromXDU(DU_OUTERMARGIN, mbm.cxMsgFontChar), // X co-ordinate + YPixFromYDU(DU_OUTERMARGIN, mbm.cyMsgFontChar), // Y co-ordinate + 0, 0, // For Icons, CX and CY are ignored, can be zero + (LPWSTR)OrdNum, // Ordinal number of Icon + ARRAYSIZE(OrdNum), // Length of OrdNum + STATICCODE); + } + + // Add the Text of the Message to the Dlg Template + if (lpmb->lpszText) { + // + // Center the text if shorter than the icon. + // + if (cyText >= cyIcon) + cxFoo = 0; + else + cxFoo = (cyIcon - cyText) / 2; + + dwStyleText = SS_NOPREFIX | WS_GROUP | WS_CHILD | WS_VISIBLE | SS_EDITCONTROL; + if (dwStyleMsg & MB_RIGHT) { + dwStyleText |= SS_RIGHT; + xText = cxBox - (SYSMET(CXSIZE) + cxText); + } + else { + dwStyleText |= SS_LEFT; + xText = cxIcon + XPixFromXDU(DU_INNERMARGIN, mbm.cxMsgFontChar); + } + + MB_UpdateDlgItem((LPDLGITEMTEMPLATE)lpDlgTmp, -1, dwStyleText, dwExStyleMsg, xText, + YPixFromYDU(DU_OUTERMARGIN, mbm.cyMsgFontChar) + cxFoo, + cxText, cyText, + (LPWSTR)lpmb->lpszText, wTextLen, STATICCODE); + } + + // The dialog template is ready + + // + // Set the normal cursor + // + hcurOld = SetCursor(LoadCursorW(NULL, IDC_ARROW)); + + lpmb->lpszIcon = (LPWSTR)hIcon; + + if (!(lpmb->dwStyle & MB_USERICON)) + { + int wBeep = LOWORD(lpmb->dwStyle & MB_ICONMASK); + NtUserCallOneParam(wBeep, SFI_PLAYEVENTSOUND); + } + + iRetVal = (int)DialogBoxIndirectParamW(g_hUser32, + (LPCDLGTEMPLATEW)hTemplate, hwndOwner, + MB_DlgProc, (LPARAM)lpmb); + + // + // Fix up return value + if (iRetVal == -1) + iRetVal = 0; /* Messagebox should also return error */ + + // + // If the messagebox contains only OK button, then its ID is changed as + // IDCANCEL in MB_DlgProc; So, we must change it back to IDOK irrespective + // of whether ESC is pressed or Carriage return is pressed; + // + if (((dwStyleMsg & MB_TYPEMASK) == MB_OK) && iRetVal) + iRetVal = IDOK; + + + // + // Restore the previous cursor + // + if (hcurOld) + SetCursor(hcurOld); + +SMB_Exit: + if (hTemplate) + { + LocalFree(hTemplate); + } + + if (hCaption) + { + LocalFree(hCaption); + } + + if (hText) + { + LocalFree(hText); + } + + if (mbm.hCaptionFont) + { + DeleteObject(mbm.hCaptionFont); } - return MB_DlgProc_orig(hWnd, uMsg, wParam, lParam); + if (mbm.hMessageFont) + { + DeleteObject(mbm.hMessageFont); + } + + return iRetVal; +} + +int (WINAPI *SoftModalMessageBox_orig)(LPMSGBOXDATA); +int WINAPI SoftModalMessageBox_hook(LPMSGBOXDATA lpmb) +{ + if (g_bClassic) + return SoftModalMessageBox_XP(lpmb); + return SoftModalMessageBox_orig(lpmb); } -#define LoadIntSetting(NAME) settings.NAME = Wh_GetIntSetting(L ## #NAME) +#pragma endregion // "Classic style" void LoadSettings(void) { - LoadIntSetting(font); - LoadIntSetting(background); + g_bClassic = Wh_GetIntSetting(L"background"); } +const WindhawkUtils::SYMBOL_HOOK user32DllHooks[] = { + { + { + L"struct HFONT__ * " +#ifdef _WIN64 + L"__cdecl" +#else + L"__stdcall" +#endif + L" GetMessageBoxFontForDpi(unsigned int)" + }, + &GetMessageBoxFontForDpi_orig, + GetMessageBoxFontForDpi_hook, + false + } +}; + BOOL Wh_ModInit(void) { LoadSettings(); - HMODULE hUser32 = LoadLibraryW(L"user32.dll"); - if (!hUser32) + // Load undocumented NtUserCallOneParam function for playing message box sound + HMODULE hWin32U = GetModuleHandleW(L"win32u.dll"); + if (!hWin32U) { - MessageBoxW( - NULL, - L"Failed to load user32.dll. There is something seriously wrong with either your Windows install or Windhawk.", - L"Windhawk: Message Box Fix", - MB_ICONERROR - ); + Wh_Log(L"Failed to get handle to win32u.dll"); + return FALSE; + } + NtUserCallOneParam = (NtUserCallOneParam_t)GetProcAddress(hWin32U, "NtUserCallOneParam"); + if (!NtUserCallOneParam) + { + Wh_Log(L"Failed to find NtUserCallOneParam in win32u.dll"); return FALSE; } - GetDpiForWindow = (GetDpiForWindow_t)GetProcAddress(hUser32, "GetDpiForWindow"); - SystemParametersInfoForDpi = (SystemParametersInfoForDpi_t)GetProcAddress(hUser32, "SystemParametersInfoForDpi"); + NtUserCallHwnd = (NtUserCallHwnd_t)GetProcAddress(hWin32U, "NtUserCallHwnd"); + if (!NtUserCallHwnd) + { + Wh_Log(L"Failed to find NtUserCallHwnd in win32u.dll"); + return FALSE; + } - WindhawkUtils::SYMBOL_HOOK hooks[] = { - { - { - L"struct HFONT__ * " - #ifdef _WIN64 - L"__cdecl" - #else - L"__stdcall" - #endif - L" GetMessageBoxFontForDpi(unsigned int)" - }, - &GetMessageBoxFontForDpi_orig, - GetMessageBoxFontForDpi_hook, - false - }, - { - { - #ifdef _WIN64 - L"__int64 __cdecl MB_DlgProc(struct HWND__ *,unsigned int,unsigned __int64,__int64)" - #else - L"int __stdcall MB_DlgProc(struct HWND__ *,unsigned int,unsigned int,long)" - #endif - }, - &MB_DlgProc_orig, - MB_DlgProc_hook, - false - } - }; + g_hUser32 = GetModuleHandleW(L"user32.dll"); + void *SoftModalMessageBox = (void *)GetProcAddress(g_hUser32, "SoftModalMessageBox"); + if (!SoftModalMessageBox) + { + Wh_Log(L"Failed to find SoftModalMessageBox in user32.dll"); + return FALSE; + } - if (!WindhawkUtils::HookSymbols( - hUser32, - hooks, - ARRAYSIZE(hooks) + if (!Wh_SetFunctionHook( + SoftModalMessageBox, + (void *)SoftModalMessageBox_hook, + (void **)&SoftModalMessageBox_orig )) { - Wh_Log(L"Failed to hook one or more symbol functions"); + Wh_Log(L"Failed to hook SoftModalMessageBox in user32.dll"); return FALSE; } - return TRUE; -} - -/** - * Remove any subclasses from message box texts that are still there - * If we don't do this, programs with open message boxes will crash - */ -void Wh_ModUninit(void) -{ - size_t len = g_subclassed.size(); - for (size_t i = 0; i < len; i++) + if (!WindhawkUtils::HookSymbols( + g_hUser32, + user32DllHooks, + ARRAYSIZE(user32DllHooks) + )) { - WindhawkUtils::RemoveWindowSubclassFromAnyThread( - g_subclassed[i], - MsgBoxTextSubclassProc - ); + Wh_Log(L"Failed to hook GetMessageBoxFontForDpi in user32.dll"); + return FALSE; } + + return TRUE; } void Wh_ModSettingsChanged(void) From 5caa900cecb8fd17107363c40fdc102d6f68c512 Mon Sep 17 00:00:00 2001 From: aubrey <44238627+aubymori@users.noreply.github.com> Date: Wed, 18 Sep 2024 17:19:15 -0500 Subject: [PATCH 21/52] Custom Desktop Watermark 1.0.0 (#961) * Custom Desktop Watermark 1.0.0 * Limit to x86-64, fix memory leak * Fix typo and memory leak --- mods/custom-desktop-watermark.wh.cpp | 204 +++++++++++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 mods/custom-desktop-watermark.wh.cpp diff --git a/mods/custom-desktop-watermark.wh.cpp b/mods/custom-desktop-watermark.wh.cpp new file mode 100644 index 00000000..3a26e4f2 --- /dev/null +++ b/mods/custom-desktop-watermark.wh.cpp @@ -0,0 +1,204 @@ +// ==WindhawkMod== +// @id custom-desktop-watermark +// @name Custom Desktop Watermark +// @description Lets you set your own desktop watermark text +// @version 1.0.0 +// @author aubymori +// @github https://github.com/aubymori +// @include explorer.exe +// @architecture x86-64 +// @compilerOptions -lgdi32 -DWINVER=0x0A00 +// ==/WindhawkMod== + +// ==WindhawkModReadme== +/* +# Custom Desktop Watermark +This mod allows you to completely customize the desktop watermark, +putting in your own lines of text. + +## Examples + +**Windows 7 (not genuine)**: +![Windows 7 (not genuine)](https://raw.githubusercontent.com/aubymori/images/main/custom-desktop-watermark-win7.png) + +**Windows XP Professional x64 Edition**: +![Windows XP Professional x64 Edition](https://raw.githubusercontent.com/aubymori/images/main/custom-desktop-watermark-winxp.png) +*/ +// ==/WindhawkModReadme== + +// ==WindhawkModSettings== +/* +- lines: + - - text: "" + $name: Text + - title: false + $name: Use caption font + $description: Use caption font instead of message font when classic fonts are enabled. + $name: Text lines +- classic: false + $name: Use classic fonts + $description: Use caption/message font instead of just caption font. +*/ +// ==/WindhawkModSettings== + +#include +#include +#include + +#define RECTWIDTH(rc) ((rc).right - (rc).left) +#define RECTHEIGHT(rc) ((rc).bottom - (rc).top) + +WINUSERAPI BOOL WINAPI SystemParametersInfoForDpi(UINT uiAction, UINT uiParam, PVOID pvParam, UINT fWinIni, UINT dpi); + +struct WatermarkLine +{ + std::wstring text; + bool title; +}; + +std::vector g_lines; +bool g_bClassic = false; + +bool (*CDesktopWatermark_s_WantWatermark_orig)(void); +bool CDesktopWatermark_s_WantWatermark_hook(void) +{ + return true; +} + +int PaintLine( + HDC hDC, + LPCRECT lprc, + LPCWSTR lpszText, + HFONT hFont, + int offset +) +{ + HFONT hfOld = (HFONT)SelectObject(hDC, hFont); + + RECT rcText = { 0 }; + int newOffset = DrawTextW( + hDC, lpszText, -1, + &rcText, DT_CALCRECT | DT_SINGLELINE + ); + + RECT rcPaint = { 0 }; + rcPaint.left = lprc->right - RECTWIDTH(rcText); + rcPaint.top = lprc->bottom - RECTHEIGHT(rcText); + rcPaint.right = lprc->right; + rcPaint.bottom = lprc->bottom; + + int padding = MulDiv(5, GetDeviceCaps(hDC, LOGPIXELSX), 96); + OffsetRect(&rcPaint, -padding, -offset); + + DrawTextW(hDC, lpszText, -1, &rcPaint, DT_SINGLELINE); + + SelectObject(hDC, hfOld); + return newOffset; +} + +void (*CDesktopWatermark_s_DesktopBuildPaint_orig)(HDC, LPCRECT, HFONT); +void CDesktopWatermark_s_DesktopBuildPaint_hook( + HDC hDC, + LPCRECT lprc, + HFONT hFont +) +{ + COLORREF cr = SetTextColor(hDC, RGB(255, 255, 255)); + int offset = 0; + + NONCLIENTMETRICSW ncm = { sizeof(ncm) }; + int dpi = GetDeviceCaps(hDC, LOGPIXELSX); + SystemParametersInfoForDpi( + SPI_GETNONCLIENTMETRICS, + sizeof(ncm), + &ncm, + 0, + dpi + ); + HFONT hTitleFont = CreateFontIndirectW(&ncm.lfCaptionFont); + HFONT hMessageFont = CreateFontIndirectW(&ncm.lfMessageFont); + int padding = MulDiv(3, dpi, 96); + offset += MulDiv(g_bClassic ? 4 : 1, dpi, 96); + + for (size_t i = g_lines.size(); i--;) + { + WatermarkLine line = g_lines.at(i); + bool bMessageFont = g_bClassic && !line.title; + + offset += PaintLine( + hDC, lprc, line.text.c_str(), + bMessageFont ? hMessageFont : hTitleFont, + offset + ) + padding; + } + + SetTextColor(hDC, cr); +} + +const WindhawkUtils::SYMBOL_HOOK shell32DllHooks[] = { + { + { + L"public: static bool __cdecl CDesktopWatermark::s_WantWatermark(void)" + }, + &CDesktopWatermark_s_WantWatermark_orig, + CDesktopWatermark_s_WantWatermark_hook, + false + }, + { + { + L"private: static void __cdecl CDesktopWatermark::s_DesktopBuildPaint(struct HDC__ *,struct tagRECT const *,struct HFONT__ *)" + }, + &CDesktopWatermark_s_DesktopBuildPaint_orig, + CDesktopWatermark_s_DesktopBuildPaint_hook, + false + } +}; + +void LoadSettings(void) +{ + g_lines.clear(); + g_bClassic = Wh_GetIntSetting(L"classic"); + + for (int i = 0;; i++) + { + LPCWSTR lpszText = Wh_GetStringSetting(L"lines[%i].text", i); + if (!*lpszText) + { + Wh_FreeStringSetting(lpszText); + break; + } + bool title = Wh_GetIntSetting(L"lines[%i].title", i); + WatermarkLine line = { lpszText, title }; + g_lines.push_back(line); + Wh_FreeStringSetting(lpszText); + } +} + +BOOL Wh_ModInit(void) +{ + LoadSettings(); + + HMODULE hShell32 = LoadLibraryW(L"shell32.dll"); + if (!hShell32) + { + Wh_Log(L"Failed to load shell32.dll"); + return FALSE; + } + + if (!WindhawkUtils::HookSymbols( + hShell32, + shell32DllHooks, + ARRAYSIZE(shell32DllHooks) + )) + { + Wh_Log(L"Failed to hook one or more symbol functions in shell32.dll"); + return FALSE; + } + + return TRUE; +} + +void Wh_ModSettingsChanged(void) +{ + LoadSettings(); +} From 7b9cc220e6128fbdbd9f95e8588b674f98e407e7 Mon Sep 17 00:00:00 2001 From: aubrey <44238627+aubymori@users.noreply.github.com> Date: Thu, 19 Sep 2024 00:23:09 -0500 Subject: [PATCH 22/52] Custom Desktop Watermark 1.0.1: Ensure transparent text background (#965) Ensure transparent text background --- mods/custom-desktop-watermark.wh.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mods/custom-desktop-watermark.wh.cpp b/mods/custom-desktop-watermark.wh.cpp index 3a26e4f2..3c905d75 100644 --- a/mods/custom-desktop-watermark.wh.cpp +++ b/mods/custom-desktop-watermark.wh.cpp @@ -2,7 +2,7 @@ // @id custom-desktop-watermark // @name Custom Desktop Watermark // @description Lets you set your own desktop watermark text -// @version 1.0.0 +// @version 1.0.1 // @author aubymori // @github https://github.com/aubymori // @include explorer.exe @@ -42,7 +42,6 @@ putting in your own lines of text. // ==/WindhawkModSettings== #include -#include #include #define RECTWIDTH(rc) ((rc).right - (rc).left) @@ -104,6 +103,7 @@ void CDesktopWatermark_s_DesktopBuildPaint_hook( ) { COLORREF cr = SetTextColor(hDC, RGB(255, 255, 255)); + int bk = SetBkMode(hDC, TRANSPARENT); int offset = 0; NONCLIENTMETRICSW ncm = { sizeof(ncm) }; @@ -132,6 +132,7 @@ void CDesktopWatermark_s_DesktopBuildPaint_hook( ) + padding; } + SetBkMode(hDC, bk); SetTextColor(hDC, cr); } From 2ed0cde47915e2af9f136673ff1f10a36484acb1 Mon Sep 17 00:00:00 2001 From: Anixx Date: Thu, 19 Sep 2024 14:33:47 +0300 Subject: [PATCH 23/52] Update win10-taskbar-on-win11.wh.cpp (#967) Simplified description --- mods/win10-taskbar-on-win11.wh.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/mods/win10-taskbar-on-win11.wh.cpp b/mods/win10-taskbar-on-win11.wh.cpp index dc832064..2937b2f4 100644 --- a/mods/win10-taskbar-on-win11.wh.cpp +++ b/mods/win10-taskbar-on-win11.wh.cpp @@ -2,7 +2,7 @@ // @id win10-taskbar-on-win11 // @name Windows 10 taskbar on Windows 11 // @description Enables Windows 10 taskbar on Windows 11 versions 21H2 to 23H2 -// @version 1.0.1 +// @version 1.0.2 // @author Anixx // @github https://github.com/Anixx // @include explorer.exe @@ -10,14 +10,11 @@ // ==WindhawkModReadme== /* -This mod enables Windows 10 taskbar on Windows 11 versions 21H2 to 23H2 via registry query hook, without -using Explorer Patcher. The actual registry values remain unaffected. - -It may be useful also on later versions of Windows with Explorer.exe downgraded to previous versions -and/or manipulated Windows features using vivetool utility. +This mod enables Windows 10 taskbar on Windows 11 versions 21H2 to 23H2, without +using Explorer Patcher. Make sure you are using the appropriate version of Windows 11. If you want to get rid of the cogwheel icon in the tray area, use the mod "Hide Action Center icon". -You can restore the clock and set up the tray icons using legacy tray setup dialog, +You can set up the clock and the tray icons appearance using legacy tray setup dialog, which you can start with the command `explorer shell:::{05d7b0f4-2121-4eff-bf6b-ed3f69b894d9}`. */ // ==/WindhawkModReadme== From 0b018deaa9febe57c9ed85c1ad65aac49966d743 Mon Sep 17 00:00:00 2001 From: Michael Maltsev <4129781+m417z@users.noreply.github.com> Date: Thu, 19 Sep 2024 22:34:34 +0300 Subject: [PATCH 24/52] Vertical Taskbar for Windows 11 v1.2 (#969) * Added support for Windows 11 version 24H2. * Added an option to move the taskbar to the right. * Added an option for a custom start menu width, can be useful for custom start menu themes. * If taskbar labels are enabled, they're no longer rotated sideways. * The Copilot icon and widget icons are no longer rotated sideways. * Fixed thumbnails being cut off after closing an item. --- mods/taskbar-vertical.wh.cpp | 1043 +++++++++++++++++++++++++++------- 1 file changed, 849 insertions(+), 194 deletions(-) diff --git a/mods/taskbar-vertical.wh.cpp b/mods/taskbar-vertical.wh.cpp index 84458fe5..3e784988 100644 --- a/mods/taskbar-vertical.wh.cpp +++ b/mods/taskbar-vertical.wh.cpp @@ -2,7 +2,7 @@ // @id taskbar-vertical // @name Vertical Taskbar for Windows 11 // @description Finally, the missing vertical taskbar option for Windows 11! -// @version 1.1 +// @version 1.2 // @author m417z // @github https://github.com/m417z // @twitter https://twitter.com/m417z @@ -11,6 +11,7 @@ // @include StartMenuExperienceHost.exe // @include SearchHost.exe // @include ShellExperienceHost.exe +// @include ShellHost.exe // @architecture x86-64 // @compilerOptions -DWINVER=0x0605 -lgdi32 -lole32 -loleaut32 -lruntimeobject -lshcore // ==/WindhawkMod== @@ -29,9 +30,12 @@ Finally, the missing vertical taskbar option for Windows 11! +The taskbar can be placed on the left or on the right, and a custom width can be +set in the mod settings. + ## Compatibility -The mod was designed for up-to-date Windows 11 versions 22H2 and 23H2. Other +The mod was designed for up-to-date Windows 11 versions 22H2 to 24H2. Other versions weren't tested and are probably not compatible. Some of the other taskbar mods, such as [Taskbar height and icon @@ -43,16 +47,36 @@ mod. The development of this mod was funded by [AuthLite LLC](https://authlite.com/). Thank you for contributing and allowing all Windhawk users to enjoy it! -![Screenshot](https://i.imgur.com/BxQMy5K.png) +![Screenshot](https://i.imgur.com/RBK5Mv9.png) + +With labels: + +![Screenshot with labels](https://i.imgur.com/JloORIB.png) */ // ==/WindhawkModReadme== // ==WindhawkModSettings== /* +- taskbarLocation: left + $name: Taskbar location + $options: + - left: Left + - right: Right - TaskbarWidth: 80 $name: Taskbar width $description: >- The width, in pixels, of the taskbar +- taskbarLocationSecondary: sameAsPrimary + $name: Taskbar location on secondary monitors + $options: + - sameAsPrimary: Same as on primary monitor + - left: Left + - right: Right +- startMenuWidth: 0 + $name: Start menu width + $description: >- + Set to zero to use the system default width, set to a custom value if using + a customized start menu, e.g. with the Windows 11 Start Menu Styler mod */ // ==/WindhawkModSettings== @@ -84,8 +108,16 @@ using namespace winrt::Windows::UI::Xaml; #define SPI_SETLOGICALDPIOVERRIDE 0x009F #endif +enum class TaskbarLocation { + left, + right, +}; + struct { + TaskbarLocation taskbarLocation; + TaskbarLocation taskbarLocationSecondary; int taskbarWidth; + int startMenuWidth; } g_settings; enum class Target { @@ -93,6 +125,7 @@ enum class Target { StartMenu, SearchHost, ShellExperienceHost, + ShellHost, // Win11 24H2. }; Target g_target; @@ -105,9 +138,10 @@ std::atomic g_hookCallCounter; int g_originalTaskbarHeight; bool g_windowRgnChanging; -bool g_inSystemTraySecondaryController_UpdateFrameSize; +bool g_inSystemTrayController_UpdateFrameSize; bool g_inAugmentedEntryPointButton_UpdateButtonPadding; bool g_inCTaskListThumbnailWnd_DisplayUI; +bool g_inCTaskListThumbnailWnd_LayoutThumbnails; bool g_inChevronSystemTrayIconDataModel2_OnIconClicked; std::vector> g_notifyIconsUpdated; @@ -235,6 +269,19 @@ FrameworkElement FindChildByClassName(FrameworkElement element, }); } +TaskbarLocation GetTaskbarLocationForMonitor(HMONITOR monitor) { + if (g_settings.taskbarLocation == g_settings.taskbarLocationSecondary) { + return g_settings.taskbarLocation; + } + + const POINT ptZero = {0, 0}; + HMONITOR primaryMonitor = + MonitorFromPoint(ptZero, MONITOR_DEFAULTTOPRIMARY); + + return monitor == primaryMonitor ? g_settings.taskbarLocation + : g_settings.taskbarLocationSecondary; +} + using IconContainer_IsStorageRecreationRequired_t = bool(WINAPI*)(void* pThis, void* param1, int flags); @@ -306,17 +353,30 @@ DWORD WINAPI TrayUI_GetDockedRect_Hook(void* pThis, RECT* rect, BOOL param2) { RECT monitorRect; GetMonitorRect(monitor, &monitorRect); - UINT monitorDpiX = 96; - UINT monitorDpiY = 96; - GetDpiForMonitor(monitor, MDT_DEFAULT, &monitorDpiX, &monitorDpiY); - int taskbarOriginalHeightScaled = - MulDiv(g_originalTaskbarHeight, monitorDpiY, 96); - if (!g_unloading) { rect->top = monitorRect.top; - rect->right = rect->left + (rect->bottom - rect->top); + + switch (GetTaskbarLocationForMonitor(monitor)) { + case TaskbarLocation::left: + rect->left = monitorRect.left; + rect->right = rect->left + (rect->bottom - rect->top); + break; + + case TaskbarLocation::right: + rect->right = monitorRect.right; + rect->left = rect->right - (rect->bottom - rect->top); + break; + } } else { + UINT monitorDpiX = 96; + UINT monitorDpiY = 96; + GetDpiForMonitor(monitor, MDT_DEFAULT, &monitorDpiX, &monitorDpiY); + + int taskbarOriginalHeightScaled = + MulDiv(g_originalTaskbarHeight, monitorDpiY, 96); + rect->top = rect->bottom - taskbarOriginalHeightScaled; + rect->left = monitorRect.left; rect->right = monitorRect.right; } @@ -355,15 +415,30 @@ void WINAPI TrayUI_MakeStuckRect_Hook(void* pThis, UINT monitorDpiX = 96; UINT monitorDpiY = 96; GetDpiForMonitor(monitor, MDT_DEFAULT, &monitorDpiX, &monitorDpiY); - int taskbarWidthScaled = MulDiv(g_settings.taskbarWidth, monitorDpiX, 96); - int taskbarOriginalHeightScaled = - MulDiv(g_originalTaskbarHeight, monitorDpiY, 96); if (!g_unloading) { + int taskbarWidthScaled = + MulDiv(g_settings.taskbarWidth, monitorDpiX, 96); + rect->top = monitorRect.top; - rect->right = rect->left + taskbarWidthScaled; + + switch (GetTaskbarLocationForMonitor(monitor)) { + case TaskbarLocation::left: + rect->left = monitorRect.left; + rect->right = rect->left + taskbarWidthScaled; + break; + + case TaskbarLocation::right: + rect->right = monitorRect.right; + rect->left = rect->right - taskbarWidthScaled; + break; + } } else { + int taskbarOriginalHeightScaled = + MulDiv(g_originalTaskbarHeight, monitorDpiY, 96); + rect->top = rect->bottom - taskbarOriginalHeightScaled; + rect->left = monitorRect.left; rect->right = monitorRect.right; } } @@ -381,7 +456,8 @@ LRESULT TaskbarWndProcPostProcess(HWND hWnd, // Calling CreateRectRgn has two reasons: To actually set the region, and to // post window size change events which cause element sizes and positions to // be recalculated. - auto updateWindowRgn = [](HWND hWnd, int width, int height) { + auto updateWindowRgn = [](HMONITOR monitor, HWND hWnd, int width, + int height, int windowWidth) { // Avoid handling this recursively as SetWindowRgn triggers // WM_WINDOWPOSCHANGED again. if (g_windowRgnChanging) { @@ -391,7 +467,18 @@ LRESULT TaskbarWndProcPostProcess(HWND hWnd, g_windowRgnChanging = true; if (!g_unloading) { - HRGN windowRegion = CreateRectRgn(0, 0, width, height); + HRGN windowRegion; + switch (GetTaskbarLocationForMonitor(monitor)) { + case TaskbarLocation::left: + windowRegion = CreateRectRgn(0, 0, width, height); + break; + + case TaskbarLocation::right: + windowRegion = CreateRectRgn(windowWidth - width, 0, + windowWidth, height); + break; + } + if (windowRegion) { SetWindowRgn(hWnd, windowRegion, TRUE); } @@ -408,52 +495,93 @@ LRESULT TaskbarWndProcPostProcess(HWND hWnd, RECT* rect = (RECT*)lParam; - if (!g_unloading) { - HMONITOR monitor = - MonitorFromRect(rect, MONITOR_DEFAULTTONEAREST); + HMONITOR monitor = MonitorFromRect(rect, MONITOR_DEFAULTTONEAREST); + if (!g_unloading) { RECT monitorRect; GetMonitorRect(monitor, &monitorRect); rect->top = monitorRect.top; - rect->right = rect->left + (rect->bottom - rect->top); + + switch (GetTaskbarLocationForMonitor(monitor)) { + case TaskbarLocation::left: + rect->left = monitorRect.left; + rect->right = rect->left + (rect->bottom - rect->top); + break; + + case TaskbarLocation::right: + rect->right = monitorRect.right; + rect->left = rect->right - (rect->bottom - rect->top); + break; + } } updateWindowRgn( - hWnd, + monitor, hWnd, MulDiv(g_settings.taskbarWidth, GetDpiForWindow(hWnd), 96), - rect->bottom - rect->top); + rect->bottom - rect->top, rect->right - rect->left); break; } case WM_WINDOWPOSCHANGING: { auto* windowpos = (WINDOWPOS*)lParam; - if (!(windowpos->flags & SWP_NOSIZE)) { - Wh_Log(L"WM_WINDOWPOSCHANGING (no SWP_NOSIZE): %08X", + if ((windowpos->flags & (SWP_NOSIZE | SWP_NOMOVE)) != + (SWP_NOSIZE | SWP_NOMOVE)) { + Wh_Log(L"WM_WINDOWPOSCHANGING (size or move): %08X", (DWORD)(ULONG_PTR)hWnd); + RECT rect{ + .left = windowpos->x, + .top = windowpos->y, + .right = windowpos->x + windowpos->cx, + .bottom = windowpos->y + windowpos->cy, + }; + HMONITOR monitor = + MonitorFromRect(&rect, MONITOR_DEFAULTTONEAREST); + if (!g_unloading) { - windowpos->cx = windowpos->cy; + if (!(windowpos->flags & SWP_NOSIZE)) { + windowpos->cx = windowpos->cy; + } + + if (!(windowpos->flags & SWP_NOMOVE) && + GetTaskbarLocationForMonitor(monitor) == + TaskbarLocation::right) { + RECT monitorRect; + GetMonitorRect(monitor, &monitorRect); + + windowpos->x = monitorRect.right - windowpos->cx; + } } updateWindowRgn( - hWnd, + monitor, hWnd, MulDiv(g_settings.taskbarWidth, GetDpiForWindow(hWnd), 96), - windowpos->cy); + windowpos->cy, windowpos->cx); } break; } case WM_WINDOWPOSCHANGED: { auto* windowpos = (WINDOWPOS*)lParam; - if (!(windowpos->flags & SWP_NOSIZE)) { - Wh_Log(L"WM_WINDOWPOSCHANGED (no SWP_NOSIZE): %08X", + if ((windowpos->flags & (SWP_NOSIZE | SWP_NOMOVE)) != + (SWP_NOSIZE | SWP_NOMOVE)) { + Wh_Log(L"WM_WINDOWPOSCHANGED (size or move): %08X", (DWORD)(ULONG_PTR)hWnd); + RECT rect{ + .left = windowpos->x, + .top = windowpos->y, + .right = windowpos->x + windowpos->cx, + .bottom = windowpos->y + windowpos->cy, + }; + HMONITOR monitor = + MonitorFromRect(&rect, MONITOR_DEFAULTTONEAREST); + updateWindowRgn( - hWnd, + monitor, hWnd, MulDiv(g_settings.taskbarWidth, GetDpiForWindow(hWnd), 96), - windowpos->cy); + windowpos->cy, windowpos->cx); } break; } @@ -463,9 +591,9 @@ LRESULT TaskbarWndProcPostProcess(HWND hWnd, GetWindowRect_Original(hWnd, &rc); updateWindowRgn( - hWnd, + MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST), hWnd, MulDiv(g_settings.taskbarWidth, GetDpiForWindow(hWnd), 96), - rc.bottom - rc.top); + rc.bottom - rc.top, rc.right - rc.left); break; } } @@ -555,7 +683,22 @@ HRESULT WINAPI CTaskListWnd_ComputeJumpViewPosition_Hook( UINT monitorDpiY = 96; GetDpiForMonitor(monitor, MDT_DEFAULT, &monitorDpiX, &monitorDpiY); - point->X = pt.x; + RECT rc; + GetMonitorRect(monitor, &rc); + + int taskbarWidthScaled = MulDiv(g_settings.taskbarWidth, monitorDpiX, 96); + + switch (GetTaskbarLocationForMonitor(monitor)) { + case TaskbarLocation::left: + point->X = rc.left + taskbarWidthScaled; + break; + + case TaskbarLocation::right: + point->X = rc.right - taskbarWidthScaled; + point->X -= 159; + break; + } + point->Y = pt.y; // Move a bit lower to vertically center the cursor on the close item. @@ -589,6 +732,19 @@ void* WINAPI CTaskListThumbnailWnd_DisplayUI_Hook(void* pThis, return ret; } +using CTaskListThumbnailWnd_LayoutThumbnails_t = void(WINAPI*)(void* pThis); +CTaskListThumbnailWnd_LayoutThumbnails_t + CTaskListThumbnailWnd_LayoutThumbnails_Original; +void WINAPI CTaskListThumbnailWnd_LayoutThumbnails_Hook(void* pThis) { + Wh_Log(L">"); + + g_inCTaskListThumbnailWnd_LayoutThumbnails = true; + + CTaskListThumbnailWnd_LayoutThumbnails_Original(pThis); + + g_inCTaskListThumbnailWnd_LayoutThumbnails = false; +} + using ResourceDictionary_Lookup_t = winrt::Windows::Foundation::IInspectable*( WINAPI*)(void* pThis, void** result, @@ -625,9 +781,19 @@ ResourceDictionary_Lookup_Hook(void* pThis, return ret; } - valueThickness->Bottom -= - MulDiv(rc.right - rc.left, 96, GetDpiForWindow(hTaskbarWnd)); - valueThickness->Bottom += g_settings.taskbarWidth; + // TODO: Handle secondary taskbars. + switch (g_settings.taskbarLocation) { + case TaskbarLocation::left: + valueThickness->Bottom -= + MulDiv(rc.right - rc.left, 96, GetDpiForWindow(hTaskbarWnd)); + valueThickness->Bottom += g_settings.taskbarWidth; + break; + + case TaskbarLocation::right: + valueThickness->Bottom -= g_settings.taskbarWidth; + valueThickness->Bottom -= 230; + break; + } Wh_Log(L"Overriding value %s", keyString->c_str()); *ret = winrt::box_value(*valueThickness); @@ -684,24 +850,102 @@ double WINAPI TaskbarConfiguration_GetFrameSize_Hook(int enumTaskbarSize) { return TaskbarConfiguration_GetFrameSize_Original(enumTaskbarSize); } -using TaskbarFrame_MaxHeight_double_t = void(WINAPI*)(void* pThis, - double value); -TaskbarFrame_MaxHeight_double_t TaskbarFrame_MaxHeight_double_Original; +using SystemTrayController_UpdateFrameSize_t = void(WINAPI*)(void* pThis); +SystemTrayController_UpdateFrameSize_t + SystemTrayController_UpdateFrameSize_SymbolAddress; +SystemTrayController_UpdateFrameSize_t + SystemTrayController_UpdateFrameSize_Original; +void WINAPI SystemTrayController_UpdateFrameSize_Hook(void* pThis) { + Wh_Log(L">"); + + static LONG lastHeightOffset = []() -> LONG { + // Find the last height offset to reset the height value. + // + // 66 0f 2e b3 b0 00 00 00 UCOMISD uVar4,qword ptr [RBX + 0xb0] + // 7a 4c JP LAB_180075641 + // 75 4a JNZ LAB_180075641 + const BYTE* start = + (const BYTE*)SystemTrayController_UpdateFrameSize_SymbolAddress; + const BYTE* end = start + 0x200; + for (const BYTE* p = start; p != end; p++) { + if (p[0] == 0x66 && p[1] == 0x0F && p[2] == 0x2E && p[3] == 0xB3 && + p[8] == 0x7A && p[10] == 0x75) { + LONG offset = *(LONG*)(p + 4); + Wh_Log(L"lastHeightOffset=0x%X", offset); + return offset; + } + } + + Wh_Log(L"lastHeightOffset not found"); + return 0; + }(); -using TaskbarFrame_Height_double_t = void(WINAPI*)(void* pThis, double value); -TaskbarFrame_Height_double_t TaskbarFrame_Height_double_Original; -void WINAPI TaskbarFrame_Height_double_Hook(void* pThis, double value) { + if (lastHeightOffset > 0) { + *(double*)((BYTE*)pThis + lastHeightOffset) = 0; + } + + g_inSystemTrayController_UpdateFrameSize = true; + + SystemTrayController_UpdateFrameSize_Original(pThis); + + g_inSystemTrayController_UpdateFrameSize = false; +} + +void* TaskbarController_OnGroupingModeChanged; + +using TaskbarController_UpdateFrameHeight_t = void(WINAPI*)(void* pThis); +TaskbarController_UpdateFrameHeight_t + TaskbarController_UpdateFrameHeight_Original; +void WINAPI TaskbarController_UpdateFrameHeight_Hook(void* pThis) { Wh_Log(L">"); - if (TaskbarFrame_MaxHeight_double_Original) { - TaskbarFrame_MaxHeight_double_Original( - pThis, std::numeric_limits::infinity()); + static LONG taskbarFrameOffset = []() -> LONG { + // 48:83EC 28 | sub rsp,28 + // 48:8B81 88020000 | mov rax,qword ptr ds:[rcx+288] + // or + // 4C:8B81 80020000 | mov r8,qword ptr ds:[rcx+280] + const BYTE* p = (const BYTE*)TaskbarController_OnGroupingModeChanged; + if (p[0] == 0x48 && p[1] == 0x83 && p[2] == 0xEC && + (p[4] == 0x48 || p[4] == 0x4C) && p[5] == 0x8B && + (p[6] & 0xC0) == 0x80) { + LONG offset = *(LONG*)(p + 7); + Wh_Log(L"taskbarFrameOffset=0x%X", offset); + return offset; + } + + Wh_Log(L"taskbarFrameOffset not found"); + return 0; + }(); + + if (taskbarFrameOffset <= 0) { + Wh_Log(L"taskbarFrameOffset <= 0"); + TaskbarController_UpdateFrameHeight_Original(pThis); + return; } - // Set the height to NaN (Auto) to always match the parent height. - value = std::numeric_limits::quiet_NaN(); + void* taskbarFrame = *(void**)((BYTE*)pThis + taskbarFrameOffset); + if (!taskbarFrame) { + Wh_Log(L"!taskbarFrame"); + TaskbarController_UpdateFrameHeight_Original(pThis); + return; + } + + FrameworkElement taskbarFrameElement = nullptr; + ((IUnknown**)taskbarFrame)[1]->QueryInterface( + winrt::guid_of(), + winrt::put_abi(taskbarFrameElement)); + if (!taskbarFrameElement) { + Wh_Log(L"!taskbarFrameElement"); + TaskbarController_UpdateFrameHeight_Original(pThis); + return; + } + + taskbarFrameElement.MaxHeight(std::numeric_limits::infinity()); - return TaskbarFrame_Height_double_Original(pThis, value); + TaskbarController_UpdateFrameHeight_Original(pThis); + + // Set the height to NaN (Auto) to always match the parent height. + taskbarFrameElement.Height(std::numeric_limits::quiet_NaN()); } using SystemTraySecondaryController_UpdateFrameSize_t = @@ -711,11 +955,11 @@ SystemTraySecondaryController_UpdateFrameSize_t void WINAPI SystemTraySecondaryController_UpdateFrameSize_Hook(void* pThis) { Wh_Log(L">"); - g_inSystemTraySecondaryController_UpdateFrameSize = true; + g_inSystemTrayController_UpdateFrameSize = true; SystemTraySecondaryController_UpdateFrameSize_Original(pThis); - g_inSystemTraySecondaryController_UpdateFrameSize = false; + g_inSystemTrayController_UpdateFrameSize = false; } using SystemTrayFrame_Height_t = void(WINAPI*)(void* pThis, double value); @@ -723,9 +967,10 @@ SystemTrayFrame_Height_t SystemTrayFrame_Height_Original; void WINAPI SystemTrayFrame_Height_Hook(void* pThis, double value) { // Wh_Log(L">"); - if (g_inSystemTraySecondaryController_UpdateFrameSize) { - // Set the secondary taskbar clock height to NaN, otherwise it may not - // match the custom taskbar height. + if (g_inSystemTrayController_UpdateFrameSize) { + Wh_Log(L">"); + // Set the system tray height to NaN, otherwise it may not match the + // custom taskbar height. value = std::numeric_limits::quiet_NaN(); } @@ -747,22 +992,25 @@ bool ApplyStyle(FrameworkElement taskbarFrame, float origin = g_unloading ? 0 : 0.5; rootGrid.RenderTransformOrigin({origin, origin}); - double marginTop = g_unloading ? 0 : size.Height - g_settings.taskbarWidth; - if (marginTop < 0) { - marginTop = 0; + Thickness margin{}; + if (!g_unloading) { + double marginValue = size.Height - g_settings.taskbarWidth; + if (marginValue > 0) { + margin.Top = marginValue; + } } FrameworkElement child = rootGrid; if (child && (child = FindChildByName(child, L"TaskbarFrame")) && (child = FindChildByName(child, L"RootGrid"))) { - child.Margin(Thickness{0, marginTop, 0, 0}); + child.Margin(margin); } child = rootGrid; if (child && (child = FindChildByClassName(child, L"SystemTray.SystemTrayFrame")) && (child = FindChildByName(child, L"SystemTrayFrameGrid"))) { - child.Margin(Thickness{0, marginTop, 0, 0}); + child.Margin(margin); } auto xamlRoot = taskbarFrame.XamlRoot(); @@ -778,31 +1026,31 @@ bool ApplyStyle(FrameworkElement taskbarFrame, return true; } -using TaskbarFrame_MeasureOverride_t = winrt::Windows::Foundation::Size*( - WINAPI*)(void* pThis, void* param1, void* param2); +using TaskbarFrame_MeasureOverride_t = + int(WINAPI*)(void* pThis, + void* param1, + winrt::Windows::Foundation::Size* resultSize); TaskbarFrame_MeasureOverride_t TaskbarFrame_MeasureOverride_Original; -winrt::Windows::Foundation::Size* WINAPI -TaskbarFrame_MeasureOverride_Hook(void* pThis, void* param1, void* param2) { +int WINAPI TaskbarFrame_MeasureOverride_Hook( + void* pThis, + void* param1, + winrt::Windows::Foundation::Size* resultSize) { g_hookCallCounter++; Wh_Log(L">"); - winrt::Windows::Foundation::Size* ret = - TaskbarFrame_MeasureOverride_Original(pThis, param1, param2); - - IUnknown* taskbarFrameElementIUnknownPtr = *((IUnknown**)pThis + 1); - if (taskbarFrameElementIUnknownPtr) { - FrameworkElement taskbarFrameElement = nullptr; - taskbarFrameElementIUnknownPtr->QueryInterface( - winrt::guid_of(), - winrt::put_abi(taskbarFrameElement)); - if (taskbarFrameElement) { - try { - ApplyStyle(taskbarFrameElement, *ret); - } catch (...) { - HRESULT hr = winrt::to_hresult(); - Wh_Log(L"Error %08X", hr); - } + int ret = TaskbarFrame_MeasureOverride_Original(pThis, param1, resultSize); + + FrameworkElement taskbarFrameElement = nullptr; + ((IUnknown*)pThis) + ->QueryInterface(winrt::guid_of(), + winrt::put_abi(taskbarFrameElement)); + if (taskbarFrameElement) { + try { + ApplyStyle(taskbarFrameElement, *resultSize); + } catch (...) { + HRESULT hr = winrt::to_hresult(); + Wh_Log(L"Error %08X", hr); } } @@ -813,6 +1061,134 @@ TaskbarFrame_MeasureOverride_Hook(void* pThis, void* param1, void* param2) { return ret; } +using AugmentedEntryPointButton_UpdateButtonPadding_t = + void(WINAPI*)(void* pThis); +AugmentedEntryPointButton_UpdateButtonPadding_t + AugmentedEntryPointButton_UpdateButtonPadding_Original; +void WINAPI AugmentedEntryPointButton_UpdateButtonPadding_Hook(void* pThis) { + Wh_Log(L">"); + + g_inAugmentedEntryPointButton_UpdateButtonPadding = true; + + AugmentedEntryPointButton_UpdateButtonPadding_Original(pThis); + + g_inAugmentedEntryPointButton_UpdateButtonPadding = false; +} + +using RepeatButton_Width_t = void(WINAPI*)(void* pThis, double width); +RepeatButton_Width_t RepeatButton_Width_Original; +void WINAPI RepeatButton_Width_Hook(void* pThis, double width) { + Wh_Log(L">"); + + RepeatButton_Width_Original(pThis, width); + + if (!g_inAugmentedEntryPointButton_UpdateButtonPadding) { + return; + } + + FrameworkElement button = nullptr; + (*(IUnknown**)pThis) + ->QueryInterface(winrt::guid_of(), + winrt::put_abi(button)); + if (!button) { + return; + } + + FrameworkElement augmentedEntryPointContentGrid = + FindChildByName(button, L"AugmentedEntryPointContentGrid"); + if (!augmentedEntryPointContentGrid) { + return; + } + + EnumChildElements(augmentedEntryPointContentGrid, [](FrameworkElement + child) { + if (winrt::get_class_name(child) != L"Windows.UI.Xaml.Controls.Grid") { + return false; + } + + FrameworkElement panelGrid = + FindChildByClassName(child, L"Windows.UI.Xaml.Controls.Grid"); + if (!panelGrid) { + return false; + } + + FrameworkElement panel = FindChildByClassName( + panelGrid, L"AdaptiveCards.Rendering.Uwp.WholeItemsPanel"); + if (!panel) { + return false; + } + + Wh_Log(L"Processing %f x %f widget", panelGrid.Width(), + panelGrid.Height()); + + bool widePanel = panelGrid.Width() > panelGrid.Height(); + if (!widePanel) { + double angle = g_unloading ? 0 : -90; + + Wh_Log(L"Setting angle=%f for child", angle); + + Media::RotateTransform transform; + transform.Angle(angle); + child.RenderTransform(transform); + + float origin = g_unloading ? 0 : 0.5; + child.RenderTransformOrigin({origin, origin}); + + return false; + } + + FrameworkElement tickerGrid = panel; + if ((tickerGrid = FindChildByClassName( + tickerGrid, L"Windows.UI.Xaml.Controls.Border")) && + (tickerGrid = FindChildByClassName( + tickerGrid, L"AdaptiveCards.Rendering.Uwp.WholeItemsPanel")) && + (tickerGrid = FindChildByClassName( + tickerGrid, L"Windows.UI.Xaml.Controls.Grid"))) { + // OK. + } else { + return false; + } + + FrameworkElement badgeSmall = tickerGrid; + if ((badgeSmall = FindChildByName(badgeSmall, L"SmallTicker1")) && + (badgeSmall = FindChildByClassName( + badgeSmall, L"AdaptiveCards.Rendering.Uwp.WholeItemsPanel")) && + (badgeSmall = + FindChildByName(badgeSmall, L"BadgeAnchorSmallTicker"))) { + double angle = g_unloading ? 0 : -90; + + Wh_Log(L"Setting angle=%f for small badge", angle); + + Media::RotateTransform transform; + transform.Angle(angle); + badgeSmall.RenderTransform(transform); + + float origin = g_unloading ? 0 : 0.5; + badgeSmall.RenderTransformOrigin({origin, origin}); + } + + FrameworkElement badgeLarge = tickerGrid; + if ((badgeLarge = FindChildByName(badgeLarge, L"LargeTicker1")) && + (badgeLarge = FindChildByClassName( + badgeLarge, L"AdaptiveCards.Rendering.Uwp.WholeItemsPanel")) && + (badgeLarge = + FindChildByName(badgeLarge, L"BadgeAnchorLargeTicker"))) { + double angle = g_unloading ? 0 : -90; + + Wh_Log(L"Setting angle=%f for small badge", angle); + + Media::RotateTransform transform; + transform.Angle(angle); + badgeLarge.RenderTransform(transform); + + float origin = g_unloading ? 0 : 0.5; + badgeLarge.RenderTransformOrigin({origin, origin}); + } + + return false; + }); +} + void ApplyNotifyIconViewStyle(FrameworkElement notifyIconViewElement) { FrameworkElement child = notifyIconViewElement; if ((child = FindChildByName(child, L"ContainerGrid")) && @@ -1167,19 +1543,7 @@ bool UpdateNotifyIconsIfNeeded(XamlRoot xamlRoot) { return true; } -using TaskListButton_UpdateVisualStates_t = void(WINAPI*)(void* pThis); -TaskListButton_UpdateVisualStates_t TaskListButton_UpdateVisualStates_Original; -void WINAPI TaskListButton_UpdateVisualStates_Hook(void* pThis) { - Wh_Log(L">"); - - TaskListButton_UpdateVisualStates_Original(pThis); - - void* taskListButtonIUnknownPtr = (void**)pThis + 3; - winrt::Windows::Foundation::IUnknown taskListButtonIUnknown; - winrt::copy_from_abi(taskListButtonIUnknown, taskListButtonIUnknownPtr); - - auto taskListButtonElement = taskListButtonIUnknown.as(); - +void UpdateTaskListButton(FrameworkElement taskListButtonElement) { auto iconPanelElement = FindChildByName(taskListButtonElement, L"IconPanel"); if (!iconPanelElement) { @@ -1203,6 +1567,78 @@ void WINAPI TaskListButton_UpdateVisualStates_Hook(void* pThis) { iconElement.Translation( winrt::Windows::Foundation::Numerics::float3::zero()); + auto labelControlElement = + FindChildByName(iconPanelElement, L"LabelControl"); + if (labelControlElement) { + double angle = g_unloading ? 0 : -90; + Media::RotateTransform transform; + transform.Angle(angle); + labelControlElement.RenderTransform(transform); + + float origin = g_unloading ? 0 : 0.5; + labelControlElement.RenderTransformOrigin({origin, origin}); + + Controls::Grid::SetColumn(labelControlElement, g_unloading ? 1 : 0); + + Thickness margin{}; + if (!g_unloading) { + margin.Left = -40 - g_settings.taskbarWidth / 2.0; + margin.Top = 0; + margin.Right = -g_settings.taskbarWidth / 2.0; + margin.Bottom = iconElement.ActualWidth() + 20; + } + labelControlElement.Margin(margin); + + auto width = g_settings.taskbarWidth - iconElement.ActualWidth() - 20; + + labelControlElement.MinWidth(g_unloading ? 0 : width); + labelControlElement.MaxWidth(g_unloading ? 136 : width); + } + + Thickness margin{}; + if (!g_unloading && labelControlElement) { + margin.Top = g_settings.taskbarWidth - iconElement.ActualWidth() - 24; + } + iconElement.Margin(margin); + + auto overlayIconElement = FindChildByName(iconPanelElement, L"OverlayIcon"); + if (overlayIconElement) { + double angle = g_unloading ? 0 : -90; + Media::RotateTransform transform; + transform.Angle(angle); + overlayIconElement.RenderTransform(transform); + + winrt::Windows::Foundation::Point origin{}; + if (!g_unloading) { + origin.Y = labelControlElement ? 1.25 : 0.75; + } + + overlayIconElement.RenderTransformOrigin(origin); + + overlayIconElement.Margin(margin); + } +} + +using TaskListButton_UpdateVisualStates_t = void(WINAPI*)(void* pThis); +TaskListButton_UpdateVisualStates_t TaskListButton_UpdateVisualStates_Original; +void WINAPI TaskListButton_UpdateVisualStates_Hook(void* pThis) { + Wh_Log(L">"); + + TaskListButton_UpdateVisualStates_Original(pThis); + + void* taskListButtonIUnknownPtr = (void**)pThis + 3; + winrt::Windows::Foundation::IUnknown taskListButtonIUnknown; + winrt::copy_from_abi(taskListButtonIUnknown, taskListButtonIUnknownPtr); + + auto taskListButtonElement = taskListButtonIUnknown.as(); + + try { + UpdateTaskListButton(taskListButtonElement); + } catch (...) { + HRESULT hr = winrt::to_hresult(); + Wh_Log(L"Error %08X", hr); + } + auto xamlRoot = taskListButtonElement.XamlRoot(); if (xamlRoot) { try { @@ -1424,13 +1860,51 @@ void WINAPI OverflowXamlIslandManager_Show_Hook(void* pThis, point.x = monitorInfo.rcWork.right; } - point.x += MulDiv(104 + 12, monitorDpiX, 96); + switch (GetTaskbarLocationForMonitor(monitor)) { + case TaskbarLocation::left: + point.x += MulDiv(104 + 12, monitorDpiX, 96); + break; + + case TaskbarLocation::right: + point.x -= MulDiv(104 + 12, monitorDpiX, 96); + break; + } + point.y += MulDiv(28, monitorDpiY, 96); } OverflowXamlIslandManager_Show_Original(pThis, point, param2); } +using CopilotIcon_UpdateVisualStates_t = void(WINAPI*)(void* pThis); +CopilotIcon_UpdateVisualStates_t CopilotIcon_UpdateVisualStates_Original; +void WINAPI CopilotIcon_UpdateVisualStates_Hook(void* pThis) { + Wh_Log(L">"); + + CopilotIcon_UpdateVisualStates_Original(pThis); + + FrameworkElement copilotIcon = nullptr; + ((IUnknown**)pThis)[1]->QueryInterface(winrt::guid_of(), + winrt::put_abi(copilotIcon)); + if (!copilotIcon) { + return; + } + + FrameworkElement child = copilotIcon; + if ((child = FindChildByName(child, L"ContainerGrid")) && + (child = FindChildByName(child, L"ContentPresenter")) && + (child = FindChildByName(child, L"ContentGrid")) && + (child = FindChildByName(child, L"LottieIcon"))) { + double angle = g_unloading ? 0 : -90; + Media::RotateTransform transform; + transform.Angle(angle); + child.RenderTransform(transform); + + float origin = g_unloading ? 0 : 0.5; + child.RenderTransformOrigin({origin, origin}); + } +} + using SHAppBarMessage_t = decltype(&SHAppBarMessage); SHAppBarMessage_t SHAppBarMessage_Original; auto WINAPI SHAppBarMessage_Hook(DWORD dwMessage, PAPPBARDATA pData) { @@ -1454,7 +1928,10 @@ BOOL WINAPI GetWindowRect_Hook(HWND hWnd, LPRECT lpRect) { BOOL ret = GetWindowRect_Original(hWnd, lpRect); if (ret && !g_unloading && hWnd == GetTaskbarWnd()) { if (GetCurrentThreadId() == GetWindowThreadProcessId(hWnd, nullptr) && - g_inCTaskListThumbnailWnd_DisplayUI) { + (g_inCTaskListThumbnailWnd_DisplayUI || + g_inCTaskListThumbnailWnd_LayoutThumbnails)) { + Wh_Log(L"Adjusting taskbar rect for TaskListThumbnailWnd"); + // Fix thumbnails always displaying as list. HMONITOR monitor = MonitorFromRect(lpRect, MONITOR_DEFAULTTONEAREST); @@ -1489,6 +1966,12 @@ BOOL WINAPI SetWindowPos_Hook(HWND hWnd, uFlags); }; + if ((uFlags & (SWP_NOSIZE | SWP_NOMOVE)) || + (!g_inCTaskListThumbnailWnd_DisplayUI && + !g_inCTaskListThumbnailWnd_LayoutThumbnails)) { + return original(); + } + WCHAR szClassName[32]; if (GetClassName(hWnd, szClassName, ARRAYSIZE(szClassName)) == 0) { return original(); @@ -1504,29 +1987,162 @@ BOOL WINAPI SetWindowPos_Hook(HWND hWnd, GET_Y_LPARAM(messagePos), }; - const int distance = MulDiv(12, GetDpiForWindow(hWnd), 96); + HMONITOR monitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST); - SIZE sz{ - .cx = cx + distance * 2, - .cy = cy + distance * 2, - }; + RECT rc{}; + if (g_inCTaskListThumbnailWnd_DisplayUI) { + const int distance = MulDiv(12, GetDpiForWindow(hWnd), 96); - RECT rc; - CalculatePopupWindowPosition( - &pt, &sz, TPM_LEFTALIGN | TPM_VCENTERALIGN | TPM_WORKAREA, nullptr, - &rc); + SIZE sz{ + .cx = cx + distance * 2, + .cy = cy + distance * 2, + }; - rc.left += distance; - rc.right -= distance; - rc.top += distance; - rc.bottom -= distance; + UINT alignment; + switch (GetTaskbarLocationForMonitor(monitor)) { + case TaskbarLocation::left: + alignment = TPM_LEFTALIGN; + break; + + case TaskbarLocation::right: + alignment = TPM_RIGHTALIGN; + break; + } + + CalculatePopupWindowPosition( + &pt, &sz, alignment | TPM_VCENTERALIGN | TPM_WORKAREA, nullptr, + &rc); + + rc.left += distance; + rc.right -= distance; + rc.top += distance; + rc.bottom -= distance; + } else { + // Keep current position. + GetWindowRect_Original(hWnd, &rc); + rc.bottom = rc.top + cy; + + switch (GetTaskbarLocationForMonitor(monitor)) { + case TaskbarLocation::left: + rc.right = rc.left + cx; + break; + + case TaskbarLocation::right: + rc.left = rc.right - cx; + break; + } + } + + Wh_Log(L"Adjusting pos for TaskListThumbnailWnd: %dx%d, %dx%d", rc.left, + rc.right, rc.top, rc.bottom); return SetWindowPos_Original(hWnd, hWndInsertAfter, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, uFlags); } +using MoveWindow_t = decltype(&MoveWindow); +MoveWindow_t MoveWindow_Original; +BOOL WINAPI MoveWindow_Hook(HWND hWnd, + int X, + int Y, + int nWidth, + int nHeight, + WINBOOL bRepaint) { + auto original = [&]() { + return MoveWindow_Original(hWnd, X, Y, nWidth, nHeight, bRepaint); + }; + + if (g_unloading) { + return original(); + } + + WCHAR szClassName[64]; + if (GetClassName(hWnd, szClassName, ARRAYSIZE(szClassName)) == 0) { + return original(); + } + + if (_wcsicmp(szClassName, + L"Windows.UI.Composition.DesktopWindowContentBridge") != 0) { + return original(); + } + + Wh_Log(L">"); + + HMONITOR monitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST); + + if (GetTaskbarLocationForMonitor(monitor) == TaskbarLocation::right) { + UINT monitorDpiX = 96; + UINT monitorDpiY = 96; + GetDpiForMonitor(monitor, MDT_DEFAULT, &monitorDpiX, &monitorDpiY); + + int taskbarWidthScaled = + MulDiv(g_settings.taskbarWidth, monitorDpiX, 96); + + X = nWidth - taskbarWidthScaled; + } + + return original(); +} + namespace CoreWindowUI { +bool IsTargetCoreWindow(HWND hWnd, int* extraXAdjustment) { + DWORD threadId = 0; + DWORD processId = 0; + if (!hWnd || !(threadId = GetWindowThreadProcessId(hWnd, &processId)) || + processId != GetCurrentProcessId()) { + return false; + } + + WCHAR szClassName[32]; + if (GetClassName(hWnd, szClassName, ARRAYSIZE(szClassName)) == 0) { + return false; + } + + if (g_target == Target::ShellHost) { + if (_wcsicmp(szClassName, L"ControlCenterWindow") != 0) { + return false; + } + } else { + if (_wcsicmp(szClassName, L"Windows.UI.Core.CoreWindow") != 0) { + return false; + } + } + + if (g_target == Target::ShellExperienceHost) { + HANDLE thread = + OpenThread(THREAD_QUERY_LIMITED_INFORMATION, FALSE, threadId); + if (!thread) { + return false; + } + + PWSTR threadDescription; + HRESULT hr = pGetThreadDescription + ? pGetThreadDescription(thread, &threadDescription) + : E_FAIL; + CloseHandle(thread); + if (FAILED(hr)) { + return false; + } + + bool isActionCenter = wcscmp(threadDescription, L"ActionCenter") == 0; + bool isQuickActions = wcscmp(threadDescription, L"QuickActions") == 0; + + Wh_Log(L"%s", threadDescription); + LocalFree(threadDescription); + + if (!isActionCenter && !isQuickActions) { + return false; + } + + if (isQuickActions && extraXAdjustment && + g_settings.taskbarLocation == TaskbarLocation::left) { + *extraXAdjustment = MulDiv(-29, GetDpiForWindow(hWnd), 96); + } + } + + return true; +} std::vector GetCoreWindows() { struct ENUM_WINDOWS_PARAM { @@ -1539,18 +2155,7 @@ std::vector GetCoreWindows() { [](HWND hWnd, LPARAM lParam) WINAPI -> BOOL { ENUM_WINDOWS_PARAM& param = *(ENUM_WINDOWS_PARAM*)lParam; - DWORD dwProcessId = 0; - if (!GetWindowThreadProcessId(hWnd, &dwProcessId) || - dwProcessId != GetCurrentProcessId()) { - return TRUE; - } - - WCHAR szClassName[32]; - if (GetClassName(hWnd, szClassName, ARRAYSIZE(szClassName)) == 0) { - return TRUE; - } - - if (_wcsicmp(szClassName, L"Windows.UI.Core.CoreWindow") == 0) { + if (IsTargetCoreWindow(hWnd, nullptr)) { param.hWnds->push_back(hWnd); } @@ -1584,7 +2189,9 @@ void AdjustCoreWindowSize(int x, int y, int* width, int* height) { UINT monitorDpiY = 96; GetDpiForMonitor(monitor, MDT_DEFAULT, &monitorDpiX, &monitorDpiY); - const int w1 = MulDiv(660, monitorDpiX, 96); + const int w1 = + MulDiv(g_settings.startMenuWidth ? g_settings.startMenuWidth : 660, + monitorDpiX, 96); if (*width > w1) { *width = w1; } @@ -1622,7 +2229,16 @@ void AdjustCoreWindowPos(int* x, int* y, int width, int height) { int taskbarWidthScaled = MulDiv(g_settings.taskbarWidth, monitorDpiX, 96); - *x = rc.left + taskbarWidthScaled; + switch (GetTaskbarLocationForMonitor(monitor)) { + case TaskbarLocation::left: + *x = rc.left + taskbarWidthScaled; + break; + + case TaskbarLocation::right: + *x = rc.right - width - taskbarWidthScaled; + break; + } + *y = rc.top; } @@ -1742,78 +2358,43 @@ BOOL WINAPI SetWindowPos_Hook(HWND hWnd, uFlags); }; - DWORD processId = 0; - if (!hWnd || !GetWindowThreadProcessId(hWnd, &processId) || - processId != GetCurrentProcessId()) { + int extraXAdjustment = 0; + if (!IsTargetCoreWindow(hWnd, &extraXAdjustment)) { return original(); } - WCHAR szClassName[32]; - if (GetClassName(hWnd, szClassName, ARRAYSIZE(szClassName)) == 0) { - return original(); - } + Wh_Log(L"%08X %08X", (DWORD)(ULONG_PTR)hWnd, uFlags); - if (_wcsicmp(szClassName, L"Windows.UI.Core.CoreWindow") != 0) { + if ((uFlags & (SWP_NOSIZE | SWP_NOMOVE)) == (SWP_NOSIZE | SWP_NOMOVE)) { return original(); } - Wh_Log(L"%08X %08X", (DWORD)(ULONG_PTR)hWnd, uFlags); - - int extraXAdjustment = 0; + RECT rc{}; + GetWindowRect(hWnd, &rc); - if (g_target == Target::ShellExperienceHost) { - PWSTR threadDescription; - HRESULT hr = - pGetThreadDescription - ? pGetThreadDescription(GetCurrentThread(), &threadDescription) - : E_FAIL; - if (FAILED(hr)) { - return original(); - } - - bool isActionCenter = wcscmp(threadDescription, L"ActionCenter") == 0; - bool isQuickActions = wcscmp(threadDescription, L"QuickActions") == 0; - - Wh_Log(L"%s", threadDescription); - LocalFree(threadDescription); - - if (!isActionCenter && !isQuickActions) { - return original(); - } - - if (isQuickActions) { - extraXAdjustment = MulDiv(-29, GetDpiForWindow(hWnd), 96); - } + // SearchHost is being moved by explorer.exe, then the size is adjusted + // by SearchHost itself. Make SearchHost adjust the position too. A + // similar workaround is needed for other windows. + if (uFlags & SWP_NOMOVE) { + uFlags &= ~SWP_NOMOVE; + X = rc.left; + Y = rc.top; } - if ((uFlags & (SWP_NOSIZE | SWP_NOMOVE)) != (SWP_NOSIZE | SWP_NOMOVE)) { - RECT rc; - GetWindowRect(hWnd, &rc); - - // SearchHost is being moved by explorer.exe, then the size is adjusted - // by SearchHost itself. Make SearchHost adjust the position too. A - // similar workaround is needed for other windows. - if (uFlags & SWP_NOMOVE) { - uFlags &= ~SWP_NOMOVE; - X = rc.left; - Y = rc.top; - } - - int width; - int height; - if (uFlags & SWP_NOSIZE) { - width = rc.right - rc.left; - height = rc.bottom - rc.top; - AdjustCoreWindowSize(X, Y, &width, &height); - } else { - AdjustCoreWindowSize(X, Y, &cx, &cy); - width = cx; - height = cy; - } + int width; + int height; + if (uFlags & SWP_NOSIZE) { + width = rc.right - rc.left; + height = rc.bottom - rc.top; + AdjustCoreWindowSize(X, Y, &width, &height); + } else { + AdjustCoreWindowSize(X, Y, &cx, &cy); + width = cx; + height = cy; + } - if (!(uFlags & SWP_NOMOVE)) { - AdjustCoreWindowPos(&X, &Y, width, height); - } + if (!(uFlags & SWP_NOMOVE)) { + AdjustCoreWindowPos(&X, &Y, width, height); } X += extraXAdjustment; @@ -1824,7 +2405,25 @@ BOOL WINAPI SetWindowPos_Hook(HWND hWnd, } // namespace CoreWindowUI void LoadSettings() { + PCWSTR taskbarLocation = Wh_GetStringSetting(L"taskbarLocation"); + g_settings.taskbarLocation = TaskbarLocation::left; + if (wcscmp(taskbarLocation, L"right") == 0) { + g_settings.taskbarLocation = TaskbarLocation::right; + } + Wh_FreeStringSetting(taskbarLocation); + + PCWSTR taskbarLocationSecondary = + Wh_GetStringSetting(L"taskbarLocationSecondary"); + g_settings.taskbarLocationSecondary = g_settings.taskbarLocation; + if (wcscmp(taskbarLocationSecondary, L"left") == 0) { + g_settings.taskbarLocationSecondary = TaskbarLocation::left; + } else if (wcscmp(taskbarLocationSecondary, L"right") == 0) { + g_settings.taskbarLocationSecondary = TaskbarLocation::right; + } + Wh_FreeStringSetting(taskbarLocationSecondary); + g_settings.taskbarWidth = Wh_GetIntSetting(L"TaskbarWidth"); + g_settings.startMenuWidth = Wh_GetIntSetting(L"startMenuWidth"); } HWND GetTaskbarWnd() { @@ -1929,6 +2528,7 @@ bool HookTaskbarViewDllSymbols(HMODULE module) { }, (void**)&SystemTrayController_GetFrameSize_Original, (void*)SystemTrayController_GetFrameSize_Hook, + true, // From Windows 11 version 22H2, inlined sometimes. }, { { @@ -1946,16 +2546,23 @@ bool HookTaskbarViewDllSymbols(HMODULE module) { }, { { - LR"(public: __cdecl winrt::impl::consume_Windows_UI_Xaml_IFrameworkElement::MaxHeight(double)const )", + LR"(private: void __cdecl winrt::SystemTray::implementation::SystemTrayController::UpdateFrameSize(void))", + }, + (void**)&SystemTrayController_UpdateFrameSize_SymbolAddress, + nullptr, // Hooked manually, we need the symbol address. + }, + { + { + LR"(private: void __cdecl winrt::Taskbar::implementation::TaskbarController::OnGroupingModeChanged(void))", }, - (void**)&TaskbarFrame_MaxHeight_double_Original, + (void**)&TaskbarController_OnGroupingModeChanged, }, { { - LR"(public: __cdecl winrt::impl::consume_Windows_UI_Xaml_IFrameworkElement::Height(double)const )", + LR"(private: void __cdecl winrt::Taskbar::implementation::TaskbarController::UpdateFrameHeight(void))", }, - (void**)&TaskbarFrame_Height_double_Original, - (void*)TaskbarFrame_Height_double_Hook, + (void**)&TaskbarController_UpdateFrameHeight_Original, + (void*)TaskbarController_UpdateFrameHeight_Hook, }, { { @@ -1973,11 +2580,25 @@ bool HookTaskbarViewDllSymbols(HMODULE module) { }, { { - LR"(public: struct winrt::Windows::Foundation::Size __cdecl winrt::Taskbar::implementation::TaskbarFrame::MeasureOverride(struct winrt::Windows::Foundation::Size))", + LR"(public: virtual int __cdecl winrt::impl::produce::MeasureOverride(struct winrt::Windows::Foundation::Size,struct winrt::Windows::Foundation::Size *))", }, (void**)&TaskbarFrame_MeasureOverride_Original, (void*)TaskbarFrame_MeasureOverride_Hook, }, + { + { + LR"(protected: virtual void __cdecl winrt::Taskbar::implementation::AugmentedEntryPointButton::UpdateButtonPadding(void))", + }, + (void**)&AugmentedEntryPointButton_UpdateButtonPadding_Original, + (void*)AugmentedEntryPointButton_UpdateButtonPadding_Hook, + }, + { + { + LR"(public: __cdecl winrt::impl::consume_Windows_UI_Xaml_IFrameworkElement::Width(double)const )", + }, + (void**)&RepeatButton_Width_Original, + (void*)RepeatButton_Width_Hook, + }, { { LR"(public: __cdecl winrt::SystemTray::implementation::IconView::IconView(void))", @@ -2027,10 +2648,28 @@ bool HookTaskbarViewDllSymbols(HMODULE module) { (void**)&OverflowXamlIslandManager_Show_Original, (void*)OverflowXamlIslandManager_Show_Hook, }, + { + { + LR"(private: void __cdecl winrt::SystemTray::implementation::CopilotIcon::UpdateVisualStates(void))", + }, + (void**)&CopilotIcon_UpdateVisualStates_Original, + (void*)CopilotIcon_UpdateVisualStates_Hook, + }, }; - return HookSymbols(module, symbolHooks, ARRAYSIZE(symbolHooks)); + if (!HookSymbols(module, symbolHooks, ARRAYSIZE(symbolHooks))) { + return false; + } + } + + if (SystemTrayController_UpdateFrameSize_SymbolAddress) { + Wh_SetFunctionHook( + (void*)SystemTrayController_UpdateFrameSize_SymbolAddress, + (void*)SystemTrayController_UpdateFrameSize_Hook, + (void**)&SystemTrayController_UpdateFrameSize_Original); } + + return true; } bool HookTaskbarDllSymbols() { @@ -2110,6 +2749,13 @@ bool HookTaskbarDllSymbols() { (void**)&CTaskListThumbnailWnd_DisplayUI_Original, (void*)CTaskListThumbnailWnd_DisplayUI_Hook, }, + { + { + LR"(public: virtual void __cdecl CTaskListThumbnailWnd::LayoutThumbnails(void))", + }, + (void**)&CTaskListThumbnailWnd_LayoutThumbnails_Original, + (void*)CTaskListThumbnailWnd_LayoutThumbnails_Hook, + }, }; return HookSymbols(module, taskbarDllHooks, ARRAYSIZE(taskbarDllHooks)); @@ -2133,6 +2779,9 @@ BOOL ModInitWithTaskbarView(HMODULE taskbarViewModule) { Wh_SetFunctionHook((void*)SetWindowPos, (void*)SetWindowPos_Hook, (void**)&SetWindowPos_Original); + Wh_SetFunctionHook((void*)MoveWindow, (void*)MoveWindow_Hook, + (void**)&MoveWindow_Original); + return TRUE; } @@ -2167,6 +2816,8 @@ BOOL Wh_ModInit() { } else if (_wcsicmp(moduleFileName, L"ShellExperienceHost.exe") == 0) { g_target = Target::ShellExperienceHost; + } else if (_wcsicmp(moduleFileName, L"ShellHost.exe") == 0) { + g_target = Target::ShellHost; } } else { Wh_Log(L"GetModuleFileName returned an unsupported path"); @@ -2175,7 +2826,8 @@ BOOL Wh_ModInit() { } if (g_target == Target::StartMenu || g_target == Target::SearchHost || - g_target == Target::ShellExperienceHost) { + g_target == Target::ShellExperienceHost || + g_target == Target::ShellHost) { if (g_target == Target::StartMenu || g_target == Target::SearchHost) { HMODULE user32Module = LoadLibrary(L"user32.dll"); if (user32Module) { @@ -2227,7 +2879,8 @@ void Wh_ModAfterInit() { ApplySettings(); } else if (g_target == Target::StartMenu || g_target == Target::SearchHost || - g_target == Target::ShellExperienceHost) { + g_target == Target::ShellExperienceHost || + g_target == Target::ShellHost) { CoreWindowUI::ApplySettings(); } } @@ -2245,7 +2898,8 @@ void Wh_ModBeforeUninit() { Sleep(400); } else if (g_target == Target::StartMenu || g_target == Target::SearchHost || - g_target == Target::ShellExperienceHost) { + g_target == Target::ShellExperienceHost || + g_target == Target::ShellHost) { CoreWindowUI::ApplySettings(); } } @@ -2267,7 +2921,8 @@ void Wh_ModSettingsChanged() { ApplySettings(/*waitForApply=*/false); } else if (g_target == Target::StartMenu || g_target == Target::SearchHost || - g_target == Target::ShellExperienceHost) { + g_target == Target::ShellExperienceHost || + g_target == Target::ShellHost) { CoreWindowUI::ApplySettings(); } } From 824532afd6f3568f26b5676f950ef0557602df99 Mon Sep 17 00:00:00 2001 From: Michael Maltsev <4129781+m417z@users.noreply.github.com> Date: Fri, 20 Sep 2024 14:43:35 +0300 Subject: [PATCH 25/52] Vertical Taskbar for Windows 11 v1.2.1 (#978) * Improved core implementation, which improves compatibility with other mods and programs. * Fixed overflow tray icons cut off when the taskbar is on the right. * Fixed copilot opening behind the taskbar when it's on the right. --- mods/taskbar-vertical.wh.cpp | 379 +++++++++++++++++++++-------------- 1 file changed, 225 insertions(+), 154 deletions(-) diff --git a/mods/taskbar-vertical.wh.cpp b/mods/taskbar-vertical.wh.cpp index 3e784988..42affcf2 100644 --- a/mods/taskbar-vertical.wh.cpp +++ b/mods/taskbar-vertical.wh.cpp @@ -2,7 +2,7 @@ // @id taskbar-vertical // @name Vertical Taskbar for Windows 11 // @description Finally, the missing vertical taskbar option for Windows 11! -// @version 1.2 +// @version 1.2.1 // @author m417z // @github https://github.com/m417z // @twitter https://twitter.com/m417z @@ -137,7 +137,6 @@ std::atomic g_unloading; std::atomic g_hookCallCounter; int g_originalTaskbarHeight; -bool g_windowRgnChanging; bool g_inSystemTrayController_UpdateFrameSize; bool g_inAugmentedEntryPointButton_UpdateButtonPadding; bool g_inCTaskListThumbnailWnd_DisplayUI; @@ -152,6 +151,9 @@ using FrameworkElementLoadedEventRevoker = winrt::impl::event_revoker< std::list g_notifyIconAutoRevokerList; +int g_copilotPosTimerCounter; +UINT_PTR g_copilotPosTimer; + WINUSERAPI UINT WINAPI GetDpiForWindow(HWND hwnd); typedef enum MONITOR_DPI_TYPE { MDT_EFFECTIVE_DPI = 0, @@ -353,25 +355,28 @@ DWORD WINAPI TrayUI_GetDockedRect_Hook(void* pThis, RECT* rect, BOOL param2) { RECT monitorRect; GetMonitorRect(monitor, &monitorRect); + UINT monitorDpiX = 96; + UINT monitorDpiY = 96; + GetDpiForMonitor(monitor, MDT_DEFAULT, &monitorDpiX, &monitorDpiY); + if (!g_unloading) { + int taskbarWidthScaled = + MulDiv(g_settings.taskbarWidth, monitorDpiX, 96); + rect->top = monitorRect.top; switch (GetTaskbarLocationForMonitor(monitor)) { case TaskbarLocation::left: rect->left = monitorRect.left; - rect->right = rect->left + (rect->bottom - rect->top); + rect->right = rect->left + taskbarWidthScaled; break; case TaskbarLocation::right: rect->right = monitorRect.right; - rect->left = rect->right - (rect->bottom - rect->top); + rect->left = rect->right - taskbarWidthScaled; break; } } else { - UINT monitorDpiX = 96; - UINT monitorDpiY = 96; - GetDpiForMonitor(monitor, MDT_DEFAULT, &monitorDpiX, &monitorDpiY); - int taskbarOriginalHeightScaled = MulDiv(g_originalTaskbarHeight, monitorDpiY, 96); @@ -446,6 +451,16 @@ void WINAPI TrayUI_MakeStuckRect_Hook(void* pThis, using GetWindowRect_t = decltype(&GetWindowRect); GetWindowRect_t GetWindowRect_Original; +bool IsTaskbarWindow(HWND hWnd) { + WCHAR szClassName[32]; + if (!GetClassName(hWnd, szClassName, ARRAYSIZE(szClassName))) { + return false; + } + + return _wcsicmp(szClassName, L"Shell_TrayWnd") == 0 || + _wcsicmp(szClassName, L"Shell_SecondaryTrayWnd") == 0; +} + HWND GetTaskbarWnd(); LRESULT TaskbarWndProcPostProcess(HWND hWnd, @@ -453,73 +468,36 @@ LRESULT TaskbarWndProcPostProcess(HWND hWnd, WPARAM wParam, LPARAM lParam, LRESULT result) { - // Calling CreateRectRgn has two reasons: To actually set the region, and to - // post window size change events which cause element sizes and positions to - // be recalculated. - auto updateWindowRgn = [](HMONITOR monitor, HWND hWnd, int width, - int height, int windowWidth) { - // Avoid handling this recursively as SetWindowRgn triggers - // WM_WINDOWPOSCHANGED again. - if (g_windowRgnChanging) { - return; - } - - g_windowRgnChanging = true; - - if (!g_unloading) { - HRGN windowRegion; - switch (GetTaskbarLocationForMonitor(monitor)) { - case TaskbarLocation::left: - windowRegion = CreateRectRgn(0, 0, width, height); - break; - - case TaskbarLocation::right: - windowRegion = CreateRectRgn(windowWidth - width, 0, - windowWidth, height); - break; - } - - if (windowRegion) { - SetWindowRgn(hWnd, windowRegion, TRUE); - } - } else { - SetWindowRgn(hWnd, nullptr, TRUE); - } - - g_windowRgnChanging = false; - }; - switch (Msg) { case WM_SIZING: { Wh_Log(L"WM_SIZING: %08X", (DWORD)(ULONG_PTR)hWnd); - RECT* rect = (RECT*)lParam; + if (!g_unloading) { + RECT* rect = (RECT*)lParam; - HMONITOR monitor = MonitorFromRect(rect, MONITOR_DEFAULTTONEAREST); + HMONITOR monitor = + MonitorFromRect(rect, MONITOR_DEFAULTTONEAREST); - if (!g_unloading) { RECT monitorRect; GetMonitorRect(monitor, &monitorRect); + int taskbarWidthScaled = + MulDiv(g_settings.taskbarWidth, GetDpiForWindow(hWnd), 96); + rect->top = monitorRect.top; switch (GetTaskbarLocationForMonitor(monitor)) { case TaskbarLocation::left: rect->left = monitorRect.left; - rect->right = rect->left + (rect->bottom - rect->top); + rect->right = rect->left + taskbarWidthScaled; break; case TaskbarLocation::right: rect->right = monitorRect.right; - rect->left = rect->right - (rect->bottom - rect->top); + rect->left = rect->right - taskbarWidthScaled; break; } } - - updateWindowRgn( - monitor, hWnd, - MulDiv(g_settings.taskbarWidth, GetDpiForWindow(hWnd), 96), - rect->bottom - rect->top, rect->right - rect->left); break; } @@ -530,18 +508,21 @@ LRESULT TaskbarWndProcPostProcess(HWND hWnd, Wh_Log(L"WM_WINDOWPOSCHANGING (size or move): %08X", (DWORD)(ULONG_PTR)hWnd); - RECT rect{ - .left = windowpos->x, - .top = windowpos->y, - .right = windowpos->x + windowpos->cx, - .bottom = windowpos->y + windowpos->cy, - }; - HMONITOR monitor = - MonitorFromRect(&rect, MONITOR_DEFAULTTONEAREST); - if (!g_unloading) { + RECT rect{ + .left = windowpos->x, + .top = windowpos->y, + .right = windowpos->x + windowpos->cx, + .bottom = windowpos->y + windowpos->cy, + }; + HMONITOR monitor = + MonitorFromRect(&rect, MONITOR_DEFAULTTONEAREST); + if (!(windowpos->flags & SWP_NOSIZE)) { - windowpos->cx = windowpos->cy; + int taskbarWidthScaled = MulDiv( + g_settings.taskbarWidth, GetDpiForWindow(hWnd), 96); + + windowpos->cx = taskbarWidthScaled; } if (!(windowpos->flags & SWP_NOMOVE) && @@ -553,49 +534,9 @@ LRESULT TaskbarWndProcPostProcess(HWND hWnd, windowpos->x = monitorRect.right - windowpos->cx; } } - - updateWindowRgn( - monitor, hWnd, - MulDiv(g_settings.taskbarWidth, GetDpiForWindow(hWnd), 96), - windowpos->cy, windowpos->cx); - } - break; - } - - case WM_WINDOWPOSCHANGED: { - auto* windowpos = (WINDOWPOS*)lParam; - if ((windowpos->flags & (SWP_NOSIZE | SWP_NOMOVE)) != - (SWP_NOSIZE | SWP_NOMOVE)) { - Wh_Log(L"WM_WINDOWPOSCHANGED (size or move): %08X", - (DWORD)(ULONG_PTR)hWnd); - - RECT rect{ - .left = windowpos->x, - .top = windowpos->y, - .right = windowpos->x + windowpos->cx, - .bottom = windowpos->y + windowpos->cy, - }; - HMONITOR monitor = - MonitorFromRect(&rect, MONITOR_DEFAULTTONEAREST); - - updateWindowRgn( - monitor, hWnd, - MulDiv(g_settings.taskbarWidth, GetDpiForWindow(hWnd), 96), - windowpos->cy, windowpos->cx); } break; } - - case WM_PAINT: { - RECT rc; - GetWindowRect_Original(hWnd, &rc); - - updateWindowRgn( - MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST), hWnd, - MulDiv(g_settings.taskbarWidth, GetDpiForWindow(hWnd), 96), - rc.bottom - rc.top, rc.right - rc.left); - break; - } } return result; @@ -785,12 +726,13 @@ ResourceDictionary_Lookup_Hook(void* pThis, switch (g_settings.taskbarLocation) { case TaskbarLocation::left: valueThickness->Bottom -= - MulDiv(rc.right - rc.left, 96, GetDpiForWindow(hTaskbarWnd)); + MulDiv(rc.bottom - rc.top, 96, GetDpiForWindow(hTaskbarWnd)); valueThickness->Bottom += g_settings.taskbarWidth; break; case TaskbarLocation::right: - valueThickness->Bottom -= g_settings.taskbarWidth; + valueThickness->Bottom -= + MulDiv(rc.bottom - rc.top, 96, GetDpiForWindow(hTaskbarWnd)); valueThickness->Bottom -= 230; break; } @@ -944,7 +886,9 @@ void WINAPI TaskbarController_UpdateFrameHeight_Hook(void* pThis) { TaskbarController_UpdateFrameHeight_Original(pThis); - // Set the height to NaN (Auto) to always match the parent height. + // Set the width/height to NaN (Auto) to always match the parent + // width/height. + taskbarFrameElement.Width(std::numeric_limits::quiet_NaN()); taskbarFrameElement.Height(std::numeric_limits::quiet_NaN()); } @@ -981,16 +925,16 @@ bool UpdateNotifyIconsIfNeeded(XamlRoot xamlRoot); bool ApplyStyle(FrameworkElement taskbarFrame, const winrt::Windows::Foundation::Size& size) { - auto rootGrid = + auto contentGrid = Media::VisualTreeHelper::GetParent(taskbarFrame).as(); double angle = g_unloading ? 0 : 90; Media::RotateTransform transform; transform.Angle(angle); - rootGrid.RenderTransform(transform); + contentGrid.RenderTransform(transform); float origin = g_unloading ? 0 : 0.5; - rootGrid.RenderTransformOrigin({origin, origin}); + contentGrid.RenderTransformOrigin({origin, origin}); Thickness margin{}; if (!g_unloading) { @@ -1000,13 +944,13 @@ bool ApplyStyle(FrameworkElement taskbarFrame, } } - FrameworkElement child = rootGrid; + FrameworkElement child = contentGrid; if (child && (child = FindChildByName(child, L"TaskbarFrame")) && (child = FindChildByName(child, L"RootGrid"))) { child.Margin(margin); } - child = rootGrid; + child = contentGrid; if (child && (child = FindChildByClassName(child, L"SystemTray.SystemTrayFrame")) && (child = FindChildByName(child, L"SystemTrayFrameGrid"))) { @@ -1374,17 +1318,20 @@ bool ApplyStyleIfNeeded(XamlRoot xamlRoot) { return true; } - FrameworkElement rootGrid = xamlRoot.Content().try_as(); + FrameworkElement contentGrid = + xamlRoot.Content().try_as(); - FrameworkElement taskbarFrame = nullptr; + auto taskbarFrame = FindChildByName(contentGrid, L"TaskbarFrame"); + if (!taskbarFrame) { + return true; + } - FrameworkElement child = rootGrid; - if (child && (child = FindChildByName(child, L"TaskbarFrame"))) { - taskbarFrame = child; + FrameworkElement rootGrid = FindChildByName(taskbarFrame, L"RootGrid"); + if (!rootGrid) { + return true; } - if (!taskbarFrame || - taskbarFrame.ActualHeight() == g_settings.taskbarWidth) { + if (rootGrid.ActualHeight() == g_settings.taskbarWidth) { return true; } @@ -1905,6 +1852,118 @@ void WINAPI CopilotIcon_UpdateVisualStates_Hook(void* pThis) { } } +std::wstring GetProcessFileName(DWORD dwProcessId) { + HANDLE hProcess = + OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, dwProcessId); + if (!hProcess) { + return std::wstring{}; + } + + WCHAR processPath[MAX_PATH]; + + DWORD dwSize = ARRAYSIZE(processPath); + if (!QueryFullProcessImageName(hProcess, 0, processPath, &dwSize)) { + CloseHandle(hProcess); + return std::wstring{}; + } + + CloseHandle(hProcess); + + PCWSTR processFileNameUpper = wcsrchr(processPath, L'\\'); + if (!processFileNameUpper) { + return std::wstring{}; + } + + processFileNameUpper++; + return processFileNameUpper; +} + +bool UpdateCopilotPosition() { + bool updatedPosition = false; + + auto enumWindowProc = [&](HWND hWnd, LPARAM lParam) -> BOOL { + DWORD style = GetWindowStyle(hWnd); + if (style & WS_CAPTION) { + return TRUE; + } + + DWORD exStyle = GetWindowExStyle(hWnd); + if (!(exStyle & WS_EX_TOOLWINDOW)) { + return TRUE; + } + + DWORD dwProcessId = 0; + if (!GetWindowThreadProcessId(hWnd, &dwProcessId) || + _wcsicmp(GetProcessFileName(dwProcessId).c_str(), L"msedge.exe") != + 0) { + return TRUE; + } + + RECT rc{}; + GetWindowRect_Original(hWnd, &rc); + + HMONITOR monitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST); + + MONITORINFO monitorInfo{ + .cbSize = sizeof(MONITORINFO), + }; + GetMonitorInfo(monitor, &monitorInfo); + + if (rc.top != monitorInfo.rcWork.top || + rc.bottom != monitorInfo.rcWork.bottom || + rc.right != monitorInfo.rcMonitor.right) { + return TRUE; + } + + if (monitorInfo.rcWork.right != monitorInfo.rcMonitor.right) { + int x = rc.left - monitorInfo.rcMonitor.right + + monitorInfo.rcWork.right; + int y = rc.top; + SetWindowPos(hWnd, nullptr, x, y, 0, 0, + SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); + } + + updatedPosition = true; + return FALSE; + }; + + EnumWindows( + [](HWND hWnd, LPARAM lParam) WINAPI -> BOOL { + auto& proc = *reinterpret_cast(lParam); + return proc(hWnd, lParam); + }, + reinterpret_cast(&enumWindowProc)); + + return updatedPosition; +} + +using CopilotIcon_ToggleEdgeCopilot_t = void(WINAPI*)(void* pThis); +CopilotIcon_ToggleEdgeCopilot_t CopilotIcon_ToggleEdgeCopilot_Original; +void WINAPI CopilotIcon_ToggleEdgeCopilot_Hook(void* pThis) { + Wh_Log(L">"); + + CopilotIcon_ToggleEdgeCopilot_Original(pThis); + + if (g_settings.taskbarLocation != TaskbarLocation::right) { + return; + } + + g_copilotPosTimerCounter = 0; + g_copilotPosTimer = SetTimer( + nullptr, g_copilotPosTimer, 100, + [](HWND hwnd, // handle of window for timer messages + UINT uMsg, // WM_TIMER message + UINT_PTR idEvent, // timer identifier + DWORD dwTime // current system time + ) WINAPI { + g_copilotPosTimerCounter++; + if (UpdateCopilotPosition() || g_copilotPosTimerCounter >= 10) { + KillTimer(nullptr, g_copilotPosTimer); + g_copilotPosTimer = 0; + } + }); +} + using SHAppBarMessage_t = decltype(&SHAppBarMessage); SHAppBarMessage_t SHAppBarMessage_Original; auto WINAPI SHAppBarMessage_Hook(DWORD dwMessage, PAPPBARDATA pData) { @@ -1926,27 +1985,22 @@ auto WINAPI SHAppBarMessage_Hook(DWORD dwMessage, PAPPBARDATA pData) { BOOL WINAPI GetWindowRect_Hook(HWND hWnd, LPRECT lpRect) { BOOL ret = GetWindowRect_Original(hWnd, lpRect); - if (ret && !g_unloading && hWnd == GetTaskbarWnd()) { - if (GetCurrentThreadId() == GetWindowThreadProcessId(hWnd, nullptr) && - (g_inCTaskListThumbnailWnd_DisplayUI || - g_inCTaskListThumbnailWnd_LayoutThumbnails)) { - Wh_Log(L"Adjusting taskbar rect for TaskListThumbnailWnd"); - - // Fix thumbnails always displaying as list. - HMONITOR monitor = - MonitorFromRect(lpRect, MONITOR_DEFAULTTONEAREST); - - MONITORINFO monitorInfo{ - .cbSize = sizeof(MONITORINFO), - }; - GetMonitorInfo(monitor, &monitorInfo); - - CopyRect(lpRect, &monitorInfo.rcWork); - } else { - // Adjust rect so that desktop icons will be positioned correctly. - lpRect->right = lpRect->left + MulDiv(g_settings.taskbarWidth, - GetDpiForWindow(hWnd), 96); - } + if (ret && !g_unloading && + (g_inCTaskListThumbnailWnd_DisplayUI || + g_inCTaskListThumbnailWnd_LayoutThumbnails) && + IsTaskbarWindow(hWnd) && + GetCurrentThreadId() == GetWindowThreadProcessId(hWnd, nullptr)) { + Wh_Log(L"Adjusting taskbar rect for TaskListThumbnailWnd"); + + // Fix thumbnails always displaying as list. + HMONITOR monitor = MonitorFromRect(lpRect, MONITOR_DEFAULTTONEAREST); + + MONITORINFO monitorInfo{ + .cbSize = sizeof(MONITORINFO), + }; + GetMonitorInfo(monitor, &monitorInfo); + + CopyRect(lpRect, &monitorInfo.rcWork); } return ret; @@ -2067,25 +2121,17 @@ BOOL WINAPI MoveWindow_Hook(HWND hWnd, return original(); } - Wh_Log(L">"); - - HMONITOR monitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST); - - if (GetTaskbarLocationForMonitor(monitor) == TaskbarLocation::right) { - UINT monitorDpiX = 96; - UINT monitorDpiY = 96; - GetDpiForMonitor(monitor, MDT_DEFAULT, &monitorDpiX, &monitorDpiY); - - int taskbarWidthScaled = - MulDiv(g_settings.taskbarWidth, monitorDpiX, 96); - - X = nWidth - taskbarWidthScaled; + if (!IsTaskbarWindow(GetAncestor(hWnd, GA_ROOT))) { + return original(); } - return original(); + Wh_Log(L">"); + + return MoveWindow_Original(hWnd, X, Y, nHeight, nHeight, bRepaint); } namespace CoreWindowUI { + bool IsTargetCoreWindow(HWND hWnd, int* extraXAdjustment) { DWORD threadId = 0; DWORD processId = 0; @@ -2479,6 +2525,24 @@ void ApplySettings(bool waitForApply = true) { } } + // Calling CreateRectRgn posts window size change events which cause element + // sizes and positions to be recalculated. + EnumWindows( + [](HWND hWnd, LPARAM lParam) WINAPI -> BOOL { + DWORD dwProcessId = 0; + if (!GetWindowThreadProcessId(hWnd, &dwProcessId) || + dwProcessId != GetCurrentProcessId()) { + return TRUE; + } + + if (IsTaskbarWindow(hWnd)) { + SetWindowRgn(hWnd, nullptr, TRUE); + } + + return TRUE; + }, + 0); + g_applyingSettings = false; } @@ -2655,6 +2719,13 @@ bool HookTaskbarViewDllSymbols(HMODULE module) { (void**)&CopilotIcon_UpdateVisualStates_Original, (void*)CopilotIcon_UpdateVisualStates_Hook, }, + { + { + LR"(private: void __cdecl winrt::SystemTray::implementation::CopilotIcon::ToggleEdgeCopilot(void))", + }, + (void**)&CopilotIcon_ToggleEdgeCopilot_Original, + (void*)CopilotIcon_ToggleEdgeCopilot_Hook, + }, }; if (!HookSymbols(module, symbolHooks, ARRAYSIZE(symbolHooks))) { From a90c4ab676b14803b36ae376b63928dc6b483b51 Mon Sep 17 00:00:00 2001 From: Michael Maltsev <4129781+m417z@users.noreply.github.com> Date: Fri, 20 Sep 2024 22:20:38 +0300 Subject: [PATCH 26/52] Vertical Taskbar for Windows 11 v1.2.2 (#984) * Fixed a possible crash loop on mod initialization with multiple monitors. * Fixed the taskbar becoming blank after changing the settings. * Fixed all taskbar items hidden in an overflow view after enabling the mod. * Improved handling of the right click menu on the top of the taskbar, reducing the chance of the menu being cut off. --- mods/taskbar-vertical.wh.cpp | 52 +++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/mods/taskbar-vertical.wh.cpp b/mods/taskbar-vertical.wh.cpp index 42affcf2..5ced3ba4 100644 --- a/mods/taskbar-vertical.wh.cpp +++ b/mods/taskbar-vertical.wh.cpp @@ -2,7 +2,7 @@ // @id taskbar-vertical // @name Vertical Taskbar for Windows 11 // @description Finally, the missing vertical taskbar option for Windows 11! -// @version 1.2.1 +// @version 1.2.2 // @author m417z // @github https://github.com/m417z // @twitter https://twitter.com/m417z @@ -13,7 +13,7 @@ // @include ShellExperienceHost.exe // @include ShellHost.exe // @architecture x86-64 -// @compilerOptions -DWINVER=0x0605 -lgdi32 -lole32 -loleaut32 -lruntimeobject -lshcore +// @compilerOptions -DWINVER=0x0605 -lole32 -loleaut32 -lruntimeobject -lshcore // ==/WindhawkMod== // Source code is published under The GNU General Public License v3.0. @@ -537,6 +537,15 @@ LRESULT TaskbarWndProcPostProcess(HWND hWnd, } break; } + + case WM_ERASEBKGND: { + Wh_Log(L"WM_ERASEBKGND: %08X", (DWORD)(ULONG_PTR)hWnd); + + // Calling CreateRectRgn posts window size change events which cause + // element sizes and positions to be recalculated. + SetWindowRgn(hWnd, nullptr, TRUE); + break; + } } return result; @@ -645,6 +654,13 @@ HRESULT WINAPI CTaskListWnd_ComputeJumpViewPosition_Hook( // Move a bit lower to vertically center the cursor on the close item. point->Y += MulDiv(30, monitorDpiY, 96); + // Avoid returning a point too close to the top of the monitor which causes + // the menu to be cut off. + int minY = rc.top + MulDiv(130, monitorDpiY, 96); + if (point->Y < minY) { + point->Y = minY; + } + *verticalAlignment = VerticalAlignment::Center; return ret; @@ -889,7 +905,15 @@ void WINAPI TaskbarController_UpdateFrameHeight_Hook(void* pThis) { // Set the width/height to NaN (Auto) to always match the parent // width/height. taskbarFrameElement.Width(std::numeric_limits::quiet_NaN()); - taskbarFrameElement.Height(std::numeric_limits::quiet_NaN()); + + // Setting the height right away can result in ellipsis. + // https://github.com/ramensoftware/windhawk-mods/issues/981 + taskbarFrameElement.Dispatcher().TryRunAsync( + winrt::Windows::UI::Core::CoreDispatcherPriority::High, + [taskbarFrameElement]() { + taskbarFrameElement.Height( + std::numeric_limits::quiet_NaN()); + }); } using SystemTraySecondaryController_UpdateFrameSize_t = @@ -1964,25 +1988,6 @@ void WINAPI CopilotIcon_ToggleEdgeCopilot_Hook(void* pThis) { }); } -using SHAppBarMessage_t = decltype(&SHAppBarMessage); -SHAppBarMessage_t SHAppBarMessage_Original; -auto WINAPI SHAppBarMessage_Hook(DWORD dwMessage, PAPPBARDATA pData) { - auto ret = SHAppBarMessage_Original(dwMessage, pData); - - // This is used to position secondary taskbars. - if (dwMessage == ABM_QUERYPOS && ret && !g_unloading && pData->hWnd) { - HMONITOR monitor = - MonitorFromWindow(pData->hWnd, MONITOR_DEFAULTTONEAREST); - - RECT monitorRect; - GetMonitorRect(monitor, &monitorRect); - - pData->rc.top = monitorRect.top; - } - - return ret; -} - BOOL WINAPI GetWindowRect_Hook(HWND hWnd, LPRECT lpRect) { BOOL ret = GetWindowRect_Original(hWnd, lpRect); if (ret && !g_unloading && @@ -2841,9 +2846,6 @@ BOOL ModInitWithTaskbarView(HMODULE taskbarViewModule) { return FALSE; } - Wh_SetFunctionHook((void*)SHAppBarMessage, (void*)SHAppBarMessage_Hook, - (void**)&SHAppBarMessage_Original); - Wh_SetFunctionHook((void*)GetWindowRect, (void*)GetWindowRect_Hook, (void**)&GetWindowRect_Original); From b7f2aa2c8f470f49cc294ed6500b42d5a7c1d313 Mon Sep 17 00:00:00 2001 From: Michael Maltsev <4129781+m417z@users.noreply.github.com> Date: Fri, 20 Sep 2024 23:13:11 +0300 Subject: [PATCH 27/52] Add author to mods RSS feed --- deploy.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/deploy.ts b/deploy.ts index 69ace264..283df3db 100644 --- a/deploy.ts +++ b/deploy.ts @@ -208,6 +208,10 @@ function generateRssFeed() { content: string; url: string; date: Date; + author: { + name?: string; + link?: string; + }; }; let feedItems: FeedItem[] = []; @@ -275,6 +279,10 @@ function generateRssFeed() { content, url: `https://windhawk.net/mods/${modId}`, date: new Date(commitTime * 1000), + author: { + name: metadata.author, + link: metadata.github, + }, }); if (feedItems.length >= 20) { @@ -309,6 +317,7 @@ function generateRssFeed() { link: feedItem.url, content: markdownToHtml(feedItem.content), date: feedItem.date, + author: [feedItem.author], }); } From e522fbec2bacd951c355649004aea1927462851f Mon Sep 17 00:00:00 2001 From: Michael Maltsev <4129781+m417z@users.noreply.github.com> Date: Sat, 21 Sep 2024 14:11:06 +0300 Subject: [PATCH 28/52] Start button always on the left v1.1 (#988) * The start menu is now positioned on the left too. Can be disabled in the mod settings. * Added support for multiple monitors. --- mods/taskbar-start-button-position.wh.cpp | 645 +++++++++++++++++++--- 1 file changed, 555 insertions(+), 90 deletions(-) diff --git a/mods/taskbar-start-button-position.wh.cpp b/mods/taskbar-start-button-position.wh.cpp index 989ae887..ba4f5fdf 100644 --- a/mods/taskbar-start-button-position.wh.cpp +++ b/mods/taskbar-start-button-position.wh.cpp @@ -2,14 +2,16 @@ // @id taskbar-start-button-position // @name Start button always on the left // @description Forces the start button to be on the left of the taskbar, even when taskbar icons are centered (Windows 11 only) -// @version 1.0 +// @version 1.1 // @author m417z // @github https://github.com/m417z // @twitter https://twitter.com/m417z // @homepage https://m417z.com/ // @include explorer.exe +// @include StartMenuExperienceHost.exe +// @include SearchHost.exe // @architecture x86-64 -// @compilerOptions -lole32 -loleaut32 -lruntimeobject +// @compilerOptions -lole32 -loleaut32 -lruntimeobject -lshcore // ==/WindhawkMod== // Source code is published under The GNU General Public License v3.0. @@ -31,12 +33,25 @@ Only Windows 11 is supported. Known limitations: * There's a jumpy animation when a centered taskbar is animated. -* Secondary monitors aren't supported. ![Screenshot](https://i.imgur.com/bEqvfOE.png) */ // ==/WindhawkModReadme== +// ==WindhawkModSettings== +/* +- startMenuOnTheLeft: true + $name: Start menu on the left + $description: >- + Make the start menu open on the left even if taskbar icons are centered +- startMenuWidth: 0 + $name: Start menu width + $description: >- + Set to zero to use the system default width, set to a custom value if using + a customized start menu, e.g. with the Windows 11 Start Menu Styler mod +*/ +// ==/WindhawkModSettings== + #include #include @@ -52,14 +67,42 @@ Known limitations: using namespace winrt::Windows::UI::Xaml; -std::atomic g_appliedSettings; +struct { + bool startMenuOnTheLeft; + int startMenuWidth; +} g_settings; + +enum class Target { + Explorer, + StartMenu, + SearchHost, +}; + +Target g_target; + std::atomic g_unloading; -FrameworkElement g_startButtonElement = nullptr; -winrt::event_token g_taskbarFrameRepeaterLayoutUpdatedToken; UINT_PTR g_updateStartButtonPositionTimer; int g_updateStartButtonPositionTimerCounter; +struct TaskbarData { + winrt::weak_ref xamlRoot; + winrt::weak_ref startButtonElement; +}; + +std::vector g_taskbarData; + +typedef enum MONITOR_DPI_TYPE { + MDT_EFFECTIVE_DPI = 0, + MDT_ANGULAR_DPI = 1, + MDT_RAW_DPI = 2, + MDT_DEFAULT = MDT_EFFECTIVE_DPI +} MONITOR_DPI_TYPE; +STDAPI GetDpiForMonitor(HMONITOR hmonitor, + MONITOR_DPI_TYPE dpiType, + UINT* dpiX, + UINT* dpiY); + HWND GetTaskbarWnd() { static HWND hTaskbarWnd; @@ -110,11 +153,17 @@ FrameworkElement FindChildByClassName(FrameworkElement element, }); } -void UpdateStartButtonPosition(FrameworkElement startButton) { +void UpdateStartButtonPosition(XamlRoot xamlRoot, + FrameworkElement startButton) { Wh_Log(L">"); + if (startButton.XamlRoot() != xamlRoot) { + Wh_Log(L"XamlRoot mismatch"); + return; + } + FrameworkElement xamlRootContent = - startButton.XamlRoot().Content().try_as(); + xamlRoot.Content().try_as(); auto pt = startButton.TransformToVisual(xamlRootContent) .TransformPoint(winrt::Windows::Foundation::Point{0, 0}); @@ -149,24 +198,40 @@ void ResetStartButtonPosition(FrameworkElement startButton) { void ScheduleUpdateStartButtonPosition() { g_updateStartButtonPositionTimerCounter = 0; - g_updateStartButtonPositionTimer = - SetTimer(nullptr, g_updateStartButtonPositionTimer, 100, - [](HWND hwnd, // handle of window for timer messages - UINT uMsg, // WM_TIMER message - UINT_PTR idEvent, // timer identifier - DWORD dwTime // current system time - ) WINAPI { - g_updateStartButtonPositionTimerCounter++; - if (g_updateStartButtonPositionTimerCounter >= 10) { - KillTimer(nullptr, g_updateStartButtonPositionTimer); - g_updateStartButtonPositionTimer = 0; - } - - UpdateStartButtonPosition(g_startButtonElement); - }); + g_updateStartButtonPositionTimer = SetTimer( + nullptr, g_updateStartButtonPositionTimer, 100, + [](HWND hwnd, // handle of window for timer messages + UINT uMsg, // WM_TIMER message + UINT_PTR idEvent, // timer identifier + DWORD dwTime // current system time + ) WINAPI { + g_updateStartButtonPositionTimerCounter++; + if (g_updateStartButtonPositionTimerCounter >= 30) { + KillTimer(nullptr, g_updateStartButtonPositionTimer); + g_updateStartButtonPositionTimer = 0; + } + + for (const auto& item : g_taskbarData) { + if (auto xamlRoot = item.xamlRoot.get()) { + if (auto startButtonElement = + item.startButtonElement.get()) { + UpdateStartButtonPosition(xamlRoot, startButtonElement); + } + } + } + }); } bool ApplyStyle(XamlRoot xamlRoot) { + auto dataItem = std::find_if( + g_taskbarData.begin(), g_taskbarData.end(), [&xamlRoot](auto x) { + auto xamlRootIter = x.xamlRoot.get(); + return xamlRootIter && xamlRootIter == xamlRoot; + }); + if (dataItem != g_taskbarData.end()) { + return true; + } + FrameworkElement xamlRootContent = xamlRoot.Content().try_as(); @@ -209,44 +274,51 @@ bool ApplyStyle(XamlRoot xamlRoot) { startButtonMargin.Left = g_unloading ? 0 : -startButtonWidth; startButton.Margin(startButtonMargin); - if (!g_unloading) { - g_startButtonElement = startButton; - - ScheduleUpdateStartButtonPosition(); - - if (!g_taskbarFrameRepeaterLayoutUpdatedToken) { - g_taskbarFrameRepeaterLayoutUpdatedToken = - taskbarFrameRepeater.LayoutUpdated([](auto&&, auto&& args) { - ScheduleUpdateStartButtonPosition(); - }); - } - } else { - if (g_taskbarFrameRepeaterLayoutUpdatedToken) { - taskbarFrameRepeater.LayoutUpdated( - g_taskbarFrameRepeaterLayoutUpdatedToken); - g_taskbarFrameRepeaterLayoutUpdatedToken = winrt::event_token{}; - } - - if (g_updateStartButtonPositionTimer) { - KillTimer(nullptr, g_updateStartButtonPositionTimer); - g_updateStartButtonPositionTimer = 0; - } - - ResetStartButtonPosition(startButton); - g_startButtonElement = nullptr; - } + g_taskbarData.push_back({ + .xamlRoot = xamlRoot, + .startButtonElement = startButton, + }); return true; } void* CTaskBand_ITaskListWndSite_vftable; +void* CSecondaryTaskBand_ITaskListWndSite_vftable; + using CTaskBand_GetTaskbarHost_t = PVOID(WINAPI*)(PVOID pThis, PVOID* result); CTaskBand_GetTaskbarHost_t CTaskBand_GetTaskbarHost_Original; +using CSecondaryTaskBand_GetTaskbarHost_t = PVOID(WINAPI*)(PVOID pThis, + PVOID* result); +CSecondaryTaskBand_GetTaskbarHost_t CSecondaryTaskBand_GetTaskbarHost_Original; + using std__Ref_count_base__Decref_t = void(WINAPI*)(PVOID pThis); std__Ref_count_base__Decref_t std__Ref_count_base__Decref_Original; +XamlRoot XamlRootFromTaskbarHostSharedPtr(PVOID taskbarHostSharedPtr[2]) { + if (!taskbarHostSharedPtr[0] && !taskbarHostSharedPtr[1]) { + return nullptr; + } + + // Reference: TaskbarHost::FrameHeight + constexpr size_t kTaskbarElementIUnknownOffset = 0x40; + + auto* taskbarElementIUnknown = + *(IUnknown**)((BYTE*)taskbarHostSharedPtr[0] + + kTaskbarElementIUnknownOffset); + + FrameworkElement taskbarElement = nullptr; + taskbarElementIUnknown->QueryInterface(winrt::guid_of(), + winrt::put_abi(taskbarElement)); + + auto result = taskbarElement ? taskbarElement.XamlRoot() : nullptr; + + std__Ref_count_base__Decref_Original(taskbarHostSharedPtr[1]); + + return result; +} + XamlRoot GetTaskbarXamlRoot(HWND hTaskbarWnd) { HWND hTaskSwWnd = (HWND)GetProp(hTaskbarWnd, L"TaskbandHWND"); if (!hTaskSwWnd) { @@ -263,26 +335,29 @@ XamlRoot GetTaskbarXamlRoot(HWND hTaskbarWnd) { PVOID taskbarHostSharedPtr[2]{}; CTaskBand_GetTaskbarHost_Original(taskBandForTaskListWndSite, taskbarHostSharedPtr); - if (!taskbarHostSharedPtr[0] && !taskbarHostSharedPtr[1]) { - return nullptr; - } - - // Reference: TaskbarHost::FrameHeight - constexpr size_t kTaskbarElementIUnknownOffset = 0x40; - auto* taskbarElementIUnknown = - *(IUnknown**)((BYTE*)taskbarHostSharedPtr[0] + - kTaskbarElementIUnknownOffset); + return XamlRootFromTaskbarHostSharedPtr(taskbarHostSharedPtr); +} - FrameworkElement taskbarElement = nullptr; - taskbarElementIUnknown->QueryInterface(winrt::guid_of(), - winrt::put_abi(taskbarElement)); +XamlRoot GetSecondaryTaskbarXamlRoot(HWND hSecondaryTaskbarWnd) { + HWND hTaskSwWnd = + (HWND)FindWindowEx(hSecondaryTaskbarWnd, nullptr, L"WorkerW", nullptr); + if (!hTaskSwWnd) { + return nullptr; + } - auto result = taskbarElement ? taskbarElement.XamlRoot() : nullptr; + PVOID taskBand = (PVOID)GetWindowLongPtr(hTaskSwWnd, 0); + PVOID taskBandForTaskListWndSite = taskBand; + while (*(PVOID*)taskBandForTaskListWndSite != + CSecondaryTaskBand_ITaskListWndSite_vftable) { + taskBandForTaskListWndSite = (PVOID*)taskBandForTaskListWndSite + 1; + } - std__Ref_count_base__Decref_Original(taskbarHostSharedPtr[1]); + PVOID taskbarHostSharedPtr[2]{}; + CSecondaryTaskBand_GetTaskbarHost_Original(taskBandForTaskListWndSite, + taskbarHostSharedPtr); - return result; + return XamlRootFromTaskbarHostSharedPtr(taskbarHostSharedPtr); } using RunFromWindowThreadProc_t = void(WINAPI*)(PVOID parameter); @@ -337,33 +412,63 @@ bool RunFromWindowThread(HWND hWnd, return true; } -void ApplySettings(HWND hTaskbarWnd) { +void ApplySettingsFromTaskbarThread() { Wh_Log(L"Applying settings"); - struct ApplySettingsParam { - HWND hTaskbarWnd; - }; - - ApplySettingsParam param{ - .hTaskbarWnd = hTaskbarWnd, - }; + EnumThreadWindows( + GetCurrentThreadId(), + [](HWND hWnd, LPARAM lParam) WINAPI -> BOOL { + WCHAR szClassName[32]; + if (GetClassName(hWnd, szClassName, ARRAYSIZE(szClassName)) == 0) { + return TRUE; + } - RunFromWindowThread( - hTaskbarWnd, - [](PVOID pParam) WINAPI { - ApplySettingsParam& param = *(ApplySettingsParam*)pParam; + XamlRoot xamlRoot = nullptr; + if (_wcsicmp(szClassName, L"Shell_TrayWnd") == 0) { + xamlRoot = GetTaskbarXamlRoot(hWnd); + } else if (_wcsicmp(szClassName, L"Shell_SecondaryTrayWnd") == 0) { + xamlRoot = GetSecondaryTaskbarXamlRoot(hWnd); + } else { + return TRUE; + } - auto xamlRoot = GetTaskbarXamlRoot(param.hTaskbarWnd); if (!xamlRoot) { Wh_Log(L"Getting XamlRoot failed"); - return; + return TRUE; } if (!ApplyStyle(xamlRoot)) { Wh_Log(L"ApplyStyles failed"); + return TRUE; + } + + return TRUE; + }, + 0); +} + +void ApplySettings(HWND hTaskbarWnd) { + RunFromWindowThread( + hTaskbarWnd, + [](PVOID pParam) WINAPI { + if (!g_unloading) { + ApplySettingsFromTaskbarThread(); + ScheduleUpdateStartButtonPosition(); + } else { + for (const auto& item : g_taskbarData) { + if (auto startButtonElement = + item.startButtonElement.get()) { + ResetStartButtonPosition(startButtonElement); + } + } + + if (g_updateStartButtonPositionTimer) { + KillTimer(nullptr, g_updateStartButtonPositionTimer); + g_updateStartButtonPositionTimer = 0; + } } }, - ¶m); + 0); } using CPearl_SetBounds_t = HRESULT(WINAPI*)(void* pThis, void* param1); @@ -371,12 +476,9 @@ CPearl_SetBounds_t CPearl_SetBounds_Original; HRESULT WINAPI CPearl_SetBounds_Hook(void* pThis, void* param1) { Wh_Log(L">"); - if (!g_appliedSettings) { - HWND hTaskbarWnd = GetTaskbarWnd(); - if (hTaskbarWnd) { - g_appliedSettings = true; - ApplySettings(hTaskbarWnd); - } + if (!g_unloading) { + ApplySettingsFromTaskbarThread(); + ScheduleUpdateStartButtonPosition(); } return CPearl_SetBounds_Original(pThis, param1); @@ -399,10 +501,18 @@ BOOL HookTaskbarDllSymbols() { {LR"(const CTaskBand::`vftable'{for `ITaskListWndSite'})"}, (void**)&CTaskBand_ITaskListWndSite_vftable, }, + { + {LR"(const CSecondaryTaskBand::`vftable'{for `ITaskListWndSite'})"}, + (void**)&CSecondaryTaskBand_ITaskListWndSite_vftable, + }, { {LR"(public: virtual class std::shared_ptr __cdecl CTaskBand::GetTaskbarHost(void)const )"}, (void**)&CTaskBand_GetTaskbarHost_Original, }, + { + {LR"(public: virtual class std::shared_ptr __cdecl CSecondaryTaskBand::GetTaskbarHost(void)const )"}, + (void**)&CSecondaryTaskBand_GetTaskbarHost_Original, + }, { {LR"(public: void __cdecl std::_Ref_count_base::_Decref(void))"}, (void**)&std__Ref_count_base__Decref_Original, @@ -412,9 +522,339 @@ BOOL HookTaskbarDllSymbols() { return HookSymbols(module, taskbarDllHooks, ARRAYSIZE(taskbarDllHooks)); } +namespace CoreWindowUI { + +using SetWindowPos_t = decltype(&SetWindowPos); +SetWindowPos_t SetWindowPos_Original; + +bool IsTargetCoreWindow(HWND hWnd) { + DWORD threadId = 0; + DWORD processId = 0; + if (!hWnd || !(threadId = GetWindowThreadProcessId(hWnd, &processId)) || + processId != GetCurrentProcessId()) { + return false; + } + + WCHAR szClassName[32]; + if (GetClassName(hWnd, szClassName, ARRAYSIZE(szClassName)) == 0) { + return false; + } + + if (_wcsicmp(szClassName, L"Windows.UI.Core.CoreWindow") != 0) { + return false; + } + + return true; +} + +std::vector GetCoreWindows() { + struct ENUM_WINDOWS_PARAM { + std::vector* hWnds; + }; + + std::vector hWnds; + ENUM_WINDOWS_PARAM param = {&hWnds}; + EnumWindows( + [](HWND hWnd, LPARAM lParam) WINAPI -> BOOL { + ENUM_WINDOWS_PARAM& param = *(ENUM_WINDOWS_PARAM*)lParam; + + if (IsTargetCoreWindow(hWnd)) { + param.hWnds->push_back(hWnd); + } + + return TRUE; + }, + (LPARAM)¶m); + + return hWnds; +} + +void AdjustCoreWindowSize(int x, int y, int* width, int* height) { + if (g_target != Target::StartMenu) { + return; + } + + const POINT pt = {x, y}; + HMONITOR monitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST); + + if (g_unloading) { + MONITORINFO monitorInfo{ + .cbSize = sizeof(MONITORINFO), + }; + GetMonitorInfo(monitor, &monitorInfo); + + *width = monitorInfo.rcWork.right - monitorInfo.rcWork.left; + // *height = monitorInfo.rcWork.bottom - monitorInfo.rcWork.top; + return; + } + + UINT monitorDpiX = 96; + UINT monitorDpiY = 96; + GetDpiForMonitor(monitor, MDT_DEFAULT, &monitorDpiX, &monitorDpiY); + + const int w1 = + MulDiv(g_settings.startMenuWidth ? g_settings.startMenuWidth : 660, + monitorDpiX, 96); + if (*width > w1) { + *width = w1; + } + + // const int h1 = MulDiv(750, monitorDpiY, 96); + // const int h2 = MulDiv(694, monitorDpiY, 96); + // if (*height >= h1) { + // *height = h1; + // } else if (*height >= h2) { + // *height = h2; + // } +} + +void AdjustCoreWindowPos(int* x, int* y, int width, int height) { + if (g_unloading) { + if (g_target == Target::StartMenu) { + *x = 0; + *y = 0; + } + + return; + } + + const POINT pt = {*x, *y}; + HMONITOR monitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST); + + MONITORINFO monitorInfo{ + .cbSize = sizeof(MONITORINFO), + }; + GetMonitorInfo(monitor, &monitorInfo); + + *x = monitorInfo.rcWork.left; +} + +void ApplySettings() { + for (HWND hCoreWnd : GetCoreWindows()) { + Wh_Log(L"Adjusting core window %08X", (DWORD)(ULONG_PTR)hCoreWnd); + + RECT rc; + if (!GetWindowRect(hCoreWnd, &rc)) { + continue; + } + + int x = rc.left; + int y = rc.top; + int cx = rc.right - rc.left; + int cy = rc.bottom - rc.top; + + AdjustCoreWindowSize(x, y, &cx, &cy); + AdjustCoreWindowPos(&x, &y, cx, cy); + + SetWindowPos_Original(hCoreWnd, nullptr, x, y, cx, cy, + SWP_NOZORDER | SWP_NOACTIVATE); + } +} + +using CreateWindowInBand_t = HWND(WINAPI*)(DWORD dwExStyle, + LPCWSTR lpClassName, + LPCWSTR lpWindowName, + DWORD dwStyle, + int X, + int Y, + int nWidth, + int nHeight, + HWND hWndParent, + HMENU hMenu, + HINSTANCE hInstance, + PVOID lpParam, + DWORD dwBand); +CreateWindowInBand_t CreateWindowInBand_Original; +HWND WINAPI CreateWindowInBand_Hook(DWORD dwExStyle, + LPCWSTR lpClassName, + LPCWSTR lpWindowName, + DWORD dwStyle, + int X, + int Y, + int nWidth, + int nHeight, + HWND hWndParent, + HMENU hMenu, + HINSTANCE hInstance, + PVOID lpParam, + DWORD dwBand) { + BOOL bTextualClassName = ((ULONG_PTR)lpClassName & ~(ULONG_PTR)0xffff) != 0; + if (bTextualClassName && + _wcsicmp(lpClassName, L"Windows.UI.Core.CoreWindow") == 0) { + Wh_Log(L"Creating core window"); + AdjustCoreWindowSize(X, Y, &nWidth, &nHeight); + AdjustCoreWindowPos(&X, &Y, nWidth, nHeight); + } + + return CreateWindowInBand_Original( + dwExStyle, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, + hWndParent, hMenu, hInstance, lpParam, dwBand); +} + +using CreateWindowInBandEx_t = HWND(WINAPI*)(DWORD dwExStyle, + LPCWSTR lpClassName, + LPCWSTR lpWindowName, + DWORD dwStyle, + int X, + int Y, + int nWidth, + int nHeight, + HWND hWndParent, + HMENU hMenu, + HINSTANCE hInstance, + PVOID lpParam, + DWORD dwBand, + DWORD dwTypeFlags); +CreateWindowInBandEx_t CreateWindowInBandEx_Original; +HWND WINAPI CreateWindowInBandEx_Hook(DWORD dwExStyle, + LPCWSTR lpClassName, + LPCWSTR lpWindowName, + DWORD dwStyle, + int X, + int Y, + int nWidth, + int nHeight, + HWND hWndParent, + HMENU hMenu, + HINSTANCE hInstance, + PVOID lpParam, + DWORD dwBand, + DWORD dwTypeFlags) { + BOOL bTextualClassName = ((ULONG_PTR)lpClassName & ~(ULONG_PTR)0xffff) != 0; + if (bTextualClassName && + _wcsicmp(lpClassName, L"Windows.UI.Core.CoreWindow") == 0) { + Wh_Log(L"Creating core window"); + AdjustCoreWindowSize(X, Y, &nWidth, &nHeight); + AdjustCoreWindowPos(&X, &Y, nWidth, nHeight); + } + + return CreateWindowInBandEx_Original( + dwExStyle, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, + hWndParent, hMenu, hInstance, lpParam, dwBand, dwTypeFlags); +} + +BOOL WINAPI SetWindowPos_Hook(HWND hWnd, + HWND hWndInsertAfter, + int X, + int Y, + int cx, + int cy, + UINT uFlags) { + auto original = [&]() { + return SetWindowPos_Original(hWnd, hWndInsertAfter, X, Y, cx, cy, + uFlags); + }; + + if (!IsTargetCoreWindow(hWnd)) { + return original(); + } + + Wh_Log(L"%08X %08X", (DWORD)(ULONG_PTR)hWnd, uFlags); + + if ((uFlags & (SWP_NOSIZE | SWP_NOMOVE)) == (SWP_NOSIZE | SWP_NOMOVE)) { + return original(); + } + + RECT rc{}; + GetWindowRect(hWnd, &rc); + + // SearchHost is being moved by explorer.exe, then the size is adjusted + // by SearchHost itself. Make SearchHost adjust the position too. A + // similar workaround is needed for other windows. + if (uFlags & SWP_NOMOVE) { + uFlags &= ~SWP_NOMOVE; + X = rc.left; + Y = rc.top; + } + + int width; + int height; + if (uFlags & SWP_NOSIZE) { + width = rc.right - rc.left; + height = rc.bottom - rc.top; + AdjustCoreWindowSize(X, Y, &width, &height); + } else { + AdjustCoreWindowSize(X, Y, &cx, &cy); + width = cx; + height = cy; + } + + if (!(uFlags & SWP_NOMOVE)) { + AdjustCoreWindowPos(&X, &Y, width, height); + } + + return SetWindowPos_Original(hWnd, hWndInsertAfter, X, Y, cx, cy, uFlags); +} + +} // namespace CoreWindowUI + +void LoadSettings() { + g_settings.startMenuOnTheLeft = Wh_GetIntSetting(L"startMenuOnTheLeft"); + g_settings.startMenuWidth = Wh_GetIntSetting(L"startMenuWidth"); +} + BOOL Wh_ModInit() { Wh_Log(L">"); + LoadSettings(); + + g_target = Target::Explorer; + + WCHAR moduleFilePath[MAX_PATH]; + switch ( + GetModuleFileName(nullptr, moduleFilePath, ARRAYSIZE(moduleFilePath))) { + case 0: + case ARRAYSIZE(moduleFilePath): + Wh_Log(L"GetModuleFileName failed"); + break; + + default: + if (PCWSTR moduleFileName = wcsrchr(moduleFilePath, L'\\')) { + moduleFileName++; + if (_wcsicmp(moduleFileName, L"StartMenuExperienceHost.exe") == + 0) { + g_target = Target::StartMenu; + } else if (_wcsicmp(moduleFileName, L"SearchHost.exe") == 0) { + g_target = Target::SearchHost; + } + } else { + Wh_Log(L"GetModuleFileName returned an unsupported path"); + } + break; + } + + if (g_target == Target::StartMenu || g_target == Target::SearchHost) { + if (!g_settings.startMenuOnTheLeft) { + return FALSE; + } + + HMODULE user32Module = LoadLibrary(L"user32.dll"); + if (user32Module) { + void* pCreateWindowInBand = + (void*)GetProcAddress(user32Module, "CreateWindowInBand"); + if (pCreateWindowInBand) { + Wh_SetFunctionHook( + pCreateWindowInBand, + (void*)CoreWindowUI::CreateWindowInBand_Hook, + (void**)&CoreWindowUI::CreateWindowInBand_Original); + } + + void* pCreateWindowInBandEx = + (void*)GetProcAddress(user32Module, "CreateWindowInBandEx"); + if (pCreateWindowInBandEx) { + Wh_SetFunctionHook( + pCreateWindowInBandEx, + (void*)CoreWindowUI::CreateWindowInBandEx_Hook, + (void**)&CoreWindowUI::CreateWindowInBandEx_Original); + } + } + + Wh_SetFunctionHook((void*)SetWindowPos, + (void*)CoreWindowUI::SetWindowPos_Hook, + (void**)&CoreWindowUI::SetWindowPos_Original); + return TRUE; + } + if (!HookTaskbarDllSymbols()) { return FALSE; } @@ -425,10 +865,14 @@ BOOL Wh_ModInit() { void Wh_ModAfterInit() { Wh_Log(L">"); - HWND hTaskbarWnd = GetTaskbarWnd(); - if (hTaskbarWnd) { - g_appliedSettings = true; - ApplySettings(hTaskbarWnd); + if (g_target == Target::Explorer) { + HWND hTaskbarWnd = GetTaskbarWnd(); + if (hTaskbarWnd) { + ApplySettings(hTaskbarWnd); + } + } else if (g_target == Target::StartMenu || + g_target == Target::SearchHost) { + CoreWindowUI::ApplySettings(); } } @@ -437,14 +881,35 @@ void Wh_ModBeforeUninit() { g_unloading = true; - if (g_appliedSettings) { + if (g_target == Target::Explorer) { HWND hTaskbarWnd = GetTaskbarWnd(); if (hTaskbarWnd) { ApplySettings(hTaskbarWnd); } + } else if (g_target == Target::StartMenu || + g_target == Target::SearchHost) { + CoreWindowUI::ApplySettings(); } } void Wh_ModUninit() { Wh_Log(L">"); } + +BOOL Wh_ModSettingsChanged(BOOL* bReload) { + Wh_Log(L">"); + + LoadSettings(); + + if (!g_settings.startMenuOnTheLeft) { + return FALSE; + } + + *bReload = FALSE; + + if (g_target == Target::StartMenu || g_target == Target::SearchHost) { + CoreWindowUI::ApplySettings(); + } + + return TRUE; +} From 98a5f9617e14032ec6156a90ddbd2959f3deb4db Mon Sep 17 00:00:00 2001 From: aubrey <44238627+aubymori@users.noreply.github.com> Date: Sat, 21 Sep 2024 15:42:07 -0500 Subject: [PATCH 29/52] Classic Menus 1.1.0: Refactor method (#991) - This fixes third party applications which render their own menus with UXTheme data from incorrectly rendering or even crashing - This also makes the changes instantly apply without restarting programs --- mods/classic-menus.wh.cpp | 59 +++++++++++++++------------------------ 1 file changed, 23 insertions(+), 36 deletions(-) diff --git a/mods/classic-menus.wh.cpp b/mods/classic-menus.wh.cpp index 04c43174..64adc2a5 100644 --- a/mods/classic-menus.wh.cpp +++ b/mods/classic-menus.wh.cpp @@ -2,7 +2,7 @@ // @id classic-menus // @name Classic Menus // @description Makes menus classic themed while keeping other controls themed -// @version 1.0.0 +// @version 1.1.0 // @author aubymori // @github https://github.com/aubymori // @include * @@ -15,8 +15,6 @@ In Windows XP, menus remained unthemed even with themes enabled. This mod replicates that functionality. -**This mod will only work on Windhawk v1.4 and greater.** - **Before**: ![Before](https://raw.githubusercontent.com/aubymori/images/main/classic-menus-before.png) @@ -30,26 +28,28 @@ that functionality. #include #include -typedef HTHEME (__fastcall *OpenThemeDataExInternal_t)(HWND, LPCWSTR, int, INT_PTR, int); -OpenThemeDataExInternal_t OpenThemeDataExInternal_orig; -HTHEME __fastcall OpenThemeDataExInternal_hook( - HWND hWnd, - LPCWSTR pszClassList, - int i1, - INT_PTR i2, - int i3 -) +int (__fastcall *CThemeMenu_Attach_orig)(HWND, HMENU, int, bool, void **); +int __fastcall CThemeMenu_Attach_hook(HWND, HMENU, int, bool, void **) { - if (!wcsicmp(pszClassList, L"MENU")) + return 0; +} + +WindhawkUtils::SYMBOL_HOOK uxThemeDllHooks[] = { { - SetLastError(E_POINTER); - return 0; + { + L"protected: static int " +#ifdef _WIN64 + L"__cdecl" +#else + L"__stdcall" +#endif + L" CThemeMenu::Attach(struct HWND__ *,struct HMENU__ *,int,bool,class CThemeMenu * *)" + }, + &CThemeMenu_Attach_orig, + CThemeMenu_Attach_hook, + false } - - return OpenThemeDataExInternal_orig( - hWnd, pszClassList, i1, i2, i3 - ); -} +}; BOOL Wh_ModInit(void) { @@ -60,26 +60,13 @@ BOOL Wh_ModInit(void) return FALSE; } - WindhawkUtils::SYMBOL_HOOK hook = { - { - #ifdef _WIN64 - L"OpenThemeDataExInternal" - #else - L"_OpenThemeDataExInternal@20" - #endif - }, - &OpenThemeDataExInternal_orig, - OpenThemeDataExInternal_hook, - false - }; - if (!WindhawkUtils::HookSymbols( hUxTheme, - &hook, - 1 + uxThemeDllHooks, + ARRAYSIZE(uxThemeDllHooks) )) { - Wh_Log(L"Failed to hook OpenThemeDataExInternal"); + Wh_Log(L"Failed to hook one or more symbol functions in uxtheme.dll"); return FALSE; } From 24a59b390771c24436afc2ef3b64c145f8547082 Mon Sep 17 00:00:00 2001 From: Milan Herbig Date: Mon, 23 Sep 2024 17:56:29 +0200 Subject: [PATCH 30/52] Fixed build errors introduced with latest version of Windhawk 1.5x (#996) * Fixed build errors introduced with latest version of Windhawk 1.5x * Use CoInitializeEx with COINIT_APARTMENTTHREADED (fixes issue #12: Can't Restart Explorer While this Mod is Enabled) --- mods/taskbar-empty-space-clicks.wh.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/mods/taskbar-empty-space-clicks.wh.cpp b/mods/taskbar-empty-space-clicks.wh.cpp index ba787553..7806bb4e 100644 --- a/mods/taskbar-empty-space-clicks.wh.cpp +++ b/mods/taskbar-empty-space-clicks.wh.cpp @@ -2,11 +2,11 @@ // @id taskbar-empty-space-clicks // @name Click on empty taskbar space // @description Trigger custom action when empty space on a taskbar is double/middle clicked -// @version 1.3 +// @version 1.4 // @author m1lhaus // @github https://github.com/m1lhaus // @include explorer.exe -// @compilerOptions -DWINVER=0x0602 -D_WIN32_WINNT=0x0602 -lcomctl32 -loleaut32 -lole32 -lversion +// @compilerOptions -DWINVER=0x0A00 -lcomctl32 -loleaut32 -lole32 -lversion // ==/WindhawkMod== // Source code is published under The GNU General Public License v3.0. @@ -138,8 +138,7 @@ If you have request for new functions, suggestions or you are experiencing some */ // ==/WindhawkModSettings== -// Note: If intellisense is giving you a trouble, add -DWINVER=0x0602 -D_WIN32_WINNT=0x0602 flags to compile_flags.txt (Ctrl+E). - +#include #include #include #include @@ -175,11 +174,12 @@ using bstr_ptr = _bstr_t; // ===================================================================== +// following block is to keep compatibility with pre Windhawk 1.5 versions +#ifndef __IUIAutomationElement_INTERFACE_DEFINED__ + // following include are taken from Qt project since builtin compiler is missing those definitions #pragma region uiautomation_includes -#include - // Pasted below and commented duplicate definitions: // https://github.com/qt/qtbase/blob/dev/src/gui/accessible/windows/apisupport/uiatypes_p.h // https://github.com/qt/qtbase/blob/dev/src/gui/accessible/windows/apisupport/uiaclientinterfaces_p.h @@ -666,6 +666,8 @@ typedef class CUIAutomation CUIAutomation; #pragma endregion +#endif + // ===================================================================== #define ENABLE_LOG_INFO // info messages will be enabled @@ -846,7 +848,7 @@ class COMInitializer { if (!initialized) { - initialized = SUCCEEDED(CoInitializeEx(NULL, COINIT_MULTITHREADED)); + initialized = SUCCEEDED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)); } return initialized; } From 49847340a6c3c43595838754233700782c9c7ccd Mon Sep 17 00:00:00 2001 From: Michael Maltsev <4129781+m417z@users.noreply.github.com> Date: Mon, 23 Sep 2024 22:05:23 +0300 Subject: [PATCH 31/52] Exclude Windhawk 1.3.1, which is quite old, from the compatibility check --- .github/workflows/mod_compatibility_check.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/mod_compatibility_check.yml b/.github/workflows/mod_compatibility_check.yml index fa50d5d0..d7074282 100644 --- a/.github/workflows/mod_compatibility_check.yml +++ b/.github/workflows/mod_compatibility_check.yml @@ -23,6 +23,9 @@ jobs: # '1.5', # Same compiler as 1.5.1. '1.5.1', ] + exclude: + # Exclude 1.3.1, which is quite old, for everyone but m417z. + - version: ${{ github.event.pull_request.user.login != 'm417z' && '1.3.1' }} steps: - name: Checkout uses: actions/checkout@v4 From 6d0410bd49c348557a4d562d5bed6f75889b9827 Mon Sep 17 00:00:00 2001 From: Michael Maltsev <4129781+m417z@users.noreply.github.com> Date: Mon, 23 Sep 2024 22:42:07 +0300 Subject: [PATCH 32/52] Fix mod compatibility check --- .github/workflows/mod_compatibility_check.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/mod_compatibility_check.yml b/.github/workflows/mod_compatibility_check.yml index d7074282..a51cfcd2 100644 --- a/.github/workflows/mod_compatibility_check.yml +++ b/.github/workflows/mod_compatibility_check.yml @@ -23,9 +23,9 @@ jobs: # '1.5', # Same compiler as 1.5.1. '1.5.1', ] - exclude: - # Exclude 1.3.1, which is quite old, for everyone but m417z. - - version: ${{ github.event.pull_request.user.login != 'm417z' && '1.3.1' }} + exclude: + # Exclude 1.3.1, which is quite old, for everyone but m417z. + - version: ${{ github.event.pull_request.user.login != 'm417z' && '1.3.1' }} steps: - name: Checkout uses: actions/checkout@v4 From 18d85f2bce2e5fbb27499f93281a2d79b13c6670 Mon Sep 17 00:00:00 2001 From: Michael Maltsev <4129781+m417z@users.noreply.github.com> Date: Sat, 28 Sep 2024 17:23:40 +0300 Subject: [PATCH 33/52] Taskbar Labels for Windows 11 v1.3 (#1002) * Added four modes, last two of which weren't possible before: * Show labels, don't combine taskbar buttons * Hide labels, combine taskbar buttons * Show labels, combine taskbar buttons * Hide labels, don't combine taskbar buttons * Added an option to show or hide labels for specific programs. * Improved custom indicator compatibility with adaptive width labels. Previously, it could break the layout or in rare cases cause a crash. * Fixed the "Minimum taskbar item width" option in newer Windows 11 versions. --- mods/taskbar-labels.wh.cpp | 644 +++++++++++++++++++++++++++++-------- 1 file changed, 504 insertions(+), 140 deletions(-) diff --git a/mods/taskbar-labels.wh.cpp b/mods/taskbar-labels.wh.cpp index e476827a..88b03016 100644 --- a/mods/taskbar-labels.wh.cpp +++ b/mods/taskbar-labels.wh.cpp @@ -1,15 +1,15 @@ // ==WindhawkMod== // @id taskbar-labels // @name Taskbar Labels for Windows 11 -// @description Show and customize text labels for running programs on the taskbar (Windows 11 only) -// @version 1.2.5 +// @description Customize text labels and combining for running programs on the taskbar (Windows 11 only) +// @version 1.3 // @author m417z // @github https://github.com/m417z // @twitter https://twitter.com/m417z // @homepage https://m417z.com/ // @include explorer.exe // @architecture x86-64 -// @compilerOptions -DWINVER=0x0605 -loleaut32 -lole32 -lruntimeobject -lwininet +// @compilerOptions -DWINVER=0x0A00 -loleaut32 -lole32 -lruntimeobject -lwininet // ==/WindhawkMod== // Source code is published under The GNU General Public License v3.0. @@ -24,53 +24,69 @@ /* # Taskbar Labels for Windows 11 -Show and customize text labels for running programs on the taskbar (Windows 11 -only). +Customize text labels and combining for running programs on the taskbar. -**Older Windows 11 versions:** By default, the original Windows 11 taskbar only -shows icons for taskbar items, without any text labels. This mod adds text -labels, similarly to the way it was possible to configure in older Windows -versions. +The mod improves the native Windows taskbar labels implementation by making all +taskbar items have the same width (optional), adding ellipsis for long labels, +and providing other customization options. -**Newer Windows 11 versions:** A native taskbar labels implementation was added -in newer Windows 11 versions. For these versions, the mod improves it by making -all taskbar items have the same width (optional), adding ellipsis for long -labels, and providing other customization options. +The mod allows to choose one of the four available modes: -Before: +![Show labels, don't combine taskbar buttons](https://i.imgur.com/v8Idmjy.png) \ +*Show labels, don't combine taskbar buttons (default)* -![Before screenshot](https://i.imgur.com/SjHSF7g.png) +![Hide labels, combine taskbar buttons](https://i.imgur.com/6Fg5h0d.png) \ +*Hide labels, combine taskbar buttons* -After: +![Show labels, combine taskbar buttons](https://i.imgur.com/Y5HA6Xv.png) \ +*Show labels, combine taskbar buttons* -![After screenshot](https://i.imgur.com/qpc4iFh.png) +![Hide labels, don't combine taskbar buttons](https://i.imgur.com/Buh8KnZ.png) \ +*Hide labels, don't combine taskbar buttons* + +Only the first two modes are available natively in Windows. + +**Older Windows 11 versions:** In older versions, the taskbar can only show +icons for taskbar items, without any text labels. This mod adds text labels +using a custom implementation. The additional modes, and some other options, +aren't available. Additional customization is available in the settings. For example, you can choose one of the following running indicator styles: -![Running indicator styles](https://i.imgur.com/HpytGBO.png) +![Centered, fixed size](https://i.imgur.com/zWxTGRb.png) \ +*Centered, fixed size (default)* + +![Centered, dynamic size](https://i.imgur.com/YiPSZdI.png) \ +*Centered, dynamic size* + +![On the left (below the icon)](https://i.imgur.com/7M5x5EJ.png) \ +*On the left (below the icon)* + +![Full width](https://i.imgur.com/T7YjTfk.png) \ +*Full width* + +Labels can also be shown or hidden per-program in the settings. */ // ==/WindhawkModReadme== // ==WindhawkModSettings== /* +- mode: labelsWithoutCombining + $name: Mode + $description: >- + Note: When switching to or from the last two modes, restarting explorer + might be required to fully apply the new configuration + $options: + - labelsWithoutCombining: Show labels, don't combine taskbar buttons + - noLabelsWithCombining: Hide labels, combine taskbar buttons + - labelsWithCombining: Show labels, combine taskbar buttons + - noLabelsWithoutCombining: Hide labels, don't combine taskbar buttons - taskbarItemWidth: 160 $name: Taskbar item width $description: >- - Set to 0 to use the Windows adaptive width, set to -1 to hide labels, only - for newer Windows versions with the built-in taskbar labels implementation -- minimumTaskbarItemWidth: 50 - $name: Minimum taskbar item width - $description: >- - The minimum width before the taskbar overflows, only for newer Windows - versions with the built-in taskbar labels implementation - - Values larger than the Windows minimum width are unsupported and have no - effect -- maximumTaskbarItemWidth: 176 - $name: Maximum taskbar item width - $description: >- - The maximum width, only used for the Windows adaptive width + The width to use when labels are shown, set to 0 to use the Windows adaptive + width - runningIndicatorStyle: centerFixed $name: Running indicator style $options: @@ -84,6 +100,27 @@ choose one of the following running indicator styles: - sameAsRunningIndicatorStyle: Same as running indicator style - centerDynamic: Centered, dynamic size - fullWidth: Full width +- excludedPrograms: [excluded1.exe] + $name: Excluded programs + $description: >- + If the "Show labels, don't combine taskbar buttons" mode is used, labels + won't be shown for these programs + + If the "Hide labels, don't combine taskbar buttons" mode is used, labels + will be shown for these programs + + If another mode is used, this list is ignored +- minimumTaskbarItemWidth: 50 + $name: Minimum taskbar item width + $description: >- + The minimum width before the taskbar overflows + + Values larger than the Windows minimum width are unsupported and have no + effect +- maximumTaskbarItemWidth: 176 + $name: Maximum taskbar item width + $description: >- + The maximum width, only used for the Windows adaptive width - fontSize: 12 $name: Font size - leftAndRightPaddingSize: 8 @@ -115,9 +152,11 @@ choose one of the following running indicator styles: #include #include +#include #include #include #include +#include #include #include @@ -140,6 +179,13 @@ struct deleter_from_fn { using string_setting_unique_ptr = std::unique_ptr>; +enum class Mode { + labelsWithoutCombining, + noLabelsWithCombining, + labelsWithCombining, + noLabelsWithoutCombining, +}; + enum class IndicatorStyle { centerFixed, centerDynamic, @@ -148,11 +194,13 @@ enum class IndicatorStyle { }; struct { + Mode mode; int taskbarItemWidth; - int minimumTaskbarItemWidth; - int maximumTaskbarItemWidth; IndicatorStyle runningIndicatorStyle; IndicatorStyle progressIndicatorStyle; + std::unordered_set excludedPrograms; + int minimumTaskbarItemWidth; + int maximumTaskbarItemWidth; int fontSize; int leftAndRightPaddingSize; int spaceBetweenIconAndLabel; @@ -765,7 +813,6 @@ void UpdateTaskListButtonWithLabelStyle( } double taskListButtonWidth = taskListButtonElement.ActualWidth(); - double iconPanelWidth = iconPanelElement.ActualWidth(); double iconWidth = iconElement.ActualWidth(); auto columnDefinitions = @@ -791,6 +838,14 @@ void UpdateTaskListButtonWithLabelStyle( .as(); if (secondColumnWidthPixels > 0 && labelControlElement) { + // In labelsWithCombining mode, pinned items have labels too. Hide them. + if (g_settings.mode == Mode::labelsWithCombining && + !TaskListButton_IsRunning(taskListButtonElement)) { + secondColumnWidthPixels = 0; + labelControlElement.Visibility(Visibility::Collapsed); + labelControlElement = nullptr; + } + columnDefinitions.GetAt(1).Width(GridLength({ .Value = secondColumnWidthPixels, .GridUnitType = GridUnitType::Pixel, @@ -803,6 +858,8 @@ void UpdateTaskListButtonWithLabelStyle( } if (labelControlElement) { + labelControlElement.Visibility(Visibility::Visible); + auto horizontalAlignment = g_unloading ? HorizontalAlignment::Center : HorizontalAlignment::Left; if (labelControlElement.HorizontalAlignment() != horizontalAlignment) { @@ -853,10 +910,6 @@ void UpdateTaskListButtonWithLabelStyle( iconMargin.Right = 0; iconElement.Margin(iconMargin); - auto iconPanelMargin = iconPanelElement.Margin(); - double overflowWidth = - iconPanelMargin.Left + iconPanelWidth - taskListButtonWidth; - PCWSTR indicatorClassNames[] = { L"RunningIndicator", L"ProgressIndicator", @@ -894,23 +947,45 @@ void UpdateTaskListButtonWithLabelStyle( } } else if (indicatorStyle == IndicatorStyle::fullWidth) { minWidth = taskListButtonWidth - 6; + if (minWidth < 0) { + minWidth = 0; + } } - indicatorElement.MinWidth(minWidth); + // High values of maximumTaskbarItemWidth together with a fullWidth + // indicator can crash the process due to a refresh loop. Use this as a + // workaround. + if (g_settings.taskbarItemWidth == 0 && + indicatorStyle == IndicatorStyle::fullWidth) { + double currentMinWidth = indicatorElement.MinWidth(); + if (minWidth != currentMinWidth) { + indicatorElement.MinWidth(0); + indicatorElement.Dispatcher().TryRunAsync( + winrt::Windows::UI::Core::CoreDispatcherPriority::High, + [indicatorElement, minWidth]() { + indicatorElement.MinWidth(minWidth); + }); + } + } else { + indicatorElement.MinWidth(minWidth); + } auto indicatorMargin = indicatorElement.Margin(); - if (indicatorStyle == IndicatorStyle::left) { - indicatorMargin.Left = - (g_unloading || !labelControlElement) - ? 0 - : (iconWidth - 24 + - (g_settings.leftAndRightPaddingSize - 8) * 2); - indicatorMargin.Right = 0; - } else { - indicatorMargin.Left = 0; - indicatorMargin.Right = overflowWidth; + indicatorMargin.Left = 0; + indicatorMargin.Right = 0; + auto indicatorHorizontalAlignment = HorizontalAlignment::Stretch; + if (!g_unloading && labelControlElement) { + if (indicatorStyle == IndicatorStyle::left) { + indicatorMargin.Left = + iconWidth - 24 + + (g_settings.leftAndRightPaddingSize - 8) * 2; + } else { + indicatorMargin.Left = (taskListButtonWidth - minWidth) / 2 - 2; + indicatorHorizontalAlignment = HorizontalAlignment::Left; + } } indicatorElement.Margin(indicatorMargin); + indicatorElement.HorizontalAlignment(indicatorHorizontalAlignment); if (isProgressIndicator) { auto element = indicatorElement; @@ -1222,11 +1297,13 @@ DWORD WINAPI TaskbarSettings_GroupingMode_Hook(void* pThis) { DWORD ret = TaskbarSettings_GroupingMode_Original(pThis); if (!g_unloading) { - if (g_settings.taskbarItemWidth == -1) { - // Switch to "Always". + // 0 - Always + // 1 - When taskbar is full + // 2 - Never + if (g_settings.mode == Mode::noLabelsWithCombining || + g_settings.mode == Mode::labelsWithCombining) { ret = 0; } else if (ret == 0) { - // "Always" mode isn't supported, switch to "Never". ret = 2; } } @@ -1244,26 +1321,198 @@ DWORD WINAPI TaskbarSettings_GroupingMode_Hook(void* pThis) { using TaskListButton_MinScalableWidth_t = float(WINAPI*)(void* pThis); TaskListButton_MinScalableWidth_t TaskListButton_MinScalableWidth_Original; -float WINAPI TaskListButton_MinScalableWidth_Hook(void* pThis) { +// RDX is expected to be preserved. +[[clang::preserve_most]] +float TaskListButton_MinScalableWidth_Hook(void* pThis) { Wh_Log(L">"); float ret = TaskListButton_MinScalableWidth_Original(pThis); - if (!g_unloading && g_hasNativeLabelsImplementation) { + if (!g_unloading && g_hasNativeLabelsImplementation && ret > 0) { // Allow to create many taskbar items before overflow appears. int minimumTaskbarItemWidth = g_settings.minimumTaskbarItemWidth; if (minimumTaskbarItemWidth < 44) { minimumTaskbarItemWidth = 44; + Wh_Log(L"minimumTaskbarItemWidth too small, using %d", + minimumTaskbarItemWidth); } - if (ret > minimumTaskbarItemWidth) { + if (ret >= minimumTaskbarItemWidth) { ret = minimumTaskbarItemWidth; + } else { + Wh_Log(L"minimumTaskbarItemWidth too large, using default (%f)", + ret); } } return ret; } +bool g_inITaskbarAppItemViewModel_HasLabels; + +using ITaskbarAppItemViewModel_HasLabels_t = bool(WINAPI*)(void* pThis); +ITaskbarAppItemViewModel_HasLabels_t + ITaskbarAppItemViewModel_HasLabels_Original; +bool WINAPI ITaskbarAppItemViewModel_HasLabels_Hook(void* pThis) { + Wh_Log(L">"); + + g_inITaskbarAppItemViewModel_HasLabels = true; + + bool ret = ITaskbarAppItemViewModel_HasLabels_Original(pThis); + + g_inITaskbarAppItemViewModel_HasLabels = false; + + return ret; +} + +void* ITaskListWindowViewModel_vftable; + +using ITaskListWindowViewModel_get_TaskItem_t = + HRESULT(WINAPI*)(void* pThis, void** taskItem); +ITaskListWindowViewModel_get_TaskItem_t ITaskListWindowViewModel_get_TaskItem; + +using TaskListWindowViewModel_ITaskbarAppItemViewModel_get_HasLabel_t = + HRESULT(WINAPI*)(void* pThis, bool* hasLabels); +TaskListWindowViewModel_ITaskbarAppItemViewModel_get_HasLabel_t + TaskListWindowViewModel_ITaskbarAppItemViewModel_get_HasLabel_Original; +HRESULT WINAPI +TaskListWindowViewModel_ITaskbarAppItemViewModel_get_HasLabel_Hook( + void* pThis, + bool* hasLabels) { + Wh_Log(L">"); + + HRESULT ret = + TaskListWindowViewModel_ITaskbarAppItemViewModel_get_HasLabel_Original( + pThis, hasLabels); + if (g_unloading || !g_inITaskbarAppItemViewModel_HasLabels || FAILED(ret) || + !*hasLabels) { + return ret; + } + + bool hideLabels = false; + if (g_settings.mode == Mode::noLabelsWithoutCombining) { + hideLabels = true; + } + + if (!g_settings.excludedPrograms.empty() && + ITaskListWindowViewModel_vftable && + ITaskListWindowViewModel_get_TaskItem) { + PVOID pITaskListWindowViewModel = pThis; + while (*(PVOID*)pITaskListWindowViewModel != + ITaskListWindowViewModel_vftable) { + pITaskListWindowViewModel = (PVOID*)pITaskListWindowViewModel - 1; + } + + HWND hWnd = nullptr; + + winrt::com_ptr taskItem; + HRESULT hr = ITaskListWindowViewModel_get_TaskItem( + pITaskListWindowViewModel, taskItem.put_void()); + if (SUCCEEDED(hr) && taskItem) { + // public: virtual int __cdecl winrt::impl::produce::get_WindowId(unsigned + // __int64 *) + using ITaskItem_get_WindowId_t = + HRESULT(WINAPI*)(void* pThis, HWND* hWnd); + + void** vtable = *(void***)taskItem.get(); + auto ITaskItem_get_WindowId = (ITaskItem_get_WindowId_t)vtable[8]; + + hr = ITaskItem_get_WindowId(taskItem.get(), &hWnd); + } + + if (SUCCEEDED(hr) && hWnd) { + DWORD resolvedWindowProcessPathLen = 0; + WCHAR resolvedWindowProcessPath[MAX_PATH]; + WCHAR resolvedWindowProcessPathUpper[MAX_PATH]; + + DWORD dwProcessId = 0; + if (GetWindowThreadProcessId(hWnd, &dwProcessId)) { + HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, + FALSE, dwProcessId); + if (hProcess) { + DWORD dwSize = ARRAYSIZE(resolvedWindowProcessPath); + if (QueryFullProcessImageName( + hProcess, 0, resolvedWindowProcessPath, &dwSize)) { + resolvedWindowProcessPathLen = dwSize; + } + + CloseHandle(hProcess); + } + } + + if (resolvedWindowProcessPathLen > 0) { + LCMapStringEx( + LOCALE_NAME_USER_DEFAULT, LCMAP_UPPERCASE, + resolvedWindowProcessPath, resolvedWindowProcessPathLen + 1, + resolvedWindowProcessPathUpper, + resolvedWindowProcessPathLen + 1, nullptr, nullptr, 0); + } else { + *resolvedWindowProcessPath = L'\0'; + *resolvedWindowProcessPathUpper = L'\0'; + } + + bool excluded = false; + + if (!excluded && resolvedWindowProcessPathLen > 0 && + g_settings.excludedPrograms.contains( + resolvedWindowProcessPathUpper)) { + excluded = true; + } + + if (!excluded) { + if (PCWSTR programFileNameUpper = + wcsrchr(resolvedWindowProcessPathUpper, L'\\')) { + programFileNameUpper++; + if (*programFileNameUpper && + g_settings.excludedPrograms.contains( + programFileNameUpper)) { + excluded = true; + } + } + } + + if (excluded) { + Wh_Log(L"Excluding %s", resolvedWindowProcessPath); + hideLabels = !hideLabels; + } + } + } + + if (hideLabels) { + *hasLabels = false; + } + + return ret; +} + +using TaskListGroupViewModel_ITaskbarAppItemViewModel_get_HasLabel_t = + HRESULT(WINAPI*)(void* pThis, bool* hasLabels); +TaskListGroupViewModel_ITaskbarAppItemViewModel_get_HasLabel_t + TaskListGroupViewModel_ITaskbarAppItemViewModel_get_HasLabel_Original; +HRESULT WINAPI +TaskListGroupViewModel_ITaskbarAppItemViewModel_get_HasLabel_Hook( + void* pThis, + bool* hasLabels) { + Wh_Log(L">"); + + HRESULT ret = + TaskListGroupViewModel_ITaskbarAppItemViewModel_get_HasLabel_Original( + pThis, hasLabels); + if (g_unloading || !g_inITaskbarAppItemViewModel_HasLabels || FAILED(ret)) { + return ret; + } + + if (g_settings.mode == Mode::labelsWithCombining) { + *hasLabels = true; + } else if (g_settings.mode == Mode::noLabelsWithoutCombining) { + *hasLabels = false; + } + + return ret; +} + void* wil_Feature_GetImpl_Original; using WilFeatureTraits_Feature_29785186_IsEnabled_t = @@ -1272,11 +1521,38 @@ WilFeatureTraits_Feature_29785186_IsEnabled_t WilFeatureTraits_Feature_29785186_IsEnabled_Original; void LoadSettings() { + PCWSTR mode = Wh_GetStringSetting(L"mode"); + g_settings.mode = Mode::labelsWithoutCombining; + if (wcscmp(mode, L"noLabelsWithCombining") == 0) { + g_settings.mode = Mode::noLabelsWithCombining; + } else if (wcscmp(mode, L"labelsWithCombining") == 0) { + g_settings.mode = Mode::labelsWithCombining; + } else if (wcscmp(mode, L"noLabelsWithoutCombining") == 0) { + g_settings.mode = Mode::noLabelsWithoutCombining; + } + Wh_FreeStringSetting(mode); + g_settings.taskbarItemWidth = Wh_GetIntSetting(L"taskbarItemWidth"); - g_settings.minimumTaskbarItemWidth = - Wh_GetIntSetting(L"minimumTaskbarItemWidth"); - g_settings.maximumTaskbarItemWidth = - Wh_GetIntSetting(L"maximumTaskbarItemWidth"); + + // For compatibility - in previous versions, width -1 was documented to hide + // labels. + if (g_settings.taskbarItemWidth == -1) { + switch (g_settings.mode) { + case Mode::labelsWithoutCombining: + g_settings.mode = Mode::noLabelsWithoutCombining; + break; + + case Mode::labelsWithCombining: + g_settings.mode = Mode::noLabelsWithCombining; + break; + + case Mode::noLabelsWithCombining: + case Mode::noLabelsWithoutCombining: + break; + } + + g_settings.taskbarItemWidth = 160; + } PCWSTR runningIndicatorStyle = Wh_GetStringSetting(L"runningIndicatorStyle"); @@ -1300,6 +1576,34 @@ void LoadSettings() { } Wh_FreeStringSetting(progressIndicatorStyle); + g_settings.excludedPrograms.clear(); + + for (int i = 0;; i++) { + PCWSTR program = Wh_GetStringSetting(L"excludedPrograms[%d]", i); + + bool hasProgram = *program; + if (hasProgram) { + std::wstring programUpper = program; + LCMapStringEx( + LOCALE_NAME_USER_DEFAULT, LCMAP_UPPERCASE, &programUpper[0], + static_cast(programUpper.length()), &programUpper[0], + static_cast(programUpper.length()), nullptr, nullptr, 0); + + g_settings.excludedPrograms.insert(std::move(programUpper)); + } + + Wh_FreeStringSetting(program); + + if (!hasProgram) { + break; + } + } + + g_settings.minimumTaskbarItemWidth = + Wh_GetIntSetting(L"minimumTaskbarItemWidth"); + g_settings.maximumTaskbarItemWidth = + Wh_GetIntSetting(L"maximumTaskbarItemWidth"); + g_settings.fontSize = Wh_GetIntSetting(L"fontSize"); if (g_settings.fontSize < 1) { g_settings.fontSize = 1; @@ -1344,7 +1648,8 @@ struct SYMBOL_HOOK { bool HookSymbols(HMODULE module, const SYMBOL_HOOK* symbolHooks, - size_t symbolHooksCount) { + size_t symbolHooksCount, + bool cacheOnly = false) { const WCHAR cacheVer = L'1'; const WCHAR cacheSep = L'#'; constexpr size_t cacheMaxSize = 10240; @@ -1486,7 +1791,14 @@ bool HookSymbols(HMODULE module, if (noAddressMatchCount == symbolHooks[i].symbols.size()) { Wh_Log(L"Optional symbol %d doesn't exist (from cache)", i); + symbolResolved[i] = true; + + for (auto hookSymbol : symbolHooks[i].symbols) { + newSystemCacheStr += cacheSep; + newSystemCacheStr += hookSymbol; + newSystemCacheStr += cacheSep; + } } } @@ -1498,6 +1810,10 @@ bool HookSymbols(HMODULE module, Wh_Log(L"Couldn't resolve all symbols from cache"); + if (cacheOnly) { + return false; + } + WH_FIND_SYMBOL findSymbol; HANDLE findSymbolHandle = Wh_FindFirstSymbol(module, nullptr, &findSymbol); if (!findSymbolHandle) { @@ -1615,11 +1931,12 @@ bool HookSymbolsWithOnlineCacheFallback(HMODULE module, size_t symbolHooksCount) { constexpr WCHAR kModIdForCache[] = L"taskbar-labels"; - if (HookSymbols(module, symbolHooks, symbolHooksCount)) { + if (HookSymbols(module, symbolHooks, symbolHooksCount, + /*cacheOnly=*/true)) { return true; } - Wh_Log(L"HookSymbols() failed, trying to get an online cache"); + Wh_Log(L"HookSymbols() from cache failed, trying to get an online cache"); WCHAR moduleFilePath[MAX_PATH]; DWORD moduleFilePathLen = @@ -1674,13 +1991,12 @@ bool HookSymbolsWithOnlineCacheFallback(HMODULE module, Wh_Log(L"Looking for an online cache at %s", onlineCacheUrl.c_str()); auto onlineCache = GetUrlContent(onlineCacheUrl.c_str()); - if (!onlineCache) { + if (onlineCache) { + Wh_SetStringValue(cacheStrKey.c_str(), onlineCache->c_str()); + } else { Wh_Log(L"Failed to get online cache"); - return false; } - Wh_SetStringValue(cacheStrKey.c_str(), onlineCache->c_str()); - return HookSymbols(module, symbolHooks, symbolHooksCount); } @@ -1714,97 +2030,145 @@ bool GetTaskbarViewDllPath(WCHAR path[MAX_PATH]) { } bool HookTaskbarViewDllSymbols(HMODULE module) { - SYMBOL_HOOK symbolHooks[] = { + // Taskbar.View.dll, ExplorerExtensions.dll + SYMBOL_HOOK symbolHooks[] = // { { - LR"(public: virtual int __cdecl winrt::impl::produce::get_IsRunning(bool *))", - LR"(public: virtual int __cdecl winrt::impl::produce::get_IsRunning(bool * __ptr64) __ptr64)", + { + LR"(public: virtual int __cdecl winrt::impl::produce::get_IsRunning(bool *))", + LR"(public: virtual int __cdecl winrt::impl::produce::get_IsRunning(bool * __ptr64) __ptr64)", + }, + (void**)&TaskListButton_get_IsRunning_Original, }, - (void**)&TaskListButton_get_IsRunning_Original, - }, - { { - LR"(private: void __cdecl winrt::Taskbar::implementation::TaskListButton::UpdateVisualStates(void))", - LR"(private: void __cdecl winrt::Taskbar::implementation::TaskListButton::UpdateVisualStates(void) __ptr64)", + { + LR"(private: void __cdecl winrt::Taskbar::implementation::TaskListButton::UpdateVisualStates(void))", + LR"(private: void __cdecl winrt::Taskbar::implementation::TaskListButton::UpdateVisualStates(void) __ptr64)", + }, + (void**)&TaskListButton_UpdateVisualStates_Original, + (void*)TaskListButton_UpdateVisualStates_Hook, }, - (void**)&TaskListButton_UpdateVisualStates_Original, - (void*)TaskListButton_UpdateVisualStates_Hook, - }, - { { - LR"(private: void __cdecl winrt::Taskbar::implementation::TaskListButton::UpdateButtonPadding(void))", - LR"(private: void __cdecl winrt::Taskbar::implementation::TaskListButton::UpdateButtonPadding(void) __ptr64)", + { + LR"(private: void __cdecl winrt::Taskbar::implementation::TaskListButton::UpdateButtonPadding(void))", + LR"(private: void __cdecl winrt::Taskbar::implementation::TaskListButton::UpdateButtonPadding(void) __ptr64)", + }, + (void**)&TaskListButton_UpdateButtonPadding_Original, + (void*)TaskListButton_UpdateButtonPadding_Hook, }, - (void**)&TaskListButton_UpdateButtonPadding_Original, - (void*)TaskListButton_UpdateButtonPadding_Hook, - }, - { { - LR"(private: void __cdecl winrt::Taskbar::implementation::TaskListButton::UpdateBadgeSize(void))", - LR"(private: void __cdecl winrt::Taskbar::implementation::TaskListButton::UpdateBadgeSize(void) __ptr64)", + { + LR"(private: void __cdecl winrt::Taskbar::implementation::TaskListButton::UpdateBadgeSize(void))", + LR"(private: void __cdecl winrt::Taskbar::implementation::TaskListButton::UpdateBadgeSize(void) __ptr64)", + }, + (void**)&TaskListButton_UpdateBadgeSize_Original, + (void*)TaskListButton_UpdateBadgeSize_Hook, }, - (void**)&TaskListButton_UpdateBadgeSize_Original, - (void*)TaskListButton_UpdateBadgeSize_Hook, - }, - { { - LR"(private: void __cdecl winrt::Taskbar::implementation::TaskbarFrame::OnTaskbarLayoutChildBoundsChanged(void))", - LR"(private: void __cdecl winrt::Taskbar::implementation::TaskbarFrame::OnTaskbarLayoutChildBoundsChanged(void) __ptr64)", + { + LR"(private: void __cdecl winrt::Taskbar::implementation::TaskbarFrame::OnTaskbarLayoutChildBoundsChanged(void))", + LR"(private: void __cdecl winrt::Taskbar::implementation::TaskbarFrame::OnTaskbarLayoutChildBoundsChanged(void) __ptr64)", + }, + (void**)&TaskbarFrame_OnTaskbarLayoutChildBoundsChanged_Original, + (void*)TaskbarFrame_OnTaskbarLayoutChildBoundsChanged_Hook, }, - (void**)&TaskbarFrame_OnTaskbarLayoutChildBoundsChanged_Original, - (void*)TaskbarFrame_OnTaskbarLayoutChildBoundsChanged_Hook, - }, - { { - LR"(public: void __cdecl winrt::Taskbar::implementation::TaskListButton::Icon(struct winrt::Windows::Storage::Streams::IRandomAccessStream))", - LR"(public: void __cdecl winrt::Taskbar::implementation::TaskListButton::Icon(struct winrt::Windows::Storage::Streams::IRandomAccessStream) __ptr64)", + { + LR"(public: void __cdecl winrt::Taskbar::implementation::TaskListButton::Icon(struct winrt::Windows::Storage::Streams::IRandomAccessStream))", + LR"(public: void __cdecl winrt::Taskbar::implementation::TaskListButton::Icon(struct winrt::Windows::Storage::Streams::IRandomAccessStream) __ptr64)", + }, + (void**)&TaskListButton_Icon_Original, + (void*)TaskListButton_Icon_Hook, }, - (void**)&TaskListButton_Icon_Original, - (void*)TaskListButton_Icon_Hook, - }, - { { - LR"(public: __cdecl winrt::impl::consume_WindowsUdk_UI_Shell_ITaskbarSettings5::GroupingMode(void)const )", - LR"(public: __cdecl winrt::impl::consume_WindowsUdk_UI_Shell_ITaskbarSettings5::GroupingMode(void)const __ptr64)", + { + LR"(public: __cdecl winrt::impl::consume_WindowsUdk_UI_Shell_ITaskbarSettings5::GroupingMode(void)const )", + LR"(public: __cdecl winrt::impl::consume_WindowsUdk_UI_Shell_ITaskbarSettings5::GroupingMode(void)const __ptr64)", + }, + (void**)&TaskbarSettings_GroupingMode_Original, + (void*)TaskbarSettings_GroupingMode_Hook, + true, }, - (void**)&TaskbarSettings_GroupingMode_Original, - (void*)TaskbarSettings_GroupingMode_Hook, - true, - }, - { { - LR"(public: float __cdecl winrt::Taskbar::implementation::TaskListButton::MinScalableWidth(void))", - LR"(public: float __cdecl winrt::Taskbar::implementation::TaskListButton::MinScalableWidth(void) __ptr64)", + { + LR"(public: float __cdecl winrt::Taskbar::implementation::TaskListButton::MinScalableWidth(void))", + LR"(public: float __cdecl winrt::Taskbar::implementation::TaskListButton::MinScalableWidth(void) __ptr64)", + }, + (void**)&TaskListButton_MinScalableWidth_Original, + (void*)TaskListButton_MinScalableWidth_Hook, + true, }, - (void**)&TaskListButton_MinScalableWidth_Original, - (void*)TaskListButton_MinScalableWidth_Hook, - true, - }, - { { - LR"(class wil::details::FeatureImpl `private: static class wil::details::FeatureImpl & __cdecl wil::Feature::GetImpl(void)'::`2'::impl)", - - // Symbol before update KB5036980: - LR"(class wil::details::FeatureImpl `private: static class wil::details::FeatureImpl & __cdecl wil::Feature::GetImpl(void)'::`2'::impl)", + { + LR"(public: __cdecl winrt::impl::consume_Taskbar_ITaskbarAppItemViewModel::HasLabel(void)const )", + LR"(public: __cdecl winrt::impl::consume_Taskbar_ITaskbarAppItemViewModel::HasLabel(void)const __ptr64)", + }, + (void**)&ITaskbarAppItemViewModel_HasLabels_Original, + (void*)ITaskbarAppItemViewModel_HasLabels_Hook, + true, }, - (void**)&wil_Feature_GetImpl_Original, - nullptr, - true, - }, - { { - LR"(public: bool __cdecl wil::details::FeatureImpl::__private_IsEnabled(enum wil::ReportingKind))", - LR"(public: bool __cdecl wil::details::FeatureImpl::__private_IsEnabled(enum wil::ReportingKind) __ptr64)", - - // Symbols before update KB5036980: - LR"(public: bool __cdecl wil::details::FeatureImpl::__private_IsEnabled(enum wil::ReportingKind))", - LR"(public: bool __cdecl wil::details::FeatureImpl::__private_IsEnabled(enum wil::ReportingKind) __ptr64)", + { + LR"(const winrt::impl::produce::`vftable')", + }, + (void**)&ITaskListWindowViewModel_vftable, + nullptr, + true, }, - (void**)&WilFeatureTraits_Feature_29785186_IsEnabled_Original, - nullptr, - true, - }, - }; + { + { + LR"(public: virtual int __cdecl winrt::impl::produce::get_TaskItem(void * *))", + LR"(public: virtual int __cdecl winrt::impl::produce::get_TaskItem(void * __ptr64 * __ptr64) __ptr64)", + }, + (void**)&ITaskListWindowViewModel_get_TaskItem, + nullptr, + true, + }, + { + { + LR"(public: virtual int __cdecl winrt::impl::produce::get_HasLabel(bool *))", + LR"(public: virtual int __cdecl winrt::impl::produce::get_HasLabel(bool * __ptr64) __ptr64)", + }, + (void**)&TaskListWindowViewModel_ITaskbarAppItemViewModel_get_HasLabel_Original, + (void*) + TaskListWindowViewModel_ITaskbarAppItemViewModel_get_HasLabel_Hook, + true, + }, + { + { + LR"(public: virtual int __cdecl winrt::impl::produce::get_HasLabel(bool *))", + LR"(public: virtual int __cdecl winrt::impl::produce::get_HasLabel(bool * __ptr64) __ptr64)", + }, + (void**)&TaskListGroupViewModel_ITaskbarAppItemViewModel_get_HasLabel_Original, + (void*) + TaskListGroupViewModel_ITaskbarAppItemViewModel_get_HasLabel_Hook, + true, + }, + { + { + LR"(class wil::details::FeatureImpl `private: static class wil::details::FeatureImpl & __cdecl wil::Feature::GetImpl(void)'::`2'::impl)", + + // Symbol before update KB5036980: + LR"(class wil::details::FeatureImpl `private: static class wil::details::FeatureImpl & __cdecl wil::Feature::GetImpl(void)'::`2'::impl)", + }, + (void**)&wil_Feature_GetImpl_Original, + nullptr, + true, + }, + { + { + LR"(public: bool __cdecl wil::details::FeatureImpl::__private_IsEnabled(enum wil::ReportingKind))", + LR"(public: bool __cdecl wil::details::FeatureImpl::__private_IsEnabled(enum wil::ReportingKind) __ptr64)", + + // Symbols before update KB5036980: + LR"(public: bool __cdecl wil::details::FeatureImpl::__private_IsEnabled(enum wil::ReportingKind))", + LR"(public: bool __cdecl wil::details::FeatureImpl::__private_IsEnabled(enum wil::ReportingKind) __ptr64)", + }, + (void**)&WilFeatureTraits_Feature_29785186_IsEnabled_Original, + nullptr, + true, + }, + }; return HookSymbolsWithOnlineCacheFallback(module, symbolHooks, ARRAYSIZE(symbolHooks)); @@ -1817,7 +2181,7 @@ bool HookTaskbarDllSymbols() { return false; } - SYMBOL_HOOK symbolHooks[] = { + SYMBOL_HOOK taskbarDllHooks[] = { { { LR"(protected: struct ITaskBtnGroup * __cdecl CTaskListWnd::_GetTBGroupFromGroup(struct ITaskGroup *,int *))", @@ -1884,8 +2248,8 @@ bool HookTaskbarDllSymbols() { }, }; - return HookSymbolsWithOnlineCacheFallback(module, symbolHooks, - ARRAYSIZE(symbolHooks)); + return HookSymbolsWithOnlineCacheFallback(module, taskbarDllHooks, + ARRAYSIZE(taskbarDllHooks)); } BOOL ModInitWithTaskbarView(HMODULE taskbarViewModule) { From 3065efada7b6b5dd413a165287b76560ff0c0210 Mon Sep 17 00:00:00 2001 From: Michael Maltsev <4129781+m417z@users.noreply.github.com> Date: Sat, 28 Sep 2024 21:55:48 +0300 Subject: [PATCH 34/52] Vertical Taskbar for Windows 11 v1.2.3 (#1003) * Fixed rotated badges (e.g. unread counters) for some programs. * Fixed the edge of the taskbar being non-clickable in some cases. * Fixed the taskbar not showing up at startup in some cases. * Fixed the tray icons flyout arrow being reversed when the taskbar is on the right. * Fixed taskbar overflow flyout opening on the left of the monitor when the taskbar is on the right. --- mods/taskbar-vertical.wh.cpp | 191 ++++++++++++++++++++++++++++++----- 1 file changed, 167 insertions(+), 24 deletions(-) diff --git a/mods/taskbar-vertical.wh.cpp b/mods/taskbar-vertical.wh.cpp index 5ced3ba4..9f1123bf 100644 --- a/mods/taskbar-vertical.wh.cpp +++ b/mods/taskbar-vertical.wh.cpp @@ -2,7 +2,7 @@ // @id taskbar-vertical // @name Vertical Taskbar for Windows 11 // @description Finally, the missing vertical taskbar option for Windows 11! -// @version 1.2.2 +// @version 1.2.3 // @author m417z // @github https://github.com/m417z // @twitter https://twitter.com/m417z @@ -13,7 +13,7 @@ // @include ShellExperienceHost.exe // @include ShellHost.exe // @architecture x86-64 -// @compilerOptions -DWINVER=0x0605 -lole32 -loleaut32 -lruntimeobject -lshcore +// @compilerOptions -DWINVER=0x0A00 -lole32 -loleaut32 -lruntimeobject -lshcore // ==/WindhawkMod== // Source code is published under The GNU General Public License v3.0. @@ -66,6 +66,9 @@ With labels: $name: Taskbar width $description: >- The width, in pixels, of the taskbar + + Note: If the clock is too wide for the taskbar width you prefer, you can use + the "Taskbar Clock Customization" mod to customize the taskbar clock format - taskbarLocationSecondary: sameAsPrimary $name: Taskbar location on secondary monitors $options: @@ -142,6 +145,7 @@ bool g_inAugmentedEntryPointButton_UpdateButtonPadding; bool g_inCTaskListThumbnailWnd_DisplayUI; bool g_inCTaskListThumbnailWnd_LayoutThumbnails; bool g_inChevronSystemTrayIconDataModel2_OnIconClicked; +bool g_inOverflowFlyoutModel_Show; std::vector> g_notifyIconsUpdated; @@ -538,8 +542,9 @@ LRESULT TaskbarWndProcPostProcess(HWND hWnd, break; } + case WM_PAINT: case WM_ERASEBKGND: { - Wh_Log(L"WM_ERASEBKGND: %08X", (DWORD)(ULONG_PTR)hWnd); + Wh_Log(L"%04X: %08X", Msg, (DWORD)(ULONG_PTR)hWnd); // Calling CreateRectRgn posts window size change events which cause // element sizes and positions to be recalculated. @@ -702,6 +707,56 @@ void WINAPI CTaskListThumbnailWnd_LayoutThumbnails_Hook(void* pThis) { g_inCTaskListThumbnailWnd_LayoutThumbnails = false; } +using XamlExplorerHostWindow_XamlExplorerHostWindow_t = + void*(WINAPI*)(void* pThis, + unsigned int param1, + winrt::Windows::Foundation::Rect* rect, + unsigned int param3); +XamlExplorerHostWindow_XamlExplorerHostWindow_t + XamlExplorerHostWindow_XamlExplorerHostWindow_Original; +void* WINAPI XamlExplorerHostWindow_XamlExplorerHostWindow_Hook( + void* pThis, + unsigned int param1, + winrt::Windows::Foundation::Rect* rect, + unsigned int param3) { + Wh_Log(L">"); + + if (g_inOverflowFlyoutModel_Show) { + RECT rc{ + .left = static_cast(rect->X), + .top = static_cast(rect->Y), + .right = static_cast(rect->X + rect->Width), + .bottom = static_cast(rect->Y + rect->Height), + }; + + HMONITOR monitor = MonitorFromRect(&rc, MONITOR_DEFAULTTONEAREST); + + MONITORINFO monitorInfo{ + .cbSize = sizeof(MONITORINFO), + }; + GetMonitorInfo(monitor, &monitorInfo); + + winrt::Windows::Foundation::Rect rectNew = *rect; + rectNew.Width = 72; + + switch (GetTaskbarLocationForMonitor(monitor)) { + case TaskbarLocation::left: + rectNew.X = monitorInfo.rcWork.left; + break; + + case TaskbarLocation::right: + rectNew.X = monitorInfo.rcWork.right - rectNew.Width; + break; + } + + return XamlExplorerHostWindow_XamlExplorerHostWindow_Original( + pThis, param1, &rectNew, param3); + } + + return XamlExplorerHostWindow_XamlExplorerHostWindow_Original(pThis, param1, + rect, param3); +} + using ResourceDictionary_Lookup_t = winrt::Windows::Foundation::IInspectable*( WINAPI*)(void* pThis, void** result, @@ -966,6 +1021,11 @@ bool ApplyStyle(FrameworkElement taskbarFrame, if (marginValue > 0) { margin.Top = marginValue; } + + // Fix the edge of the taskbar being non-clickable by moving the edge + // pixel out of the screen. + margin.Top += 1; + margin.Bottom -= 1; } FrameworkElement child = contentGrid; @@ -1283,6 +1343,37 @@ void ApplySystemTrayIconStyle(FrameworkElement systemTrayIconElement) { } } +void ApplySystemTrayChevronIconViewStyle( + FrameworkElement systemTrayChevronIconViewElement) { + if (g_settings.taskbarLocation != TaskbarLocation::right) { + return; + } + + FrameworkElement baseTextBlock = nullptr; + + FrameworkElement child = systemTrayChevronIconViewElement; + if ((child = FindChildByName(child, L"ContainerGrid")) && + (child = FindChildByName(child, L"ContentPresenter")) && + (child = FindChildByName(child, L"ContentGrid")) && + (child = FindChildByClassName(child, L"SystemTray.TextIconContent")) && + (child = FindChildByName(child, L"ContainerGrid")) && + (child = FindChildByName(child, L"Base"))) { + baseTextBlock = child; + } + + if (!baseTextBlock) { + return; + } + + double angle = g_unloading ? 0 : 180; + Media::RotateTransform transform; + transform.Angle(angle); + baseTextBlock.RenderTransform(transform); + + float origin = g_unloading ? 0 : 0.5; + baseTextBlock.RenderTransformOrigin({origin, origin}); +} + using IconView_IconView_t = void(WINAPI*)(PVOID pThis); IconView_IconView_t IconView_IconView_Original; void WINAPI IconView_IconView_Hook(PVOID pThis) { @@ -1332,6 +1423,10 @@ void WINAPI IconView_IconView_Hook(PVOID pThis) { L"NotificationCenterButton"))) { ApplySystemTrayIconStyle(iconView); } + } else if (className == L"SystemTray.ChevronIconView") { + if (IsChildOfElementByName(iconView, L"NotifyIconStack")) { + ApplySystemTrayChevronIconViewStyle(iconView); + } } }); } @@ -1452,8 +1547,9 @@ bool UpdateNotifyIconsIfNeeded(XamlRoot xamlRoot) { } for (PCWSTR containerName : - {L"MainStack", L"NonActivatableStack", L"SecondaryClockStack", - L"ControlCenterButton", L"NotificationCenterButton"}) { + {L"NotifyIconStack", L"MainStack", L"NonActivatableStack", + L"SecondaryClockStack", L"ControlCenterButton", + L"NotificationCenterButton"}) { FrameworkElement container = FindChildByName(systemTrayFrameGrid, containerName); if (!container) { @@ -1490,7 +1586,7 @@ bool UpdateNotifyIconsIfNeeded(XamlRoot xamlRoot) { continue; } - EnumChildElements(stackPanel, [](FrameworkElement child) { + EnumChildElements(stackPanel, [containerName](FrameworkElement child) { auto childClassName = winrt::get_class_name(child); if (childClassName != L"Windows.UI.Xaml.Controls.ContentPresenter") { @@ -1499,14 +1595,28 @@ bool UpdateNotifyIconsIfNeeded(XamlRoot xamlRoot) { return false; } - FrameworkElement systemTrayIconElement = - FindChildByName(child, L"SystemTrayIcon"); - if (!systemTrayIconElement) { - Wh_Log(L"Failed to get SystemTrayIcon of child"); - return false; + if (wcscmp(containerName, L"NotifyIconStack") == 0) { + FrameworkElement systemTrayChevronIconViewElement = + FindChildByClassName(child, L"SystemTray.ChevronIconView"); + if (!systemTrayChevronIconViewElement) { + Wh_Log( + L"Failed to get SystemTray.ChevronIconView of child"); + return false; + } + + ApplySystemTrayChevronIconViewStyle( + systemTrayChevronIconViewElement); + } else { + FrameworkElement systemTrayIconElement = + FindChildByName(child, L"SystemTrayIcon"); + if (!systemTrayIconElement) { + Wh_Log(L"Failed to get SystemTrayIcon of child"); + return false; + } + + ApplySystemTrayIconStyle(systemTrayIconElement); } - ApplySystemTrayIconStyle(systemTrayIconElement); return false; }); } @@ -1572,21 +1682,28 @@ void UpdateTaskListButton(FrameworkElement taskListButtonElement) { } iconElement.Margin(margin); - auto overlayIconElement = FindChildByName(iconPanelElement, L"OverlayIcon"); - if (overlayIconElement) { - double angle = g_unloading ? 0 : -90; - Media::RotateTransform transform; - transform.Angle(angle); - overlayIconElement.RenderTransform(transform); + for (PCWSTR badgeElementName : { + // Badge for non-UWP apps. + L"OverlayIcon", + // Badge for UWP apps. + L"BadgeControl", + }) { + auto badgeElement = FindChildByName(iconPanelElement, badgeElementName); + if (badgeElement) { + double angle = g_unloading ? 0 : -90; + Media::RotateTransform transform; + transform.Angle(angle); + badgeElement.RenderTransform(transform); - winrt::Windows::Foundation::Point origin{}; - if (!g_unloading) { - origin.Y = labelControlElement ? 1.25 : 0.75; - } + winrt::Windows::Foundation::Point origin{}; + if (!g_unloading) { + origin.Y = labelControlElement ? 1.25 : 0.75; + } - overlayIconElement.RenderTransformOrigin(origin); + badgeElement.RenderTransformOrigin(origin); - overlayIconElement.Margin(margin); + badgeElement.Margin(margin); + } } } @@ -1988,6 +2105,18 @@ void WINAPI CopilotIcon_ToggleEdgeCopilot_Hook(void* pThis) { }); } +using OverflowFlyoutModel_Show_t = void(WINAPI*)(void* pThis); +OverflowFlyoutModel_Show_t OverflowFlyoutModel_Show_Original; +void WINAPI OverflowFlyoutModel_Show_Hook(void* pThis) { + Wh_Log(L">"); + + g_inOverflowFlyoutModel_Show = true; + + OverflowFlyoutModel_Show_Original(pThis); + + g_inOverflowFlyoutModel_Show = false; +} + BOOL WINAPI GetWindowRect_Hook(HWND hWnd, LPRECT lpRect) { BOOL ret = GetWindowRect_Original(hWnd, lpRect); if (ret && !g_unloading && @@ -2731,6 +2860,13 @@ bool HookTaskbarViewDllSymbols(HMODULE module) { (void**)&CopilotIcon_ToggleEdgeCopilot_Original, (void*)CopilotIcon_ToggleEdgeCopilot_Hook, }, + { + { + LR"(public: void __cdecl winrt::Taskbar::implementation::OverflowFlyoutModel::Show(void))", + }, + (void**)&OverflowFlyoutModel_Show_Original, + (void*)OverflowFlyoutModel_Show_Hook, + }, }; if (!HookSymbols(module, symbolHooks, ARRAYSIZE(symbolHooks))) { @@ -2832,6 +2968,13 @@ bool HookTaskbarDllSymbols() { (void**)&CTaskListThumbnailWnd_LayoutThumbnails_Original, (void*)CTaskListThumbnailWnd_LayoutThumbnails_Hook, }, + { + { + LR"(public: __cdecl winrt::Windows::Internal::Shell::XamlExplorerHost::XamlExplorerHostWindow::XamlExplorerHostWindow(unsigned int,struct winrt::Windows::Foundation::Rect const &,unsigned int))", + }, + (void**)&XamlExplorerHostWindow_XamlExplorerHostWindow_Original, + (void*)XamlExplorerHostWindow_XamlExplorerHostWindow_Hook, + }, }; return HookSymbols(module, taskbarDllHooks, ARRAYSIZE(taskbarDllHooks)); From 8feb509bfbeb74b97aa1f3cd2c36b67b40efad29 Mon Sep 17 00:00:00 2001 From: Michael Maltsev <4129781+m417z@users.noreply.github.com> Date: Sun, 29 Sep 2024 00:01:44 +0300 Subject: [PATCH 35/52] Taskbar Background Helper v1.0 (#1005) --- mods/taskbar-background-helper.wh.cpp | 520 ++++++++++++++++++++++++++ 1 file changed, 520 insertions(+) create mode 100644 mods/taskbar-background-helper.wh.cpp diff --git a/mods/taskbar-background-helper.wh.cpp b/mods/taskbar-background-helper.wh.cpp new file mode 100644 index 00000000..bdf45092 --- /dev/null +++ b/mods/taskbar-background-helper.wh.cpp @@ -0,0 +1,520 @@ +// ==WindhawkMod== +// @id taskbar-background-helper +// @name Taskbar Background Helper +// @description Sets the taskbar background for the transparent parts, always or only when there's a maximized window, designed to be used with Windows 11 Taskbar Styler +// @version 1.0 +// @author m417z +// @github https://github.com/m417z +// @twitter https://twitter.com/m417z +// @homepage https://m417z.com/ +// @include explorer.exe +// @architecture x86-64 +// ==/WindhawkMod== + +// Source code is published under The GNU General Public License v3.0. +// +// For bug reports and feature requests, please open an issue here: +// https://github.com/ramensoftware/windhawk-mods/issues +// +// For pull requests, development takes place here: +// https://github.com/m417z/my-windhawk-mods + +// ==WindhawkModReadme== +/* +# Taskbar Background Helper + +Sets the taskbar background for the transparent parts, always or only when +there's a maximized window, designed to be used with [Windows 11 Taskbar +Styler](https://windhawk.net/mods/windows-11-taskbar-styler). + +Also, Windows 11 Taskbar Styler has [a known +limitation](https://github.com/ramensoftware/windhawk-mods/issues/742) which +makes some styles only work if there's a single monitor. This mod can be used as +a workaround. + +![Demonstration](https://i.imgur.com/lMp8OLp.gif) +*/ +// ==/WindhawkModReadme== + +// ==WindhawkModSettings== +/* +- backgroundStyle: blur + $name: Background style + $options: + - blur: Blur + - acrylicBlur: Acrylic blur + - color: Color +- color: + - red: 255 + - green: 127 + - blue: 39 + - transparency: 128 + $name: Custom color + $description: Values are between 0 and 255 +- onlyWhenMaximized: true + $name: Only when maximized + $description: >- + Only apply the style when there's a maximized window on the monitor +*/ +// ==/WindhawkModSettings== + +#include + +enum class BackgroundStyle { + blur, + acrylicBlur, + color, +}; + +struct { + BackgroundStyle backgroundStyle; + COLORREF color; + bool onlyWhenMaximized; +} g_settings; + +HANDLE g_winObjectLocationChangeThread; +std::unordered_set g_pendingMonitors; +UINT_PTR g_pendingMonitorsTimer; + +enum WINDOWCOMPOSITIONATTRIB { + WCA_UNDEFINED = 0, + WCA_NCRENDERING_ENABLED = 1, + WCA_NCRENDERING_POLICY = 2, + WCA_TRANSITIONS_FORCEDISABLED = 3, + WCA_ALLOW_NCPAINT = 4, + WCA_CAPTION_BUTTON_BOUNDS = 5, + WCA_NONCLIENT_RTL_LAYOUT = 6, + WCA_FORCE_ICONIC_REPRESENTATION = 7, + WCA_EXTENDED_FRAME_BOUNDS = 8, + WCA_HAS_ICONIC_BITMAP = 9, + WCA_THEME_ATTRIBUTES = 10, + WCA_NCRENDERING_EXILED = 11, + WCA_NCADORNMENTINFO = 12, + WCA_EXCLUDED_FROM_LIVEPREVIEW = 13, + WCA_VIDEO_OVERLAY_ACTIVE = 14, + WCA_FORCE_ACTIVEWINDOW_APPEARANCE = 15, + WCA_DISALLOW_PEEK = 16, + WCA_CLOAK = 17, + WCA_CLOAKED = 18, + WCA_ACCENT_POLICY = 19, + WCA_FREEZE_REPRESENTATION = 20, + WCA_EVER_UNCLOAKED = 21, + WCA_VISUAL_OWNER = 22, + WCA_HOLOGRAPHIC = 23, + WCA_EXCLUDED_FROM_DDA = 24, + WCA_PASSIVEUPDATEMODE = 25, + WCA_USEDARKMODECOLORS = 26, + WCA_CORNER_STYLE = 27, + WCA_PART_COLOR = 28, + WCA_DISABLE_MOVESIZE_FEEDBACK = 29, + WCA_LAST = 30 +}; + +// Affects the rendering of the background of a window. +enum ACCENT_STATE { + // Default value. Background is black. + ACCENT_DISABLED = 0, + // Background is GradientColor, alpha channel ignored. + ACCENT_ENABLE_GRADIENT = 1, + // Background is GradientColor. + ACCENT_ENABLE_TRANSPARENTGRADIENT = 2, + // Background is GradientColor, with blur effect. + ACCENT_ENABLE_BLURBEHIND = 3, + // Background is GradientColor, with acrylic blur effect. + ACCENT_ENABLE_ACRYLICBLURBEHIND = 4, + // Allows desktop apps to use Compositor.CreateHostBackdropBrush + ACCENT_ENABLE_HOSTBACKDROP = 5, + // Unknown. Seems to draw background fully transparent. + ACCENT_INVALID_STATE = 6, +}; + +struct ACCENTPOLICY { + ACCENT_STATE accentState; + UINT accentFlags; + COLORREF gradientColor; + LONG animationId; +}; + +struct WINDOWCOMPOSITIONATTRIBDATA { + WINDOWCOMPOSITIONATTRIB attrib; + void* pvData; + UINT cbData; +}; + +using SetWindowCompositionAttribute_t = + BOOL(WINAPI*)(HWND hWnd, const WINDOWCOMPOSITIONATTRIBDATA* pAttrData); +SetWindowCompositionAttribute_t SetWindowCompositionAttribute_Original; + +BOOL SetTaskbarStyle(HWND hWnd) { + ACCENT_STATE accentState; + switch (g_settings.backgroundStyle) { + case BackgroundStyle::blur: + accentState = ACCENT_ENABLE_BLURBEHIND; + break; + + case BackgroundStyle::acrylicBlur: + accentState = ACCENT_ENABLE_ACRYLICBLURBEHIND; + break; + + case BackgroundStyle::color: + accentState = ACCENT_ENABLE_TRANSPARENTGRADIENT; + break; + } + + ACCENTPOLICY policy = {accentState, 0, g_settings.color, 0}; + + WINDOWCOMPOSITIONATTRIBDATA data = {WCA_ACCENT_POLICY, &policy, + sizeof(policy)}; + return SetWindowCompositionAttribute_Original(hWnd, &data); +} + +BOOL ResetTaskbarStyle(HWND hWnd) { + // TrayUI::_OnThemeChanged + // TrayUI::OnShellModeChanged + ACCENTPOLICY policy = {ACCENT_ENABLE_TRANSPARENTGRADIENT, 0x13, 0, 0}; + WINDOWCOMPOSITIONATTRIBDATA data = {WCA_ACCENT_POLICY, &policy, + sizeof(policy)}; + return SetWindowCompositionAttribute_Original(hWnd, &data); +} + +HWND GetTaskbarForMonitor(HMONITOR monitor) { + HWND hTaskbarWnd = FindWindow(L"Shell_TrayWnd", nullptr); + if (!hTaskbarWnd) { + return nullptr; + } + + DWORD taskbarThreadId = 0; + DWORD taskbarProcessId = 0; + if (!(taskbarThreadId = + GetWindowThreadProcessId(hTaskbarWnd, &taskbarProcessId)) || + taskbarProcessId != GetCurrentProcessId()) { + return nullptr; + } + + if (MonitorFromWindow(hTaskbarWnd, MONITOR_DEFAULTTONEAREST) == monitor) { + return hTaskbarWnd; + } + + HWND hResultWnd = nullptr; + + auto enumWindowsProc = [monitor, &hResultWnd](HWND hWnd) -> BOOL { + WCHAR szClassName[32]; + if (GetClassName(hWnd, szClassName, ARRAYSIZE(szClassName)) == 0) { + return TRUE; + } + + if (_wcsicmp(szClassName, L"Shell_SecondaryTrayWnd") != 0) { + return TRUE; + } + + if (MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST) != monitor) { + return TRUE; + } + + hResultWnd = hWnd; + return FALSE; + }; + + EnumThreadWindows( + taskbarThreadId, + [](HWND hWnd, LPARAM lParam) WINAPI -> BOOL { + auto& proc = *reinterpret_cast(lParam); + return proc(hWnd); + }, + reinterpret_cast(&enumWindowsProc)); + + return hResultWnd; +} + +bool DoesMonitorHaveMaximizedWindow(HMONITOR monitor) { + bool hasMaximized = false; + + auto enumWindowsProc = [monitor, &hasMaximized](HWND hWnd) -> BOOL { + if (MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST) != monitor) { + return TRUE; + } + + WINDOWPLACEMENT wp{ + .length = sizeof(WINDOWPLACEMENT), + }; + if (!GetWindowPlacement(hWnd, &wp) || wp.showCmd != SW_SHOWMAXIMIZED) { + return TRUE; + } + + hasMaximized = true; + return FALSE; + }; + + EnumWindows( + [](HWND hWnd, LPARAM lParam) WINAPI -> BOOL { + auto& proc = *reinterpret_cast(lParam); + return proc(hWnd); + }, + reinterpret_cast(&enumWindowsProc)); + + return hasMaximized; +} + +void CALLBACK LocationChangeWinEventProc(HWINEVENTHOOK hWinEventHook, + DWORD event, + HWND hWnd, + LONG idObject, + LONG idChild, + DWORD dwEventThread, + DWORD dwmsEventTime) { + if (idObject != OBJID_WINDOW) { + return; + } + + Wh_Log(L">"); + + HMONITOR monitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST); + g_pendingMonitors.insert(monitor); + + if (g_pendingMonitorsTimer) { + return; + } + + g_pendingMonitorsTimer = + SetTimer(nullptr, 0, 200, + [](HWND hwnd, // handle of window for timer messages + UINT uMsg, // WM_TIMER message + UINT_PTR idEvent, // timer identifier + DWORD dwTime // current system time + ) WINAPI { + Wh_Log(L">"); + + KillTimer(nullptr, g_pendingMonitorsTimer); + g_pendingMonitorsTimer = 0; + + for (HMONITOR monitor : g_pendingMonitors) { + HWND hMMTaskbarWnd = GetTaskbarForMonitor(monitor); + if (DoesMonitorHaveMaximizedWindow(monitor)) { + SetTaskbarStyle(hMMTaskbarWnd); + } else { + ResetTaskbarStyle(hMMTaskbarWnd); + } + } + + g_pendingMonitors.clear(); + }); +} + +BOOL AdjustTaskbarStyle(HWND hWnd) { + if (g_settings.onlyWhenMaximized) { + HMONITOR monitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST); + if (!DoesMonitorHaveMaximizedWindow(monitor)) { + return ResetTaskbarStyle(hWnd); + } + } + + return SetTaskbarStyle(hWnd); +} + +BOOL WINAPI SetWindowCompositionAttribute_Hook( + HWND hWnd, + const WINDOWCOMPOSITIONATTRIBDATA* pAttrData) { + auto original = [=]() { + return SetWindowCompositionAttribute_Original(hWnd, pAttrData); + }; + + if (pAttrData->attrib != WCA_ACCENT_POLICY) { + return original(); + } + + DWORD dwProcessId = 0; + if (!GetWindowThreadProcessId(hWnd, &dwProcessId) || + dwProcessId != GetCurrentProcessId()) { + return original(); + } + + WCHAR szClassName[32]; + if (GetClassName(hWnd, szClassName, ARRAYSIZE(szClassName)) == 0) { + return original(); + } + + if (_wcsicmp(szClassName, L"Shell_TrayWnd") != 0 && + _wcsicmp(szClassName, L"Shell_SecondaryTrayWnd") != 0) { + return original(); + } + + return AdjustTaskbarStyle(hWnd); +} + +HWND FindCurrentProcessTaskbarWindows( + std::unordered_set* secondaryTaskbarWindows) { + struct ENUM_WINDOWS_PARAM { + HWND* hWnd; + std::unordered_set* secondaryTaskbarWindows; + }; + + HWND hWnd = nullptr; + ENUM_WINDOWS_PARAM param = {&hWnd, secondaryTaskbarWindows}; + EnumWindows( + [](HWND hWnd, LPARAM lParam) WINAPI -> BOOL { + ENUM_WINDOWS_PARAM& param = *(ENUM_WINDOWS_PARAM*)lParam; + + DWORD dwProcessId = 0; + if (!GetWindowThreadProcessId(hWnd, &dwProcessId) || + dwProcessId != GetCurrentProcessId()) { + return TRUE; + } + + WCHAR szClassName[32]; + if (GetClassName(hWnd, szClassName, ARRAYSIZE(szClassName)) == 0) { + return TRUE; + } + + if (_wcsicmp(szClassName, L"Shell_TrayWnd") == 0) { + *param.hWnd = hWnd; + } else if (_wcsicmp(szClassName, L"Shell_SecondaryTrayWnd") == 0) { + param.secondaryTaskbarWindows->insert(hWnd); + } + + return TRUE; + }, + (LPARAM)¶m); + + return hWnd; +} + +void AdjustAllTaskbarStyles() { + std::unordered_set secondaryTaskbarWindows; + HWND hWnd = FindCurrentProcessTaskbarWindows(&secondaryTaskbarWindows); + if (hWnd) { + AdjustTaskbarStyle(hWnd); + } + + for (HWND hSecondaryWnd : secondaryTaskbarWindows) { + AdjustTaskbarStyle(hSecondaryWnd); + } +} + +void LoadSettings() { + PCWSTR backgroundStyle = Wh_GetStringSetting(L"backgroundStyle"); + g_settings.backgroundStyle = BackgroundStyle::blur; + if (wcscmp(backgroundStyle, L"acrylicBlur") == 0) { + g_settings.backgroundStyle = BackgroundStyle::acrylicBlur; + } else if (wcscmp(backgroundStyle, L"color") == 0) { + g_settings.backgroundStyle = BackgroundStyle::color; + } + Wh_FreeStringSetting(backgroundStyle); + + int red = Wh_GetIntSetting(L"color.red"); + int green = Wh_GetIntSetting(L"color.green"); + int blue = Wh_GetIntSetting(L"color.blue"); + int transparency = Wh_GetIntSetting(L"color.transparency"); + + g_settings.color = (COLORREF)((BYTE)red | ((WORD)((BYTE)green) << 8) | + (((DWORD)(BYTE)blue) << 16) | + (((DWORD)(BYTE)transparency) << 24)); + + g_settings.onlyWhenMaximized = Wh_GetIntSetting(L"onlyWhenMaximized"); +} + +BOOL Wh_ModInit() { + Wh_Log(L">"); + + LoadSettings(); + + HMODULE hUser32 = LoadLibrary(L"user32.dll"); + if (!hUser32) { + Wh_Log(L"Error loading user32.dll"); + return FALSE; + } + + SetWindowCompositionAttribute_t pSetWindowCompositionAttribute = + (SetWindowCompositionAttribute_t)GetProcAddress( + hUser32, "SetWindowCompositionAttribute"); + if (!pSetWindowCompositionAttribute) { + Wh_Log(L"Error getting SetWindowCompositionAttribute"); + return FALSE; + } + + Wh_SetFunctionHook((void*)pSetWindowCompositionAttribute, + (void*)SetWindowCompositionAttribute_Hook, + (void**)&SetWindowCompositionAttribute_Original); + + return TRUE; +} + +void Wh_ModAfterInit() { + WNDCLASS wndclass; + if (GetClassInfo(GetModuleHandle(NULL), L"Shell_TrayWnd", &wndclass)) { + AdjustAllTaskbarStyles(); + } + + if (g_settings.onlyWhenMaximized) { + g_winObjectLocationChangeThread = CreateThread( + nullptr, 0, + [](LPVOID lpParameter) WINAPI -> DWORD { + HWINEVENTHOOK winObjectLocationChangeEventHook = + SetWinEventHook(EVENT_OBJECT_LOCATIONCHANGE, + EVENT_OBJECT_LOCATIONCHANGE, nullptr, + LocationChangeWinEventProc, 0, 0, + WINEVENT_OUTOFCONTEXT); + if (!winObjectLocationChangeEventHook) { + Wh_Log(L"Error: SetWinEventHook"); + return 0; + } + + BOOL bRet; + MSG msg; + while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) { + if (bRet == -1) { + msg.wParam = 0; + break; + } + + if (msg.hwnd == NULL && msg.message == WM_APP) { + PostQuitMessage(0); + continue; + } + + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + UnhookWinEvent(winObjectLocationChangeEventHook); + return 0; + }, + nullptr, 0, nullptr); + } +} + +void Wh_ModUninit() { + Wh_Log(L">"); + + if (g_winObjectLocationChangeThread) { + PostThreadMessage(GetThreadId(g_winObjectLocationChangeThread), WM_APP, + 0, 0); + WaitForSingleObject(g_winObjectLocationChangeThread, INFINITE); + CloseHandle(g_winObjectLocationChangeThread); + } + + std::unordered_set secondaryTaskbarWindows; + HWND hWnd = FindCurrentProcessTaskbarWindows(&secondaryTaskbarWindows); + if (hWnd) { + ResetTaskbarStyle(hWnd); + } + + for (HWND hSecondaryWnd : secondaryTaskbarWindows) { + ResetTaskbarStyle(hSecondaryWnd); + } +} + +BOOL Wh_ModSettingsChanged(BOOL* bReload) { + Wh_Log(L">"); + + bool prevOnlyWhenMaximized = g_settings.onlyWhenMaximized; + + LoadSettings(); + + *bReload = g_settings.onlyWhenMaximized != prevOnlyWhenMaximized; + + if (!*bReload) { + AdjustAllTaskbarStyles(); + } + + return TRUE; +} From 984a75a743bba52a247387158c47db2ede70f31a Mon Sep 17 00:00:00 2001 From: Michael Maltsev <4129781+m417z@users.noreply.github.com> Date: Sun, 29 Sep 2024 09:50:55 +0300 Subject: [PATCH 36/52] Taskbar Labels for Windows 11 v1.3.1 (#1010) * Improved badge placement (e.g. unread counters) in some cases. * Fixed the "Centered, fixed size" indicator not being always centered. --- mods/taskbar-labels.wh.cpp | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/mods/taskbar-labels.wh.cpp b/mods/taskbar-labels.wh.cpp index 88b03016..bf3274e3 100644 --- a/mods/taskbar-labels.wh.cpp +++ b/mods/taskbar-labels.wh.cpp @@ -2,7 +2,7 @@ // @id taskbar-labels // @name Taskbar Labels for Windows 11 // @description Customize text labels and combining for running programs on the taskbar (Windows 11 only) -// @version 1.3 +// @version 1.3.1 // @author m417z // @github https://github.com/m417z // @twitter https://twitter.com/m417z @@ -905,11 +905,28 @@ void UpdateTaskListButtonWithLabelStyle( auto iconMargin = iconElement.Margin(); iconMargin.Left = (g_unloading || !labelControlElement) - ? 0 + ? 0.0 : g_settings.leftAndRightPaddingSize; iconMargin.Right = 0; iconElement.Margin(iconMargin); + for (PCWSTR badgeElementName : { + // Badge for non-UWP apps. + L"OverlayIcon", + // Badge for UWP apps. + L"BadgeControl", + }) { + auto badgeElement = FindChildByName(iconPanelElement, badgeElementName); + if (badgeElement) { + badgeElement.Margin(Thickness{ + .Right = (g_unloading || !labelControlElement) + ? 0.0 + : 16 - g_settings.leftAndRightPaddingSize + + (24 - iconWidth), + }); + } + } + PCWSTR indicatorClassNames[] = { L"RunningIndicator", L"ProgressIndicator", @@ -940,7 +957,10 @@ void UpdateTaskListButtonWithLabelStyle( double minWidth = 0; - if (indicatorStyle == IndicatorStyle::centerDynamic) { + if (indicatorStyle == IndicatorStyle::centerFixed) { + // Without this, the indicator isn't centered. + minWidth = indicatorElement.Width(); + } else if (indicatorStyle == IndicatorStyle::centerDynamic) { if (firstColumnWidthPixels > 0) { minWidth = indicatorElement.Width() * taskListButtonWidth / firstColumnWidthPixels; From e95b757e0e4e00850e4c62aab675955ab393b44f Mon Sep 17 00:00:00 2001 From: AM_Erizur <50849821+Erizur@users.noreply.github.com> Date: Sun, 29 Sep 2024 17:04:40 -0500 Subject: [PATCH 37/52] StartIsBack++ Tweaker 0.7.1 (#1016) - Fixed a restart loop fix --- mods/sib-plusplus-tweaker.wh.cpp | 113 +++++++++++++++++++++++++------ 1 file changed, 93 insertions(+), 20 deletions(-) diff --git a/mods/sib-plusplus-tweaker.wh.cpp b/mods/sib-plusplus-tweaker.wh.cpp index e00ca957..c67d8c67 100644 --- a/mods/sib-plusplus-tweaker.wh.cpp +++ b/mods/sib-plusplus-tweaker.wh.cpp @@ -2,12 +2,12 @@ // @id sib-plusplus-tweaker // @name StartIsBack++ Tweaker // @description Modify StartIsBack++'s features (2.9.20) -// @version 0.7 +// @version 0.7.1 // @author Erizur // @github https://github.com/Erizur // @include explorer.exe // @architecture x86-64 -// @compilerOptions -lcomdlg32 -luser32 -lole32 -lgdi32 -lshell32 +// @compilerOptions -lcomctl32 -lcomdlg32 -luser32 -lole32 -lgdi32 -lshell32 -luxtheme // ==/WindhawkMod== // ==WindhawkModReadme== @@ -73,19 +73,19 @@ Tries to match as close as possible the Start Menu Links used in Windows 7's def - DisableCDSB: false $name: Disable Custom Drawn Scrollbar $description: Enable this to draw the Start Menu scrollbar natively. -- DisableCustomOrb: FALSE +- DisableCustomOrb: false $name: Disable Custom Orb $description: Enable this to make SIB++ not hook into the Start Button. -- RestoreAPPadding: FALSE +- RestoreAPPadding: false $name: Fix "All Programs" Menu Padding $description: Windows 7 has smaller buttons for the "All Programs" menu. Use this to restore the old padding. -- MatchSevenFolders: FALSE +- MatchSevenFolders: false $name: Match Windows 7 Start Menu Links $description: Enable this to replace some of the start menu links to match Windows 7's Start Menu. (Swaps Computer's placement, Replace Connect To with Games, Replace Command Prompt with Help & Support) -- FixUserFolders: FALSE +- FixUserFolders: false $name: Fix User Folders On Corrupted Namespace $description: Based on a patch originally made by YukisCoffee. Fixes the User Folders from opening up in a corrupted namespace if you used Aerexplorer or a registry hack to move them back from "This PC". -- DisableImmersiveCPL: FALSE +- DisableImmersiveCPL: false $name: Hide UWP Settings results $description: Enable this to remove UWP settings from search results. */ @@ -110,6 +110,8 @@ Tries to match as close as possible the Start Menu Links used in Windows 7's def #include #include #include +#include +#include struct _settings { LPCWSTR SIBPath = L"%PROGRAMFILES(X86)%\\StartIsBack\\StartIsBack64.dll"; @@ -149,6 +151,15 @@ struct __declspec(align(4)) FOLDERDEFINITION wchar_t fontImage; }; +HANDLE g_restartExplorerPromptThread; +std::atomic g_restartExplorerPromptWindow; + +constexpr WCHAR kRestartExplorerPromptTitle[] = + L"StartIsBack++ Tweaker - Windhawk"; +constexpr WCHAR kRestartExplorerPromptText[] = + L"Explorer needs to be restarted to apply the new changes. " + L"Restart now?"; + HMODULE g_hStartIsBackModule = nullptr; ULONGLONG g_StartIsBackSize = 0; @@ -436,6 +447,70 @@ BOOL WINAPI InflateRect_hook( ); } +// Taken from Taskbar height and icon size mod by Ramen Software (m417z) +// https://github.com/ramensoftware/windhawk-mods/blob/7e0bd27ae1d12ae639497fbc9b48bb791f98b078/mods/taskbar-icon-size.wh.cpp#L723 +void PromptForExplorerRestart() { + if (g_restartExplorerPromptThread) { + if (WaitForSingleObject(g_restartExplorerPromptThread, 0) != + WAIT_OBJECT_0) { + return; + } + + CloseHandle(g_restartExplorerPromptThread); + } + + g_restartExplorerPromptThread = CreateThread( + nullptr, 0, + [](LPVOID lpParameter) WINAPI -> DWORD { + TASKDIALOGCONFIG taskDialogConfig{ + .cbSize = sizeof(taskDialogConfig), + .dwFlags = TDF_ALLOW_DIALOG_CANCELLATION, + .dwCommonButtons = TDCBF_YES_BUTTON | TDCBF_NO_BUTTON, + .pszWindowTitle = kRestartExplorerPromptTitle, + .pszMainIcon = TD_INFORMATION_ICON, + .pszContent = kRestartExplorerPromptText, + .pfCallback = [](HWND hwnd, UINT msg, WPARAM wParam, + LPARAM lParam, LONG_PTR lpRefData) + WINAPI -> HRESULT { + switch (msg) { + case TDN_CREATED: + g_restartExplorerPromptWindow = hwnd; + SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE); + break; + + case TDN_DESTROYED: + g_restartExplorerPromptWindow = nullptr; + break; + } + + return S_OK; + }, + }; + + int button; + if (SUCCEEDED(TaskDialogIndirect(&taskDialogConfig, &button, + nullptr, nullptr)) && + button == IDYES) { + WCHAR commandLine[] = + L"cmd.exe /c " + L"\"taskkill /F /IM explorer.exe & start explorer\""; + STARTUPINFO si = { + .cb = sizeof(si), + }; + PROCESS_INFORMATION pi{}; + if (CreateProcess(nullptr, commandLine, nullptr, nullptr, FALSE, + 0, nullptr, nullptr, &si, &pi)) { + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + } + } + + return 0; + }, + nullptr, 0, nullptr); +} + void LoadSettings(void) { mod_settings.SIBPath = Wh_GetStringSetting(L"SIBPath"); @@ -482,17 +557,6 @@ BOOL CheckForStartIsBack() // The mod is being initialized, load settings, hook functions, and do other // initialization stuff if required. BOOL Wh_ModInit() { - #ifdef _WIN64 - const size_t OFFSET_SAME_TEB_FLAGS = 0x17EE; - #else - const size_t OFFSET_SAME_TEB_FLAGS = 0x0FCA; - #endif - bool isInitialThread = *(USHORT*)((BYTE*)NtCurrentTeb() + OFFSET_SAME_TEB_FLAGS) & 0x0400; - if (!isInitialThread) { - system("taskkill /F /IM explorer.exe & start explorer"); - return FALSE; - } - Wh_Log(L"Initalize SIB++ Tweaker."); LoadSettings(); @@ -543,12 +607,21 @@ BOOL Wh_ModInit() { void Wh_ModUninit() { Wh_Log(L"Exiting SIB++ Tweaker."); - system("taskkill /F /IM explorer.exe & start explorer"); + HWND restartExplorerPromptWindow = g_restartExplorerPromptWindow; + if (restartExplorerPromptWindow) { + PostMessage(restartExplorerPromptWindow, WM_CLOSE, 0, 0); + } + + if (g_restartExplorerPromptThread) { + WaitForSingleObject(g_restartExplorerPromptThread, INFINITE); + CloseHandle(g_restartExplorerPromptThread); + g_restartExplorerPromptThread = nullptr; + } } // The mod setting were changed, reload them. void Wh_ModSettingsChanged() { Wh_Log(L"SIB++ Tweaker settings changed. Attempting to restart explorer."); - system("taskkill /F /IM explorer.exe & start explorer"); + PromptForExplorerRestart(); } \ No newline at end of file From d4c63c187231bcd43dc7c476269b6377a89f9827 Mon Sep 17 00:00:00 2001 From: Michael Maltsev <4129781+m417z@users.noreply.github.com> Date: Mon, 30 Sep 2024 21:37:22 +0300 Subject: [PATCH 38/52] Start button always on the left v1.1.1 (#1018) * Fixed taskbar buttons not being completely centered. * Fixed restoring styles on unload. --- mods/taskbar-start-button-position.wh.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/mods/taskbar-start-button-position.wh.cpp b/mods/taskbar-start-button-position.wh.cpp index ba4f5fdf..cea0fd48 100644 --- a/mods/taskbar-start-button-position.wh.cpp +++ b/mods/taskbar-start-button-position.wh.cpp @@ -2,7 +2,7 @@ // @id taskbar-start-button-position // @name Start button always on the left // @description Forces the start button to be on the left of the taskbar, even when taskbar icons are centered (Windows 11 only) -// @version 1.1 +// @version 1.1.1 // @author m417z // @github https://github.com/m417z // @twitter https://twitter.com/m417z @@ -183,7 +183,7 @@ void UpdateStartButtonPosition(XamlRoot xamlRoot, startButton.RenderTransform(transform); } -void ResetStartButtonPosition(FrameworkElement startButton) { +void ResetStartButtonStyles(FrameworkElement startButton) { Wh_Log(L">"); Media::TranslateTransform transform; @@ -194,6 +194,10 @@ void ResetStartButtonPosition(FrameworkElement startButton) { transform.X(0); startButton.RenderTransform(transform); + + Thickness startButtonMargin = startButton.Margin(); + startButtonMargin.Right = 0; + startButton.Margin(startButtonMargin); } void ScheduleUpdateStartButtonPosition() { @@ -266,12 +270,8 @@ bool ApplyStyle(XamlRoot xamlRoot) { double startButtonWidth = startButton.ActualWidth(); - Thickness taskbarFrameRepeaterMargin = taskbarFrameRepeater.Margin(); - taskbarFrameRepeaterMargin.Left = g_unloading ? 0 : startButtonWidth; - taskbarFrameRepeater.Margin(taskbarFrameRepeaterMargin); - Thickness startButtonMargin = startButton.Margin(); - startButtonMargin.Left = g_unloading ? 0 : -startButtonWidth; + startButtonMargin.Right = -startButtonWidth; startButton.Margin(startButtonMargin); g_taskbarData.push_back({ @@ -458,7 +458,7 @@ void ApplySettings(HWND hTaskbarWnd) { for (const auto& item : g_taskbarData) { if (auto startButtonElement = item.startButtonElement.get()) { - ResetStartButtonPosition(startButtonElement); + ResetStartButtonStyles(startButtonElement); } } From 056d7c496f105eecebcf9527d06841b5a923e384 Mon Sep 17 00:00:00 2001 From: Michael Maltsev <4129781+m417z@users.noreply.github.com> Date: Mon, 30 Sep 2024 22:32:30 +0300 Subject: [PATCH 39/52] Vertical Taskbar for Windows 11 v1.2.4 (#1019) * Fixed a single pixel of the background not aligning with maximized windows. * Fixed notifications being moved to the top of the screen. --- mods/taskbar-vertical.wh.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mods/taskbar-vertical.wh.cpp b/mods/taskbar-vertical.wh.cpp index 9f1123bf..26859914 100644 --- a/mods/taskbar-vertical.wh.cpp +++ b/mods/taskbar-vertical.wh.cpp @@ -2,7 +2,7 @@ // @id taskbar-vertical // @name Vertical Taskbar for Windows 11 // @description Finally, the missing vertical taskbar option for Windows 11! -// @version 1.2.3 +// @version 1.2.4 // @author m417z // @github https://github.com/m417z // @twitter https://twitter.com/m417z @@ -1024,7 +1024,6 @@ bool ApplyStyle(FrameworkElement taskbarFrame, // Fix the edge of the taskbar being non-clickable by moving the edge // pixel out of the screen. - margin.Top += 1; margin.Bottom -= 1; } @@ -2419,7 +2418,9 @@ void AdjustCoreWindowPos(int* x, int* y, int width, int height) { break; } - *y = rc.top; + if (g_target == Target::StartMenu || g_target == Target::SearchHost) { + *y = rc.top; + } } void ApplySettings() { From 97ef7cd2031fef8f112ae3de6cfea173bbda0975 Mon Sep 17 00:00:00 2001 From: Michael Maltsev <4129781+m417z@users.noreply.github.com> Date: Tue, 1 Oct 2024 09:04:04 +0300 Subject: [PATCH 40/52] Slick Window Arrangement v1.0.2 (#1020) * Fixed snapping frame being stuck in some cases. * Fixed windows jumping to the top left corner of the screen while moving in some cases. --- mods/slick-window-arrangement.wh.cpp | 152 +++++++++++++++++++++------ 1 file changed, 119 insertions(+), 33 deletions(-) diff --git a/mods/slick-window-arrangement.wh.cpp b/mods/slick-window-arrangement.wh.cpp index c9df95cc..52c4c822 100644 --- a/mods/slick-window-arrangement.wh.cpp +++ b/mods/slick-window-arrangement.wh.cpp @@ -2,7 +2,7 @@ // @id slick-window-arrangement // @name Slick Window Arrangement // @description Make window arrangement more slick and pleasant with a sliding animation and snapping -// @version 1.0.1 +// @version 1.0.2 // @author m417z // @github https://github.com/m417z // @twitter https://twitter.com/m417z @@ -11,9 +11,18 @@ // @compilerOptions -lcomctl32 -ldwmapi // ==/WindhawkMod== +// Source code is published under The GNU General Public License v3.0. +// +// For bug reports and feature requests, please open an issue here: +// https://github.com/ramensoftware/windhawk-mods/issues +// +// For pull requests, development takes place here: +// https://github.com/m417z/my-windhawk-mods + // ==WindhawkModReadme== /* # Slick Window Arrangement + Window arrangement is boring, make it more slick and pleasant with a sliding animation and window snapping. @@ -91,6 +100,9 @@ GetDpiForSystem_t pGetDpiForSystem; typedef UINT (WINAPI *GetDpiForWindow_t)(HWND hwnd); GetDpiForWindow_t pGetDpiForWindow; +typedef BOOL (WINAPI *IsWindowArranged_t)(HWND hwnd); +IsWindowArranged_t pIsWindowArranged; + typedef HRESULT (WINAPI *GetDpiForMonitor_t)(HMONITOR hmonitor, MONITOR_DPI_TYPE dpiType, UINT *dpiX, UINT *dpiY); GetDpiForMonitor_t pGetDpiForMonitor; @@ -439,30 +451,38 @@ class WindowMoving { windowMagnet(hTargetWnd) {} void PreProcessPos(HWND hTargetWnd, int* x, int* y, int* cx, int* cy) { - DWORD messagePos = GetMessagePos(); - - if (firstMoveDone) { + MovingState state = GetCurrentMovingState(hTargetWnd, *x, *y); + + // If window state changes, e.g. the window is snapped, don't adjust its + // position, which could interfere with the snapping and doesn't make + // sense in general. + if (lastState && lastState->isMaximized == state.isMaximized && + lastState->isMinimized == state.isMinimized && + lastState->isArranged == state.isArranged) { // Adjust window pos, which can be off in the DPI contexts: // DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE // DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 - int lastDeltaX = GET_X_LPARAM(lastMessagePos) - lastX; - int lastDeltaY = GET_Y_LPARAM(lastMessagePos) - lastY; + int lastDeltaX = GET_X_LPARAM(lastState->messagePos) - lastState->x; + int lastDeltaY = GET_Y_LPARAM(lastState->messagePos) - lastState->y; - int deltaX = GET_X_LPARAM(messagePos) - *x; - int deltaY = GET_Y_LPARAM(messagePos) - *y; + int deltaX = GET_X_LPARAM(state.messagePos) - state.x; + int deltaY = GET_Y_LPARAM(state.messagePos) - state.y; - *x -= lastDeltaX - deltaX; - *y -= lastDeltaY - deltaY; + state.x -= lastDeltaX - deltaX; + state.y -= lastDeltaY - deltaY; + + *x = state.x; + *y = state.y; } - lastMessagePos = messagePos; - lastX = *x; - lastY = *y; + lastState = state; windowMagnet.MagnetMove(hTargetWnd, x, y, cx, cy); + } - firstMoveDone = true; + void ForgetLastPos() { + lastState.reset(); } WindowMagnet& GetWindowMagnet() { @@ -470,9 +490,27 @@ class WindowMoving { } private: - bool firstMoveDone = false; - DWORD lastMessagePos; - int lastX, lastY; + struct MovingState { + bool isMinimized; + bool isMaximized; + bool isArranged; + DWORD messagePos; + int x; + int y; + }; + + static MovingState GetCurrentMovingState(HWND hTargetWnd, int x, int y) { + return MovingState{ + .isMinimized = !!IsMaximized(hTargetWnd), + .isMaximized = !!IsMinimized(hTargetWnd), + .isArranged = pIsWindowArranged && !!pIsWindowArranged(hTargetWnd), + .messagePos = GetMessagePos(), + .x = x, + .y = y, + }; + } + + std::optional lastState; WindowMagnet windowMagnet; }; @@ -481,12 +519,13 @@ class WindowMove { WindowMove() {} void Reset() { + lastState.reset(); for (auto& snapshotItem : snapshot) { snapshotItem = nullptr; } } - void UpdateWithNewPos(int x, int y) { + void UpdateWithNewPos(HWND hTargetWnd, int x, int y) { DWORD tickCount = GetTickCount(); if (snapshot[0] && tickCount - snapshot[0]->tickCount >= 100) { @@ -494,6 +533,16 @@ class WindowMove { Reset(); } + WindowState state = GetWindowState(hTargetWnd); + if (lastState && (state.isMinimized != lastState->isMinimized || + state.isMaximized != lastState->isMaximized || + state.isArranged != lastState->isArranged)) { + // Window state changed, e.g. it was snapped, reset. + Reset(); + } + + lastState = state; + if (!snapshot[0]) { snapshot[0] = &snapshotStorage[0]; } @@ -546,11 +595,26 @@ class WindowMove { } private: + struct WindowState { + bool isMinimized; + bool isMaximized; + bool isArranged; + }; + struct MoveSnapshot { DWORD tickCount; int x, y; }; + static WindowState GetWindowState(HWND hTargetWnd) { + return WindowState{ + .isMinimized = !!IsMaximized(hTargetWnd), + .isMaximized = !!IsMinimized(hTargetWnd), + .isArranged = pIsWindowArranged && !!pIsWindowArranged(hTargetWnd), + }; + } + + std::optional lastState; MoveSnapshot snapshotStorage[3]; MoveSnapshot* snapshot[3]{}; }; @@ -748,7 +812,6 @@ bool KillWindowSlideTimer(HWND hWnd) return false; } - auto& timer = it->second; g_winSlideTimers.erase(it); return true; } @@ -843,18 +906,40 @@ void OnWindowPosChanging(HWND hWnd, WINDOWPOS* windowPos) return; } - bool posChanged = rc.left != windowPos->x || rc.top != windowPos->y; - bool sizeChanged = rc.right - rc.left != windowPos->cx || rc.bottom - rc.top != windowPos->cy; + int x = (windowPos->flags & SWP_NOMOVE) ? rc.left : windowPos->x; + int y = (windowPos->flags & SWP_NOMOVE) ? rc.top : windowPos->y; + int cx = (windowPos->flags & SWP_NOSIZE) ? (rc.right - rc.left) : windowPos->cx; + int cy = (windowPos->flags & SWP_NOSIZE) ? (rc.bottom - rc.top) : windowPos->cy; + + bool posChanged = rc.left != x || rc.top != y; + bool sizeChanged = rc.right - rc.left != cx || rc.bottom - rc.top != cy; + if (!posChanged && !sizeChanged) { + return; + } + + auto it = g_winMoving.find(hWnd); + if (it == g_winMoving.end()) { + return; + } + + auto& windowMoving = it->second; if (posChanged && !sizeChanged) { - auto it = g_winMoving.find(hWnd); - if (it != g_winMoving.end()) { - auto& windowMoving = it->second; - windowMoving.PreProcessPos(hWnd, &windowPos->x, &windowPos->y, &windowPos->cx, &windowPos->cy); + windowMoving.PreProcessPos(hWnd, &x, &y, &cx, &cy); + + if (!(windowPos->flags & SWP_NOMOVE)) { + windowPos->x = x; + windowPos->y = y; + } + + if (!(windowPos->flags & SWP_NOSIZE)) { + windowPos->cx = cx; + windowPos->cy = cy; } } - else if (sizeChanged) { + else { // Maybe support resize one day... + windowMoving.ForgetLastPos(); } } @@ -882,7 +967,7 @@ void OnWindowPosChanged(HWND hWnd, const WINDOWPOS* windowPos) return; } - windowMove.UpdateWithNewPos(windowPos->x, windowPos->y); + windowMove.UpdateWithNewPos(hWnd, windowPos->x, windowPos->y); } void OnSysCommand(HWND hWnd, WPARAM command) @@ -905,7 +990,7 @@ void OnSysCommand(HWND hWnd, WPARAM command) } } -void OnDestroy(HWND hWnd) +void OnNcDestroy(HWND hWnd) { KillWindowSlideTimer(hWnd); UnsubclassWindow(hWnd); @@ -936,8 +1021,8 @@ LRESULT CALLBACK SubclassWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPa OnSysCommand(hWnd, wParam); break; - case WM_DESTROY: - OnDestroy(hWnd); + case WM_NCDESTROY: + OnNcDestroy(hWnd); break; default: @@ -1090,7 +1175,7 @@ void LoadSettings() g_settings.slidingAnimationSlowdown = Wh_GetIntSetting(L"SlidingAnimationSlowdown"); } -BOOL Wh_ModInit(void) +BOOL Wh_ModInit() { Wh_Log(L"Init"); @@ -1103,6 +1188,7 @@ BOOL Wh_ModInit(void) pGetAwarenessFromDpiAwarenessContext = (GetAwarenessFromDpiAwarenessContext_t)GetProcAddress(hUser32, "GetAwarenessFromDpiAwarenessContext"); pGetDpiForSystem = (GetDpiForSystem_t)GetProcAddress(hUser32, "GetDpiForSystem"); pGetDpiForWindow = (GetDpiForWindow_t)GetProcAddress(hUser32, "GetDpiForWindow"); + pIsWindowArranged = (IsWindowArranged_t)GetProcAddress(hUser32, "IsWindowArranged"); } HMODULE hShcore = LoadLibrary(L"shcore.dll"); @@ -1121,7 +1207,7 @@ BOOL Wh_ModInit(void) return TRUE; } -void Wh_ModUninit(void) +void Wh_ModUninit() { Wh_Log(L"Uninit"); @@ -1153,7 +1239,7 @@ void Wh_ModUninit(void) } } -void Wh_ModSettingsChanged(void) +void Wh_ModSettingsChanged() { Wh_Log(L"SettingsChanged"); From 4ab0994ab79cb76e217a4b658639f98875959b4a Mon Sep 17 00:00:00 2001 From: CatmanFan <30674270+CatmanFan@users.noreply.github.com> Date: Wed, 2 Oct 2024 08:49:57 +0100 Subject: [PATCH 41/52] Update Accent Color Sync to v1.41 (#1024) * Hotfix to account for new OpenGlass registry values as announced in the [latest update](https://github.com/ALTaleX531/OpenGlass/releases/tag/v1.2-legacy). * Updated readme. --- mods/accent-color-sync.wh.cpp | 36 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/mods/accent-color-sync.wh.cpp b/mods/accent-color-sync.wh.cpp index e0e40b1c..29a7cae3 100644 --- a/mods/accent-color-sync.wh.cpp +++ b/mods/accent-color-sync.wh.cpp @@ -4,7 +4,7 @@ // @description Synchronises OpenGlass and Control Panel color settings // @description:fr-FR Synchronisation des couleurs d'OpenGlass et du Panneau de configuration // @description:es-ES Sincroniza los colores de OpenGlass y del Panel de control -// @version 1.4 +// @version 1.41 // @author CatmanFan / Mr._Lechkar // @github https://github.com/CatmanFan // @include explorer.exe @@ -17,18 +17,16 @@ /* Brings back the functionality of the 'Color intensity' slider to Windows 10 using OpenGlass. The mod synchronises OpenGlass's Aero settings with the slider value. ----- - -## ⚠️ To use this mod, you will need [kfh83](https://github.com/kfh83)'s OpenGlass-legacy fork. - -To get this mod to function fully, perform the following steps: -1. Install the OpenGlass-legacy fork; this can be done by compiling the source code from **[the official repo](https://github.com/ALTaleX531/OpenGlass/tree/legacy)**, or by getting an existing binary version from **[this GitHub post](https://github.com/ALTaleX531/OpenGlass/pull/14#issue-2415161314)**. - * If you are updating this mod from version 1.0, it is required to disable or uninstall any other existing DWM shader software (such as regular OpenGlass or DWMBlurGlass). -2. Afterwards, go to *HKCU\SOFTWARE\Microsoft\Windows\DWM* in the registry and add any one of the three DWORD values of **og_ColorizationColorBalance**, **og_ColorizationAfterglowBalance** and **og_ColorizationBlurBalance** before enabling this mod. These registry values will be handled automatically. -If you are updating this mod from version 1.3, it is recommended to also enable the *Sync with DWM* option from the mod's settings, although this can have some minor bugs (see below). +## **⚠️ To use this mod, you will need [kfh83](https://github.com/kfh83)'s OpenGlass-legacy fork.** +### It is generally recommended to compile the source code from *[the official repo](https://github.com/ALTaleX531/OpenGlass/tree/legacy)*, but a list of OpenGlass-legacy downloads is also available *[here](https://github.com/ALTaleX531/OpenGlass/releases)*. -You may need to try changing the accent color manually if changes do not automatically take effect. +### *Additional tips:* +* If you are updating this mod from any prior versions, you must rename the three colorization values in the DWM registry starting with "***og_**(name)*" to "*(name)**Override***"; for example, ***og_**ColorizationColorBalance* to *ColorizationColorBalance**Override***. This is required so that the mod will be able to read the new values. + * Do **not** touch the "*og_Opacity*" value, as that is only used by this mod and is thus left unchanged. +* If you are updating this mod from version 1.0, it is required to disable or uninstall any other existing DWM shader software (such as regular OpenGlass or DWMBlurGlass). +* If you are updating this mod from version 1.3, it is recommended to also enable the *Sync with DWM* option from the mod's settings, although this can have some minor bugs (see below). +* You may need to try changing the accent color manually if changes do not automatically take effect. ---- @@ -564,9 +562,9 @@ void writeColorizationBalance(bool bruteforce = FALSE) // ********************************************* // Actually do the registry editing // ********************************************* - set_DWORD(dwmKey, L"og_ColorizationColorBalance", dwmSettings.color_balance); - set_DWORD(dwmKey, L"og_ColorizationAfterglowBalance", dwmSettings.afterglow_balance); - set_DWORD(dwmKey, L"og_ColorizationBlurBalance", dwmSettings.blur_balance); + set_DWORD(dwmKey, L"ColorizationColorBalanceOverride", dwmSettings.color_balance); + set_DWORD(dwmKey, L"ColorizationAfterglowBalanceOverride", dwmSettings.afterglow_balance); + set_DWORD(dwmKey, L"ColorizationBlurBalanceOverride", dwmSettings.blur_balance); // Other registry values set_DWORD(dwmKey, L"GlassOpacity", 0); // settings.boolTransparency ? 0 : 100); @@ -761,8 +759,8 @@ WinVersion getWinVer() BOOL isOpenGlassInstalled(bool strict = FALSE) { - return strict ? exists_DWORD(dwmKey, L"og_ColorizationColorBalance") && exists_DWORD(dwmKey, L"og_ColorizationAfterglowBalance") && exists_DWORD(dwmKey, L"og_ColorizationBlurBalance") - : exists_DWORD(dwmKey, L"og_ColorizationColorBalance") || exists_DWORD(dwmKey, L"og_ColorizationAfterglowBalance") || exists_DWORD(dwmKey, L"og_ColorizationBlurBalance"); + return strict ? exists_DWORD(dwmKey, L"ColorizationColorBalanceOverride") && exists_DWORD(dwmKey, L"ColorizationAfterglowBalanceOverride") && exists_DWORD(dwmKey, L"ColorizationBlurBalanceOverride") + : exists_DWORD(dwmKey, L"ColorizationColorBalanceOverride") || exists_DWORD(dwmKey, L"ColorizationAfterglowBalanceOverride") || exists_DWORD(dwmKey, L"ColorizationBlurBalanceOverride"); } BOOL LoadSettings() @@ -794,9 +792,9 @@ BOOL LoadSettings() Wh_Log(L"Setting up registry"); settings.opacity = 42; - set_DWORD(dwmKey, L"og_ColorizationColorBalance", 0x08); - set_DWORD(dwmKey, L"og_ColorizationAfterglowBalance", 0x2b); - set_DWORD(dwmKey, L"og_ColorizationBlurBalance", 0x31); + set_DWORD(dwmKey, L"ColorizationColorBalanceOverride", 0x08); + set_DWORD(dwmKey, L"ColorizationAfterglowBalanceOverride", 0x2b); + set_DWORD(dwmKey, L"ColorizationBlurBalanceOverride", 0x31); if (!set_DWORD(dwmKey, opacityValue, settings.opacity)) return FALSE; From dd2309b76c8963fe5a2949677725025be7fd0f84 Mon Sep 17 00:00:00 2001 From: Michael Maltsev <4129781+m417z@users.noreply.github.com> Date: Wed, 2 Oct 2024 17:34:19 +0300 Subject: [PATCH 42/52] Windows 11 Notification Center Styler v1.1.3: (#1026) * Added support for the action center (Win+A) in Windows 11 version 24H2. --- ...ndows-11-notification-center-styler.wh.cpp | 270 +++++++++++++----- 1 file changed, 193 insertions(+), 77 deletions(-) diff --git a/mods/windows-11-notification-center-styler.wh.cpp b/mods/windows-11-notification-center-styler.wh.cpp index 08ca6606..8059cd44 100644 --- a/mods/windows-11-notification-center-styler.wh.cpp +++ b/mods/windows-11-notification-center-styler.wh.cpp @@ -2,12 +2,13 @@ // @id windows-11-notification-center-styler // @name Windows 11 Notification Center Styler // @description Customize the Notification Center with themes contributed by others or create your own -// @version 1.1.2 +// @version 1.1.3 // @author m417z // @github https://github.com/m417z // @twitter https://twitter.com/m417z // @homepage https://m417z.com/ // @include ShellExperienceHost.exe +// @include ShellHost.exe // @architecture x86-64 // @compilerOptions -lcomctl32 -lole32 -loleaut32 -lruntimeobject -Wl,--export-all-symbols // ==/WindhawkMod== @@ -53,7 +54,8 @@ the target elements. Resource variables allow to override predefined variables. For a more detailed explanation and examples, refer to the sections below. The [UWPSpy](https://ramensoftware.com/uwpspy) tool can be used to inspect the -notification center control elements in real time, and experiment with various styles. +notification center control elements in real time, and experiment with various +styles. For a collection of commonly requested notification center styling customizations, check out [The Windows 11 notification center styling @@ -65,9 +67,9 @@ Each entry has a target control and a list of styles. The target control is written as `Class` or `Class#Name`, i.e. the target control class name (the tag name in XAML resource files), such as -`ActionCenter.FocusSessionControl` or `Rectangle`, optionally followed by `#` and the -target control's name (`x:Name` attribute in XAML resource files). The target -control can also include: +`ActionCenter.FocusSessionControl` or `Rectangle`, optionally followed by `#` +and the target control's name (`x:Name` attribute in XAML resource files). The +target control can also include: * Child control index, for example: `Class#Name[2]` will only match the relevant control that's also the second child among all of its parent's child controls. * Control properties, for example: @@ -659,6 +661,13 @@ HRESULT InjectWindhawkTAP() noexcept using namespace winrt::Windows::UI::Xaml; +enum class Target { + ShellExperienceHost, + ShellHost, // Win11 24H2. +}; + +Target g_target; + // https://stackoverflow.com/a/51274008 template struct deleter_from_fn { @@ -1769,6 +1778,148 @@ void InitializeSettingsAndTap() { } } +using RunFromWindowThreadProc_t = void(WINAPI*)(PVOID parameter); + +bool RunFromWindowThread(HWND hWnd, + RunFromWindowThreadProc_t proc, + PVOID procParam) { + static const UINT runFromWindowThreadRegisteredMsg = + RegisterWindowMessage(L"Windhawk_RunFromWindowThread_" WH_MOD_ID); + + struct RUN_FROM_WINDOW_THREAD_PARAM { + RunFromWindowThreadProc_t proc; + PVOID procParam; + }; + + DWORD dwThreadId = GetWindowThreadProcessId(hWnd, nullptr); + if (dwThreadId == 0) { + return false; + } + + if (dwThreadId == GetCurrentThreadId()) { + proc(procParam); + return true; + } + + HHOOK hook = SetWindowsHookEx( + WH_CALLWNDPROC, + [](int nCode, WPARAM wParam, LPARAM lParam) WINAPI -> LRESULT { + if (nCode == HC_ACTION) { + const CWPSTRUCT* cwp = (const CWPSTRUCT*)lParam; + if (cwp->message == runFromWindowThreadRegisteredMsg) { + RUN_FROM_WINDOW_THREAD_PARAM* param = + (RUN_FROM_WINDOW_THREAD_PARAM*)cwp->lParam; + param->proc(param->procParam); + } + } + + return CallNextHookEx(nullptr, nCode, wParam, lParam); + }, + nullptr, dwThreadId); + if (!hook) { + return false; + } + + RUN_FROM_WINDOW_THREAD_PARAM param; + param.proc = proc; + param.procParam = procParam; + SendMessage(hWnd, runFromWindowThreadRegisteredMsg, 0, (LPARAM)¶m); + + UnhookWindowsHookEx(hook); + + return true; +} + +bool RunFromWindowThreadViaPostMessage(HWND hWnd, + RunFromWindowThreadProc_t proc, + PVOID procParam) { + static const UINT runFromWindowThreadRegisteredMsgViaPostMessage = + RegisterWindowMessage( + L"Windhawk_RunFromWindowThreadViaPostMessage_" WH_MOD_ID); + + struct RUN_FROM_WINDOW_THREAD_PARAM { + RunFromWindowThreadProc_t proc; + PVOID procParam; + HHOOK hook; + }; + + DWORD dwThreadId = GetWindowThreadProcessId(hWnd, nullptr); + if (dwThreadId == 0) { + return false; + } + + HHOOK hook = SetWindowsHookEx( + WH_GETMESSAGE, + [](int nCode, WPARAM wParam, LPARAM lParam) WINAPI -> LRESULT { + if (nCode == HC_ACTION && wParam == PM_REMOVE) { + MSG* msg = (MSG*)lParam; + if (msg->message == + runFromWindowThreadRegisteredMsgViaPostMessage) { + auto* param = (RUN_FROM_WINDOW_THREAD_PARAM*)msg->lParam; + if (param) { + param->proc(param->procParam); + UnhookWindowsHookEx(param->hook); + delete param; + msg->lParam = 0; + } + } + } + + return CallNextHookEx(nullptr, nCode, wParam, lParam); + }, + nullptr, dwThreadId); + if (!hook) { + return false; + } + + auto* param = new RUN_FROM_WINDOW_THREAD_PARAM{ + .proc = proc, + .procParam = procParam, + .hook = hook, + }; + if (!PostMessage(hWnd, runFromWindowThreadRegisteredMsgViaPostMessage, 0, + (LPARAM)param)) { + UnhookWindowsHookEx(hook); + delete param; + return false; + } + + return true; +} + +void OnWindowCreated(HWND hWnd, LPCWSTR lpClassName, PCSTR funcName) { + BOOL bTextualClassName = ((ULONG_PTR)lpClassName & ~(ULONG_PTR)0xffff) != 0; + + switch (g_target) { + case Target::ShellExperienceHost: + if (bTextualClassName && + _wcsicmp(lpClassName, L"Windows.UI.Core.CoreWindow") == 0) { + Wh_Log(L"Initializing - Created core window: %08X via %S", + (DWORD)(ULONG_PTR)hWnd, funcName); + InitializeForCurrentThread(); + InitializeSettingsAndTap(); + } + break; + + case Target::ShellHost: + if (bTextualClassName && + _wcsicmp(lpClassName, L"ControlCenterWindow") == 0) { + Wh_Log( + L"Initializing - Created ControlCenterWindow: %08X via %S", + (DWORD)(ULONG_PTR)hWnd, funcName); + // Initializing at this point is too early and doesn't work. + RunFromWindowThreadViaPostMessage( + hWnd, + [](PVOID) WINAPI { + InitializeForCurrentThread(); + InitializeSettingsAndTap(); + }, + nullptr); + } + break; + } +} + using CreateWindowInBand_t = HWND(WINAPI*)(DWORD dwExStyle, LPCWSTR lpClassName, LPCWSTR lpWindowName, @@ -1803,15 +1954,7 @@ HWND WINAPI CreateWindowInBand_Hook(DWORD dwExStyle, return hWnd; } - BOOL bTextualClassName = ((ULONG_PTR)lpClassName & ~(ULONG_PTR)0xffff) != 0; - - if (bTextualClassName && - _wcsicmp(lpClassName, L"Windows.UI.Core.CoreWindow") == 0) { - Wh_Log(L"Initializing - Created core window: %08X", - (DWORD)(ULONG_PTR)hWnd); - InitializeForCurrentThread(); - InitializeSettingsAndTap(); - } + OnWindowCreated(hWnd, lpClassName, __FUNCTION__); return hWnd; } @@ -1852,71 +1995,11 @@ HWND WINAPI CreateWindowInBandEx_Hook(DWORD dwExStyle, return hWnd; } - BOOL bTextualClassName = ((ULONG_PTR)lpClassName & ~(ULONG_PTR)0xffff) != 0; - - if (bTextualClassName && - _wcsicmp(lpClassName, L"Windows.UI.Core.CoreWindow") == 0) { - Wh_Log(L"Initializing - Created core window: %08X", - (DWORD)(ULONG_PTR)hWnd); - InitializeForCurrentThread(); - InitializeSettingsAndTap(); - } + OnWindowCreated(hWnd, lpClassName, __FUNCTION__); return hWnd; } -using RunFromWindowThreadProc_t = void(WINAPI*)(PVOID parameter); - -bool RunFromWindowThread(HWND hWnd, - RunFromWindowThreadProc_t proc, - PVOID procParam) { - static const UINT runFromWindowThreadRegisteredMsg = - RegisterWindowMessage(L"Windhawk_RunFromWindowThread_" WH_MOD_ID); - - struct RUN_FROM_WINDOW_THREAD_PARAM { - RunFromWindowThreadProc_t proc; - PVOID procParam; - }; - - DWORD dwThreadId = GetWindowThreadProcessId(hWnd, nullptr); - if (dwThreadId == 0) { - return false; - } - - if (dwThreadId == GetCurrentThreadId()) { - proc(procParam); - return true; - } - - HHOOK hook = SetWindowsHookEx( - WH_CALLWNDPROC, - [](int nCode, WPARAM wParam, LPARAM lParam) WINAPI -> LRESULT { - if (nCode == HC_ACTION) { - const CWPSTRUCT* cwp = (const CWPSTRUCT*)lParam; - if (cwp->message == runFromWindowThreadRegisteredMsg) { - RUN_FROM_WINDOW_THREAD_PARAM* param = - (RUN_FROM_WINDOW_THREAD_PARAM*)cwp->lParam; - param->proc(param->procParam); - } - } - - return CallNextHookEx(nullptr, nCode, wParam, lParam); - }, - nullptr, dwThreadId); - if (!hook) { - return false; - } - - RUN_FROM_WINDOW_THREAD_PARAM param; - param.proc = proc; - param.procParam = procParam; - SendMessage(hWnd, runFromWindowThreadRegisteredMsg, 0, (LPARAM)¶m); - - UnhookWindowsHookEx(hook); - - return true; -} - std::vector GetCoreWnds() { struct ENUM_WINDOWS_PARAM { std::vector* hWnds; @@ -1939,8 +2022,19 @@ std::vector GetCoreWnds() { return TRUE; } - if (_wcsicmp(szClassName, L"Windows.UI.Core.CoreWindow") == 0) { - param.hWnds->push_back(hWnd); + switch (g_target) { + case Target::ShellExperienceHost: + if (_wcsicmp(szClassName, L"Windows.UI.Core.CoreWindow") == + 0) { + param.hWnds->push_back(hWnd); + } + break; + + case Target::ShellHost: + if (_wcsicmp(szClassName, L"ControlCenterWindow") == 0) { + param.hWnds->push_back(hWnd); + } + break; } return TRUE; @@ -1953,6 +2047,28 @@ std::vector GetCoreWnds() { BOOL Wh_ModInit() { Wh_Log(L">"); + g_target = Target::ShellExperienceHost; + + WCHAR moduleFilePath[MAX_PATH]; + switch ( + GetModuleFileName(nullptr, moduleFilePath, ARRAYSIZE(moduleFilePath))) { + case 0: + case ARRAYSIZE(moduleFilePath): + Wh_Log(L"GetModuleFileName failed"); + break; + + default: + if (PCWSTR moduleFileName = wcsrchr(moduleFilePath, L'\\')) { + moduleFileName++; + if (_wcsicmp(moduleFileName, L"ShellHost.exe") == 0) { + g_target = Target::ShellHost; + } + } else { + Wh_Log(L"GetModuleFileName returned an unsupported path"); + } + break; + } + HMODULE user32Module = LoadLibrary(L"user32.dll"); if (user32Module) { void* pCreateWindowInBand = From b43269d44eb047e3f27c015faca6fd365b0960d1 Mon Sep 17 00:00:00 2001 From: Michael Maltsev <4129781+m417z@users.noreply.github.com> Date: Fri, 4 Oct 2024 18:33:22 +0300 Subject: [PATCH 43/52] Taskbar on top for Windows 11 v1.0 (#1030) --- mods/taskbar-on-top.wh.cpp | 1424 ++++++++++++++++++++++++++++++++++++ 1 file changed, 1424 insertions(+) create mode 100644 mods/taskbar-on-top.wh.cpp diff --git a/mods/taskbar-on-top.wh.cpp b/mods/taskbar-on-top.wh.cpp new file mode 100644 index 00000000..f849d611 --- /dev/null +++ b/mods/taskbar-on-top.wh.cpp @@ -0,0 +1,1424 @@ +// ==WindhawkMod== +// @id taskbar-on-top +// @name Taskbar on top for Windows 11 +// @description Moves the Windows 11 taskbar to the top of the screen +// @version 1.0 +// @author m417z +// @github https://github.com/m417z +// @twitter https://twitter.com/m417z +// @homepage https://m417z.com/ +// @include explorer.exe +// @include StartMenuExperienceHost.exe +// @include SearchHost.exe +// @architecture x86-64 +// @compilerOptions -DWINVER=0x0A00 -lole32 -loleaut32 -lruntimeobject -lshcore +// ==/WindhawkMod== + +// Source code is published under The GNU General Public License v3.0. +// +// For bug reports and feature requests, please open an issue here: +// https://github.com/ramensoftware/windhawk-mods/issues +// +// For pull requests, development takes place here: +// https://github.com/m417z/my-windhawk-mods + +// ==WindhawkModReadme== +/* +# Taskbar on top for Windows 11 + +Moves the Windows 11 taskbar to the top of the screen. + +## Compatibility + +The mod was designed for up-to-date Windows 11 versions 22H2 to 24H2. Other +versions weren't tested and are probably not compatible. + +![Screenshot](https://i.imgur.com/dCABsum.png) +*/ +// ==/WindhawkModReadme== + +// ==WindhawkModSettings== +/* +- taskbarLocation: top + $name: Taskbar location + $options: + - top: Top + - bottom: Bottom +- taskbarLocationSecondary: sameAsPrimary + $name: Taskbar location on secondary monitors + $options: + - sameAsPrimary: Same as on primary monitor + - top: Top + - bottom: Bottom +*/ +// ==/WindhawkModSettings== + +#include + +#include // must come before knownfolders.h + +#include +#include +#include + +#undef GetCurrentTime + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace winrt::Windows::UI::Xaml; + +#ifndef SPI_SETLOGICALDPIOVERRIDE +#define SPI_SETLOGICALDPIOVERRIDE 0x009F +#endif + +enum class TaskbarLocation { + top, + bottom, +}; + +struct { + TaskbarLocation taskbarLocation; + TaskbarLocation taskbarLocationSecondary; +} g_settings; + +enum class Target { + Explorer, + StartMenu, + SearchHost, +}; + +Target g_target; + +HANDLE g_initializedEvent; +WCHAR g_taskbarViewDllPath[MAX_PATH]; +std::atomic g_applyingSettings; +std::atomic g_pendingMeasureOverride; +std::atomic g_unloading; +std::atomic g_hookCallCounter; + +int g_originalTaskbarHeight; +bool g_inSystemTrayController_UpdateFrameSize; +bool g_inAugmentedEntryPointButton_UpdateButtonPadding; +bool g_inCTaskListThumbnailWnd_DisplayUI; +bool g_inCTaskListThumbnailWnd_LayoutThumbnails; +bool g_inOverflowFlyoutModel_Show; + +std::vector> g_notifyIconsUpdated; + +using FrameworkElementLoadedEventRevoker = winrt::impl::event_revoker< + IFrameworkElement, + &winrt::impl::abi::type::remove_Loaded>; + +std::list g_notifyIconAutoRevokerList; + +int g_copilotPosTimerCounter; +UINT_PTR g_copilotPosTimer; + +WINUSERAPI UINT WINAPI GetDpiForWindow(HWND hwnd); +typedef enum MONITOR_DPI_TYPE { + MDT_EFFECTIVE_DPI = 0, + MDT_ANGULAR_DPI = 1, + MDT_RAW_DPI = 2, + MDT_DEFAULT = MDT_EFFECTIVE_DPI +} MONITOR_DPI_TYPE; +STDAPI GetDpiForMonitor(HMONITOR hmonitor, + MONITOR_DPI_TYPE dpiType, + UINT* dpiX, + UINT* dpiY); + +bool GetMonitorRect(HMONITOR monitor, RECT* rc) { + MONITORINFO monitorInfo{ + .cbSize = sizeof(MONITORINFO), + }; + return GetMonitorInfo(monitor, &monitorInfo) && + CopyRect(rc, &monitorInfo.rcMonitor); +} + +bool GetMonitorRectDpiUnscaled(HMONITOR monitor, RECT* rc) { + if (!GetMonitorRect(monitor, rc)) { + return false; + } + + UINT monitorDpiX = 96; + UINT monitorDpiY = 96; + GetDpiForMonitor(monitor, MDT_DEFAULT, &monitorDpiX, &monitorDpiY); + + rc->left = MulDiv(rc->left, 96, monitorDpiX); + rc->top = MulDiv(rc->top, 96, monitorDpiY); + rc->right = MulDiv(rc->right, 96, monitorDpiX); + rc->bottom = MulDiv(rc->bottom, 96, monitorDpiY); + return true; +} + +int GetPrimaryMonitorHeightDpiUnscaled() { + const POINT ptZero = {0, 0}; + HMONITOR primaryMonitor = + MonitorFromPoint(ptZero, MONITOR_DEFAULTTOPRIMARY); + RECT monitorRect; + if (!GetMonitorRectDpiUnscaled(primaryMonitor, &monitorRect)) { + return 0; + } + + return monitorRect.bottom - monitorRect.top; +} + +bool IsChildOfElementByName(FrameworkElement element, PCWSTR name) { + auto parent = element; + while (true) { + parent = Media::VisualTreeHelper::GetParent(parent) + .try_as(); + if (!parent) { + return false; + } + + if (parent.Name() == name) { + return true; + } + } +} + +bool IsChildOfElementByClassName(FrameworkElement element, PCWSTR className) { + auto parent = element; + while (true) { + parent = Media::VisualTreeHelper::GetParent(parent) + .try_as(); + if (!parent) { + return false; + } + + if (winrt::get_class_name(parent) == className) { + return true; + } + } +} + +FrameworkElement EnumChildElements( + FrameworkElement element, + std::function enumCallback) { + int childrenCount = Media::VisualTreeHelper::GetChildrenCount(element); + + for (int i = 0; i < childrenCount; i++) { + auto child = Media::VisualTreeHelper::GetChild(element, i) + .try_as(); + if (!child) { + Wh_Log(L"Failed to get child %d of %d", i + 1, childrenCount); + continue; + } + + if (enumCallback(child)) { + return child; + } + } + + return nullptr; +} + +FrameworkElement FindChildByName(FrameworkElement element, PCWSTR name) { + return EnumChildElements(element, [name](FrameworkElement child) { + return child.Name() == name; + }); +} + +FrameworkElement FindChildByClassName(FrameworkElement element, + PCWSTR className) { + return EnumChildElements(element, [className](FrameworkElement child) { + return winrt::get_class_name(child) == className; + }); +} + +TaskbarLocation GetTaskbarLocationForMonitor(HMONITOR monitor) { + if (g_unloading) { + return TaskbarLocation::bottom; + } + + if (g_settings.taskbarLocation == g_settings.taskbarLocationSecondary) { + return g_settings.taskbarLocation; + } + + const POINT ptZero = {0, 0}; + HMONITOR primaryMonitor = + MonitorFromPoint(ptZero, MONITOR_DEFAULTTOPRIMARY); + + return monitor == primaryMonitor ? g_settings.taskbarLocation + : g_settings.taskbarLocationSecondary; +} + +using TrayUI__StuckTrayChange_t = void(WINAPI*)(void* pThis); +TrayUI__StuckTrayChange_t TrayUI__StuckTrayChange_Original; + +using TrayUI__HandleSettingChange_t = void(WINAPI*)(void* pThis, + void* param1, + void* param2, + void* param3, + void* param4); +TrayUI__HandleSettingChange_t TrayUI__HandleSettingChange_Original; +void WINAPI TrayUI__HandleSettingChange_Hook(void* pThis, + void* param1, + void* param2, + void* param3, + void* param4) { + Wh_Log(L">"); + + TrayUI__HandleSettingChange_Original(pThis, param1, param2, param3, param4); + + if (g_applyingSettings) { + TrayUI__StuckTrayChange_Original(pThis); + } +} + +using TrayUI_GetDockedRect_t = DWORD(WINAPI*)(void* pThis, + RECT* rect, + BOOL param2); +TrayUI_GetDockedRect_t TrayUI_GetDockedRect_Original; +DWORD WINAPI TrayUI_GetDockedRect_Hook(void* pThis, RECT* rect, BOOL param2) { + Wh_Log(L">"); + + DWORD ret = TrayUI_GetDockedRect_Original(pThis, rect, param2); + + HMONITOR monitor = MonitorFromRect(rect, MONITOR_DEFAULTTONEAREST); + + RECT monitorRect; + GetMonitorRect(monitor, &monitorRect); + + UINT monitorDpiX = 96; + UINT monitorDpiY = 96; + GetDpiForMonitor(monitor, MDT_DEFAULT, &monitorDpiX, &monitorDpiY); + + int height = rect->bottom - rect->top; + + switch (GetTaskbarLocationForMonitor(monitor)) { + case TaskbarLocation::top: + rect->top = monitorRect.top; + rect->bottom = monitorRect.top + height; + break; + + case TaskbarLocation::bottom: + rect->top = monitorRect.bottom - height; + rect->bottom = monitorRect.bottom; + break; + } + + return ret; +} + +using TrayUI_MakeStuckRect_t = void(WINAPI*)(void* pThis, + RECT* rect, + RECT* param2, + SIZE param3, + DWORD taskbarPos); +TrayUI_MakeStuckRect_t TrayUI_MakeStuckRect_Original; +void WINAPI TrayUI_MakeStuckRect_Hook(void* pThis, + RECT* rect, + RECT* param2, + SIZE param3, + DWORD taskbarPos) { + Wh_Log(L">"); + + TrayUI_MakeStuckRect_Original(pThis, rect, param2, param3, taskbarPos); + + // taskbarPos: + // 0: left + // 1: top + // 2: right + // 3: bottom + if (taskbarPos != 3) { + return; + } + + HMONITOR monitor = MonitorFromRect(rect, MONITOR_DEFAULTTONEAREST); + + RECT monitorRect; + GetMonitorRect(monitor, &monitorRect); + + UINT monitorDpiX = 96; + UINT monitorDpiY = 96; + GetDpiForMonitor(monitor, MDT_DEFAULT, &monitorDpiX, &monitorDpiY); + + int height = rect->bottom - rect->top; + + switch (GetTaskbarLocationForMonitor(monitor)) { + case TaskbarLocation::top: + rect->top = monitorRect.top; + rect->bottom = monitorRect.top + height; + break; + + case TaskbarLocation::bottom: + rect->top = monitorRect.bottom - height; + rect->bottom = monitorRect.bottom; + break; + } +} + +LRESULT TaskbarWndProcPostProcess(HWND hWnd, + UINT Msg, + WPARAM wParam, + LPARAM lParam, + LRESULT result) { + switch (Msg) { + case WM_SIZING: { + Wh_Log(L"WM_SIZING: %08X", (DWORD)(ULONG_PTR)hWnd); + + if (!g_unloading) { + RECT* rect = (RECT*)lParam; + + HMONITOR monitor = + MonitorFromRect(rect, MONITOR_DEFAULTTONEAREST); + + RECT monitorRect; + GetMonitorRect(monitor, &monitorRect); + + int height = rect->bottom - rect->top; + + switch (GetTaskbarLocationForMonitor(monitor)) { + case TaskbarLocation::top: + rect->top = monitorRect.top; + rect->bottom = monitorRect.top + height; + break; + + case TaskbarLocation::bottom: + rect->top = monitorRect.bottom - height; + rect->bottom = monitorRect.bottom; + break; + } + } + break; + } + + case WM_WINDOWPOSCHANGING: { + auto* windowpos = (WINDOWPOS*)lParam; + if ((windowpos->flags & (SWP_NOSIZE | SWP_NOMOVE)) != + (SWP_NOSIZE | SWP_NOMOVE)) { + Wh_Log(L"WM_WINDOWPOSCHANGING (size or move): %08X", + (DWORD)(ULONG_PTR)hWnd); + + if (!g_unloading) { + RECT rect{ + .left = windowpos->x, + .top = windowpos->y, + .right = windowpos->x + windowpos->cx, + .bottom = windowpos->y + windowpos->cy, + }; + HMONITOR monitor = + MonitorFromRect(&rect, MONITOR_DEFAULTTONEAREST); + + if (!(windowpos->flags & SWP_NOMOVE) && + GetTaskbarLocationForMonitor(monitor) == + TaskbarLocation::top) { + RECT monitorRect; + GetMonitorRect(monitor, &monitorRect); + + windowpos->y = monitorRect.top; + } + } + } + break; + } + } + + return result; +} + +using TrayUI_WndProc_t = LRESULT(WINAPI*)(void* pThis, + HWND hWnd, + UINT Msg, + WPARAM wParam, + LPARAM lParam, + bool* flag); +TrayUI_WndProc_t TrayUI_WndProc_Original; +LRESULT WINAPI TrayUI_WndProc_Hook(void* pThis, + HWND hWnd, + UINT Msg, + WPARAM wParam, + LPARAM lParam, + bool* flag) { + g_hookCallCounter++; + + LRESULT ret = + TrayUI_WndProc_Original(pThis, hWnd, Msg, wParam, lParam, flag); + + ret = TaskbarWndProcPostProcess(hWnd, Msg, wParam, lParam, ret); + + g_hookCallCounter--; + + return ret; +} + +using CSecondaryTray_v_WndProc_t = LRESULT( + WINAPI*)(void* pThis, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); +CSecondaryTray_v_WndProc_t CSecondaryTray_v_WndProc_Original; +LRESULT WINAPI CSecondaryTray_v_WndProc_Hook(void* pThis, + HWND hWnd, + UINT Msg, + WPARAM wParam, + LPARAM lParam) { + g_hookCallCounter++; + + LRESULT ret = + CSecondaryTray_v_WndProc_Original(pThis, hWnd, Msg, wParam, lParam); + + ret = TaskbarWndProcPostProcess(hWnd, Msg, wParam, lParam, ret); + + g_hookCallCounter--; + + return ret; +} + +using CTaskListWnd_ComputeJumpViewPosition_t = + HRESULT(WINAPI*)(void* pThis, + void* taskBtnGroup, + int param2, + winrt::Windows::Foundation::Point* point, + HorizontalAlignment* horizontalAlignment, + VerticalAlignment* verticalAlignment); +CTaskListWnd_ComputeJumpViewPosition_t + CTaskListWnd_ComputeJumpViewPosition_Original; +HRESULT WINAPI CTaskListWnd_ComputeJumpViewPosition_Hook( + void* pThis, + void* taskBtnGroup, + int param2, + winrt::Windows::Foundation::Point* point, + HorizontalAlignment* horizontalAlignment, + VerticalAlignment* verticalAlignment) { + Wh_Log(L">"); + + HRESULT ret = CTaskListWnd_ComputeJumpViewPosition_Original( + pThis, taskBtnGroup, param2, point, horizontalAlignment, + verticalAlignment); + + DWORD messagePos = GetMessagePos(); + POINT pt{ + GET_X_LPARAM(messagePos), + GET_Y_LPARAM(messagePos), + }; + + HMONITOR monitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST); + if (GetTaskbarLocationForMonitor(monitor) == TaskbarLocation::bottom) { + return ret; + } + + MONITORINFO monitorInfo{ + .cbSize = sizeof(MONITORINFO), + }; + GetMonitorInfo(monitor, &monitorInfo); + UINT monitorDpiX = 96; + UINT monitorDpiY = 96; + GetDpiForMonitor(monitor, MDT_DEFAULT, &monitorDpiX, &monitorDpiY); + + point->Y = monitorInfo.rcWork.top + MulDiv(125, monitorDpiY, 96); + + return ret; +} + +using CTaskListThumbnailWnd_DisplayUI_t = void*(WINAPI*)(void* pThis, + void* param1, + void* param2, + void* param3, + void* param4); +CTaskListThumbnailWnd_DisplayUI_t CTaskListThumbnailWnd_DisplayUI_Original; +void* WINAPI CTaskListThumbnailWnd_DisplayUI_Hook(void* pThis, + void* param1, + void* param2, + void* param3, + void* param4) { + Wh_Log(L">"); + + g_inCTaskListThumbnailWnd_DisplayUI = true; + + void* ret = CTaskListThumbnailWnd_DisplayUI_Original(pThis, param1, param2, + param3, param4); + + g_inCTaskListThumbnailWnd_DisplayUI = false; + + return ret; +} + +using CTaskListThumbnailWnd_LayoutThumbnails_t = void(WINAPI*)(void* pThis); +CTaskListThumbnailWnd_LayoutThumbnails_t + CTaskListThumbnailWnd_LayoutThumbnails_Original; +void WINAPI CTaskListThumbnailWnd_LayoutThumbnails_Hook(void* pThis) { + Wh_Log(L">"); + + g_inCTaskListThumbnailWnd_LayoutThumbnails = true; + + CTaskListThumbnailWnd_LayoutThumbnails_Original(pThis); + + g_inCTaskListThumbnailWnd_LayoutThumbnails = false; +} + +using XamlExplorerHostWindow_XamlExplorerHostWindow_t = + void*(WINAPI*)(void* pThis, + unsigned int param1, + winrt::Windows::Foundation::Rect* rect, + unsigned int param3); +XamlExplorerHostWindow_XamlExplorerHostWindow_t + XamlExplorerHostWindow_XamlExplorerHostWindow_Original; +void* WINAPI XamlExplorerHostWindow_XamlExplorerHostWindow_Hook( + void* pThis, + unsigned int param1, + winrt::Windows::Foundation::Rect* rect, + unsigned int param3) { + Wh_Log(L">"); + + if (g_inOverflowFlyoutModel_Show) { + RECT rc{ + .left = static_cast(rect->X), + .top = static_cast(rect->Y), + .right = static_cast(rect->X + rect->Width), + .bottom = static_cast(rect->Y + rect->Height), + }; + + HMONITOR monitor = MonitorFromRect(&rc, MONITOR_DEFAULTTONEAREST); + if (GetTaskbarLocationForMonitor(monitor) == TaskbarLocation::top) { + MONITORINFO monitorInfo{ + .cbSize = sizeof(MONITORINFO), + }; + GetMonitorInfo(monitor, &monitorInfo); + UINT monitorDpiX = 96; + UINT monitorDpiY = 96; + GetDpiForMonitor(monitor, MDT_DEFAULT, &monitorDpiX, &monitorDpiY); + + winrt::Windows::Foundation::Rect rectNew = *rect; + rectNew.Y = monitorInfo.rcWork.top + MulDiv(12, monitorDpiY, 96); + + return XamlExplorerHostWindow_XamlExplorerHostWindow_Original( + pThis, param1, &rectNew, param3); + } + } + + return XamlExplorerHostWindow_XamlExplorerHostWindow_Original(pThis, param1, + rect, param3); +} + +void ApplySystemTrayChevronIconViewStyle( + FrameworkElement systemTrayChevronIconViewElement) { + if (g_settings.taskbarLocation != TaskbarLocation::top) { + return; + } + + FrameworkElement baseTextBlock = nullptr; + + FrameworkElement child = systemTrayChevronIconViewElement; + if ((child = FindChildByName(child, L"ContainerGrid")) && + (child = FindChildByName(child, L"ContentPresenter")) && + (child = FindChildByName(child, L"ContentGrid")) && + (child = FindChildByClassName(child, L"SystemTray.TextIconContent")) && + (child = FindChildByName(child, L"ContainerGrid")) && + (child = FindChildByName(child, L"Base"))) { + baseTextBlock = child; + } + + if (!baseTextBlock) { + return; + } + + double angle = g_unloading ? 0 : 180; + Media::RotateTransform transform; + transform.Angle(angle); + baseTextBlock.RenderTransform(transform); + + float origin = g_unloading ? 0 : 0.5; + baseTextBlock.RenderTransformOrigin({origin, origin}); +} + +using IconView_IconView_t = void(WINAPI*)(PVOID pThis); +IconView_IconView_t IconView_IconView_Original; +void WINAPI IconView_IconView_Hook(PVOID pThis) { + Wh_Log(L">"); + + IconView_IconView_Original(pThis); + + FrameworkElement iconView = nullptr; + ((IUnknown**)pThis)[1]->QueryInterface(winrt::guid_of(), + winrt::put_abi(iconView)); + if (!iconView) { + return; + } + + g_notifyIconAutoRevokerList.emplace_back(); + auto autoRevokerIt = g_notifyIconAutoRevokerList.end(); + --autoRevokerIt; + + *autoRevokerIt = iconView.Loaded( + winrt::auto_revoke_t{}, + [autoRevokerIt](winrt::Windows::Foundation::IInspectable const& sender, + winrt::Windows::UI::Xaml::RoutedEventArgs const& e) { + Wh_Log(L">"); + + g_notifyIconAutoRevokerList.erase(autoRevokerIt); + + auto iconView = sender.try_as(); + if (!iconView) { + return; + } + + auto className = winrt::get_class_name(iconView); + Wh_Log(L"className: %s", className.c_str()); + + if (className == L"SystemTray.ChevronIconView") { + if (IsChildOfElementByName(iconView, L"NotifyIconStack")) { + ApplySystemTrayChevronIconViewStyle(iconView); + } + } + }); +} + +using OverflowFlyoutModel_Show_t = void(WINAPI*)(void* pThis); +OverflowFlyoutModel_Show_t OverflowFlyoutModel_Show_Original; +void WINAPI OverflowFlyoutModel_Show_Hook(void* pThis) { + Wh_Log(L">"); + + g_inOverflowFlyoutModel_Show = true; + + OverflowFlyoutModel_Show_Original(pThis); + + g_inOverflowFlyoutModel_Show = false; +} + +using MenuFlyout_ShowAt_t = void*(WINAPI*)(void* pThis, + void* placementTarget, + void* showOptions); +MenuFlyout_ShowAt_t MenuFlyout_ShowAt_Original; +void* WINAPI +MenuFlyout_ShowAt_Hook(void* pThis, + DependencyObject* placementTarget, + Controls::Primitives::FlyoutShowOptions* showOptions) { + Wh_Log(L">"); + + auto original = [=]() { + return MenuFlyout_ShowAt_Original(pThis, placementTarget, showOptions); + }; + + if (!showOptions) { + return original(); + } + + DWORD messagePos = GetMessagePos(); + POINT pt{ + GET_X_LPARAM(messagePos), + GET_Y_LPARAM(messagePos), + }; + + HMONITOR monitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST); + if (GetTaskbarLocationForMonitor(monitor) == TaskbarLocation::bottom) { + return original(); + } + + auto placement = showOptions->Placement(); + Wh_Log(L"Placement=%d", (int)placement); + if (placement == Controls::Primitives::FlyoutPlacementMode::Top) { + showOptions->Placement( + Controls::Primitives::FlyoutPlacementMode::Bottom); + } + + auto point = + showOptions->Position().try_as(); + if (point) { + Wh_Log(L"Point=%fx%f", point->X, point->Y); + if (point->Y < 0) { + point->Y = -point->Y; + + FrameworkElement targetElement = + placementTarget->try_as(); + if (targetElement) { + point->Y += targetElement.ActualHeight(); + } + + showOptions->Position(point); + } + } + + return MenuFlyout_ShowAt_Original(pThis, placementTarget, showOptions); +} + +using SetWindowPos_t = decltype(&SetWindowPos); +SetWindowPos_t SetWindowPos_Original; +BOOL WINAPI SetWindowPos_Hook(HWND hWnd, + HWND hWndInsertAfter, + int X, + int Y, + int cx, + int cy, + UINT uFlags) { + auto original = [=]() { + return SetWindowPos_Original(hWnd, hWndInsertAfter, X, Y, cx, cy, + uFlags); + }; + + if (uFlags & (SWP_NOSIZE | SWP_NOMOVE)) { + return original(); + } + + WCHAR szClassName[64]; + if (GetClassName(hWnd, szClassName, ARRAYSIZE(szClassName)) == 0) { + return original(); + } + + if (_wcsicmp(szClassName, L"TaskListThumbnailWnd") == 0) { + if (!g_inCTaskListThumbnailWnd_DisplayUI && + !g_inCTaskListThumbnailWnd_LayoutThumbnails) { + return original(); + } + + DWORD messagePos = GetMessagePos(); + POINT pt{ + GET_X_LPARAM(messagePos), + GET_Y_LPARAM(messagePos), + }; + + HMONITOR monitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST); + if (GetTaskbarLocationForMonitor(monitor) == TaskbarLocation::bottom) { + return original(); + } + + MONITORINFO monitorInfo{ + .cbSize = sizeof(MONITORINFO), + }; + GetMonitorInfo(monitor, &monitorInfo); + + if (g_inCTaskListThumbnailWnd_DisplayUI) { + Y = std::max(monitorInfo.rcWork.top, pt.y); + Y += MulDiv(12, GetDpiForWindow(hWnd), 96); + } else { + // Keep current position. + RECT rc; + GetWindowRect(hWnd, &rc); + Y = rc.top; + } + } else if (_wcsicmp(szClassName, L"TopLevelWindowForOverflowXamlIsland") == + 0 || + _wcsicmp(szClassName, L"Xaml_WindowedPopupClass") == 0) { + DWORD messagePos = GetMessagePos(); + POINT pt{ + GET_X_LPARAM(messagePos), + GET_Y_LPARAM(messagePos), + }; + + HMONITOR monitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST); + if (GetTaskbarLocationForMonitor(monitor) == TaskbarLocation::bottom) { + return original(); + } + + MONITORINFO monitorInfo{ + .cbSize = sizeof(MONITORINFO), + }; + GetMonitorInfo(monitor, &monitorInfo); + + if (Y < monitorInfo.rcWork.top) { + Y = monitorInfo.rcWork.top; + } else if (Y > monitorInfo.rcWork.bottom - cy) { + Y = monitorInfo.rcWork.bottom - cy; + } + } else { + return original(); + } + + Wh_Log(L"Adjusting pos for %s: %dx%d, %dx%d", szClassName, X, Y, X + cx, + Y + cy); + + return SetWindowPos_Original(hWnd, hWndInsertAfter, X, Y, cx, cy, uFlags); +} + +namespace CoreWindowUI { + +bool IsTargetCoreWindow(HWND hWnd, int* extraXAdjustment) { + DWORD threadId = 0; + DWORD processId = 0; + if (!hWnd || !(threadId = GetWindowThreadProcessId(hWnd, &processId)) || + processId != GetCurrentProcessId()) { + return false; + } + + WCHAR szClassName[32]; + if (GetClassName(hWnd, szClassName, ARRAYSIZE(szClassName)) == 0) { + return false; + } + + if (_wcsicmp(szClassName, L"Windows.UI.Core.CoreWindow") != 0) { + return false; + } + + return true; +} + +std::vector GetCoreWindows() { + struct ENUM_WINDOWS_PARAM { + std::vector* hWnds; + }; + + std::vector hWnds; + ENUM_WINDOWS_PARAM param = {&hWnds}; + EnumWindows( + [](HWND hWnd, LPARAM lParam) WINAPI -> BOOL { + ENUM_WINDOWS_PARAM& param = *(ENUM_WINDOWS_PARAM*)lParam; + + if (IsTargetCoreWindow(hWnd, nullptr)) { + param.hWnds->push_back(hWnd); + } + + return TRUE; + }, + (LPARAM)¶m); + + return hWnds; +} + +void AdjustCoreWindowSize(int x, int y, int* width, int* height) { + if (g_target != Target::StartMenu) { + return; + } + + const POINT pt = {x, y}; + HMONITOR monitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST); + if (GetTaskbarLocationForMonitor(monitor) == TaskbarLocation::bottom) { + MONITORINFO monitorInfo{ + .cbSize = sizeof(MONITORINFO), + }; + GetMonitorInfo(monitor, &monitorInfo); + + *height = monitorInfo.rcWork.bottom - monitorInfo.rcWork.top; + return; + } + + UINT monitorDpiX = 96; + UINT monitorDpiY = 96; + GetDpiForMonitor(monitor, MDT_DEFAULT, &monitorDpiX, &monitorDpiY); + + const int h1 = MulDiv(750, monitorDpiY, 96); + const int h2 = MulDiv(694, monitorDpiY, 96); + if (*height >= h1) { + *height = h1; + } else if (*height >= h2) { + *height = h2; + } +} + +void AdjustCoreWindowPos(int* x, int* y, int width, int height) { + const POINT pt = {*x, *y}; + HMONITOR monitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST); + if (GetTaskbarLocationForMonitor(monitor) == TaskbarLocation::bottom) { + if (g_target == Target::StartMenu) { + *x = 0; + *y = 0; + } + + return; + } + + MONITORINFO monitorInfo{ + .cbSize = sizeof(MONITORINFO), + }; + GetMonitorInfo(monitor, &monitorInfo); + + if (g_target == Target::StartMenu || g_target == Target::SearchHost) { + *y = monitorInfo.rcWork.top; + } +} + +void ApplySettings() { + for (HWND hCoreWnd : GetCoreWindows()) { + Wh_Log(L"Adjusting core window %08X", (DWORD)(ULONG_PTR)hCoreWnd); + + RECT rc; + if (!GetWindowRect(hCoreWnd, &rc)) { + continue; + } + + int x = rc.left; + int y = rc.top; + int cx = rc.right - rc.left; + int cy = rc.bottom - rc.top; + + AdjustCoreWindowSize(x, y, &cx, &cy); + AdjustCoreWindowPos(&x, &y, cx, cy); + + SetWindowPos_Original(hCoreWnd, nullptr, x, y, cx, cy, + SWP_NOZORDER | SWP_NOACTIVATE); + } +} + +using CreateWindowInBand_t = HWND(WINAPI*)(DWORD dwExStyle, + LPCWSTR lpClassName, + LPCWSTR lpWindowName, + DWORD dwStyle, + int X, + int Y, + int nWidth, + int nHeight, + HWND hWndParent, + HMENU hMenu, + HINSTANCE hInstance, + PVOID lpParam, + DWORD dwBand); +CreateWindowInBand_t CreateWindowInBand_Original; +HWND WINAPI CreateWindowInBand_Hook(DWORD dwExStyle, + LPCWSTR lpClassName, + LPCWSTR lpWindowName, + DWORD dwStyle, + int X, + int Y, + int nWidth, + int nHeight, + HWND hWndParent, + HMENU hMenu, + HINSTANCE hInstance, + PVOID lpParam, + DWORD dwBand) { + BOOL bTextualClassName = ((ULONG_PTR)lpClassName & ~(ULONG_PTR)0xffff) != 0; + if (bTextualClassName && + _wcsicmp(lpClassName, L"Windows.UI.Core.CoreWindow") == 0) { + Wh_Log(L"Creating core window"); + AdjustCoreWindowSize(X, Y, &nWidth, &nHeight); + AdjustCoreWindowPos(&X, &Y, nWidth, nHeight); + } + + return CreateWindowInBand_Original( + dwExStyle, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, + hWndParent, hMenu, hInstance, lpParam, dwBand); +} + +using CreateWindowInBandEx_t = HWND(WINAPI*)(DWORD dwExStyle, + LPCWSTR lpClassName, + LPCWSTR lpWindowName, + DWORD dwStyle, + int X, + int Y, + int nWidth, + int nHeight, + HWND hWndParent, + HMENU hMenu, + HINSTANCE hInstance, + PVOID lpParam, + DWORD dwBand, + DWORD dwTypeFlags); +CreateWindowInBandEx_t CreateWindowInBandEx_Original; +HWND WINAPI CreateWindowInBandEx_Hook(DWORD dwExStyle, + LPCWSTR lpClassName, + LPCWSTR lpWindowName, + DWORD dwStyle, + int X, + int Y, + int nWidth, + int nHeight, + HWND hWndParent, + HMENU hMenu, + HINSTANCE hInstance, + PVOID lpParam, + DWORD dwBand, + DWORD dwTypeFlags) { + BOOL bTextualClassName = ((ULONG_PTR)lpClassName & ~(ULONG_PTR)0xffff) != 0; + if (bTextualClassName && + _wcsicmp(lpClassName, L"Windows.UI.Core.CoreWindow") == 0) { + Wh_Log(L"Creating core window"); + AdjustCoreWindowSize(X, Y, &nWidth, &nHeight); + AdjustCoreWindowPos(&X, &Y, nWidth, nHeight); + } + + return CreateWindowInBandEx_Original( + dwExStyle, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, + hWndParent, hMenu, hInstance, lpParam, dwBand, dwTypeFlags); +} + +BOOL WINAPI SetWindowPos_Hook(HWND hWnd, + HWND hWndInsertAfter, + int X, + int Y, + int cx, + int cy, + UINT uFlags) { + auto original = [&]() { + return SetWindowPos_Original(hWnd, hWndInsertAfter, X, Y, cx, cy, + uFlags); + }; + + int extraXAdjustment = 0; + if (!IsTargetCoreWindow(hWnd, &extraXAdjustment)) { + return original(); + } + + Wh_Log(L"%08X %08X", (DWORD)(ULONG_PTR)hWnd, uFlags); + + if ((uFlags & (SWP_NOSIZE | SWP_NOMOVE)) == (SWP_NOSIZE | SWP_NOMOVE)) { + return original(); + } + + RECT rc{}; + GetWindowRect(hWnd, &rc); + + // SearchHost is being moved by explorer.exe, then the size is adjusted + // by SearchHost itself. Make SearchHost adjust the position too. A + // similar workaround is needed for other windows. + if (uFlags & SWP_NOMOVE) { + uFlags &= ~SWP_NOMOVE; + X = rc.left; + Y = rc.top; + } + + int width; + int height; + if (uFlags & SWP_NOSIZE) { + width = rc.right - rc.left; + height = rc.bottom - rc.top; + AdjustCoreWindowSize(X, Y, &width, &height); + } else { + AdjustCoreWindowSize(X, Y, &cx, &cy); + width = cx; + height = cy; + } + + if (!(uFlags & SWP_NOMOVE)) { + AdjustCoreWindowPos(&X, &Y, width, height); + } + + X += extraXAdjustment; + + return SetWindowPos_Original(hWnd, hWndInsertAfter, X, Y, cx, cy, uFlags); +} + +} // namespace CoreWindowUI + +void LoadSettings() { + PCWSTR taskbarLocation = Wh_GetStringSetting(L"taskbarLocation"); + g_settings.taskbarLocation = TaskbarLocation::top; + if (wcscmp(taskbarLocation, L"bottom") == 0) { + g_settings.taskbarLocation = TaskbarLocation::bottom; + } + Wh_FreeStringSetting(taskbarLocation); + + PCWSTR taskbarLocationSecondary = + Wh_GetStringSetting(L"taskbarLocationSecondary"); + g_settings.taskbarLocationSecondary = g_settings.taskbarLocation; + if (wcscmp(taskbarLocationSecondary, L"top") == 0) { + g_settings.taskbarLocationSecondary = TaskbarLocation::top; + } else if (wcscmp(taskbarLocationSecondary, L"bottom") == 0) { + g_settings.taskbarLocationSecondary = TaskbarLocation::bottom; + } + Wh_FreeStringSetting(taskbarLocationSecondary); +} + +HWND GetTaskbarWnd() { + static HWND hTaskbarWnd; + + if (!hTaskbarWnd) { + HWND hWnd = FindWindow(L"Shell_TrayWnd", nullptr); + + DWORD processId = 0; + if (hWnd && GetWindowThreadProcessId(hWnd, &processId) && + processId == GetCurrentProcessId()) { + hTaskbarWnd = hWnd; + } + } + + return hTaskbarWnd; +} + +void ApplySettings() { + HWND hTaskbarWnd = GetTaskbarWnd(); + if (!hTaskbarWnd) { + return; + } + + g_applyingSettings = true; + + // Trigger TrayUI::_HandleSettingChange. + SendMessage(hTaskbarWnd, WM_SETTINGCHANGE, SPI_SETLOGICALDPIOVERRIDE, 0); + + g_applyingSettings = false; +} + +bool GetTaskbarViewDllPath(WCHAR path[MAX_PATH]) { + WCHAR szWindowsDirectory[MAX_PATH]; + if (!GetWindowsDirectory(szWindowsDirectory, + ARRAYSIZE(szWindowsDirectory))) { + Wh_Log(L"GetWindowsDirectory failed"); + return false; + } + + // Windows 11 version 22H2. + wcscpy_s(path, MAX_PATH, szWindowsDirectory); + wcscat_s( + path, MAX_PATH, + LR"(\SystemApps\MicrosoftWindows.Client.Core_cw5n1h2txyewy\Taskbar.View.dll)"); + if (GetFileAttributes(path) != INVALID_FILE_ATTRIBUTES) { + return true; + } + + // Windows 11 version 21H2. + wcscpy_s(path, MAX_PATH, szWindowsDirectory); + wcscat_s( + path, MAX_PATH, + LR"(\SystemApps\MicrosoftWindows.Client.CBS_cw5n1h2txyewy\ExplorerExtensions.dll)"); + if (GetFileAttributes(path) != INVALID_FILE_ATTRIBUTES) { + return true; + } + + return false; +} + +bool HookTaskbarViewDllSymbols(HMODULE module) { + { + // Taskbar.View.dll, ExplorerExtensions.dll + WindhawkUtils::SYMBOL_HOOK symbolHooks[] = { + { + { + LR"(public: __cdecl winrt::SystemTray::implementation::IconView::IconView(void))", + }, + (void**)&IconView_IconView_Original, + (void*)IconView_IconView_Hook, + }, + { + { + LR"(public: void __cdecl winrt::Taskbar::implementation::OverflowFlyoutModel::Show(void))", + }, + (void**)&OverflowFlyoutModel_Show_Original, + (void*)OverflowFlyoutModel_Show_Hook, + }, + { + { + LR"(public: __cdecl winrt::impl::consume_Windows_UI_Xaml_Controls_Primitives_IFlyoutBase5::ShowAt(struct winrt::Windows::UI::Xaml::DependencyObject const &,struct winrt::Windows::UI::Xaml::Controls::Primitives::FlyoutShowOptions const &)const )", + }, + (void**)&MenuFlyout_ShowAt_Original, + (void*)MenuFlyout_ShowAt_Hook, + }, + }; + + if (!HookSymbols(module, symbolHooks, ARRAYSIZE(symbolHooks))) { + return false; + } + } + + return true; +} + +bool HookTaskbarDllSymbols() { + HMODULE module = LoadLibrary(L"taskbar.dll"); + if (!module) { + Wh_Log(L"Failed to load taskbar.dll"); + return false; + } + + WindhawkUtils::SYMBOL_HOOK taskbarDllHooks[] = { + { + { + LR"(public: void __cdecl TrayUI::_StuckTrayChange(void))", + }, + (void**)&TrayUI__StuckTrayChange_Original, + }, + { + { + LR"(public: void __cdecl TrayUI::_HandleSettingChange(struct HWND__ *,unsigned int,unsigned __int64,__int64))", + }, + (void**)&TrayUI__HandleSettingChange_Original, + (void*)TrayUI__HandleSettingChange_Hook, + }, + { + { + LR"(public: virtual unsigned int __cdecl TrayUI::GetDockedRect(struct tagRECT *,int))", + }, + (void**)&TrayUI_GetDockedRect_Original, + (void*)TrayUI_GetDockedRect_Hook, + }, + { + { + LR"(public: virtual void __cdecl TrayUI::MakeStuckRect(struct tagRECT *,struct tagRECT const *,struct tagSIZE,unsigned int))", + }, + (void**)&TrayUI_MakeStuckRect_Original, + (void*)TrayUI_MakeStuckRect_Hook, + }, + { + { + LR"(public: virtual __int64 __cdecl TrayUI::WndProc(struct HWND__ *,unsigned int,unsigned __int64,__int64,bool *))", + }, + (void**)&TrayUI_WndProc_Original, + (void*)TrayUI_WndProc_Hook, + }, + { + { + LR"(private: virtual __int64 __cdecl CSecondaryTray::v_WndProc(struct HWND__ *,unsigned int,unsigned __int64,__int64))", + }, + (void**)&CSecondaryTray_v_WndProc_Original, + (void*)CSecondaryTray_v_WndProc_Hook, + }, + { + { + LR"(protected: long __cdecl CTaskListWnd::_ComputeJumpViewPosition(struct ITaskBtnGroup *,int,struct Windows::Foundation::Point &,enum Windows::UI::Xaml::HorizontalAlignment &,enum Windows::UI::Xaml::VerticalAlignment &)const )", + }, + (void**)&CTaskListWnd_ComputeJumpViewPosition_Original, + (void*)CTaskListWnd_ComputeJumpViewPosition_Hook, + }, + { + { + LR"(public: virtual int __cdecl CTaskListThumbnailWnd::DisplayUI(struct ITaskBtnGroup *,struct ITaskItem *,struct ITaskItem *,unsigned long))", + }, + (void**)&CTaskListThumbnailWnd_DisplayUI_Original, + (void*)CTaskListThumbnailWnd_DisplayUI_Hook, + }, + { + { + LR"(public: virtual void __cdecl CTaskListThumbnailWnd::LayoutThumbnails(void))", + }, + (void**)&CTaskListThumbnailWnd_LayoutThumbnails_Original, + (void*)CTaskListThumbnailWnd_LayoutThumbnails_Hook, + }, + { + { + LR"(public: __cdecl winrt::Windows::Internal::Shell::XamlExplorerHost::XamlExplorerHostWindow::XamlExplorerHostWindow(unsigned int,struct winrt::Windows::Foundation::Rect const &,unsigned int))", + }, + (void**)&XamlExplorerHostWindow_XamlExplorerHostWindow_Original, + (void*)XamlExplorerHostWindow_XamlExplorerHostWindow_Hook, + }, + }; + + return HookSymbols(module, taskbarDllHooks, ARRAYSIZE(taskbarDllHooks)); +} + +BOOL ModInitWithTaskbarView(HMODULE taskbarViewModule) { + if (!HookTaskbarViewDllSymbols(taskbarViewModule)) { + return FALSE; + } + + if (!HookTaskbarDllSymbols()) { + return FALSE; + } + + Wh_SetFunctionHook((void*)SetWindowPos, (void*)SetWindowPos_Hook, + (void**)&SetWindowPos_Original); + + return TRUE; +} + +BOOL Wh_ModInit() { + Wh_Log(L">"); + + LoadSettings(); + + g_target = Target::Explorer; + + WCHAR moduleFilePath[MAX_PATH]; + switch ( + GetModuleFileName(nullptr, moduleFilePath, ARRAYSIZE(moduleFilePath))) { + case 0: + case ARRAYSIZE(moduleFilePath): + Wh_Log(L"GetModuleFileName failed"); + break; + + default: + if (PCWSTR moduleFileName = wcsrchr(moduleFilePath, L'\\')) { + moduleFileName++; + if (_wcsicmp(moduleFileName, L"StartMenuExperienceHost.exe") == + 0) { + g_target = Target::StartMenu; + } else if (_wcsicmp(moduleFileName, L"SearchHost.exe") == 0) { + g_target = Target::SearchHost; + } + } else { + Wh_Log(L"GetModuleFileName returned an unsupported path"); + } + break; + } + + if (g_target == Target::StartMenu || g_target == Target::SearchHost) { + HMODULE user32Module = LoadLibrary(L"user32.dll"); + if (user32Module) { + void* pCreateWindowInBand = + (void*)GetProcAddress(user32Module, "CreateWindowInBand"); + if (pCreateWindowInBand) { + Wh_SetFunctionHook( + pCreateWindowInBand, + (void*)CoreWindowUI::CreateWindowInBand_Hook, + (void**)&CoreWindowUI::CreateWindowInBand_Original); + } + + void* pCreateWindowInBandEx = + (void*)GetProcAddress(user32Module, "CreateWindowInBandEx"); + if (pCreateWindowInBandEx) { + Wh_SetFunctionHook( + pCreateWindowInBandEx, + (void*)CoreWindowUI::CreateWindowInBandEx_Hook, + (void**)&CoreWindowUI::CreateWindowInBandEx_Original); + } + } + + Wh_SetFunctionHook((void*)SetWindowPos, + (void*)CoreWindowUI::SetWindowPos_Hook, + (void**)&SetWindowPos_Original); + return TRUE; + } + + if (!GetTaskbarViewDllPath(g_taskbarViewDllPath)) { + Wh_Log(L"Taskbar view module not found"); + return FALSE; + } + + HMODULE taskbarViewModule = LoadLibraryEx(g_taskbarViewDllPath, nullptr, + LOAD_WITH_ALTERED_SEARCH_PATH); + if (taskbarViewModule) { + return ModInitWithTaskbarView(taskbarViewModule); + } + + Wh_Log(L"Taskbar view module not loaded yet"); + return FALSE; +} + +void Wh_ModAfterInit() { + Wh_Log(L">"); + + g_initializedEvent = + CreateEvent(nullptr, true, false, "InitializedEvent_" WH_MOD_ID); + + if (g_target == Target::Explorer) { + ApplySettings(); + SetEvent(g_initializedEvent); + } else if (g_target == Target::StartMenu || + g_target == Target::SearchHost) { + // Wait for the taskbar reposition to complete. + WaitForSingleObject(g_initializedEvent, 1000); + CoreWindowUI::ApplySettings(); + } +} + +void Wh_ModBeforeUninit() { + Wh_Log(L">"); + + g_unloading = true; + + if (g_target == Target::Explorer) { + ApplySettings(); + } else if (g_target == Target::StartMenu || + g_target == Target::SearchHost) { + CoreWindowUI::ApplySettings(); + } +} + +void Wh_ModUninit() { + Wh_Log(L">"); + + while (g_hookCallCounter > 0) { + Sleep(100); + } + + if (g_initializedEvent) { + CloseHandle(g_initializedEvent); + } +} + +void Wh_ModSettingsChanged() { + Wh_Log(L">"); + + LoadSettings(); + + if (g_target == Target::Explorer) { + ApplySettings(); + } else if (g_target == Target::StartMenu || + g_target == Target::SearchHost) { + CoreWindowUI::ApplySettings(); + } +} From 4fd49be3b9999e5243d74bb2b79162f39039bcf3 Mon Sep 17 00:00:00 2001 From: Michael Maltsev <4129781+m417z@users.noreply.github.com> Date: Fri, 4 Oct 2024 21:02:01 +0300 Subject: [PATCH 44/52] Taskbar Background Helper v1.0.1 (#1032) * Added an optional configuration for dark mode. * Improved maximized window detection. --- mods/taskbar-background-helper.wh.cpp | 133 +++++++++++++++++++++++--- 1 file changed, 122 insertions(+), 11 deletions(-) diff --git a/mods/taskbar-background-helper.wh.cpp b/mods/taskbar-background-helper.wh.cpp index bdf45092..860ae0f5 100644 --- a/mods/taskbar-background-helper.wh.cpp +++ b/mods/taskbar-background-helper.wh.cpp @@ -2,13 +2,14 @@ // @id taskbar-background-helper // @name Taskbar Background Helper // @description Sets the taskbar background for the transparent parts, always or only when there's a maximized window, designed to be used with Windows 11 Taskbar Styler -// @version 1.0 +// @version 1.0.1 // @author m417z // @github https://github.com/m417z // @twitter https://twitter.com/m417z // @homepage https://m417z.com/ // @include explorer.exe // @architecture x86-64 +// @compilerOptions -ldwmapi // ==/WindhawkMod== // Source code is published under The GNU General Public License v3.0. @@ -46,18 +47,46 @@ a workaround. - color: Color - color: - red: 255 + $name: Red - green: 127 + $name: Green - blue: 39 + $name: Blue - transparency: 128 + $name: Transparency $name: Custom color $description: Values are between 0 and 255 - onlyWhenMaximized: true $name: Only when maximized $description: >- Only apply the style when there's a maximized window on the monitor +- styleForDarkMode: + - use: false + $name: Use a separate background for dark mode + - backgroundStyle: blur + $name: Background style + $options: + - blur: Blur + - acrylicBlur: Acrylic blur + - color: Color + - color: + - red: 255 + $name: Red + - green: 127 + $name: Green + - blue: 39 + $name: Blue + - transparency: 128 + $name: Transparency + $name: Custom color + $description: Values are between 0 and 255 + $name: Dark mode */ // ==/WindhawkModSettings== +#include + +#include #include enum class BackgroundStyle { @@ -66,10 +95,15 @@ enum class BackgroundStyle { color, }; -struct { +struct TaskbarStyle { BackgroundStyle backgroundStyle; COLORREF color; +}; + +struct { + TaskbarStyle style; bool onlyWhenMaximized; + std::optional darkModeStyle; } g_settings; HANDLE g_winObjectLocationChangeThread; @@ -107,7 +141,15 @@ enum WINDOWCOMPOSITIONATTRIB { WCA_CORNER_STYLE = 27, WCA_PART_COLOR = 28, WCA_DISABLE_MOVESIZE_FEEDBACK = 29, - WCA_LAST = 30 + WCA_SYSTEMBACKDROP_TYPE = 30, + WCA_SET_TAGGED_WINDOW_RECT = 31, + WCA_CLEAR_TAGGED_WINDOW_RECT = 32, + WCA_REMOTEAPP_POLICY = 33, + WCA_HAS_ACCENT_POLICY = 34, + WCA_REDIRECTIONBITMAP_FILL_COLOR = 35, + WCA_REDIRECTIONBITMAP_ALPHA = 36, + WCA_BORDER_MARGINS = 37, + WCA_LAST = 38, }; // Affects the rendering of the background of a window. @@ -145,9 +187,40 @@ using SetWindowCompositionAttribute_t = BOOL(WINAPI*)(HWND hWnd, const WINDOWCOMPOSITIONATTRIBDATA* pAttrData); SetWindowCompositionAttribute_t SetWindowCompositionAttribute_Original; +// https://stackoverflow.com/a/51336913 +bool IsWindowsDarkModeEnabled() { + constexpr WCHAR kSubKeyPath[] = + L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; + + DWORD value = 0; + DWORD valueSize = sizeof(value); + LONG result = + RegGetValue(HKEY_CURRENT_USER, kSubKeyPath, L"AppsUseLightTheme", + RRF_RT_REG_DWORD, nullptr, &value, &valueSize); + if (result != ERROR_SUCCESS) { + return false; + } + + return value == 0; +} + +// https://devblogs.microsoft.com/oldnewthing/20200302-00/?p=103507 +BOOL IsWindowCloaked(HWND hwnd) { + BOOL isCloaked = FALSE; + return SUCCEEDED(DwmGetWindowAttribute(hwnd, DWMWA_CLOAKED, &isCloaked, + sizeof(isCloaked))) && + isCloaked; +} + BOOL SetTaskbarStyle(HWND hWnd) { + TaskbarStyle& style = + (g_settings.darkModeStyle && IsWindowsDarkModeEnabled()) + ? *g_settings.darkModeStyle + : g_settings.style; + ACCENT_STATE accentState; - switch (g_settings.backgroundStyle) { + UINT accentFlags = 0; + switch (style.backgroundStyle) { case BackgroundStyle::blur: accentState = ACCENT_ENABLE_BLURBEHIND; break; @@ -158,10 +231,11 @@ BOOL SetTaskbarStyle(HWND hWnd) { case BackgroundStyle::color: accentState = ACCENT_ENABLE_TRANSPARENTGRADIENT; + accentFlags = 0x13; break; } - ACCENTPOLICY policy = {accentState, 0, g_settings.color, 0}; + ACCENTPOLICY policy = {accentState, accentFlags, style.color, 0}; WINDOWCOMPOSITIONATTRIBDATA data = {WCA_ACCENT_POLICY, &policy, sizeof(policy)}; @@ -234,6 +308,15 @@ bool DoesMonitorHaveMaximizedWindow(HMONITOR monitor) { return TRUE; } + if (!IsWindowVisible(hWnd) || IsWindowCloaked(hWnd) || IsIconic(hWnd)) { + return TRUE; + } + + if (GetWindowLong(hWnd, GWL_EXSTYLE) & + (WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW)) { + return TRUE; + } + WINDOWPLACEMENT wp{ .length = sizeof(WINDOWPLACEMENT), }; @@ -392,11 +475,11 @@ void AdjustAllTaskbarStyles() { void LoadSettings() { PCWSTR backgroundStyle = Wh_GetStringSetting(L"backgroundStyle"); - g_settings.backgroundStyle = BackgroundStyle::blur; + g_settings.style.backgroundStyle = BackgroundStyle::blur; if (wcscmp(backgroundStyle, L"acrylicBlur") == 0) { - g_settings.backgroundStyle = BackgroundStyle::acrylicBlur; + g_settings.style.backgroundStyle = BackgroundStyle::acrylicBlur; } else if (wcscmp(backgroundStyle, L"color") == 0) { - g_settings.backgroundStyle = BackgroundStyle::color; + g_settings.style.backgroundStyle = BackgroundStyle::color; } Wh_FreeStringSetting(backgroundStyle); @@ -405,11 +488,39 @@ void LoadSettings() { int blue = Wh_GetIntSetting(L"color.blue"); int transparency = Wh_GetIntSetting(L"color.transparency"); - g_settings.color = (COLORREF)((BYTE)red | ((WORD)((BYTE)green) << 8) | - (((DWORD)(BYTE)blue) << 16) | - (((DWORD)(BYTE)transparency) << 24)); + g_settings.style.color = (COLORREF)((BYTE)red | ((WORD)((BYTE)green) << 8) | + (((DWORD)(BYTE)blue) << 16) | + (((DWORD)(BYTE)transparency) << 24)); g_settings.onlyWhenMaximized = Wh_GetIntSetting(L"onlyWhenMaximized"); + + if (Wh_GetIntSetting(L"styleForDarkMode.use")) { + TaskbarStyle style; + + PCWSTR backgroundStyle = + Wh_GetStringSetting(L"styleForDarkMode.backgroundStyle"); + style.backgroundStyle = BackgroundStyle::blur; + if (wcscmp(backgroundStyle, L"acrylicBlur") == 0) { + style.backgroundStyle = BackgroundStyle::acrylicBlur; + } else if (wcscmp(backgroundStyle, L"color") == 0) { + style.backgroundStyle = BackgroundStyle::color; + } + Wh_FreeStringSetting(backgroundStyle); + + int red = Wh_GetIntSetting(L"styleForDarkMode.color.red"); + int green = Wh_GetIntSetting(L"styleForDarkMode.color.green"); + int blue = Wh_GetIntSetting(L"styleForDarkMode.color.blue"); + int transparency = + Wh_GetIntSetting(L"styleForDarkMode.color.transparency"); + + style.color = (COLORREF)((BYTE)red | ((WORD)((BYTE)green) << 8) | + (((DWORD)(BYTE)blue) << 16) | + (((DWORD)(BYTE)transparency) << 24)); + + g_settings.darkModeStyle = std::move(style); + } else { + g_settings.darkModeStyle.reset(); + } } BOOL Wh_ModInit() { From 31cb0bda3c0d239f220fad072bd4a2be53c6ff05 Mon Sep 17 00:00:00 2001 From: Michael Maltsev <4129781+m417z@users.noreply.github.com> Date: Sat, 5 Oct 2024 02:25:20 +0300 Subject: [PATCH 45/52] Vertical Taskbar for Windows 11 v1.2.5 (#1034) * Improved tray overflow position, now it's vertically centered to the mouse position and isn't shown far away from the taskbar. * Fixed right click menu location for some tray icons. * Fixed jump list width on high DPIs. --- mods/taskbar-vertical.wh.cpp | 280 ++++++++++++++++++----------------- 1 file changed, 143 insertions(+), 137 deletions(-) diff --git a/mods/taskbar-vertical.wh.cpp b/mods/taskbar-vertical.wh.cpp index 26859914..a26cfb29 100644 --- a/mods/taskbar-vertical.wh.cpp +++ b/mods/taskbar-vertical.wh.cpp @@ -2,7 +2,7 @@ // @id taskbar-vertical // @name Vertical Taskbar for Windows 11 // @description Finally, the missing vertical taskbar option for Windows 11! -// @version 1.2.4 +// @version 1.2.5 // @author m417z // @github https://github.com/m417z // @twitter https://twitter.com/m417z @@ -144,7 +144,6 @@ bool g_inSystemTrayController_UpdateFrameSize; bool g_inAugmentedEntryPointButton_UpdateButtonPadding; bool g_inCTaskListThumbnailWnd_DisplayUI; bool g_inCTaskListThumbnailWnd_LayoutThumbnails; -bool g_inChevronSystemTrayIconDataModel2_OnIconClicked; bool g_inOverflowFlyoutModel_Show; std::vector> g_notifyIconsUpdated; @@ -638,19 +637,13 @@ HRESULT WINAPI CTaskListWnd_ComputeJumpViewPosition_Hook( UINT monitorDpiY = 96; GetDpiForMonitor(monitor, MDT_DEFAULT, &monitorDpiX, &monitorDpiY); - RECT rc; - GetMonitorRect(monitor, &rc); - - int taskbarWidthScaled = MulDiv(g_settings.taskbarWidth, monitorDpiX, 96); - switch (GetTaskbarLocationForMonitor(monitor)) { case TaskbarLocation::left: - point->X = rc.left + taskbarWidthScaled; + point->X = monitorInfo.rcWork.left; break; case TaskbarLocation::right: - point->X = rc.right - taskbarWidthScaled; - point->X -= 159; + point->X = monitorInfo.rcWork.right - MulDiv(159, monitorDpiX, 96); break; } @@ -661,13 +654,11 @@ HRESULT WINAPI CTaskListWnd_ComputeJumpViewPosition_Hook( // Avoid returning a point too close to the top of the monitor which causes // the menu to be cut off. - int minY = rc.top + MulDiv(130, monitorDpiY, 96); + int minY = monitorInfo.rcWork.top + MulDiv(130, monitorDpiY, 96); if (point->Y < minY) { point->Y = minY; } - *verticalAlignment = VerticalAlignment::Center; - return ret; } @@ -1901,68 +1892,6 @@ void WINAPI OverflowFlyoutList_OnApplyTemplate_Hook(LPVOID pThis) { } } -using ChevronSystemTrayIconDataModel2_OnIconClicked_t = - void(WINAPI*)(void* pThis, void* param1); -ChevronSystemTrayIconDataModel2_OnIconClicked_t - ChevronSystemTrayIconDataModel2_OnIconClicked_Original; -void WINAPI ChevronSystemTrayIconDataModel2_OnIconClicked_Hook(void* pThis, - void* param1) { - Wh_Log(L">"); - - g_inChevronSystemTrayIconDataModel2_OnIconClicked = true; - - ChevronSystemTrayIconDataModel2_OnIconClicked_Original(pThis, param1); - - g_inChevronSystemTrayIconDataModel2_OnIconClicked = false; -} - -using OverflowXamlIslandManager_Show_t = void(WINAPI*)(void* pThis, - POINT point, - void* param2); -OverflowXamlIslandManager_Show_t OverflowXamlIslandManager_Show_Original; -void WINAPI OverflowXamlIslandManager_Show_Hook(void* pThis, - POINT point, - void* param2) { - Wh_Log(L">"); - - if (g_inChevronSystemTrayIconDataModel2_OnIconClicked) { - DWORD messagePos = GetMessagePos(); - point = POINT{ - GET_X_LPARAM(messagePos), - GET_Y_LPARAM(messagePos), - }; - - HMONITOR monitor = MonitorFromPoint(point, MONITOR_DEFAULTTONEAREST); - MONITORINFO monitorInfo{ - .cbSize = sizeof(MONITORINFO), - }; - GetMonitorInfo(monitor, &monitorInfo); - UINT monitorDpiX = 96; - UINT monitorDpiY = 96; - GetDpiForMonitor(monitor, MDT_DEFAULT, &monitorDpiX, &monitorDpiY); - - if (point.x < monitorInfo.rcWork.left) { - point.x = monitorInfo.rcWork.left; - } else if (point.x > monitorInfo.rcWork.right) { - point.x = monitorInfo.rcWork.right; - } - - switch (GetTaskbarLocationForMonitor(monitor)) { - case TaskbarLocation::left: - point.x += MulDiv(104 + 12, monitorDpiX, 96); - break; - - case TaskbarLocation::right: - point.x -= MulDiv(104 + 12, monitorDpiX, 96); - break; - } - - point.y += MulDiv(28, monitorDpiY, 96); - } - - OverflowXamlIslandManager_Show_Original(pThis, point, param2); -} - using CopilotIcon_UpdateVisualStates_t = void(WINAPI*)(void* pThis); CopilotIcon_UpdateVisualStates_t CopilotIcon_UpdateVisualStates_Original; void WINAPI CopilotIcon_UpdateVisualStates_Hook(void* pThis) { @@ -2116,6 +2045,44 @@ void WINAPI OverflowFlyoutModel_Show_Hook(void* pThis) { g_inOverflowFlyoutModel_Show = false; } +using IPointerPoint_RawPosition_t = winrt::Windows::Foundation::Point*( + WINAPI*)(void* pThis, winrt::Windows::Foundation::Point* point); +IPointerPoint_RawPosition_t IPointerPoint_RawPosition_Original; +winrt::Windows::Foundation::Point* WINAPI +IPointerPoint_RawPosition_Hook(void* pThis, + winrt::Windows::Foundation::Point* point) { + Wh_Log(L">"); + + auto original = [=]() { + return IPointerPoint_RawPosition_Original(pThis, point); + }; + + HWND hTaskbarWnd = GetTaskbarWnd(); + if (!hTaskbarWnd) { + return original(); + } + + RECT taskbarRectNative; + if (!GetWindowRect_Original(hTaskbarWnd, &taskbarRectNative)) { + return original(); + } + + UINT taskbarDpi = GetDpiForWindow(hTaskbarWnd); + + int taskbarHeight = MulDiv(taskbarRectNative.bottom - taskbarRectNative.top, + 96, taskbarDpi); + + auto* ret = IPointerPoint_RawPosition_Original(pThis, point); + + // Adjust to account for the taskbar rotation. Used for tray icon events. + *ret = winrt::Windows::Foundation::Point{ + taskbarHeight - ret->Y, + ret->X, + }; + + return ret; +} + BOOL WINAPI GetWindowRect_Hook(HWND hWnd, LPRECT lpRect) { BOOL ret = GetWindowRect_Original(hWnd, lpRect); if (ret && !g_unloading && @@ -2148,84 +2115,130 @@ BOOL WINAPI SetWindowPos_Hook(HWND hWnd, int cx, int cy, UINT uFlags) { - auto original = [&]() { + auto original = [=]() { return SetWindowPos_Original(hWnd, hWndInsertAfter, X, Y, cx, cy, uFlags); }; - if ((uFlags & (SWP_NOSIZE | SWP_NOMOVE)) || - (!g_inCTaskListThumbnailWnd_DisplayUI && - !g_inCTaskListThumbnailWnd_LayoutThumbnails)) { + if (uFlags & (SWP_NOSIZE | SWP_NOMOVE)) { return original(); } - WCHAR szClassName[32]; + WCHAR szClassName[64]; if (GetClassName(hWnd, szClassName, ARRAYSIZE(szClassName)) == 0) { return original(); } - if (_wcsicmp(szClassName, L"TaskListThumbnailWnd") != 0) { - return original(); - } - - DWORD messagePos = GetMessagePos(); - POINT pt{ - GET_X_LPARAM(messagePos), - GET_Y_LPARAM(messagePos), - }; + if (_wcsicmp(szClassName, L"TaskListThumbnailWnd") == 0) { + if (!g_inCTaskListThumbnailWnd_DisplayUI && + !g_inCTaskListThumbnailWnd_LayoutThumbnails) { + return original(); + } - HMONITOR monitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST); + DWORD messagePos = GetMessagePos(); + POINT pt{ + GET_X_LPARAM(messagePos), + GET_Y_LPARAM(messagePos), + }; - RECT rc{}; - if (g_inCTaskListThumbnailWnd_DisplayUI) { - const int distance = MulDiv(12, GetDpiForWindow(hWnd), 96); + HMONITOR monitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST); - SIZE sz{ - .cx = cx + distance * 2, - .cy = cy + distance * 2, - }; + RECT rc{}; + if (g_inCTaskListThumbnailWnd_DisplayUI) { + const int distance = MulDiv(12, GetDpiForWindow(hWnd), 96); + + SIZE sz{ + .cx = cx + distance * 2, + .cy = cy + distance * 2, + }; + + UINT alignment; + switch (GetTaskbarLocationForMonitor(monitor)) { + case TaskbarLocation::left: + alignment = TPM_LEFTALIGN; + break; + + case TaskbarLocation::right: + alignment = TPM_RIGHTALIGN; + break; + } - UINT alignment; - switch (GetTaskbarLocationForMonitor(monitor)) { - case TaskbarLocation::left: - alignment = TPM_LEFTALIGN; - break; + CalculatePopupWindowPosition( + &pt, &sz, alignment | TPM_VCENTERALIGN | TPM_WORKAREA, nullptr, + &rc); - case TaskbarLocation::right: - alignment = TPM_RIGHTALIGN; - break; + rc.left += distance; + rc.right -= distance; + rc.top += distance; + rc.bottom -= distance; + } else { + // Keep current position. + GetWindowRect_Original(hWnd, &rc); + rc.bottom = rc.top + cy; + + switch (GetTaskbarLocationForMonitor(monitor)) { + case TaskbarLocation::left: + rc.right = rc.left + cx; + break; + + case TaskbarLocation::right: + rc.left = rc.right - cx; + break; + } } - CalculatePopupWindowPosition( - &pt, &sz, alignment | TPM_VCENTERALIGN | TPM_WORKAREA, nullptr, - &rc); + X = rc.left; + Y = rc.top; + cx = rc.right - rc.left; + cy = rc.bottom - rc.top; + } else if (_wcsicmp(szClassName, L"TopLevelWindowForOverflowXamlIsland") == + 0) { + DWORD messagePos = GetMessagePos(); + POINT pt{ + GET_X_LPARAM(messagePos), + GET_Y_LPARAM(messagePos), + }; - rc.left += distance; - rc.right -= distance; - rc.top += distance; - rc.bottom -= distance; - } else { - // Keep current position. - GetWindowRect_Original(hWnd, &rc); - rc.bottom = rc.top + cy; + HMONITOR monitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST); + + MONITORINFO monitorInfo{ + .cbSize = sizeof(MONITORINFO), + }; + GetMonitorInfo(monitor, &monitorInfo); switch (GetTaskbarLocationForMonitor(monitor)) { case TaskbarLocation::left: - rc.right = rc.left + cx; + X = monitorInfo.rcWork.left; break; case TaskbarLocation::right: - rc.left = rc.right - cx; + X = monitorInfo.rcWork.right - cx; break; } + + if (!IsWindowVisible(hWnd)) { + Y = pt.y - cy / 2; + } else { + // Keep current position. + RECT rc; + GetWindowRect_Original(hWnd, &rc); + int prevCenterY = rc.top + (rc.bottom - rc.top) / 2; + Y = prevCenterY - cy / 2; + } + + if (Y < monitorInfo.rcWork.top) { + Y = monitorInfo.rcWork.top; + } else if (Y > monitorInfo.rcWork.bottom - cy) { + Y = monitorInfo.rcWork.bottom - cy; + } + } else { + return original(); } - Wh_Log(L"Adjusting pos for TaskListThumbnailWnd: %dx%d, %dx%d", rc.left, - rc.right, rc.top, rc.bottom); + Wh_Log(L"Adjusting pos for %s: %dx%d, %dx%d", szClassName, X, Y, X + cx, + Y + cy); - return SetWindowPos_Original(hWnd, hWndInsertAfter, rc.left, rc.top, - rc.right - rc.left, rc.bottom - rc.top, - uFlags); + return SetWindowPos_Original(hWnd, hWndInsertAfter, X, Y, cx, cy, uFlags); } using MoveWindow_t = decltype(&MoveWindow); @@ -2833,20 +2846,6 @@ bool HookTaskbarViewDllSymbols(HMODULE module) { (void**)&OverflowFlyoutList_OnApplyTemplate_Original, (void*)OverflowFlyoutList_OnApplyTemplate_Hook, }, - { - { - LR"(public: void __cdecl winrt::SystemTray::implementation::ChevronSystemTrayIconDataModel2::OnIconClicked(struct winrt::SystemTray::IconClickedEventArgs const &))", - }, - (void**)&ChevronSystemTrayIconDataModel2_OnIconClicked_Original, - (void*)ChevronSystemTrayIconDataModel2_OnIconClicked_Hook, - }, - { - { - LR"(public: void __cdecl winrt::SystemTray::OverflowXamlIslandManager::Show(struct tagPOINT,enum winrt::WindowsUdk::UI::Shell::InputDeviceKind))", - }, - (void**)&OverflowXamlIslandManager_Show_Original, - (void*)OverflowXamlIslandManager_Show_Hook, - }, { { LR"(private: void __cdecl winrt::SystemTray::implementation::CopilotIcon::UpdateVisualStates(void))", @@ -2868,6 +2867,13 @@ bool HookTaskbarViewDllSymbols(HMODULE module) { (void**)&OverflowFlyoutModel_Show_Original, (void*)OverflowFlyoutModel_Show_Hook, }, + { + { + LR"(public: __cdecl winrt::impl::consume_Windows_UI_Input_IPointerPoint::RawPosition(void)const )", + }, + (void**)&IPointerPoint_RawPosition_Original, + (void*)IPointerPoint_RawPosition_Hook, + }, }; if (!HookSymbols(module, symbolHooks, ARRAYSIZE(symbolHooks))) { From 2fb9f53e0e636376c07c33fa9a861345c4572e1a Mon Sep 17 00:00:00 2001 From: Michael Maltsev <4129781+m417z@users.noreply.github.com> Date: Sat, 5 Oct 2024 17:21:28 +0300 Subject: [PATCH 46/52] Taskbar on top for Windows 11 v1.0.1 (#1039) * Improved jump list size and position handling. * Adjusted the 1px taskbar border to be at the bottom of the taskbar. * Fixed the start menu disappearing or being positioned incorrectly sometimes. --- mods/taskbar-on-top.wh.cpp | 618 +++++++++++++++---------------------- 1 file changed, 256 insertions(+), 362 deletions(-) diff --git a/mods/taskbar-on-top.wh.cpp b/mods/taskbar-on-top.wh.cpp index f849d611..d11f15f6 100644 --- a/mods/taskbar-on-top.wh.cpp +++ b/mods/taskbar-on-top.wh.cpp @@ -2,16 +2,14 @@ // @id taskbar-on-top // @name Taskbar on top for Windows 11 // @description Moves the Windows 11 taskbar to the top of the screen -// @version 1.0 +// @version 1.0.1 // @author m417z // @github https://github.com/m417z // @twitter https://twitter.com/m417z // @homepage https://m417z.com/ // @include explorer.exe -// @include StartMenuExperienceHost.exe -// @include SearchHost.exe // @architecture x86-64 -// @compilerOptions -DWINVER=0x0A00 -lole32 -loleaut32 -lruntimeobject -lshcore +// @compilerOptions -DWINVER=0x0A00 -ldwmapi -lole32 -loleaut32 -lruntimeobject -lshcore // ==/WindhawkMod== // Source code is published under The GNU General Public License v3.0. @@ -33,7 +31,7 @@ Moves the Windows 11 taskbar to the top of the screen. The mod was designed for up-to-date Windows 11 versions 22H2 to 24H2. Other versions weren't tested and are probably not compatible. -![Screenshot](https://i.imgur.com/dCABsum.png) +![Screenshot](https://i.imgur.com/LqBwGVn.png) */ // ==/WindhawkModReadme== @@ -57,6 +55,7 @@ versions weren't tested and are probably not compatible. #include // must come before knownfolders.h +#include #include #include #include @@ -72,7 +71,6 @@ versions weren't tested and are probably not compatible. #include #include #include -#include using namespace winrt::Windows::UI::Xaml; @@ -90,38 +88,20 @@ struct { TaskbarLocation taskbarLocationSecondary; } g_settings; -enum class Target { - Explorer, - StartMenu, - SearchHost, -}; - -Target g_target; - -HANDLE g_initializedEvent; WCHAR g_taskbarViewDllPath[MAX_PATH]; std::atomic g_applyingSettings; -std::atomic g_pendingMeasureOverride; std::atomic g_unloading; std::atomic g_hookCallCounter; -int g_originalTaskbarHeight; -bool g_inSystemTrayController_UpdateFrameSize; -bool g_inAugmentedEntryPointButton_UpdateButtonPadding; bool g_inCTaskListThumbnailWnd_DisplayUI; bool g_inCTaskListThumbnailWnd_LayoutThumbnails; bool g_inOverflowFlyoutModel_Show; -std::vector> g_notifyIconsUpdated; - using FrameworkElementLoadedEventRevoker = winrt::impl::event_revoker< IFrameworkElement, &winrt::impl::abi::type::remove_Loaded>; -std::list g_notifyIconAutoRevokerList; - -int g_copilotPosTimerCounter; -UINT_PTR g_copilotPosTimer; +std::list g_elementLoadedAutoRevokerList; WINUSERAPI UINT WINAPI GetDpiForWindow(HWND hwnd); typedef enum MONITOR_DPI_TYPE { @@ -135,6 +115,11 @@ STDAPI GetDpiForMonitor(HMONITOR hmonitor, UINT* dpiX, UINT* dpiY); +// Available since Windows 10 version 1607, missing in older MinGW headers. +using GetThreadDescription_t = + WINBASEAPI HRESULT(WINAPI*)(HANDLE hThread, PWSTR* ppszThreadDescription); +GetThreadDescription_t pGetThreadDescription; + bool GetMonitorRect(HMONITOR monitor, RECT* rc) { MONITORINFO monitorInfo{ .cbSize = sizeof(MONITORINFO), @@ -143,34 +128,6 @@ bool GetMonitorRect(HMONITOR monitor, RECT* rc) { CopyRect(rc, &monitorInfo.rcMonitor); } -bool GetMonitorRectDpiUnscaled(HMONITOR monitor, RECT* rc) { - if (!GetMonitorRect(monitor, rc)) { - return false; - } - - UINT monitorDpiX = 96; - UINT monitorDpiY = 96; - GetDpiForMonitor(monitor, MDT_DEFAULT, &monitorDpiX, &monitorDpiY); - - rc->left = MulDiv(rc->left, 96, monitorDpiX); - rc->top = MulDiv(rc->top, 96, monitorDpiY); - rc->right = MulDiv(rc->right, 96, monitorDpiX); - rc->bottom = MulDiv(rc->bottom, 96, monitorDpiY); - return true; -} - -int GetPrimaryMonitorHeightDpiUnscaled() { - const POINT ptZero = {0, 0}; - HMONITOR primaryMonitor = - MonitorFromPoint(ptZero, MONITOR_DEFAULTTOPRIMARY); - RECT monitorRect; - if (!GetMonitorRectDpiUnscaled(primaryMonitor, &monitorRect)) { - return 0; - } - - return monitorRect.bottom - monitorRect.top; -} - bool IsChildOfElementByName(FrameworkElement element, PCWSTR name) { auto parent = element; while (true) { @@ -509,11 +466,10 @@ HRESULT WINAPI CTaskListWnd_ComputeJumpViewPosition_Hook( .cbSize = sizeof(MONITORINFO), }; GetMonitorInfo(monitor, &monitorInfo); - UINT monitorDpiX = 96; - UINT monitorDpiY = 96; - GetDpiForMonitor(monitor, MDT_DEFAULT, &monitorDpiX, &monitorDpiY); - point->Y = monitorInfo.rcWork.top + MulDiv(125, monitorDpiY, 96); + // Place at the bottom of the monitor, will reposition later in + // SetWindowPos. + point->Y = monitorInfo.rcWork.bottom; return ret; } @@ -598,6 +554,74 @@ void* WINAPI XamlExplorerHostWindow_XamlExplorerHostWindow_Hook( rect, param3); } +void ApplyTaskbarFrameStyle(FrameworkElement taskbarFrame) { + if (g_settings.taskbarLocation != TaskbarLocation::top) { + return; + } + + FrameworkElement backgroundStroke = nullptr; + + FrameworkElement child = taskbarFrame; + if ((child = FindChildByName(child, L"RootGrid")) && + (child = FindChildByName(child, L"BackgroundControl")) && + (child = + FindChildByClassName(child, L"Windows.UI.Xaml.Controls.Grid")) && + (child = FindChildByName(child, L"BackgroundStroke"))) { + backgroundStroke = child; + } + + if (!backgroundStroke) { + return; + } + + backgroundStroke.VerticalAlignment(VerticalAlignment::Bottom); +} + +using TaskbarFrame_TaskbarFrame_t = void*(WINAPI*)(void* pThis); +TaskbarFrame_TaskbarFrame_t TaskbarFrame_TaskbarFrame_Original; +void* WINAPI TaskbarFrame_TaskbarFrame_Hook(void* pThis) { + Wh_Log(L">"); + + pThis = TaskbarFrame_TaskbarFrame_Original(pThis); + + FrameworkElement taskbarFrame = nullptr; + ((IUnknown**)pThis)[1]->QueryInterface(winrt::guid_of(), + winrt::put_abi(taskbarFrame)); + if (!taskbarFrame) { + return pThis; + } + + g_elementLoadedAutoRevokerList.emplace_back(); + auto autoRevokerIt = g_elementLoadedAutoRevokerList.end(); + --autoRevokerIt; + + *autoRevokerIt = taskbarFrame.Loaded( + winrt::auto_revoke_t{}, + [autoRevokerIt](winrt::Windows::Foundation::IInspectable const& sender, + winrt::Windows::UI::Xaml::RoutedEventArgs const& e) { + Wh_Log(L">"); + + g_elementLoadedAutoRevokerList.erase(autoRevokerIt); + + auto taskbarFrame = sender.try_as(); + if (!taskbarFrame) { + return; + } + + auto className = winrt::get_class_name(taskbarFrame); + Wh_Log(L"className: %s", className.c_str()); + + try { + ApplyTaskbarFrameStyle(taskbarFrame); + } catch (...) { + HRESULT hr = winrt::to_hresult(); + Wh_Log(L"Error %08X", hr); + } + }); + + return pThis; +} + void ApplySystemTrayChevronIconViewStyle( FrameworkElement systemTrayChevronIconViewElement) { if (g_settings.taskbarLocation != TaskbarLocation::top) { @@ -629,22 +653,22 @@ void ApplySystemTrayChevronIconViewStyle( baseTextBlock.RenderTransformOrigin({origin, origin}); } -using IconView_IconView_t = void(WINAPI*)(PVOID pThis); +using IconView_IconView_t = void*(WINAPI*)(void* pThis); IconView_IconView_t IconView_IconView_Original; -void WINAPI IconView_IconView_Hook(PVOID pThis) { +void* WINAPI IconView_IconView_Hook(void* pThis) { Wh_Log(L">"); - IconView_IconView_Original(pThis); + pThis = IconView_IconView_Original(pThis); FrameworkElement iconView = nullptr; ((IUnknown**)pThis)[1]->QueryInterface(winrt::guid_of(), winrt::put_abi(iconView)); if (!iconView) { - return; + return pThis; } - g_notifyIconAutoRevokerList.emplace_back(); - auto autoRevokerIt = g_notifyIconAutoRevokerList.end(); + g_elementLoadedAutoRevokerList.emplace_back(); + auto autoRevokerIt = g_elementLoadedAutoRevokerList.end(); --autoRevokerIt; *autoRevokerIt = iconView.Loaded( @@ -653,7 +677,7 @@ void WINAPI IconView_IconView_Hook(PVOID pThis) { winrt::Windows::UI::Xaml::RoutedEventArgs const& e) { Wh_Log(L">"); - g_notifyIconAutoRevokerList.erase(autoRevokerIt); + g_elementLoadedAutoRevokerList.erase(autoRevokerIt); auto iconView = sender.try_as(); if (!iconView) { @@ -669,6 +693,8 @@ void WINAPI IconView_IconView_Hook(PVOID pThis) { } } }); + + return pThis; } using OverflowFlyoutModel_Show_t = void(WINAPI*)(void* pThis); @@ -753,16 +779,16 @@ BOOL WINAPI SetWindowPos_Hook(HWND hWnd, uFlags); }; - if (uFlags & (SWP_NOSIZE | SWP_NOMOVE)) { - return original(); - } - WCHAR szClassName[64]; if (GetClassName(hWnd, szClassName, ARRAYSIZE(szClassName)) == 0) { return original(); } if (_wcsicmp(szClassName, L"TaskListThumbnailWnd") == 0) { + if (uFlags & SWP_NOMOVE) { + return original(); + } + if (!g_inCTaskListThumbnailWnd_DisplayUI && !g_inCTaskListThumbnailWnd_LayoutThumbnails) { return original(); @@ -796,6 +822,10 @@ BOOL WINAPI SetWindowPos_Hook(HWND hWnd, } else if (_wcsicmp(szClassName, L"TopLevelWindowForOverflowXamlIsland") == 0 || _wcsicmp(szClassName, L"Xaml_WindowedPopupClass") == 0) { + if (uFlags & (SWP_NOMOVE | SWP_NOSIZE)) { + return original(); + } + DWORD messagePos = GetMessagePos(); POINT pt{ GET_X_LPARAM(messagePos), @@ -817,274 +847,193 @@ BOOL WINAPI SetWindowPos_Hook(HWND hWnd, } else if (Y > monitorInfo.rcWork.bottom - cy) { Y = monitorInfo.rcWork.bottom - cy; } - } else { - return original(); - } - - Wh_Log(L"Adjusting pos for %s: %dx%d, %dx%d", szClassName, X, Y, X + cx, - Y + cy); - - return SetWindowPos_Original(hWnd, hWndInsertAfter, X, Y, cx, cy, uFlags); -} - -namespace CoreWindowUI { - -bool IsTargetCoreWindow(HWND hWnd, int* extraXAdjustment) { - DWORD threadId = 0; - DWORD processId = 0; - if (!hWnd || !(threadId = GetWindowThreadProcessId(hWnd, &processId)) || - processId != GetCurrentProcessId()) { - return false; - } - - WCHAR szClassName[32]; - if (GetClassName(hWnd, szClassName, ARRAYSIZE(szClassName)) == 0) { - return false; - } + } else if (_wcsicmp(szClassName, L"Windows.UI.Core.CoreWindow") == 0) { + if (uFlags & SWP_NOMOVE) { + return original(); + } - if (_wcsicmp(szClassName, L"Windows.UI.Core.CoreWindow") != 0) { - return false; - } + DWORD threadId = GetWindowThreadProcessId(hWnd, nullptr); + if (!threadId) { + return original(); + } - return true; -} + HANDLE thread = + OpenThread(THREAD_QUERY_LIMITED_INFORMATION, FALSE, threadId); + if (!thread) { + return original(); + } -std::vector GetCoreWindows() { - struct ENUM_WINDOWS_PARAM { - std::vector* hWnds; - }; + PWSTR threadDescription; + HRESULT hr = pGetThreadDescription + ? pGetThreadDescription(thread, &threadDescription) + : E_FAIL; + CloseHandle(thread); + if (FAILED(hr)) { + return original(); + } - std::vector hWnds; - ENUM_WINDOWS_PARAM param = {&hWnds}; - EnumWindows( - [](HWND hWnd, LPARAM lParam) WINAPI -> BOOL { - ENUM_WINDOWS_PARAM& param = *(ENUM_WINDOWS_PARAM*)lParam; + bool isJumpViewUI = wcscmp(threadDescription, L"JumpViewUI") == 0; - if (IsTargetCoreWindow(hWnd, nullptr)) { - param.hWnds->push_back(hWnd); - } + LocalFree(threadDescription); - return TRUE; - }, - (LPARAM)¶m); + if (!isJumpViewUI) { + return original(); + } - return hWnds; -} + DWORD messagePos = GetMessagePos(); + POINT pt{ + GET_X_LPARAM(messagePos), + GET_Y_LPARAM(messagePos), + }; -void AdjustCoreWindowSize(int x, int y, int* width, int* height) { - if (g_target != Target::StartMenu) { - return; - } + HMONITOR monitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST); + if (GetTaskbarLocationForMonitor(monitor) == TaskbarLocation::bottom) { + return original(); + } - const POINT pt = {x, y}; - HMONITOR monitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST); - if (GetTaskbarLocationForMonitor(monitor) == TaskbarLocation::bottom) { MONITORINFO monitorInfo{ .cbSize = sizeof(MONITORINFO), }; GetMonitorInfo(monitor, &monitorInfo); - *height = monitorInfo.rcWork.bottom - monitorInfo.rcWork.top; - return; + Y = monitorInfo.rcWork.top; + } else { + return original(); } - UINT monitorDpiX = 96; - UINT monitorDpiY = 96; - GetDpiForMonitor(monitor, MDT_DEFAULT, &monitorDpiX, &monitorDpiY); + Wh_Log(L"Adjusting pos for %s: %dx%d, %dx%d", szClassName, X, Y, X + cx, + Y + cy); - const int h1 = MulDiv(750, monitorDpiY, 96); - const int h2 = MulDiv(694, monitorDpiY, 96); - if (*height >= h1) { - *height = h1; - } else if (*height >= h2) { - *height = h2; - } + return SetWindowPos_Original(hWnd, hWndInsertAfter, X, Y, cx, cy, uFlags); } -void AdjustCoreWindowPos(int* x, int* y, int width, int height) { - const POINT pt = {*x, *y}; - HMONITOR monitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST); - if (GetTaskbarLocationForMonitor(monitor) == TaskbarLocation::bottom) { - if (g_target == Target::StartMenu) { - *x = 0; - *y = 0; - } - - return; +std::wstring GetProcessFileName(DWORD dwProcessId) { + HANDLE hProcess = + OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, dwProcessId); + if (!hProcess) { + return std::wstring{}; } - MONITORINFO monitorInfo{ - .cbSize = sizeof(MONITORINFO), - }; - GetMonitorInfo(monitor, &monitorInfo); + WCHAR processPath[MAX_PATH]; - if (g_target == Target::StartMenu || g_target == Target::SearchHost) { - *y = monitorInfo.rcWork.top; + DWORD dwSize = ARRAYSIZE(processPath); + if (!QueryFullProcessImageName(hProcess, 0, processPath, &dwSize)) { + CloseHandle(hProcess); + return std::wstring{}; } -} -void ApplySettings() { - for (HWND hCoreWnd : GetCoreWindows()) { - Wh_Log(L"Adjusting core window %08X", (DWORD)(ULONG_PTR)hCoreWnd); + CloseHandle(hProcess); - RECT rc; - if (!GetWindowRect(hCoreWnd, &rc)) { - continue; - } + PCWSTR processFileNameUpper = wcsrchr(processPath, L'\\'); + if (!processFileNameUpper) { + return std::wstring{}; + } - int x = rc.left; - int y = rc.top; - int cx = rc.right - rc.left; - int cy = rc.bottom - rc.top; + processFileNameUpper++; + return processFileNameUpper; +} - AdjustCoreWindowSize(x, y, &cx, &cy); - AdjustCoreWindowPos(&x, &y, cx, cy); +using DwmSetWindowAttribute_t = decltype(&DwmSetWindowAttribute); +DwmSetWindowAttribute_t DwmSetWindowAttribute_Original; +HRESULT WINAPI DwmSetWindowAttribute_Hook(HWND hwnd, + DWORD dwAttribute, + LPCVOID pvAttribute, + DWORD cbAttribute) { + auto original = [=]() { + return DwmSetWindowAttribute_Original(hwnd, dwAttribute, pvAttribute, + cbAttribute); + }; - SetWindowPos_Original(hCoreWnd, nullptr, x, y, cx, cy, - SWP_NOZORDER | SWP_NOACTIVATE); + if (dwAttribute != DWMWA_CLOAK || cbAttribute != sizeof(BOOL)) { + return original(); } -} -using CreateWindowInBand_t = HWND(WINAPI*)(DWORD dwExStyle, - LPCWSTR lpClassName, - LPCWSTR lpWindowName, - DWORD dwStyle, - int X, - int Y, - int nWidth, - int nHeight, - HWND hWndParent, - HMENU hMenu, - HINSTANCE hInstance, - PVOID lpParam, - DWORD dwBand); -CreateWindowInBand_t CreateWindowInBand_Original; -HWND WINAPI CreateWindowInBand_Hook(DWORD dwExStyle, - LPCWSTR lpClassName, - LPCWSTR lpWindowName, - DWORD dwStyle, - int X, - int Y, - int nWidth, - int nHeight, - HWND hWndParent, - HMENU hMenu, - HINSTANCE hInstance, - PVOID lpParam, - DWORD dwBand) { - BOOL bTextualClassName = ((ULONG_PTR)lpClassName & ~(ULONG_PTR)0xffff) != 0; - if (bTextualClassName && - _wcsicmp(lpClassName, L"Windows.UI.Core.CoreWindow") == 0) { - Wh_Log(L"Creating core window"); - AdjustCoreWindowSize(X, Y, &nWidth, &nHeight); - AdjustCoreWindowPos(&X, &Y, nWidth, nHeight); + BOOL cloak = *(BOOL*)pvAttribute; + if (cloak) { + return original(); } - return CreateWindowInBand_Original( - dwExStyle, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, - hWndParent, hMenu, hInstance, lpParam, dwBand); -} + HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); + if (GetTaskbarLocationForMonitor(monitor) == TaskbarLocation::bottom) { + return original(); + } + + Wh_Log(L"> %08X", (DWORD)(DWORD_PTR)hwnd); -using CreateWindowInBandEx_t = HWND(WINAPI*)(DWORD dwExStyle, - LPCWSTR lpClassName, - LPCWSTR lpWindowName, - DWORD dwStyle, - int X, - int Y, - int nWidth, - int nHeight, - HWND hWndParent, - HMENU hMenu, - HINSTANCE hInstance, - PVOID lpParam, - DWORD dwBand, - DWORD dwTypeFlags); -CreateWindowInBandEx_t CreateWindowInBandEx_Original; -HWND WINAPI CreateWindowInBandEx_Hook(DWORD dwExStyle, - LPCWSTR lpClassName, - LPCWSTR lpWindowName, - DWORD dwStyle, - int X, - int Y, - int nWidth, - int nHeight, - HWND hWndParent, - HMENU hMenu, - HINSTANCE hInstance, - PVOID lpParam, - DWORD dwBand, - DWORD dwTypeFlags) { - BOOL bTextualClassName = ((ULONG_PTR)lpClassName & ~(ULONG_PTR)0xffff) != 0; - if (bTextualClassName && - _wcsicmp(lpClassName, L"Windows.UI.Core.CoreWindow") == 0) { - Wh_Log(L"Creating core window"); - AdjustCoreWindowSize(X, Y, &nWidth, &nHeight); - AdjustCoreWindowPos(&X, &Y, nWidth, nHeight); + DWORD processId = 0; + if (!hwnd || !GetWindowThreadProcessId(hwnd, &processId)) { + return original(); } - return CreateWindowInBandEx_Original( - dwExStyle, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, - hWndParent, hMenu, hInstance, lpParam, dwBand, dwTypeFlags); -} + std::wstring processFileName = GetProcessFileName(processId); -BOOL WINAPI SetWindowPos_Hook(HWND hWnd, - HWND hWndInsertAfter, - int X, - int Y, - int cx, - int cy, - UINT uFlags) { - auto original = [&]() { - return SetWindowPos_Original(hWnd, hWndInsertAfter, X, Y, cx, cy, - uFlags); + enum class Target { + StartMenu, + SearchHost, }; + Target target; - int extraXAdjustment = 0; - if (!IsTargetCoreWindow(hWnd, &extraXAdjustment)) { + if (_wcsicmp(processFileName.c_str(), L"StartMenuExperienceHost.exe") == + 0) { + target = Target::StartMenu; + } else if (_wcsicmp(processFileName.c_str(), L"SearchHost.exe") == 0) { + target = Target::SearchHost; + } else { return original(); } - Wh_Log(L"%08X %08X", (DWORD)(ULONG_PTR)hWnd, uFlags); + UINT monitorDpiX = 96; + UINT monitorDpiY = 96; + GetDpiForMonitor(monitor, MDT_DEFAULT, &monitorDpiX, &monitorDpiY); - if ((uFlags & (SWP_NOSIZE | SWP_NOMOVE)) == (SWP_NOSIZE | SWP_NOMOVE)) { + RECT targetRect; + if (!GetWindowRect(hwnd, &targetRect)) { return original(); } - RECT rc{}; - GetWindowRect(hWnd, &rc); + int x = targetRect.left; + int y = targetRect.top; + int cx = targetRect.right - targetRect.left; + int cy = targetRect.bottom - targetRect.top; + + if (target == Target::StartMenu) { + // Only change height. + const int h1 = MulDiv(750, monitorDpiY, 96); + const int h2 = MulDiv(694, monitorDpiY, 96); + int cyNew = cy; + if (cyNew >= h1) { + cyNew = h1; + } else if (cyNew >= h2) { + cyNew = h2; + } - // SearchHost is being moved by explorer.exe, then the size is adjusted - // by SearchHost itself. Make SearchHost adjust the position too. A - // similar workaround is needed for other windows. - if (uFlags & SWP_NOMOVE) { - uFlags &= ~SWP_NOMOVE; - X = rc.left; - Y = rc.top; - } + if (cyNew == cy) { + return original(); + } - int width; - int height; - if (uFlags & SWP_NOSIZE) { - width = rc.right - rc.left; - height = rc.bottom - rc.top; - AdjustCoreWindowSize(X, Y, &width, &height); - } else { - AdjustCoreWindowSize(X, Y, &cx, &cy); - width = cx; - height = cy; - } + cy = cyNew; + } else if (target == Target::SearchHost) { + // Only change y. + MONITORINFO monitorInfo{ + .cbSize = sizeof(MONITORINFO), + }; + GetMonitorInfo(monitor, &monitorInfo); + + int yNew = monitorInfo.rcWork.top; + + if (yNew == y) { + return original(); + } - if (!(uFlags & SWP_NOMOVE)) { - AdjustCoreWindowPos(&X, &Y, width, height); + y = yNew; } - X += extraXAdjustment; + SetWindowPos_Original(hwnd, nullptr, x, y, cx, cy, + SWP_NOZORDER | SWP_NOACTIVATE); - return SetWindowPos_Original(hWnd, hWndInsertAfter, X, Y, cx, cy, uFlags); + return original(); } -} // namespace CoreWindowUI - void LoadSettings() { PCWSTR taskbarLocation = Wh_GetStringSetting(L"taskbarLocation"); g_settings.taskbarLocation = TaskbarLocation::top; @@ -1167,6 +1116,13 @@ bool HookTaskbarViewDllSymbols(HMODULE module) { { // Taskbar.View.dll, ExplorerExtensions.dll WindhawkUtils::SYMBOL_HOOK symbolHooks[] = { + { + { + LR"(public: __cdecl winrt::Taskbar::implementation::TaskbarFrame::TaskbarFrame(void))", + }, + (void**)&TaskbarFrame_TaskbarFrame_Original, + (void*)TaskbarFrame_TaskbarFrame_Hook, + }, { { LR"(public: __cdecl winrt::SystemTray::implementation::IconView::IconView(void))", @@ -1292,6 +1248,17 @@ BOOL ModInitWithTaskbarView(HMODULE taskbarViewModule) { Wh_SetFunctionHook((void*)SetWindowPos, (void*)SetWindowPos_Hook, (void**)&SetWindowPos_Original); + HMODULE dwmapiModule = LoadLibrary(L"dwmapi.dll"); + if (dwmapiModule) { + FARPROC pDwmSetWindowAttribute = + GetProcAddress(dwmapiModule, "DwmSetWindowAttribute"); + if (pDwmSetWindowAttribute) { + Wh_SetFunctionHook((void*)pDwmSetWindowAttribute, + (void*)DwmSetWindowAttribute_Hook, + (void**)&DwmSetWindowAttribute_Original); + } + } + return TRUE; } @@ -1300,57 +1267,9 @@ BOOL Wh_ModInit() { LoadSettings(); - g_target = Target::Explorer; - - WCHAR moduleFilePath[MAX_PATH]; - switch ( - GetModuleFileName(nullptr, moduleFilePath, ARRAYSIZE(moduleFilePath))) { - case 0: - case ARRAYSIZE(moduleFilePath): - Wh_Log(L"GetModuleFileName failed"); - break; - - default: - if (PCWSTR moduleFileName = wcsrchr(moduleFilePath, L'\\')) { - moduleFileName++; - if (_wcsicmp(moduleFileName, L"StartMenuExperienceHost.exe") == - 0) { - g_target = Target::StartMenu; - } else if (_wcsicmp(moduleFileName, L"SearchHost.exe") == 0) { - g_target = Target::SearchHost; - } - } else { - Wh_Log(L"GetModuleFileName returned an unsupported path"); - } - break; - } - - if (g_target == Target::StartMenu || g_target == Target::SearchHost) { - HMODULE user32Module = LoadLibrary(L"user32.dll"); - if (user32Module) { - void* pCreateWindowInBand = - (void*)GetProcAddress(user32Module, "CreateWindowInBand"); - if (pCreateWindowInBand) { - Wh_SetFunctionHook( - pCreateWindowInBand, - (void*)CoreWindowUI::CreateWindowInBand_Hook, - (void**)&CoreWindowUI::CreateWindowInBand_Original); - } - - void* pCreateWindowInBandEx = - (void*)GetProcAddress(user32Module, "CreateWindowInBandEx"); - if (pCreateWindowInBandEx) { - Wh_SetFunctionHook( - pCreateWindowInBandEx, - (void*)CoreWindowUI::CreateWindowInBandEx_Hook, - (void**)&CoreWindowUI::CreateWindowInBandEx_Original); - } - } - - Wh_SetFunctionHook((void*)SetWindowPos, - (void*)CoreWindowUI::SetWindowPos_Hook, - (void**)&SetWindowPos_Original); - return TRUE; + if (HMODULE kernel32Module = LoadLibrary(L"kernel32.dll")) { + pGetThreadDescription = (GetThreadDescription_t)GetProcAddress( + kernel32Module, "GetThreadDescription"); } if (!GetTaskbarViewDllPath(g_taskbarViewDllPath)) { @@ -1371,18 +1290,7 @@ BOOL Wh_ModInit() { void Wh_ModAfterInit() { Wh_Log(L">"); - g_initializedEvent = - CreateEvent(nullptr, true, false, "InitializedEvent_" WH_MOD_ID); - - if (g_target == Target::Explorer) { - ApplySettings(); - SetEvent(g_initializedEvent); - } else if (g_target == Target::StartMenu || - g_target == Target::SearchHost) { - // Wait for the taskbar reposition to complete. - WaitForSingleObject(g_initializedEvent, 1000); - CoreWindowUI::ApplySettings(); - } + ApplySettings(); } void Wh_ModBeforeUninit() { @@ -1390,12 +1298,7 @@ void Wh_ModBeforeUninit() { g_unloading = true; - if (g_target == Target::Explorer) { - ApplySettings(); - } else if (g_target == Target::StartMenu || - g_target == Target::SearchHost) { - CoreWindowUI::ApplySettings(); - } + ApplySettings(); } void Wh_ModUninit() { @@ -1404,10 +1307,6 @@ void Wh_ModUninit() { while (g_hookCallCounter > 0) { Sleep(100); } - - if (g_initializedEvent) { - CloseHandle(g_initializedEvent); - } } void Wh_ModSettingsChanged() { @@ -1415,10 +1314,5 @@ void Wh_ModSettingsChanged() { LoadSettings(); - if (g_target == Target::Explorer) { - ApplySettings(); - } else if (g_target == Target::StartMenu || - g_target == Target::SearchHost) { - CoreWindowUI::ApplySettings(); - } + ApplySettings(); } From 6b0109c8f0d35deb6b81a0440bc637dc8e0f0fe3 Mon Sep 17 00:00:00 2001 From: Mark Jansen Date: Sat, 5 Oct 2024 16:45:42 +0200 Subject: [PATCH 47/52] Play-Media-Key fix in Explorer 1.1 (#1040) Simplify initialization to prevent the explorer process hanging --- mods/lm-mediakey-explorer-fix.wh.cpp | 78 ++++++++-------------------- 1 file changed, 23 insertions(+), 55 deletions(-) diff --git a/mods/lm-mediakey-explorer-fix.wh.cpp b/mods/lm-mediakey-explorer-fix.wh.cpp index 3b3f532b..0524e301 100644 --- a/mods/lm-mediakey-explorer-fix.wh.cpp +++ b/mods/lm-mediakey-explorer-fix.wh.cpp @@ -2,12 +2,11 @@ // @id lm-mediakey-explorer-fix // @name Play-Media-Key fix in Explorer // @description Fix the Media 'play' key being suppressed in Explorer when a file is selected. -// @version 1.0 +// @version 1.1 // @author Mark Jansen // @github https://github.com/learn-more // @twitter https://twitter.com/learn_more // @include explorer.exe -// @compilerOptions -luuid -lole32 // ==/WindhawkMod== // ==WindhawkModReadme== @@ -18,11 +17,11 @@ the play event never reaches a media player like Spotify. This mod makes sure that the play event is not sent to the file, but to the media player instead. */ // ==/WindhawkModReadme== -#include +#include -WNDPROC pSHELLDLL_DefViewProc = NULL; +WNDPROC pSHELLDLL_DefViewProc = NULL; LRESULT CALLBACK SHELLDLL_DefViewProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { if (uMsg == WM_APPCOMMAND && GET_APPCOMMAND_LPARAM(lParam) == APPCOMMAND_MEDIA_PLAY_PAUSE) @@ -33,6 +32,22 @@ LRESULT CALLBACK SHELLDLL_DefViewProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA return pSHELLDLL_DefViewProc(hWnd, uMsg, wParam, lParam); } +#define IS_ATOM(x) (((ULONG_PTR)(x) > 0x0) && ((ULONG_PTR)(x) < 0x10000)) +static ATOM (WINAPI *pSHELL32_RegisterClassW) (CONST WNDCLASSW *lpWndClass) = 0; +ATOM WINAPI SHELL32_RegisterClassW (CONST WNDCLASSW *lpWndClass) +{ + if (pSHELLDLL_DefViewProc == NULL && + lpWndClass && + !IS_ATOM(lpWndClass->lpszClassName) && + !wcsicmp(lpWndClass->lpszClassName, L"SHELLDLL_DefView")) + { + Wh_Log(L"Got SHELLDLL_DefView, hooking it!"); + Wh_SetFunctionHook((VOID*)lpWndClass->lpfnWndProc, (void*)SHELLDLL_DefViewProc, (void**)&pSHELLDLL_DefViewProc); + Wh_ApplyHookOperations(); + } + return pSHELL32_RegisterClassW(lpWndClass); +} + BOOL Wh_ModInit() { @@ -41,60 +56,13 @@ BOOL Wh_ModInit() BOOL bRet; if (!(bRet = GetClassInfoW(GetModuleHandleW(L"shell32.dll"), L"SHELLDLL_DefView", &SHELLDLL_DefView))) { - Wh_Log(L"SHELLDLL_DefView not available yet, trying to register it.."); - // Initialize COM and open an apartment - HRESULT hrInit = CoInitialize(NULL); - if (SUCCEEDED(hrInit) || hrInit == RPC_E_CHANGED_MODE) - { - IExplorerBrowser* pExplorerBrowser; - // Create a browser object - HRESULT hr = SHCoCreateInstance(NULL, &CLSID_ExplorerBrowser, NULL, IID_PPV_ARGS(&pExplorerBrowser)); - if (SUCCEEDED(hr)) - { - RECT rc = {}; - FOLDERSETTINGS fs = { FVM_DETAILS }; - hr = pExplorerBrowser->Initialize(GetDesktopWindow(), &rc, &fs); - if (SUCCEEDED(hr)) - { - ITEMIDLIST desktop = {}; - // Browsing to a folder will create the view - hr = pExplorerBrowser->BrowseToIDList(&desktop, SBSP_ABSOLUTE); - if (SUCCEEDED(hr)) - { - // Now the class should be registered! - bRet = GetClassInfoW(GetModuleHandleW(L"shell32.dll"), L"SHELLDLL_DefView", &SHELLDLL_DefView); - } - else - { - Wh_Log(L"BrowseToIDList failed with 0x%p", hr); - } - } - else - { - Wh_Log(L"Initialize failed with 0x%p", hr); - } - pExplorerBrowser->Destroy(); - pExplorerBrowser->Release(); - } - else - { - Wh_Log(L"SHCoCreateInstance failed with 0x%p", hr); - } - if (hrInit != RPC_E_CHANGED_MODE) - { - CoUninitialize(); - } - } - else - { - Wh_Log(L"SHCoCreateInstance failed with 0x%p", hrInit); - } + Wh_Log(L"SHELLDLL_DefView not available yet, hooking SHELL32!RegisterClassW instead"); + WindhawkUtils::Wh_SetFunctionHookT(RegisterClassW, SHELL32_RegisterClassW, &pSHELL32_RegisterClassW); } - - if (bRet) + else { Wh_Log(L"Hook WndProc"); - Wh_SetFunctionHook((VOID*)SHELLDLL_DefView.lpfnWndProc, (void*)SHELLDLL_DefViewProc, (void**)&pSHELLDLL_DefViewProc); + WindhawkUtils::Wh_SetFunctionHookT(SHELLDLL_DefView.lpfnWndProc, SHELLDLL_DefViewProc, &pSHELLDLL_DefViewProc); } return TRUE; From 4e8186fedde55e38422264383447bbb570fde161 Mon Sep 17 00:00:00 2001 From: Anixx Date: Sun, 6 Oct 2024 10:06:35 +0300 Subject: [PATCH 48/52] Update classic-theme-explorer-lite.wh.cpp (#1043) Add additional check to the ClientEdge adder at suggestion of @aubymori --- mods/classic-theme-explorer-lite.wh.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/mods/classic-theme-explorer-lite.wh.cpp b/mods/classic-theme-explorer-lite.wh.cpp index c39bee44..8ea32765 100644 --- a/mods/classic-theme-explorer-lite.wh.cpp +++ b/mods/classic-theme-explorer-lite.wh.cpp @@ -2,7 +2,7 @@ // @id classic-theme-explorer-lite // @name Classic Theme Explorer Lite // @description Classic Theme mitigations for Explorer ported from Explorer Patcher -// @version 1.1.1 +// @version 1.1.2 // @author Anixx // @github https://github.com/Anixx // @include explorer.exe @@ -35,10 +35,14 @@ HWND WINAPI CreateWindowExW_Hook(DWORD dwExStyle,LPCWSTR lpClassName,LPCWSTR lpW // Disable this block if you don't want 3D borders in folders if ((((ULONG_PTR)lpClassName & ~(ULONG_PTR)0xffff) != 0) && !wcscmp(lpClassName, L"SysListView32")) { - GetClassNameW(GetParent(hWndParent), wszClassName, 200); - if (wcscmp(wszClassName, L"Progman")) + GetClassNameW(hWndParent, wszClassName, 200); + if (!wcscmp(wszClassName, L"SHELLDLL_DefView")) { - dwExStyle |= WS_EX_CLIENTEDGE; + GetClassNameW(GetParent(hWndParent), wszClassName, 200); + if (wcscmp(wszClassName, L"Progman")) + { + dwExStyle |= WS_EX_CLIENTEDGE; + } } } From 5d24a3a59980affaff8b2e34006a3a3d788e006c Mon Sep 17 00:00:00 2001 From: Cyprinus Carpio Date: Sun, 6 Oct 2024 16:02:25 +0000 Subject: [PATCH 49/52] Classic Explorer Treeview: Update 1.1.1 (#1044) - Added a option to draw a gradient background for the header for XP-like looks - Added a context menu for the header like in real InfoBands - A theme-accurate button frame is now drawn for the X button --- mods/classic-explorer-treeview.wh.cpp | 160 ++++++++++++++++++-------- 1 file changed, 112 insertions(+), 48 deletions(-) diff --git a/mods/classic-explorer-treeview.wh.cpp b/mods/classic-explorer-treeview.wh.cpp index aa678877..d4a4fe0e 100644 --- a/mods/classic-explorer-treeview.wh.cpp +++ b/mods/classic-explorer-treeview.wh.cpp @@ -2,11 +2,11 @@ // @id classic-explorer-treeview // @name Classic Explorer Treeview // @description Modifies Folder Treeview in file explorer so as to make it look more classic. -// @version 1.1 +// @version 1.1.1 // @author Waldemar // @github https://github.com/CyprinusCarpio // @include explorer.exe -// @compilerOptions -lcomctl32 -lgdi32 -loleaut32 -lole32 -lshlwapi +// @compilerOptions -lcomctl32 -lgdi32 -loleaut32 -lole32 -lshlwapi -lruntimeobject -luxtheme // @architecture x86-64 // ==/WindhawkMod== @@ -21,6 +21,9 @@ - LinesAtRoot: false $name: Lines At Root $description: Use TVS_LINESATROOT style. +- GradientBackground: false + $name: Gradient Background + $description: Draw a gradient in the header. Enable for a more accurate XP-like look. - DrawButtons: true $name: Draw +/- Buttons $description: Should the mod draw it's own +/- buttons. Disable only if not using Classic theme. @@ -60,6 +63,11 @@ For issue reports, contact waldemar3194 on Discord, or file a report at my [gith # Changelog: +## 1.1.1 +- Added a option to draw a gradient background for the header for XP-like looks +- Added a context menu for the header like in real InfoBands +- A theme-accurate button frame is now drawn for the X button + ## 1.1 - Enabling the folders pane will now hide real InfoBands and vice versa - Fixed a hidden bug where clicking the X button many times would hang Explorer @@ -135,6 +143,7 @@ are enabled. */ // ==/WindhawkModReadme== +#include #define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp)) #define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp)) @@ -148,11 +157,14 @@ are enabled. #include #include #include +#include +#include // Mod settings bool g_settingDrawDottedLines = true; bool g_settingLinesAtRoot = false; bool g_settingDrawButtons = true; +bool g_settingsGradientBackground = false; WindhawkUtils::StringSetting g_settingLineColorOption; WindhawkUtils::StringSetting g_settingFoldersPaneText; int g_settingCloseButtonXOffset = 0; @@ -161,6 +173,8 @@ int g_settingCloseButtonYOffset = 0; int g_lineColorOptionInt = 2; wchar_t g_defaultFoldersPaneText[32]; wchar_t g_menuTreeviewText[32]; +wchar_t g_closeText[32]; +wchar_t g_toolBandText[32]; std::vector g_knownBrowserBandIds; @@ -516,18 +530,28 @@ LRESULT CALLBACK NTCSubclassProc(_In_ HWND hWnd, PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); - HBRUSH brush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE)); - HBRUSH original = (HBRUSH)SelectObject(hdc, brush); - // Get the themed band offset and calculate the rectangle for the background, and draw it int themeBandOffset = GetThemedBandOffset(); RECT rect; GetClientRect(hWnd, &rect); long origBottom = rect.bottom; - rect.bottom = rect.top + 22 + themeBandOffset; + + HBRUSH brush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE)); + HBRUSH original = (HBRUSH)SelectObject(hdc, brush); FillRect(hdc, &rect, brush); SelectObject(hdc, original); DeleteObject(brush); + + if(g_settingsGradientBackground) + { + HTHEME hThemeRebar = OpenThemeData(NULL, L"REBAR"); + if(hThemeRebar) + { + DrawThemeBackground(hThemeRebar, hdc, RP_BACKGROUND, 0, &rect, NULL); + CloseThemeData(hThemeRebar); + } + } + long origTop = rect.top; // Create and fill the top line of the rebar @@ -559,6 +583,25 @@ LRESULT CALLBACK NTCSubclassProc(_In_ HWND hWnd, } } + // If the mouse is over the button, draw the X button frame + if (eFlags & MOUSE_OVER_BUTTON) + { + RECT buttonFrame; + GetClientRect(hWnd, &buttonFrame); + SetCloseButtonRect(buttonFrame); + // If a theme is enabled, draw the theme correct button frame + HTHEME hThemeToolbar = OpenThemeData(hWnd, L"TOOLBAR"); + if(hThemeToolbar) + { + DrawThemeBackground(hThemeToolbar, hdc, TP_BUTTON, eFlags & MOUSE_PRESSED ? TS_PRESSED : TS_HOT, &buttonFrame, NULL); + CloseThemeData(hThemeToolbar); + } + else + DrawEdge(hdc, &buttonFrame, + eFlags & MOUSE_PRESSED ? BDR_SUNKENOUTER : BDR_RAISEDINNER, + BF_RECT); + } + // Create a brush for the close button HBRUSH closeColor = CreateSolidBrush(GetSysColor(COLOR_BTNTEXT)); @@ -653,20 +696,12 @@ LRESULT CALLBACK NTCSubclassProc(_In_ HWND hWnd, SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT)); SetBkColor(hdc, GetSysColor(COLOR_BTNFACE)); - //Draw the etched edge around the NSTC - DrawEdge(hdc, &rect, EDGE_ETCHED, BF_RECT); - - // If the mouse is over the button, draw a raised inner border over the close button - if (eFlags & MOUSE_OVER_BUTTON) + if(!g_settingsGradientBackground) { - RECT buttonFrame; - GetClientRect(hWnd, &buttonFrame); - SetCloseButtonRect(buttonFrame); - DrawEdge( - hdc, &buttonFrame, - eFlags & MOUSE_PRESSED ? BDR_SUNKENOUTER : BDR_RAISEDINNER, - BF_RECT); + //Draw the etched edge around the NSTC + DrawEdge(hdc, &rect, EDGE_ETCHED, BF_RECT); } + SelectObject(hdc, original); // Get the font and draw the folder pane text @@ -680,6 +715,7 @@ LRESULT CALLBACK NTCSubclassProc(_In_ HWND hWnd, PCWSTR foldersPaneText = g_settingFoldersPaneText.get(); unsigned int foldersPaneTextLength; + SetBkMode(hdc, TRANSPARENT); if (lstrcmpW(foldersPaneText, L"default") == 0) { foldersPaneTextLength = wcslen(g_defaultFoldersPaneText); @@ -820,18 +856,50 @@ LRESULT CALLBACK NTCSubclassProc(_In_ HWND hWnd, // Check if the mouse click is within the button rectangle if (xPos >= rect.left && xPos <= rect.right && yPos >= rect.top && yPos <= rect.bottom) { - size_t i = 0; - for (; i < s; i++) + for (size_t i = 0; i < s; i++) { if (hWnd == g_FEWExtras[i].hNSTC) { // Remove the flag so that the button edge won't be drawn after the treeview is enabled again g_FEWExtras[i].eFlags &= ~MOUSE_OVER_BUTTON; + g_FEWExtras[i].SetTreeviewVisible(false); + break; + } + } + } + } + break; + case WM_RBUTTONUP: + { + // Open info band context menu + POINT p; + p.x = GET_X_LPARAM(lParam); + p.y = GET_Y_LPARAM(lParam); + ClientToScreen(hWnd, &p); + + static std::wstring menuItem = L""; + if(menuItem == L"") + { + menuItem += g_closeText; + menuItem += L" "; + menuItem += g_toolBandText; + } + + HMENU hMenu = CreatePopupMenu(); + AppendMenuW(hMenu, MF_STRING, 1, menuItem.c_str()); + int sel = TrackPopupMenu(hMenu, TPM_RETURNCMD, p.x, p.y, 0, hWnd, NULL); + DestroyMenu(hMenu); + + if(sel == 1) // Close the treeview + { + for (size_t i = 0; i < g_FEWExtras.size(); i++) + { + if (hWnd == g_FEWExtras[i].hNSTC) + { + g_FEWExtras[i].SetTreeviewVisible(false); break; } } - //SetFoldersPaneVisible(GetParent(GetParent(GetParent(GetParent(hWnd)))), false); - g_FEWExtras[i].SetTreeviewVisible(false); } } break; @@ -1107,7 +1175,7 @@ BOOL Wh_ModInit() } // Define the symbol hooks for required member functions - WindhawkUtils::SYMBOL_HOOK hooks[] = + WindhawkUtils::SYMBOL_HOOK explorerframe_dll_hooks[] = { { { L"private: struct HWND__ * __cdecl CNscTree::_CreateTreeview(struct HWND__ *)" @@ -1153,7 +1221,7 @@ BOOL Wh_ModInit() } }; // Hook the symbols in explorerframe.dll, return FALSE if any of the symbols cannot be hooked - if (!WindhawkUtils::HookSymbols(hExplorerFrame, hooks, 6)) + if (!WindhawkUtils::HookSymbols(hExplorerFrame, explorerframe_dll_hooks, 6)) { Wh_Log(L"Failed to hook one or more member functions in ExplorerFrame.dll"); return FALSE; @@ -1163,6 +1231,7 @@ BOOL Wh_ModInit() g_settingDrawDottedLines = Wh_GetIntSetting(L"DrawLines"); g_settingLinesAtRoot = Wh_GetIntSetting(L"LinesAtRoot"); g_settingDrawButtons = Wh_GetIntSetting(L"DrawButtons"); + g_settingsGradientBackground = Wh_GetIntSetting(L"GradientBackground"); g_settingLineColorOption = WindhawkUtils::StringSetting::make(L"AlternateLineColor"); g_settingFoldersPaneText = @@ -1181,43 +1250,37 @@ BOOL Wh_ModInit() g_lineColorOptionInt = 2; } - // Set the folders pane text based on the string setting - if (wcscmp(g_settingFoldersPaneText.get(), L"default") == 0) + //Load the applicable localized texts from ieframe + HMODULE hIeFrame = LoadLibraryW(L"ieframe.dll"); + if(hIeFrame) { - Wh_Log(L"Loading default localized folders pane text."); - HMODULE shell32 = GetModuleHandleW(L"shell32.dll"); - if (!shell32) + if (wcscmp(g_settingFoldersPaneText.get(), L"default") == 0) { - // Load the default, non localized text - Wh_Log(L"Unable to load localized text resource: shell32 9045"); - std::wstring def = L"Folders"; - def.copy(g_defaultFoldersPaneText, 32); - } - else - { - // Load the default localized folders pane text from shell32 resources - // On failure, load the default, non localized text - if (LoadStringW(shell32, 9045, g_defaultFoldersPaneText, 32) == 0) + if (LoadStringW(hIeFrame, 12919, g_defaultFoldersPaneText, 32) == 0) { - Wh_Log( - L"Unable to load localized text resource: shell32 " - L"9045"); + Wh_Log(L"Unable to load localized text resource: ieframe 12919"); std::wstring def = L"Folders"; def.copy(g_defaultFoldersPaneText, 32); } } - } - - //Load the localized Treeview text - HMODULE hIeFrame = LoadLibraryW(L"ieframe.dll"); - if(hIeFrame) - { if (LoadStringW(hIeFrame, 3010, g_menuTreeviewText, 32) == 0) { Wh_Log(L"Unable to load localized text resource: ieframe 3010"); std::wstring def = L"Treeview"; def.copy(g_menuTreeviewText, 32); } + if (LoadStringW(hIeFrame, 41298, g_toolBandText, 32) == 0) + { + Wh_Log(L"Unable to load localized text resource: ieframe 41298"); + std::wstring def = L"toolbar"; + def.copy(g_toolBandText, 32); + } + if (LoadStringW(hIeFrame, 53810, g_closeText, 32) == 0) + { + Wh_Log(L"Unable to load localized text resource: ieframe 53810"); + std::wstring def = L"Close"; + def.copy(g_closeText, 32); + } FreeLibrary(hIeFrame); } @@ -1247,6 +1310,7 @@ void Wh_ModSettingsChanged() g_settingDrawDottedLines = Wh_GetIntSetting(L"DrawLines"); g_settingLinesAtRoot = Wh_GetIntSetting(L"LinesAtRoot"); g_settingDrawButtons = Wh_GetIntSetting(L"DrawButtons"); + g_settingsGradientBackground = Wh_GetIntSetting(L"GradientBackground"); g_settingLineColorOption = WindhawkUtils::StringSetting::make(L"AlternateLineColor"); g_settingFoldersPaneText = From 257e03044689fb70c5b9f492a19258a463b037d6 Mon Sep 17 00:00:00 2001 From: Cyprinus Carpio Date: Sun, 6 Oct 2024 16:23:10 +0000 Subject: [PATCH 50/52] Remove Command Bar: limit to 64bit arch (#1045) Limit to 64bit architecture. --- mods/remove-command-bar.wh.cpp | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/mods/remove-command-bar.wh.cpp b/mods/remove-command-bar.wh.cpp index 49594221..7f1abe0e 100644 --- a/mods/remove-command-bar.wh.cpp +++ b/mods/remove-command-bar.wh.cpp @@ -2,10 +2,11 @@ // @id remove-command-bar // @name Remove Command Bar // @description Removes the Command Bar from file explorer. -// @version 1.0 +// @version 1.0.1 // @author Waldemar // @github https://github.com/CyprinusCarpio // @include explorer.exe +// @architecture x86-64 // ==/WindhawkMod== // ==WindhawkModReadme== @@ -38,21 +39,13 @@ For issue reports, contact waldemar3194 on Discord, or file a report at my [gith #include #include -#ifdef _WIN64 -#define CALCON __cdecl -#define SCALCON L"__cdecl" -#else -#define CALCON __stdcall -#define SCALCON L"__stdcall" -#endif - const std::wstring toLookFor = L"