How to use Windows On-Screen Keyboard in C# WinFor

2019-01-14 07:14发布

问题:

  • VS 2010
  • Developing 32-bit app that runs on 64-bit OS (Windows 7, Windows 8 - desktop app)
  • C#
  • .NET 4.0
  • WinForms

I have found many threads on launching the Windows on-screen keyboard (osk.exe) from an application, but I am running into some problems. It appears to be because I am running a 32-bit app on a 64-bit OS. I've tried the code posted by WooCaSh here: Keyboard on the screen in WinForms

But none of the three different paths work for me. For the sysnative path, Process.Start fails with "Cannot find the path specified." For the system32 and osk.exe paths, I get the "Could not start the on-screen keyboard" error dialog.

I found a possible workaround here, which is a little more complicated than what I was looking for (post by eryang): http://social.msdn.microsoft.com/Forums/en-US/netfx64bit/thread/10ebc62f-e6d7-4072-9fd1-ea3784a0966f/

  1. Is there an easier or correct way to do this?
  2. I was hoping that running my app on a tablet would automatically launch the on-screen keyboard when the user selects a textbox, but no luck. Is this normal or am I missing something? I've found that the keyboard is not automatically launched in Windows 8 when in Desktop mode (which my app is): http://www.bleepingcomputer.com/forums/t/480250/keyboard-does-not-pop-up-automatically/ Apparently it works on Windows 7 tablets, but I can't test because I only have Windows 8 tablets here.

回答1:

I am now launching the "Touch Keyboard" as opposed to the "On-Screen Keyboard" (which is the keyboard I wanted on Windows 8 anyway) with:

string progFiles = @"C:\Program Files\Common Files\Microsoft Shared\ink";
string keyboardPath = Path.Combine(progFiles, "TabTip.exe");

this.keyboardProc = Process.Start(keyboardPath);

This works on my Win7 and Win8, regardless of my 32-bit app on 64-bit OS. However, I still have the problem of programmatically closing the keyboard when I'm done. The process, this.keyboardProc, does not seem to get the handle, and immediately has property HasExited = true. This means my attempts to close or kill it fail.

According to this thread, if the user manually opens the keyboard (or I programmatically launch it), the keyboard will not automatically close/hide when the text field loses focus: Windows 8 - How to Dismiss Touch Keyboard? I tried the workaround of setting the focus to a hidden button, but since I launched the keyboard myself, it doesn't close automatically.



回答2:

I had trouble closing the on screen keyboard. You can start the Touch Keyboard with

string progFiles = @"C:\Program Files\Common Files\Microsoft Shared\ink";
string onScreenKeyboardPath = System.IO.Path.Combine(progFiles, "TabTip.exe");
onScreenKeyboardProc = System.Diagnostics.Process.Start(onScreenKeyboardPath);

and close all keyboards with

//Kill all on screen keyboards
Process[] oskProcessArray = Process.GetProcessesByName("TabTip");
foreach (Process onscreenProcess in oskProcessArray)
{
    onscreenProcess.Kill();
}

For some reason onScreenKeyboardProc.Kill() or .Close() doesn't work.



回答3:

For the keyboard to open up automatically, the controls have to implement some UI Automation control patterns, specifically ITextProvider/IValueProvider. It is a nuisance, but it works (and it is a cleaner way than launching some *.exe)

I wrote a sample app and a short article here: http://blog.tombam.net/implementing-textbox-with-on-screen-touch-keyboard-part-1/



回答4:

The touch keyboard leverages the UI automation. For some reasons that are a mystery to me, the standard Windows edit box does not implement UI automatic, while other controls, like combo box, do.


One can use implementation of UI automation from UIAutomationClient.dll.

For the UI automation to be magically injected into an application, class initializer of the assembly internal class UiaCoreApi have to be triggered.

On can achieve that for example by calling seeming no-op:

AutomationElement.FromHandle(IntPtr)(-1)

Another way is to implement automation UI explicitly. For that implement the ITextProvider/IValueProvider interfaces for the respective input control.

To bind the implementation of the interfaces to the control, handle WM_GETOBJECT window message with lParam = RootObjectId.

For an example of implementation, see

  • tombam's answer to this question;
  • or directly poster's article Implementing TextBox with on-screen touch keyboard.

Though interestingly, controls, for which touch keyboard works out-of-the-box (like combo box or password edit box, see the answer), do not implement the WM_GETOBJECT/RootObjectId. There must be a different machinery behind them.


The most common "solution" to this problem, to explicitly popup the keyboard by running the TabTip.exe or osk.exe, is hardly acceptable.

If for nothing else, then because there's no clean way to hide the keyboard opened by running the TabTip.exe (solutions include hacks like killing the process or sending Esc key).

And actually the above hack does not seem to work anymore in Windows 10 Anniversary Update: Show touch keyboard (TabTip.exe) in Windows 10 Anniversary edition.



回答5:

Starting TabTip.exe no longer works in Widnows 10 Anniversary Edition. I discovered an undocumented COM interface for controlling the touch keyboard. Check the code here https://stackoverflow.com/a/40921638/332528