如何在Windows Vista/7中以编程方式控制文本输入面板(TabTip.exe)

17
我正在为触摸屏界面适配一个应用程序,我们想要使用 Windows Vista/7 中包含的平板电脑文本输入面板,特别是它的键盘。 我想在适当的时候显示和隐藏它。基本上,我需要 ShowKeyboard()HideKeyboard() 函数。 控制它的最佳方法是什么?
我查看了 ITextInputPanel API,但无法直接控制键盘(也许我错过了什么?)。 我还尝试向其窗口发送消息,但没有成功。
该应用程序采用 C++/MFC 编写。
非常感谢任何指针。

我有一个相关的问题 http://stackoverflow.com/questions/14157885/mfc-on-screen-keyboard-when-focusing-editable-control - sergiol
2个回答

14

我解决了这个问题。原来Spy ++确实是Windows程序员最好的朋友。

首先,输入面板窗口的窗口类别是"IPTip_Main_Window"。我用这个获取窗口句柄:

HWND wKB = ::FindWindow(_TEXT("IPTip_Main_Window"), NULL);

事实证明,我可以发送与菜单相同的WM_COMMAND消息。大多数操作都可以从菜单中获得:固定顶部、固定底部和浮动。发送这些消息的代码如下:

::PostMessage(wKB, WM_COMMAND, MAKEWPARAM(X,0) , 0);

X为10021表示固定底部,10023表示固定顶部,10020表示浮动。高字中的0表示该消息是从菜单发送的。

最后,我想能够显示和隐藏输入面板。我注意到我可以打开一个桌面带,其中仅包含一个按钮,用于切换输入面板的可见性。通过对从此按钮发送的消息进行Spy ++,发现它发送一个全局注册的窗口消息,名为"TabletInputPanelDeskBandClicked"。 向输入面板发送此消息会使其切换其可见性。

HideKeyboard()函数现在看起来像这样:

DWORD WM_DESKBAND_CLICKED =
    ::RegisterWindowMessage(_TEXT("TabletInputPanelDeskBandClicked"));
void HideKeyboard() { HWND wKB = ::FindWindow(_TEXT("IPTip_Main_Window"), NULL); if(wKB != NULL && ::IsWindowVisible(wKB)) { ::PostMessage(wKB, WM_DESKBAND_CLICKED, 0, 0); } }

ShowWindow()函数的实现方式类似,但它还会在键盘未运行时启动键盘。

更新:

似乎Windows Vista/7中禁止这种进程间通信。在非提升权限的进程中运行此命令将导致“访问被拒绝”的失败。我猜测这是由于Windows Vista/7中发现的用户界面进程隔离(UIPI)保护所造成的。因为平板电脑输入面板正在作为服务的子进程中运行,它具有比用户程序更高的完整性级别,因此无法接收任何(或者非常有限的)消息。

更新:

事实证明,平板电脑输入面板确实以高完整性级别运行,而由受限用户帐户启动的进程则是中间完整性级别。


11

对于Windows 8:

注意:与Windows 7的解决方案一样,这需要一个提升的进程。

输入面板不是HWND_DESKTOP的后代。(它可能是某种Metro窗口。)为了获得窗口句柄,以网格状模式进行一系列水平扫描,并使用WindowFromPoint()进行测试。对于每个测试,检查父窗口的窗口类是否为"IPTip_Main_Window"。

要显示输入面板,请启动"C:\\Program Files\\Common Files\\microsoft shared\\ink\\tabtip.exe"。要确定它是否已处于停靠模式,请读取注册表键:

HKEY_CURRENT_USER\Software\Microsoft\TabletTip\1.7\EdgeTargetDockedState

当值为0时,表示输入面板处于浮动模式。如果是这种情况,请发布以下消息以切换到停靠状态:

DWORD WM_DOCK_BUTTON_PRESSED = ::RegisterWindowMessage(_TEXT("IPTipDockButtonPressed"));
PostMessage(hwndInputPanel, WM_DOCK_BUTTON_PRESSED, 0, 0);

要隐藏键盘,请发布以下内容:

PostMessage(hwndInputPanel, WM_SYSCOMMAND, SC_CLOSE, 0);

1
谢谢提供信息!你是否知道我发布的“解决方案”在Windows 8上是否仍然有效? - Yngve Hammersland
1
我的测试表明它不起作用。看起来微软为Windows 8重新设计了输入面板。现在没有下拉菜单来设置停靠状态,因此WM_COMMAND将无法执行任何操作。现在只有一个按钮可以在停靠和未停靠之间切换。此外,由于窗口层次结构的一些更改,FindWindow()将无法实际获取窗口句柄。我必须使用WindowFromPoint()进行命中测试才能找到它。 - Ryand
这也适用于Windows 7。对于那些不太熟悉Windows API的人:您可以使用GetParent(HWND window)获取父窗口,并使用GetClassName()获取其类名。 - Knitschi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接