虚拟屏幕键盘示例
一个简单的基于 MFC 对话框的框架,模仿 MS 屏幕键盘的行为。
引言
对话框可以向任何前台窗口发送按键输入,而无需自身被激活,以充当屏幕键盘。普通的窗口在被点击时会被激活并获得焦点。为了避免这种情况,窗口需要是一个非激活窗口。您可以通过使用 "WS_EX_NOACTIVATE
" 扩展样式创建窗口,或者使用 "ModifyStyleEx
" 修改其样式来实现。但是,使用此样式,窗口在移动时,其行为会与预期有所不同。为了解决这个问题,您需要为 "WM_NCLBUTTONDOWN
" 和 "WM_MOUSEMOVE
" 消息提供您自己的实现,使其行为与系统提供的屏幕键盘相同。这段代码展示了如何做到这一点,并使用新引入的 "SendInput
" 系统函数向任何前台窗口发送您自己的键盘输入。
背景
有一天,我需要实现一个虚拟屏幕键盘,它通过 COM (串行) 端口接收来自自定义输入设备的信号,然后根据信号将键盘输入发送到系统。在做这件事的时候,我了解到了 Windows 提供的屏幕键盘辅助应用程序,并且希望我的应用程序也能表现出类似的行为。但是,由于“焦点”和“激活”问题,这并不是我最初预期的那么简单。在这个例子中,您会发现避免这些问题可以用一种简单的方式来实现。
使用代码
这是一个 VC++ 9.0 项目。
首先,您需要创建一个基于 MFC 对话框的应用程序。然后,为了使对话框成为一个非激活置顶窗口,您需要将其“No Activate(不激活)”和“Topmost(置顶)”资源属性更改为“true”。
然后,处理 "WM_NCLBUTTONDOWN
" 消息。这是为了使对话框暂时表现为一个没有 "WS_EX_NOACTIVATE
" 样式的普通窗口,以便它可以平滑移动。
void COnScreenKeyboardDlg::OnNcLButtonDown(UINT nHitTest, CPoint point)
{
if (!m_hForegroundWnd)
{
// store current foreground window
m_hForegroundWnd = ::GetForegroundWindow();
// temporarily make this dialog an activatable window
ModifyStyleEx(WS_EX_NOACTIVATE,0);
// make this dialog foreground
SetForegroundWindow();
}
CDialog::OnNcLButtonDown(nHitTest, point);
}
现在,处理 "WM_MOVE
" 消息。这是为了使对话框在鼠标指针在客户区移动时再次表现为一个非激活窗口,并使任何先前的前台窗口处于活动状态。这正是系统提供的屏幕键盘所表现出的行为。
void COnScreenKeyboardDlg::OnMouseMove(UINT nFlags, CPoint point)
{
if (m_hForegroundWnd)
{
// make the previous foreground window active
::SetForegroundWindow(m_hForegroundWnd);
// make this dialog non-activating
ModifyStyleEx(0,WS_EX_NOACTIVATE);
// set it to NULL to mark that we do not need to do this again
m_hForegroundWnd = NULL;
}
CDialog::OnMouseMove(nFlags, point);
}
现在,要向系统发送任意按键输入,您可以使用 "SendInput
" 函数。在这里,我以发送 "x" (虚拟代码 88) 键输入为例,当点击对话框中的 "Send X(发送 X)" 按钮时发送。
void COnScreenKeyboardDlg::OnBnClickedSendX()
{
INPUT keyInput;
keyInput.type = INPUT_KEYBOARD;
KEYBDINPUT key;
key.wVk = 88;
key.wScan = ::VkKeyScan(88);
key.dwFlags = 0;
keyInput.ki = key;
::SendInput(1,&keyInput,sizeof(INPUT));
key.dwFlags = KEYEVENTF_KEYUP;
keyInput.ki = key;
::SendInput(1,&keyInput,sizeof(INPUT));
}
关注点
我在互联网上找到的代码示例使用 "AttachThreadInput
" 函数以一种过于复杂的方式来实现这一点。正如您所看到的,使用 "WS_EX_NOACTIVATE
" 样式和其他一些小技巧,可以简单地实现一个窗口的行为与 Windows 屏幕键盘相同。