From ef6d40f1f868ea72c19d8893184e31ac0f3a3e20 Mon Sep 17 00:00:00 2001 From: Dimitriy Ryazantcev Date: Thu, 2 Oct 2025 15:56:40 +0300 Subject: [PATCH 1/3] Update InputLanguage.cs Streamline `InputLanguage.LayoutId` logic for clarity and maintainability, preserving existing behavior. --- .../Windows/Forms/Input/InputLanguage.cs | 76 ++++++++----------- 1 file changed, 31 insertions(+), 45 deletions(-) diff --git a/src/System.Windows.Forms/System/Windows/Forms/Input/InputLanguage.cs b/src/System.Windows.Forms/System/Windows/Forms/Input/InputLanguage.cs index 09202ccac4c..fb99871b555 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Input/InputLanguage.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Input/InputLanguage.cs @@ -112,6 +112,24 @@ public string LayoutName } } + private static int FindKeyboardLayout(int specialId) + { + using RegistryKey? layouts = Registry.LocalMachine.OpenSubKey(KeyboardLayoutsRegistryPath); + if (layouts is null) return specialId; + + foreach (string keyName in layouts.GetSubKeyNames()) + { + using RegistryKey? subKey = layouts.OpenSubKey(keyName); + if (subKey?.GetValue("Layout Id") is string layoutId && Convert.ToInt32(layoutId, 16) == specialId) + { + Debug.Assert(keyName.Length == 8, $"unexpected key length in registry: {keyName.Name}"); + return Convert.ToInt32(keyName, 16); + } + } + + return specialId; + } + /// /// Returns the /// @@ -128,52 +146,20 @@ internal string LayoutId { // There is no good way to do this in Windows. GetKeyboardLayoutName does what we want, but only for the // current input language; setting and resetting the current input language would generate spurious - // InputLanguageChanged events. Try to extract needed information manually. - + // InputLanguageChanged events. Try to extract needed information manually from HKL. + int langId = PARAM.LOWORD(_handle); + // High word of HKL contains a device handle to the physical layout of the keyboard but exact format of this - // handle is not documented. For older keyboard layouts device handle seems contains keyboard layout - // identifier. - int device = PARAM.HIWORD(_handle); - - // But for newer keyboard layouts device handle contains special layout id if its high nibble is 0xF. This - // id may be used to search for keyboard layout under registry. - // - // NOTE: this logic may break in future versions of Windows since it is not documented. - if ((device & 0xF000) == 0xF000) - { - // Extract special layout id from the device handle - int layoutId = device & 0x0FFF; - - using RegistryKey? key = Registry.LocalMachine.OpenSubKey(KeyboardLayoutsRegistryPath); - if (key is not null) - { - // Match keyboard layout by layout id - foreach (string subKeyName in key.GetSubKeyNames()) - { - using RegistryKey? subKey = key.OpenSubKey(subKeyName); - if (subKey is not null - && subKey.GetValue("Layout Id") is string subKeyLayoutId - && Convert.ToInt32(subKeyLayoutId, 16) == layoutId) - { - Debug.Assert(subKeyName.Length == 8, $"unexpected key length in registry: {subKey.Name}"); - return subKeyName.ToUpperInvariant(); - } - } - } - } - else - { - // Use input language only if keyboard layout language is not available. This is crucial in cases when - // keyboard is installed more than once or under different languages. For example when French keyboard - // is installed under US input language we need to return French keyboard identifier. - if (device == 0) - { - // According to the GetKeyboardLayout API function docs low word of HKL contains input language. - device = PARAM.LOWORD(_handle); - } - } - - return device.ToString("X8"); + // handle is not documented. For older keyboard layouts device handle contains keyboard layout identifier. + // But for newer keyboard layouts device handle contains special layout identifier if its high nibble is 0xF. + // This identifier may be used to search for keyboard layout under registry. + int deviceId = PARAM.HIWORD(_handle); + + int layoutId = (deviceId & 0xF000) == 0xF000 + ? FindKeyboardLayout(deviceId & 0x0FFF) + : (deviceId != 0 ? deviceId : langId); + + return layoutId.ToString("X8"); } } From b0a78b17569fdcb98bebcd7ff9871515a6c53f6e Mon Sep 17 00:00:00 2001 From: Dimitriy Ryazantcev Date: Thu, 2 Oct 2025 17:09:44 +0300 Subject: [PATCH 2/3] Update InputLanguage.cs --- .../System/Windows/Forms/Input/InputLanguage.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/System.Windows.Forms/System/Windows/Forms/Input/InputLanguage.cs b/src/System.Windows.Forms/System/Windows/Forms/Input/InputLanguage.cs index fb99871b555..30582d7ee62 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Input/InputLanguage.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Input/InputLanguage.cs @@ -116,17 +116,17 @@ private static int FindKeyboardLayout(int specialId) { using RegistryKey? layouts = Registry.LocalMachine.OpenSubKey(KeyboardLayoutsRegistryPath); if (layouts is null) return specialId; - + foreach (string keyName in layouts.GetSubKeyNames()) { using RegistryKey? subKey = layouts.OpenSubKey(keyName); if (subKey?.GetValue("Layout Id") is string layoutId && Convert.ToInt32(layoutId, 16) == specialId) { - Debug.Assert(keyName.Length == 8, $"unexpected key length in registry: {keyName.Name}"); + Debug.Assert(keyName.Length == 8, $"unexpected key length in registry: {subKey.Name}"); return Convert.ToInt32(keyName, 16); } } - + return specialId; } @@ -148,7 +148,7 @@ internal string LayoutId // current input language; setting and resetting the current input language would generate spurious // InputLanguageChanged events. Try to extract needed information manually from HKL. int langId = PARAM.LOWORD(_handle); - + // High word of HKL contains a device handle to the physical layout of the keyboard but exact format of this // handle is not documented. For older keyboard layouts device handle contains keyboard layout identifier. // But for newer keyboard layouts device handle contains special layout identifier if its high nibble is 0xF. From ad3f9bb6112cf0aa5cac3197e80f682c93599b64 Mon Sep 17 00:00:00 2001 From: Dimitriy Ryazantcev Date: Thu, 2 Oct 2025 17:33:00 +0300 Subject: [PATCH 3/3] Update InputLanguage.cs --- .../System/Windows/Forms/Input/InputLanguage.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/System.Windows.Forms/System/Windows/Forms/Input/InputLanguage.cs b/src/System.Windows.Forms/System/Windows/Forms/Input/InputLanguage.cs index 30582d7ee62..3917672207a 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Input/InputLanguage.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Input/InputLanguage.cs @@ -154,11 +154,11 @@ internal string LayoutId // But for newer keyboard layouts device handle contains special layout identifier if its high nibble is 0xF. // This identifier may be used to search for keyboard layout under registry. int deviceId = PARAM.HIWORD(_handle); - + int layoutId = (deviceId & 0xF000) == 0xF000 ? FindKeyboardLayout(deviceId & 0x0FFF) : (deviceId != 0 ? deviceId : langId); - + return layoutId.ToString("X8"); } }