From e2a37f8dbad409efb2ab3f1d33d3068c2b74dcbe Mon Sep 17 00:00:00 2001 From: Michael Maltsev <4129781+m417z@users.noreply.github.com> Date: Mon, 24 Feb 2025 23:11:50 +0200 Subject: [PATCH] Taskbar keyboard-only auto-hide v1.0 --- mods/taskbar-auto-hide-keyboard-only.wh.cpp | 341 ++++++++++++++++++++ 1 file changed, 341 insertions(+) create mode 100644 mods/taskbar-auto-hide-keyboard-only.wh.cpp diff --git a/mods/taskbar-auto-hide-keyboard-only.wh.cpp b/mods/taskbar-auto-hide-keyboard-only.wh.cpp new file mode 100644 index 00000000..24640fdd --- /dev/null +++ b/mods/taskbar-auto-hide-keyboard-only.wh.cpp @@ -0,0 +1,341 @@ +// ==WindhawkMod== +// @id taskbar-auto-hide-keyboard-only +// @name Taskbar keyboard-only auto-hide +// @description When taskbar auto-hide is enabled, the taskbar will only be unhidden with the keyboard, hovering the mouse over the taskbar will not unhide it +// @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== +/* +# Taskbar keyboard-only auto-hide + +When taskbar auto-hide is enabled, the taskbar will only be unhidden with the +keyboard, hovering the mouse over the taskbar will not unhide it. For example, +the Win key or Ctrl+Esc will unhide the taskbar. + +**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. +*/ +// ==/WindhawkModReadme== + +#include + +#include + +#include + +struct { + int showSpeedup; + int hideSpeedup; + int frameRate; + bool oldTaskbarOnWin11; +} g_settings; + +enum class WinVersion { + Unsupported, + Win10, + Win11, + Win11_24H2, +}; + +WinVersion g_winVersion; + +std::atomic g_initialized; +std::atomic g_explorerPatcherInitialized; + +using TrayUI_SetUnhideTimer_t = void(WINAPI*)(void* pThis, + long param1, + long param2); +TrayUI_SetUnhideTimer_t TrayUI_SetUnhideTimer_Original; +void WINAPI TrayUI_SetUnhideTimer_Hook(void* pThis, long param1, long param2) { + Wh_Log(L">"); + + // TrayUI_SetUnhideTimer_Original(pThis, param1, param2); +} + +bool HookTaskbarSymbols() { + 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; + } + } + + // Taskbar.dll, explorer.exe + WindhawkUtils::SYMBOL_HOOK symbolHooks[] = { + { + {LR"(public: virtual void __cdecl TrayUI::SetUnhideTimer(long,long))"}, + &TrayUI_SetUnhideTimer_Original, + TrayUI_SetUnhideTimer_Hook, + }, + }; + + if (!HookSymbols(module, symbolHooks, ARRAYSIZE(symbolHooks))) { + Wh_Log(L"HookSymbols failed"); + return false; + } + + return true; +} + +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 if (build < 26100) { + return WinVersion::Win11; + } else { + return WinVersion::Win11_24H2; + } + break; + } + + return WinVersion::Unsupported; +} + +struct EXPLORER_PATCHER_HOOK { + PCSTR symbol; + void** pOriginalFunction; + void* hookFunction = nullptr; + bool optional = false; + + template + EXPLORER_PATCHER_HOOK( + PCSTR symbol, + Prototype** originalFunction, + std::type_identity_t hookFunction = nullptr, + bool optional = false) + : symbol(symbol), + pOriginalFunction(reinterpret_cast(originalFunction)), + hookFunction(reinterpret_cast(hookFunction)), + optional(optional) {} +}; + +bool HookExplorerPatcherSymbols(HMODULE explorerPatcherModule) { + if (g_explorerPatcherInitialized.exchange(true)) { + return true; + } + + if (g_winVersion >= WinVersion::Win11) { + g_winVersion = WinVersion::Win10; + } + + EXPLORER_PATCHER_HOOK hooks[] = { + {R"(?SetUnhideTimer@TrayUI@@UEAAXJJ@Z)", + &TrayUI_SetUnhideTimer_Original, TrayUI_SetUnhideTimer_Hook}, + }; + + bool succeeded = true; + + for (const auto& hook : hooks) { + void* ptr = (void*)GetProcAddress(explorerPatcherModule, hook.symbol); + if (!ptr) { + Wh_Log(L"ExplorerPatcher symbol%s doesn't exist: %S", + hook.optional ? L" (optional)" : L"", hook.symbol); + if (!hook.optional) { + succeeded = false; + } + continue; + } + + if (hook.hookFunction) { + Wh_SetFunctionHook(ptr, hook.hookFunction, hook.pOriginalFunction); + } else { + *hook.pOriginalFunction = ptr; + } + } + + if (!succeeded) { + Wh_Log(L"HookExplorerPatcherSymbols failed"); + } else if (g_initialized) { + Wh_ApplyHookOperations(); + } + + return succeeded; +} + +bool IsExplorerPatcherModule(HMODULE module) { + WCHAR moduleFilePath[MAX_PATH]; + switch ( + GetModuleFileName(module, moduleFilePath, ARRAYSIZE(moduleFilePath))) { + case 0: + case ARRAYSIZE(moduleFilePath): + return false; + } + + PCWSTR moduleFileName = wcsrchr(moduleFilePath, L'\\'); + if (!moduleFileName) { + return false; + } + + moduleFileName++; + + if (_wcsnicmp(L"ep_taskbar.", moduleFileName, sizeof("ep_taskbar.") - 1) == + 0) { + Wh_Log(L"ExplorerPatcher taskbar module: %s", moduleFileName); + return true; + } + + return false; +} + +bool HandleLoadedExplorerPatcher() { + HMODULE hMods[1024]; + DWORD cbNeeded; + if (EnumProcessModules(GetCurrentProcess(), hMods, sizeof(hMods), + &cbNeeded)) { + for (size_t i = 0; i < cbNeeded / sizeof(HMODULE); i++) { + if (IsExplorerPatcherModule(hMods[i])) { + return HookExplorerPatcherSymbols(hMods[i]); + } + } + } + + return true; +} + +using LoadLibraryExW_t = decltype(&LoadLibraryExW); +LoadLibraryExW_t LoadLibraryExW_Original; +HMODULE WINAPI LoadLibraryExW_Hook(LPCWSTR lpLibFileName, + HANDLE hFile, + DWORD dwFlags) { + HMODULE module = LoadLibraryExW_Original(lpLibFileName, hFile, dwFlags); + if (module && !((ULONG_PTR)module & 3) && !g_explorerPatcherInitialized) { + if (IsExplorerPatcherModule(module)) { + HookExplorerPatcherSymbols(module); + } + } + + return module; +} + +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_settings.oldTaskbarOnWin11) { + bool hasWin10Taskbar = g_winVersion < WinVersion::Win11_24H2; + + if (g_winVersion >= WinVersion::Win11) { + g_winVersion = WinVersion::Win10; + } + + if (hasWin10Taskbar && !HookTaskbarSymbols()) { + return FALSE; + } + } else if (!HookTaskbarSymbols()) { + return FALSE; + } + + if (!HandleLoadedExplorerPatcher()) { + Wh_Log(L"HandleLoadedExplorerPatcher failed"); + return FALSE; + } + + HMODULE kernelBaseModule = GetModuleHandle(L"kernelbase.dll"); + auto pKernelBaseLoadLibraryExW = (decltype(&LoadLibraryExW))GetProcAddress( + kernelBaseModule, "LoadLibraryExW"); + WindhawkUtils::Wh_SetFunctionHookT(pKernelBaseLoadLibraryExW, + LoadLibraryExW_Hook, + &LoadLibraryExW_Original); + + g_initialized = true; + + return TRUE; +} + +void Wh_ModAfterInit() { + Wh_Log(L">"); + + // Try again in case there's a race between the previous attempt and the + // LoadLibraryExW hook. + if (!g_explorerPatcherInitialized) { + HandleLoadedExplorerPatcher(); + } +} + +void Wh_ModUninit() { + Wh_Log(L">"); +} + +BOOL Wh_ModSettingsChanged(BOOL* bReload) { + Wh_Log(L">"); + + bool prevOldTaskbarOnWin11 = g_settings.oldTaskbarOnWin11; + + LoadSettings(); + + *bReload = g_settings.oldTaskbarOnWin11 != prevOldTaskbarOnWin11; + + return TRUE; +}