Injecting input in UWP apps

Visual Studio WinUI

6 years ago

One of the less known capabilities of UWP apps is the ability to inject input. This is especially useful if you want to give the user a guided tour through the app, offer immediate feedback to users with assistive technologies or implement a remote help functionality into your app. In this article we will explore what the Windows.UI.Input.Preview.Injection namespace has to offer and how can you use it in your app.

Supported input types

Windows 10 supports several input types so does the input injector. Currently the list includes:

  • Gamepad

  • Keyboard

  • Mouse

  • Pen

  • Touch

    We will show an example of keyboard and mouse input injection in this article.

Input injection capability

Before we utilize input injection, we have to declare this capability in the application manifest, as it is a non-standard functionality. It is a restricted capability, which means you can safely publish your app into the app store with it, but require approval for store submission. Adding the capability is simple. Open the Package.appxmanifest as XML file and add the following namespace declaration to the Package element:

<Package
   ...
   xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
  IgnorableNamespaces="uap mp rescap">

The rescap namespace is home to the restricted capabilities. You can now declare the inputInjectionBrokered capability in the Capabilities section:

<Capabilities>
  <Capability Name="internetClient" />
  <rescap:Capability Name="inputInjectionBrokered" />
</Capabilities>

Basics

The main class in the Windows.UI.Input.Preview.Injection is the InputInjector. We can create it's instance by calling the static TryCreate method. Afterwards, we can call respective methods for each input type:

var inputInfo = new InjectedInputMouseInfo();
...
InputInjector inputInjector = InputInjector.TryCreate();
inputInjector.InjectMouseInput(new[] { inputInfo });

As we can see, the methods accept a list of input infos, so we can perform multiple input operations in succession.

Mouse input

We can prepare mouse input info using the InjectedInputMouseInfo class. To move the mouse we use the DeltaX and DeltaY properties:

var info = new InjectedInputMouseInfo();
info.MouseOptions = InjectedInputMouseOptions.Move;
info.DeltaY = 100; //move down 100 points

The MouseOptions property allows us to specify flags that affect what action should be performed. Among those are LeftDown and LeftUp, which we can use to simulate simple mouse click:

var down = new InjectedInputMouseInfo();
down.MouseOptions = InjectedInputMouseOptions.LeftDown;

var up = new InjectedInputMouseInfo();
up.MouseOptions = InjectedInputMouseOptions.LeftUp;

InputInjector inputInjector = InputInjector.TryCreate();
inputInjector.InjectMouseInput(new[] { down, up });

Mouse also has scrolling wheels and we can simulate both vertical and horizontal one using the Wheel and HWheel respectively. You can supply the scroll distance using the MouseData property:

var info = new InjectedInputMouseInfo();
info.MouseOptions = InjectedInputMouseOptions.Wheel;
info.MouseData = 500; //scroll up

InputInjector inputInjector = InputInjector.TryCreate();
inputInjector.InjectMouseInput(new[] { info });

Scrolling up is easy. But what about scrolling down?

info.MouseData = -500; //compile time error!

Suprisingly, MouseData property is a uint which makes it impossible to assign negative values to it. We can use overflowing to circumvent this however:

unchecked
{
    info.MouseData = (uint)-500; //scroll down
}

We have to use an unchecked block in this case, because we set the value as a constant and the compiler complains that the cast will cause numeric overflow. If we used a temporary local variable, we could avoid having to use unchecked. Mouse input also includes a TimeOffsetInMilliseconds property, which we can set to delay individual actions. This proves useful to properly simulate operations like a double-click.

Keyboard input

The InjectedInputKeyboardInfo class will the the base for keyboard input injection. The most important property is the VirtualKey, which specifies which key is the input related to. Using KeyOptions we can specify further options like simulating key up event. The following sample will print out string "hello" in the active input field:

InputInjector inputInjector = InputInjector.TryCreate();
foreach (var letter in "hello")
{
    var info = new InjectedInputKeyboardInfo();
    info.VirtualKey = (ushort)((VirtualKey)Enum.Parse(typeof(VirtualKey),
                                 letter.ToString(), true));
    inputInjector.InjectKeyboardInput(new[] { info });
    await Task.Delay(100);
}

There is a Task.Delay call at the end of the foreach loop which is not there just for the cool factor :-D . This ensures that repeated key presses are not registered as a single press, which would prevent the second letter L to be registered.

Sample code

The sample source code for this article is available on my GitHub.

Input injection sample app

Input injection sample app

Summary

We have explored the Windows.<wbr />UI.<wbr />Input.<wbr />Preview.<wbr />Injection namespace and seen how we can use it to simulate input from different types of input devices. Although not too commonly utilized, it is very nice to see this capability in the UWP API.