【问题标题】:Properly detect keyboard layout正确检测键盘布局
【发布时间】:2017-12-04 23:30:22
【问题描述】:

我有一个 winforms 应用程序,我需要在其中获取用户的当前键盘布局。为此,我使用System.Windows.Forms.InputLanguage.CurrentInputLanguage.LayoutName

只要用户将表单作为他的活动窗口,它就可以正常工作,一旦他关注其他内容并更改语言,前一个属性将不会返回正确的值,它将返回上次使用的语言,而表单仍然是活动窗口。

有没有一种方法可以获得用户键盘布局的名称,即使他没有关注表单,对可以使用的内容没有限制。

【问题讨论】:

  • 您是否预测用户会在 Windows 运行时更换键盘?
  • @MickyD 他可能没有,但我需要知道他是否有。
  • 这个 .NET 类使用 Windows API GetKeyboardLayout。 msdn.microsoft.com/en-us/library/windows/desktop/ms646296.aspx 如您所见,键盘布局可以逐个线程定义。 .NET 类为当前线程读取它,因此您看到的可能是正确的(只要您不缓存该值)。或者您正在寻找不同的信息?

标签: c# winforms layout keyboard keyboard-events


【解决方案1】:

您可能已经知道 System.Windows.Forms.InputLanguage.CurrentInputLanguage.LayoutName 属性返回当前线程的键盘布局,并且无论您选择哪种布局,它对于执行线程都将保持不变,除非您选择它窗口并更改该窗口的键盘输入布局。

也就是说,您实际上要做的是检查当前的键盘布局文化并能够知道它何时发生变化。不久前我有一个类似的要求,我想出了以下代码,这对我很有帮助:

public delegate void KeyboardLayoutChanged(int oldCultureInfo, int newCultureInfo);

class KeyboardLayoutWatcher : IDisposable
{
    private readonly Timer _timer;
    private int _currentLayout = 1033;


    public KeyboardLayoutChanged KeyboardLayoutChanged;

    public KeyboardLayoutWatcher()
    {
        _timer = new Timer(new TimerCallback(CheckKeyboardLayout), null, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1));
    }

    [DllImport("user32.dll")] static extern IntPtr GetForegroundWindow();
    [DllImport("user32.dll")] static extern uint GetWindowThreadProcessId(IntPtr hwnd, IntPtr proccess);
    [DllImport("user32.dll")] static extern IntPtr GetKeyboardLayout(uint thread);
    public int GetCurrentKeyboardLayout()
    {
        try
        {
            IntPtr foregroundWindow = GetForegroundWindow();
            uint foregroundProcess = GetWindowThreadProcessId(foregroundWindow, IntPtr.Zero);
            int keyboardLayout = GetKeyboardLayout(foregroundProcess).ToInt32() & 0xFFFF;

            if (keyboardLayout == 0)
            {
                // something has gone wrong - just assume English
                keyboardLayout = 1033;
            }
            return keyboardLayout;
        }
        catch (Exception ex)
        {
            // if something goes wrong - just assume English
            return 1033;
        }
    }

    private void CheckKeyboardLayout(object sender)
    {
        var layout = GetCurrentKeyboardLayout();
        if (_currentLayout != layout && KeyboardLayoutChanged != null)
        {
            KeyboardLayoutChanged(_currentLayout, layout);
            _currentLayout = layout;
        }

    }

    private void ReleaseUnmanagedResources()
    {
        _timer.Dispose();
    }

    public void Dispose()
    {
        ReleaseUnmanagedResources();
        GC.SuppressFinalize(this);
    }

    ~KeyboardLayoutWatcher()
    {
        ReleaseUnmanagedResources();
    }
}

并像这样使用它:

        new KeyboardLayoutWatcher().KeyboardLayoutChanged += (o, n) =>
        {
            this.CurrentLayoutLabel.Text = $"{o} -> {n}"; // old and new KB layout
        };

【讨论】:

【解决方案2】:

这里有几点需要注意。 Windows 中的键盘选择是基于每个线程完成的。这允许用户为任何给定的应用程序选择不同的键盘区域设置,Windows 将遵守该设置,同时不理会其他应用程序。

用户通过启用 Windows 任务栏中的语言栏(对于已安装的键盘)来执行此操作。如果他们选择不同的键盘,而另一个应用程序具有焦点,则选择仅适用于该应用程序。此外,如果您的应用程序没有焦点,它无论如何也无能为力。通过以这种方式使用语言栏,用户清楚地表明他们打算将所选键盘仅应用于活动应用程序。您的应用程序无法找到它,因为从 Windows 的角度来看,这与您的应用程序无关。

现在,如果您想知道用户是否更改了整个系统的键盘(使用控制面板小程序),这是可行的。如果您的应用程序没有焦点,则无法捕获通知消息。您的表单仍会认为当前语言是它开始时使用的语言。但是,系统范围的更改确实将InputLanguage.DefaultInputLanguage. 更改为新选择的键盘。因此,覆盖OnActivated 处理程序并检查默认语言的值,而不是当前语言(每个线程)。

【讨论】:

    猜你喜欢
    • 2011-02-28
    • 1970-01-01
    • 2011-04-17
    • 2020-07-29
    • 2011-12-10
    • 1970-01-01
    • 1970-01-01
    • 2021-11-14
    • 2019-07-02
    相关资源
    最近更新 更多