CefSharp.Wpf.Hwnd 阻止 WPF 应用程序中的键盘布局更改检测

问题描述 投票:0回答:1

我正在开发一个 WPF 应用程序,它集成了 CefSharp 来呈现 Web 内容。最初,我们使用 CefSharp.Wpf,一切都按预期工作,包括使用

InputLanguageManager
检测键盘布局更改的能力。但是,由于
CefSharp.Wpf
的性能问题,我们决定切换到 CefSharp.Wpf.Hwnd,这显着提高了性能。

不幸的是,切换到

CefSharp.Wpf.Hwnd
后,我们遇到了一个问题:当焦点位于浏览器窗口(Hwnd 渲染区域)内时,
InputLanguageManager
停止检测键盘布局变化。当浏览器区域失去焦点时(例如,当我们单击应用程序中的其他位置或另一个窗口(例如 Excel 或记事本)时),
InputLanguageManager
会再次开始正常工作。

  1. 使用

    InputLanguageManager

    • 这与
      CefSharp.Wpf
      完美配合,但一旦浏览器区域处于焦点状态,就停止使用
      CefSharp.Wpf.Hwnd
      检测布局更改。
  2. 实现自定义

    GetKeyboardLayout
    方法:

    • 我们尝试使用原生 Windows API 的自定义方法手动获取当前键盘布局(
      GetForegroundWindow
      GetWindowThreadProcessId
      GetKeyboardLayout
      )。

    这是我们方法的简化版本:

    [DllImport("user32.dll")]
    static extern IntPtr GetForegroundWindow();
    
    [DllImport("user32.dll")]
    static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
    
    [DllImport("user32.dll")]
    static extern IntPtr GetKeyboardLayout(uint idThread);
    
    private void DetectKeyboardLayout()
    {
        IntPtr foregroundWindow = GetForegroundWindow();
        uint processId;
        uint threadId = GetWindowThreadProcessId(foregroundWindow, out processId);
        IntPtr layout = GetKeyboardLayout(threadId);
    
        Console.WriteLine($"Current keyboard layout: {layout}");
    }
    
  3. 使用原生 Hooks (

    SetWindowsHookEx
    ):

    • 我们还尝试通过
      SetWindowsHookEx
      使用全局钩子来拦截键盘事件并检测布局更改。

    这是我们使用的基本模板:

    [DllImport("user32.dll", SetLastError = true)]
    private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
    
    [DllImport("user32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool UnhookWindowsHookEx(IntPtr hhk);
    
    [DllImport("user32.dll")]
    private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
    
    private const int WH_KEYBOARD_LL = 13;
    private LowLevelKeyboardProc _proc = HookCallback;
    private IntPtr _hookID = IntPtr.Zero;
    
    private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
    
    private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
    {
        if (nCode >= 0)
        {
            // Detect keyboard layout change here...
        }
        return CallNextHookEx(_hookID, nCode, wParam, lParam);
    }
    
    private void SetHook()
    {
        _hookID = SetWindowsHookEx(WH_KEYBOARD_LL, _proc, IntPtr.Zero, 0);
    }
    

当焦点位于 CefSharp.Wpf.Hwnd 内部时,

InputLanguageManager
和我们的自定义
GetKeyboardLayout
方法或本机键盘挂钩都不会检测到键盘布局更改。

当焦点移出

CefSharp.Wpf.Hwnd
区域(移至另一个 WPF 元素或外部应用程序)时,所有方法都能正常工作。

当焦点位于

CefSharp.Wpf.Hwnd
渲染区域内时,如何检测 WPF 应用程序中的键盘布局更改(例如,在英语和俄语之间切换)?有没有办法在使用
InputLanguageManager
时恢复
GetKeyboardLayout
、我们自定义
CefSharp.Wpf.Hwnd
方法或本机钩子的功能?

任何建议或解决方法将不胜感激!

c# wpf layout keyboard cefsharp
1个回答
0
投票

我正在回答我自己的问题,因为用户@amaitland在评论中提供了解决方案,但没有创建正式的答案。这是有效的解决方案:

要解决

CefSharp.Wpf.Hwnd
和键盘布局更改的问题,您需要禁用多线程消息循环并使用
Cef.DoMessageLoopWork()
在 UI 线程上手动调用
DispatcherTimer

解决方案:

1。禁用多线程消息循环:

var settings = new CefSettings();
settings.MultiThreadedMessageLoop = false; 
Cef.Initialize(settings);

2。设置定时器来呼叫

Cef.DoMessageLoopWork()
:

private DispatcherTimer timer;

private void StartTimer()
{
    timer = new DispatcherTimer();
    timer.Interval = TimeSpan.FromMilliseconds(1000 / 30); // 30 FPS
    timer.Tick += UiThreadTimerTick;
    timer.Start();
}

private void StopTimer()
{
    timer.Tick -= UiThreadTimerTick;
    timer.Stop();
}

private void UiThreadTimerTick(object sender, EventArgs e)
{
    Cef.DoMessageLoopWork();
}

这解决了我在专注于

CefSharp.Wpf.Hwnd
浏览器窗口时检测键盘布局更改的问题。

© www.soinside.com 2019 - 2024. All rights reserved.