private void ClearContext() { if (_hwndSource == null) { return; } IMENative.ImmAssociateContext(_hwndSource.Handle, _previousContext); IMENative.ImmReleaseContext(_defaultImeWnd, _currentContext); _currentContext = IntPtr.Zero; _defaultImeWnd = IntPtr.Zero; _hwndSource.RemoveHook(WndProc); _hwndSource = null; }
private void CreateContext() { ClearContext(); _hwndSource = (HwndSource)(PresentationSource.FromVisual(Editor) ?? throw new ArgumentNullException(nameof(Editor))); _defaultImeWnd = IMENative.ImmGetDefaultIMEWnd(IntPtr.Zero); Log($"_defaultImeWnd={_defaultImeWnd}"); if (_defaultImeWnd == IntPtr.Zero) { // 如果拿到了空的默认 IME 窗口了,那么此时也许是作为嵌套窗口放入到另一个进程的窗口 // 拿不到就需要刷新一下。否则微软拼音输入法将在屏幕的左上角上 RefreshInputMethodEditors(); // 尝试通过 _hwndSource 也就是文本所在的窗口去获取 _defaultImeWnd = IMENative.ImmGetDefaultIMEWnd(_hwndSource.Handle); Log($"_defaultImeWnd2={_defaultImeWnd}"); if (_defaultImeWnd == IntPtr.Zero) { // 如果依然获取不到,那么使用当前激活的窗口,在准备输入的时候 // 当前的窗口大部分都是对的 // 进入这里,是尽可能恢复输入法,拿到的 GetForegroundWindow 虽然预计是不对的 // 也好过没有输入法 _defaultImeWnd = IMENative.ImmGetDefaultIMEWnd(Win32.User32.GetForegroundWindow()); Log($"_defaultImeWnd3={_defaultImeWnd}"); } } // 使用 DefaultIMEWnd 可以比较好解决微软拼音的输入法到屏幕左上角的问题 _currentContext = IMENative.ImmGetContext(_defaultImeWnd); Log($"_currentContext={_currentContext}"); if (_currentContext == IntPtr.Zero) { _currentContext = IMENative.ImmGetContext(_hwndSource.Handle); Log($"_currentContext2={_currentContext}"); } // 对 Win32 使用第二套输入法框架的输入法,可以采用 ImmAssociateContext 关联 // 但是对实现 TSF 第三套输入法框架的输入法,在应用程序对接第三套输入法框架 // 就需要调用 ITfThreadMgr 的 SetFocus 方法。刚好 WPF 对接了 _previousContext = IMENative.ImmAssociateContext(_hwndSource.Handle, _currentContext); _hwndSource.AddHook(WndProc); // 尽管文档说传递null是无效的,但这似乎有助于在与WPF共享的默认输入上下文中激活IME输入法 // 这里需要了解的是,在 WPF 的逻辑,是需要传入 DefaultTextStore.Current.DocumentManager 才符合预期 var threadMgr = IMENative.GetTextFrameworkThreadManager(); threadMgr?.SetFocus(IntPtr.Zero); }