Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / MFC

Virtual On-Screen Keyboard Example

4.21/5 (11 votes)
15 Jan 2009CPOL2 min read 97.3K   7.5K  
A simple MFC dialog-based framework that mimics the behavior of the MS On-Screen Keyboard.

Introduction

A dialog can send key input to any foreground window without itself being activated to function as an on-screen keyboard. Regular windows get activated and focused when they are clicked or something. To avoid this, a window needs to be a non-activating window. You can do this by creating a window with the "WS_EX_NOACTIVATE" extended style, or by modifying its style using "ModifyStyleEx". But, with this style, a window behaves somewhat differently than expected when you move the window. To solve this, you need to provide your own implementations for the "WM_NCLBUTTONDOWN" and "WM_MOUSEMOVE" messages to behave the same way as the system-provided On-Screen Keyboard does. This code shows how to do this and send your own keyboard inputs to any foreground window using the newly-introduced "SendInput" system function.

Background

One day, I needed to implement a virtual On-Screen Keyboard that received signals - through a COM (serial) port - from a custom input device and then sent keyboard inputs to the system according to the signals. While doing this, I came to know about the On-Screen Keyboard accessory application provided by Windows, and wanted my application to behave similar to that. But, it was not as trivial a task as I first expected, because of the "focus" and "activation" problems. In this example, you will find that avoiding those problems could be done in a simple way.

Using the code

This is a VC++ 9.0 project.

Firstly, you need to create an MFC dialog-based application. And then, to make the dialog a non-activating topmost window, you need to change its "No Activate" and "Topmost" resource properties to "true".

Then, handle "WM_NCLBUTTONDOWN" message. This is for making the dialog temporarily behave as a regular window without the "WS_EX_NOACTIVATE" style so that it can move smoothly.

C++
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);
}

Now, handle the "WM_MOVE" message. This is for making the dialog behave again as a non-activating window when the mouse pointer moves around in the client area, and for making any previous foreground window active. This is the exact behavior the system-provided On-Screen Keyboard shows.

C++
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);
}

Now, to send an arbitrary key input to the system, you can use the "SendInput" function. Here, I send the "x" (virtual code 88) key input as an example when the "Send X" button in the dialog is clicked.

C++
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));
}

Points of interest

Code samples I have found on the Internet do this in an overly complicated manner using the "AttachThreadInput" function. As you can see, making a window behave the same way as the Windows On-Screen Keyboard can be done in a simple way using the "WS_EX_NOACTIVATE" style and some other small tricks.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)