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