From 023ab45022fab28b9969ea1df1521c8d2c3912e1 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 22 Mar 2025 12:06:44 +0800 Subject: [PATCH] Use PInvoke instead of DllImport & Several adjustments --- .../NativeMethods.txt | 7 +- Flow.Launcher.Infrastructure/Win32Helper.cs | 79 +++++++++++++++++ Flow.Launcher/Helper/KeyboardLayoutHelper.cs | 88 ------------------- Flow.Launcher/ViewModel/MainViewModel.cs | 5 +- 4 files changed, 87 insertions(+), 92 deletions(-) delete mode 100644 Flow.Launcher/Helper/KeyboardLayoutHelper.cs diff --git a/Flow.Launcher.Infrastructure/NativeMethods.txt b/Flow.Launcher.Infrastructure/NativeMethods.txt index f080f24de..d26478bbe 100644 --- a/Flow.Launcher.Infrastructure/NativeMethods.txt +++ b/Flow.Launcher.Infrastructure/NativeMethods.txt @@ -46,4 +46,9 @@ GetMonitorInfo MONITORINFOEXW WM_ENTERSIZEMOVE -WM_EXITSIZEMOVE \ No newline at end of file +WM_EXITSIZEMOVE + +GetKeyboardLayout +GetWindowThreadProcessId +ActivateKeyboardLayout +GetKeyboardLayoutList \ No newline at end of file diff --git a/Flow.Launcher.Infrastructure/Win32Helper.cs b/Flow.Launcher.Infrastructure/Win32Helper.cs index 8dbe3f7e9..664f428ec 100644 --- a/Flow.Launcher.Infrastructure/Win32Helper.cs +++ b/Flow.Launcher.Infrastructure/Win32Helper.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel; +using System.Linq; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Interop; @@ -7,8 +8,10 @@ using System.Windows.Media; using Windows.Win32; using Windows.Win32.Foundation; using Windows.Win32.Graphics.Dwm; +using Windows.Win32.UI.Input.KeyboardAndMouse; using Windows.Win32.UI.WindowsAndMessaging; using Flow.Launcher.Infrastructure.UserSettings; +using Point = System.Windows.Point; namespace Flow.Launcher.Infrastructure { @@ -317,5 +320,81 @@ namespace Flow.Launcher.Infrastructure } #endregion + + #region Keyboard Layout + + // https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-lcid/70feba9f-294e-491e-b6eb-56532684c37f + private static readonly uint[] EnglishLanguageIds = + { + 0x0009, 0x0409, 0x0809, 0x0C09, 0x1000, 0x1009, 0x1409, 0x1809, 0x1C09, 0x2009, 0x2409, 0x2809, 0x2C09, + 0x3009, 0x3409, 0x3C09, 0x4009, 0x4409, 0x4809, 0x4C09, + }; + + // Store the previous keyboard layout + private static HKL _previousLayout; + + private static unsafe HKL FindEnglishKeyboardLayout() + { + // Get the number of keyboard layouts + int count = PInvoke.GetKeyboardLayoutList(0, null); + if (count <= 0) return HKL.Null; + + // Get all keyboard layouts + var handles = new HKL[count]; + fixed (HKL* h = handles) + { + _ = PInvoke.GetKeyboardLayoutList(count, h); + } + + // Look for any English keyboard layout + foreach (var hkl in handles) + { + // The lower word contains the language identifier + var langId = (uint)hkl.Value & 0xFFFF; + + // Check if it's an English layout + if (EnglishLanguageIds.Contains(langId)) + { + return hkl; + } + } + + return HKL.Null; + } + + public static unsafe void SetEnglishKeyboardLayout(bool backupPrevious) + { + // Find an installed English layout + var enHKL = FindEnglishKeyboardLayout(); + + // No installed English layout found + if (enHKL == HKL.Null) return; + + // Get the current window thread ID + uint threadId = 0; + var result = PInvoke.GetWindowThreadProcessId(PInvoke.GetForegroundWindow(), &threadId); + if (result == 0 || threadId == 0) throw new Win32Exception(Marshal.GetLastWin32Error()); + + // Backup current keyboard layout + if (backupPrevious) + { + _previousLayout = PInvoke.GetKeyboardLayout(threadId); + } + + // Switch to English layout + PInvoke.ActivateKeyboardLayout(enHKL, 0); + } + + public static void SetPreviousKeyboardLayout() + { + if (_previousLayout != HKL.Null) + { + PInvoke.ActivateKeyboardLayout(_previousLayout, 0); + + _previousLayout = HKL.Null; + } + } + + #endregion } } diff --git a/Flow.Launcher/Helper/KeyboardLayoutHelper.cs b/Flow.Launcher/Helper/KeyboardLayoutHelper.cs deleted file mode 100644 index e5458f6cd..000000000 --- a/Flow.Launcher/Helper/KeyboardLayoutHelper.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System; -using System.Linq; -using System.Runtime.InteropServices; - -namespace Flow.Launcher.Helper; - -public static class KeyboardLayoutHelper -{ - #region Windows API - - [DllImport("user32.dll")] - private static extern IntPtr GetKeyboardLayout(uint idThread); - - [DllImport("user32.dll")] - private static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr lpdwProcessId); - - [DllImport("user32.dll")] - private static extern IntPtr ActivateKeyboardLayout(IntPtr hkl, uint flags); - - [DllImport("user32.dll")] - private static extern int GetKeyboardLayoutList(int nBuff, [Out] IntPtr[] lpList); - - [DllImport("user32.dll")] - private static extern IntPtr GetForegroundWindow(); - - // https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-lcid/70feba9f-294e-491e-b6eb-56532684c37f - private static readonly uint[] EnglishLanguageIds = - { - 0x0009, 0x0409, 0x0809, 0x0C09, 0x1000, 0x1009, 0x1409, 0x1809, 0x1C09, 0x2009, 0x2409, 0x2809, 0x2C09, - 0x3009, 0x3409, 0x3C09, 0x4009, 0x4409, 0x4809, 0x4C09, - }; - - private static IntPtr FindEnglishKeyboardLayout() - { - // Get the number of keyboard layouts - var count = GetKeyboardLayoutList(0, null); - if (count <= 0) return IntPtr.Zero; - - // Get all keyboard layouts - var keyboardLayouts = new IntPtr[count]; - GetKeyboardLayoutList(count, keyboardLayouts); - - // Look for any English keyboard layout - foreach (var layout in keyboardLayouts) - { - // The lower word contains the language identifier - var langId = (uint)layout.ToInt32() & 0xFFFF; - - // Check if it's an English layout - if (EnglishLanguageIds.Contains(langId)) - { - return layout; - } - } - - return IntPtr.Zero; - } - - #endregion - - // Query textbox keyboard layout - private static IntPtr _previousLayout; - - public static void SetEnglishKeyboardLayout() - { - // Find an installed English layout - var englishLayout = FindEnglishKeyboardLayout(); - - // No installed English layout found - if (englishLayout == IntPtr.Zero) return; - - var hwnd = GetForegroundWindow(); - var threadId = GetWindowThreadProcessId(hwnd, IntPtr.Zero); - - // Store current keyboard layout - _previousLayout = GetKeyboardLayout(threadId) & 0xFFFF; - - // Switch to English layout - ActivateKeyboardLayout(englishLayout, 0); - } - - public static void SetPreviousKeyboardLayout() - { - if (_previousLayout == IntPtr.Zero) return; - ActivateKeyboardLayout(_previousLayout, 0); - _previousLayout = IntPtr.Zero; - } -} diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 641825ec6..0f355a80f 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -14,7 +14,6 @@ using System.Windows.Threading; using CommunityToolkit.Mvvm.DependencyInjection; using CommunityToolkit.Mvvm.Input; using Flow.Launcher.Core.Plugin; -using Flow.Launcher.Helper; using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure.Hotkey; using Flow.Launcher.Infrastructure.Image; @@ -1377,7 +1376,7 @@ namespace Flow.Launcher.ViewModel if (StartWithEnglishMode) { - KeyboardLayoutHelper.SetEnglishKeyboardLayout(); + Win32Helper.SetEnglishKeyboardLayout(true); } }); } @@ -1450,7 +1449,7 @@ namespace Flow.Launcher.ViewModel if (StartWithEnglishMode) { - KeyboardLayoutHelper.SetPreviousKeyboardLayout(); + Win32Helper.SetPreviousKeyboardLayout(); } await Task.Delay(50);