From c850e5063a21250fa97c8b4c1dd0baaf982f9885 Mon Sep 17 00:00:00 2001 From: Valentin Radu Date: Fri, 13 Aug 2021 06:54:14 +0300 Subject: [PATCH] Fixes #2 (Win+X combination is now handled and opens the power user menu) --- ExplorerPatcher/ExplorerPatcher.rc | 8 +- .../ExplorerPatcherLibrary.rc | 8 +- ExplorerPatcherLibrary/dllmain.c | 122 ++++++++++++++++-- README.md | 5 - 4 files changed, 120 insertions(+), 23 deletions(-) diff --git a/ExplorerPatcher/ExplorerPatcher.rc b/ExplorerPatcher/ExplorerPatcher.rc index 6f234f9..f52434f 100644 --- a/ExplorerPatcher/ExplorerPatcher.rc +++ b/ExplorerPatcher/ExplorerPatcher.rc @@ -51,8 +51,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 22000,1,0,3 - PRODUCTVERSION 22000,1,0,3 + FILEVERSION 22000,1,0,4 + PRODUCTVERSION 22000,1,0,4 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -69,12 +69,12 @@ BEGIN BEGIN VALUE "CompanyName", "VALINET Solutions SRL" VALUE "FileDescription", "ExplorerPatcher Daemon" - VALUE "FileVersion", "22000.1.0.3" + VALUE "FileVersion", "22000.1.0.4" VALUE "InternalName", "ExplorerPatcher.exe" VALUE "LegalCopyright", "Copyright (C) 2006-2021 VALINET Solutions SRL. All rights reserved." VALUE "OriginalFilename", "ExplorerPatcher.exe" VALUE "ProductName", "ExplorerPatcher" - VALUE "ProductVersion", "22000.1.0.3" + VALUE "ProductVersion", "22000.1.0.4" END END BLOCK "VarFileInfo" diff --git a/ExplorerPatcherLibrary/ExplorerPatcherLibrary.rc b/ExplorerPatcherLibrary/ExplorerPatcherLibrary.rc index fff7f42..0e26f3f 100644 --- a/ExplorerPatcherLibrary/ExplorerPatcherLibrary.rc +++ b/ExplorerPatcherLibrary/ExplorerPatcherLibrary.rc @@ -51,8 +51,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 22000,1,0,3 - PRODUCTVERSION 22000,1,0,3 + FILEVERSION 22000,1,0,4 + PRODUCTVERSION 22000,1,0,4 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -69,12 +69,12 @@ BEGIN BEGIN VALUE "CompanyName", "VALINET Solutions SRL" VALUE "FileDescription", "ExplorerPatcher Library" - VALUE "FileVersion", "22000.1.0.3" + VALUE "FileVersion", "22000.1.0.4" VALUE "InternalName", "ExplorerPatcherLibrary.dll" VALUE "LegalCopyright", "Copyright (C) 2006-2021 VALINET Solutions SRL. All rights reserved." VALUE "OriginalFilename", "ExplorerPatcherLibrary.dll" VALUE "ProductName", "WinOverview" - VALUE "ProductVersion", "22000.1.0.3" + VALUE "ProductVersion", "22000.1.0.4" END END BLOCK "VarFileInfo" diff --git a/ExplorerPatcherLibrary/dllmain.c b/ExplorerPatcherLibrary/dllmain.c index 535f48c..c2104a8 100644 --- a/ExplorerPatcherLibrary/dllmain.c +++ b/ExplorerPatcherLibrary/dllmain.c @@ -16,8 +16,8 @@ funchook_t* funchook = NULL; HMODULE hModule = NULL; HWND messageWindow = NULL; - - +HANDLE hIsWinXShown = NULL; +INT64 lockEnsureWinXHotkeyOnlyOnce; static HWND(WINAPI* CreateWindowInBand)( _In_ DWORD dwExStyle, @@ -85,6 +85,12 @@ static INT64(*CImmersiveContextMenuOwnerDrawHelper_s_ContextMenuWndProc)( BOOL* a5 ); +static INT64(*CTray_HandleGlobalHotkeyFunc)( + void* _this, + unsigned int a2, + unsigned int a3 + ); + DEFINE_GUID(IID_ILauncherTipContextMenu, 0xb8c1db5f, 0xcbb3, 0x48bc, 0xaf, 0xd9, @@ -249,7 +255,7 @@ interface IImmersiveLauncher10RS -HANDLE hThread; + LRESULT CALLBACK CLauncherTipContextMenu_WndProc( _In_ HWND hWnd, @@ -434,7 +440,7 @@ DWORD ShowLauncherTipContextMenu( 0 ); free(params); - hThread = NULL; + hIsWinXShown = NULL; return 0; } @@ -443,7 +449,7 @@ INT64 CLauncherTipContextMenu_ShowLauncherTipContextMenuHook( POINT* pt ) { - if (hThread) + if (hIsWinXShown) { goto finalize; } @@ -510,7 +516,7 @@ INT64 CLauncherTipContextMenu_ShowLauncherTipContextMenuHook( params->_this = _this; params->point = point; params->iunk = iunk; - hThread = CreateThread( + hIsWinXShown = CreateThread( 0, 0, ShowLauncherTipContextMenu, @@ -523,6 +529,85 @@ INT64 CLauncherTipContextMenu_ShowLauncherTipContextMenuHook( return CLauncherTipContextMenu_ShowLauncherTipContextMenuFunc(_this, pt); } +INT64 CTray_HandleGlobalHotkeyHook( + void* _this, + unsigned int a2, + unsigned int a3 +) +{ + if (a2 == 590 && IsDesktopInputContextFunc(_this, a2)) + { + // this works just fine but is hacky because using + // the proper way does not work for some reason + // see https://github.com/valinet/ExplorerPatcher/issues/3 + if (hIsWinXShown) + { + INPUT ip[2]; + ip[0].type = INPUT_KEYBOARD; + ip[0].ki.wScan = 0; + ip[0].ki.time = 0; + ip[0].ki.dwExtraInfo = 0; + ip[0].ki.wVk = VK_ESCAPE; + ip[0].ki.dwFlags = 0; + ip[1].type = INPUT_KEYBOARD; + ip[1].ki.wScan = 0; + ip[1].ki.time = 0; + ip[1].ki.dwExtraInfo = 0; + ip[1].ki.wVk = VK_ESCAPE; + ip[1].ki.dwFlags = KEYEVENTF_KEYUP; + SendInput(2, ip, sizeof(INPUT)); + return 0; + } + + InterlockedExchange64(&lockEnsureWinXHotkeyOnlyOnce, 1); + + HWND hWnd = GetForegroundWindow(); + HWND g_ProgWin = FindWindowEx( + NULL, + NULL, + L"Progman", + NULL + ); + SetForegroundWindow(g_ProgWin); + + INPUT ip[4]; + ip[0].type = INPUT_KEYBOARD; + ip[0].ki.wScan = 0; + ip[0].ki.time = 0; + ip[0].ki.dwExtraInfo = 0; + ip[0].ki.wVk = VK_LWIN; + ip[0].ki.dwFlags = 0; + ip[1].type = INPUT_KEYBOARD; + ip[1].ki.wScan = 0; + ip[1].ki.time = 0; + ip[1].ki.dwExtraInfo = 0; + ip[1].ki.wVk = 0x51; // 0x46; + ip[1].ki.dwFlags = 0; + ip[2].type = INPUT_KEYBOARD; + ip[2].ki.wScan = 0; + ip[2].ki.time = 0; + ip[2].ki.dwExtraInfo = 0; + ip[2].ki.wVk = 0x51; // 0x46; + ip[2].ki.dwFlags = KEYEVENTF_KEYUP; + ip[3].type = INPUT_KEYBOARD; + ip[3].ki.wScan = 0; + ip[3].ki.time = 0; + ip[3].ki.dwExtraInfo = 0; + ip[3].ki.wVk = VK_LWIN; + ip[3].ki.dwFlags = KEYEVENTF_KEYUP; + SendInput(4, ip, sizeof(INPUT)); + + SetForegroundWindow(hWnd); + + return 0; + } + return CTray_HandleGlobalHotkeyFunc( + _this, + a2, + a3 + ); +} + HRESULT CImmersiveHotkeyNotification_OnMessageHook( void* _this, INT64 msg, @@ -530,9 +615,12 @@ HRESULT CImmersiveHotkeyNotification_OnMessageHook( INT64 lParam ) { - if (wParam == 28 && IsDesktopInputContextFunc(_this, msg)) // 15 + if (InterlockedExchange64(&lockEnsureWinXHotkeyOnlyOnce, 0) && + wParam == 30 && // 28, 15 + IsDesktopInputContextFunc(_this, msg) + ) { - IUnknown* pMonitor; + IUnknown* pMonitor = NULL; HRESULT hr = CImmersiveHotkeyNotification_GetMonitorForHotkeyNotificationFunc( (char*)_this - 0x68, &pMonitor, @@ -540,7 +628,7 @@ HRESULT CImmersiveHotkeyNotification_OnMessageHook( ); if (SUCCEEDED(hr)) { - IUnknown* pMenu; + IUnknown* pMenu = NULL; IUnknown_QueryService( pMonitor, &IID_ILauncherTipContextMenu, @@ -650,7 +738,7 @@ LRESULT CALLBACK OpenStartOnCurentMonitorThreadHook( ); HRESULT hr = S_OK; - IUnknown* pImmersiveShell; + IUnknown* pImmersiveShell = NULL; hr = CoCreateInstance( &CLSID_ImmersiveShell, NULL, @@ -780,6 +868,20 @@ __declspec(dllexport) DWORD WINAPI main( ); + HANDLE hExplorer = GetModuleHandle(NULL); + CTray_HandleGlobalHotkeyFunc = (INT64(*)(void*, unsigned int, unsigned int)) + ((uintptr_t)hExplorer + 0x117F8); + rv = funchook_prepare( + funchook, + (void**)&CTray_HandleGlobalHotkeyFunc, + CTray_HandleGlobalHotkeyHook + ); + if (rv != 0) + { + FreeLibraryAndExitThread(hModule, rv); + return rv; + } + HANDLE hUser32 = GetModuleHandle(L"user32.dll"); diff --git a/README.md b/README.md index 143fefa..aca58f1 100644 --- a/README.md +++ b/README.md @@ -19,11 +19,6 @@ To install, save the executable in a safe directory, run it once as an administr The application does not currently offer a way to configure its behavior. In the mean time, I recommend commenting out whatever you do not like and compile your own executable, as described below (instructions are very simple). -## Known issues - -* The power user menu (Win+X menu) is unskinned - it has the default menu appearance from Windows 11 instead of the look the clock, taskbar etc context menus get; at the moment, I think it boils down to correctly calling `ImmersiveContextMenuHelper::ApplyOwnerDrawToMenu`, but unfortunately I've yet to do it correctly -* The power user menu (Win+X menu) is mapped to Win+F for the moment, as I've yet to have the time to investigate where exactly Win+X is handled (i.e. it is not in `CImmersiveHotkeyNotification::OnMessage`) - ## License Hooking is done using the excellent [funchook](https://github.com/kubo/funchook) library (GPLv2 with linking exception), which in turn is powered by the [diStorm3](https://github.com/gdabah/distorm/) (3-clause BSD) disassembler. Thus, I am offering this under GNU General Public License Version 2.0, which I believe is compatible.