Skip to content

Commit 2b36af9

Browse files
author
Alex Vallat
committed
Improved lookup of keyboard layout names (issue #3)
1 parent 4409103 commit 2b36af9

File tree

1 file changed

+47
-23
lines changed

1 file changed

+47
-23
lines changed

KeyLayoutAutoSwitch/ExtensionMethods.cs

Lines changed: 47 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using Microsoft.Win32;
2+
using System;
3+
using System.Collections.Generic;
24
using System.Reflection;
35
using System.Windows.Forms;
46

@@ -7,43 +9,65 @@ namespace KeyLayoutAutoSwitch
79
internal static class ExtensionMethods
810
{
911
private static readonly MethodInfo GetLocalizedKeyboardLayoutNameMethod = typeof(InputLanguage).GetMethod("GetLocalizedKeyboardLayoutName", BindingFlags.NonPublic | BindingFlags.Static);
10-
public static string GetLayoutName(this InputLanguage inputLanguage)
12+
13+
private static readonly Dictionary<uint, string> _layoutKeyNamesByLayoutId = LoadLayoutKeyNames();
14+
private static readonly Dictionary<IntPtr, string> _layoutNameCache = new Dictionary<IntPtr, string>();
15+
16+
private static Dictionary<uint, string> LoadLayoutKeyNames()
1117
{
12-
var handle = (uint)inputLanguage.Handle;
13-
if ((handle & 0xf0000000) == 0xf0000000) // InputLanguage.LayoutName is bugged when layout ID is provided, so try and do better
14-
{
15-
var targetLanguage = (handle & 0xffff).ToString("X4");
16-
var targetLayoutId = ((handle >> 16) & 0x0fff).ToString("X4");
18+
var layoutKeyNames = new Dictionary<uint, string>();
1719

18-
var keyboardLayouts = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\Keyboard Layouts");
20+
using (var keyboardLayouts = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\Keyboard Layouts"))
21+
{
1922
foreach (var keyboardLayout in keyboardLayouts.GetSubKeyNames())
2023
{
21-
if (keyboardLayout.Length == 8)
24+
using (var keyboardLayoutKey = keyboardLayouts.OpenSubKey(keyboardLayout))
2225
{
23-
var layoutLanguage = keyboardLayout.Substring(4);
24-
if (layoutLanguage == targetLanguage)
26+
var layoutId = keyboardLayoutKey.GetValue("Layout Id") as string;
27+
if (layoutId != null)
2528
{
26-
// Does this match the layout too?
27-
using (var keyboardLayoutKey = keyboardLayouts.OpenSubKey(keyboardLayout))
28-
{
29-
var layoutId = keyboardLayoutKey.GetValue("Layout Id") as string;
30-
if (layoutId == targetLayoutId)
31-
{
32-
// Match by language and layout ID,
29+
layoutKeyNames[Convert.ToUInt32(layoutId, 16)] = keyboardLayout;
30+
}
31+
}
32+
}
33+
}
34+
35+
return layoutKeyNames;
36+
}
37+
38+
public static string GetLayoutName(this InputLanguage inputLanguage)
39+
{
40+
if (!_layoutNameCache.TryGetValue(inputLanguage.Handle, out var layoutName))
41+
{
42+
// InputLanguage.LayoutName is bugged (https://github.com/dotnet/winforms/issues/4345), try to do better
43+
try
44+
{
45+
var targetLayoutIdOrLanguage = (uint)inputLanguage.Handle >> 16;
3346

47+
var keyName = (targetLayoutIdOrLanguage & 0xf000) == 0xf000 ?
48+
// This is a layout ID, so get the key name from the lookup
49+
_layoutKeyNamesByLayoutId[targetLayoutIdOrLanguage & 0x0fff]
50+
:
51+
// This is a language ID, the layout name is just that.
52+
targetLayoutIdOrLanguage.ToString("X8");
3453

35-
// Attempt to extract the localized keyboard layout name, default back to legacy name from registry, final default just use InputLanguage code path
36-
return GetLocalizedKeyboardLayoutName(keyboardLayoutKey.GetValue("Layout Display Name") as string)
54+
using (var keyboardLayoutKey = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\Keyboard Layouts\" + keyName))
55+
{
56+
// Attempt to extract the localized keyboard layout name, default back to legacy name from registry, final default just use InputLanguage code path
57+
layoutName = GetLocalizedKeyboardLayoutName(keyboardLayoutKey.GetValue("Layout Display Name") as string)
3758
?? keyboardLayoutKey.GetValue("Layout Text") as string
3859
?? inputLanguage.LayoutName;
39-
}
40-
}
41-
}
4260
}
4361
}
62+
catch
63+
{
64+
// Fallback on the buggy WinForms implemenation
65+
layoutName = inputLanguage.LayoutName;
66+
}
67+
_layoutNameCache.Add(inputLanguage.Handle, layoutName);
4468
}
4569

46-
return inputLanguage.LayoutName;
70+
return layoutName;
4771
}
4872

4973
private static string GetLocalizedKeyboardLayoutName(string layoutDisplayName)

0 commit comments

Comments
 (0)