如何调用windows系统自带虚拟键盘Tabtip.exe

启动、控制显隐、监听Tabtip.exe

windows10系统下测试使用,其他系统没有测试过
介绍:
TabTip.exe 触摸键盘和手写面板写面板是Microsoft Windows操作系统的一部分。
位置是在C:\ ProgramFiles \ PublicDocuments \ microsoftshared \ ink。
过程被认为是安全的,不会对您的计算机造成任何损害。
问题困扰
1,运行tabtip.exe时,当隐藏键盘(如点击键盘界面右上角关闭按钮)后我无法再次调起虚拟键盘界面,此时任务管理器中tabtip.exe是在运行状态下的。
2,此时我获取虚拟键盘窗口句柄,不管是在show or hide 状态下我获取到窗体可见性WS_VISIBLE都是true,
3,也就是说我无法通过窗口句柄去控制键盘显隐
我也无法获取到键盘状态是show or hide
一丢丢的区别需要注意:
在非触控设备上启动tabtip.exe程序会打开键盘界面
在触控设备上仅启动该程序并不会打开键盘界面

解决方案

启动关闭程序
1,当键盘未打开时,启动tabtip.exe程序(完全使用windowsapi启用)

void TouchKeyboardManager::OpenKeyBoard() {
  TCHAR filePath[MAX_PATH];
  if (!::SHGetSpecialFolderPath(nullptr, filePath, CSIDL_PROGRAM_FILES, false)) {
    log_info << "Failed to open softkeyboard! : incorrect path";
    return;
  }
  QString path = QString::fromUtf16(reinterpret_cast(filePath));
  QString csProcess = path.split("\\").at(0) + "/Program Files/Common Files/Microsoft Shared/ink/tabtip.exe";
  QByteArray ba = csProcess.toUtf8();
  char *data = ba.data();
  int charLen = strlen(data);
  int len = MultiByteToWideChar(CP_ACP, 0, data, charLen, nullptr, 0);
  TCHAR *Cmd = new TCHAR[len + 1];
  MultiByteToWideChar(CP_ACP, 0, data, charLen, Cmd, len);
  Cmd[len] = '\0';
  static PROCESS_INFORMATION Stat;
  static STARTUPINFO SU = { sizeof SU };
  SetEnvironmentVariable(L"__compat_layer", L"RunAsInvoker");
  if (!CreateProcess(0, Cmd, 0, 0, 1, 0, 0, 0, &SU, &Stat)) {
    log_info << "Failed to open softkeyboard! CreateProcess error ";
    return;
  }
  CloseHandle(Stat.hProcess);
  CloseHandle(Stat.hThread);
  log_info <<" softkeyboard open success! ";

2,关闭tabtips

void TouchKeyboardManager::CloseKeyBoard() {
  HWND window = ::FindWindowEx(nullptr, nullptr, WINDOW_CLASS, nullptr);
  if (::IsWindow(window)) {
    HRESULT hr = ::SendMessage(window, WM_SYSCOMMAND, SC_CLOSE, 0);
    if (hr == S_OK) {
      //log_info << "softkeyboard close success! ";
      return;
    }  } }

3,判断tabtip程序是否启动

const LPCWSTR WINDOW_CLASS = L"IPTip_Main_Window";
bool TouchKeyboardManager::IsKeyboardOpen() {
  if (::FindWindowEx(nullptr, nullptr, WINDOW_CLASS, nullptr)) {
    return true;
  }
  return false;
}

控制键盘显隐
使用com组件,来控制键盘show and hide
1,IFrameworkInputPane接口

参考:https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-iframeworkinputpane

定义 com接口 ITipInvocation继承IUnknown

DEFINE_GUID(CLSID_UIHostNoLaunch, 0x4CE576FA, 0x83DC, 0x4f88, 0x95, 0x1C, 0x9D, 0x07, 0x82, 0xB4, 0xE3, 0x76);
DEFINE_GUID(IID_ITipInvocation, 0x37c994e7, 0x432b, 0x4834, 0xa2, 0xf7, 0xdc, 0xe1, 0xf1, 0x3b, 0x83, 0x4b);
struct ITipInvocation : IUnknown {
  virtual HRESULT STDMETHODCALLTYPE Toggle(HWND wnd) = 0;
};

Toggle()用来控制键盘的show和hide

void TouchKeyboardManager::ShowOrHideKeyBoard() {
 HRESULT hr = ::CoCreateInstance(CLSID_UIHostNoLaunch, 0, CLSCTX_INPROC_HANDLER | CLSCTX_LOCAL_SERVER,
    IID_ITipInvocation, (&tip_invocation_));
  if (hr == S_OK) {
    tip_invocation_->Toggle(GetDesktopWindow());
    log_info << "keyboard is showing!";
    } }

现在我们虽然可以控制了键盘打开关闭,但无法控制它做出我们想要的动作,也就是说第一次调用Toggle()时打开,第二次调用时关闭,所以我们需要监听键盘的状态来满足我们想要show or hide的动作
监听
监听tabtip有两种方法
方法一:使用windows api IFrameworkInputPaneHandler监听事件
(这个方法给我造成了一些问题,所以后面弃用了该方法)

class Handler : public RuntimeClass,
  IFrameworkInputPaneHandler>{
  STDMETHODIMP Hiding(BOOL fEnsureFocusedElementInView) override {
    SetRectEmpty(&g_rcKeyboard);
    return S_OK; }
  STDMETHODIMP Showing(RECT *prcScreenLocation, BOOL fEnsureFocusedElementInView) override{
     g_rcKeyboard = *prcScreenLocation;
    return S_OK; }
};

方法二:IFrameworkInputPane :: Location接口通过判断键盘界面大小来确定是否打开(我所采用的方法)

bool TouchKeyboardManager::JudgeKeyBoardVisivle() {
  RECT rect = { 0 };
  QRect rect_;
  HRESULT get_size = framework_input_pane_->Location(&rect);
  if (get_size = S_OK) {
     if (rect.right() - rect.left() > 5) {return true;} 
     else {return false;  }
  }}

ok,到这里基本功能已经讲完,如发现不完整后续再补充

参考文档:https://devblogs.microsoft.com/oldnewthing/?p=45431
代码附件https://download.csdn.net/download/weixin_44899507/16798309

你可能感兴趣的:(笔记,qt,c++,windows)