microsoft / microsoft-ui-xaml

Windows UI Library: the latest Windows 10 native controls and Fluent styles for your applications
MIT License
6.33k stars 675 forks source link

Problems with Input Injection #8639

Open kitswas opened 1 year ago

kitswas commented 1 year ago

Describe the bug

  1. Multiple calls to InputInjector.TryCreate() cause the app to crash.
  2. InitializeGamepadInjection() always causes an app to crash if called after a call to UninitializeGamepadInjection() on the same InputInjector instance.

Similar problems have been reported for mouse injection.

if an InputInjector is created and used a second time no mouse input is simulated (without failure).

Steps to reproduce the bug

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Windows.Gaming.Input;
using Windows.UI.Input.Preview.Injection;

namespace PC_Win
{
    internal class GamepadInjector
    {
        private InjectedInputGamepadInfo gamepadState;
        private readonly InputInjector injector;
        private bool isConnected;

        public GamepadInjector()
        {
            injector = InputInjector.TryCreate();
            gamepadState = new InjectedInputGamepadInfo();
            isConnected = false;
        }

        public void ConnectGamepad()
        {
            if (!isConnected)
            {
                try
                {
                    injector.InitializeGamepadInjection();
                    isConnected = true;
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                }
            }
        }

        public void DisconnectGamepad()
        {
            if (isConnected)
            {
                try
                {
                    injector.UninitializeGamepadInjection();
                    isConnected = false;
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                }
            }
        }

        public void Update(InjectedInputGamepadInfo state)
        {
            gamepadState = state;
        }

        public void PressButton(GamepadButtons button)
        {
            gamepadState.Buttons |= button;
        }

        public void ReleaseButton(GamepadButtons button)
        {
            gamepadState.Buttons &= ~button;
        }

        public void Inject()
        {
            if (isConnected)
            {
                injector.InjectGamepadInput(gamepadState);
            }
        }

        ~GamepadInjector()
        {
            DisconnectGamepad();
        }
    }
}

Make an object of this class and call the functions as described. The above is part of a WinUI3 app.

The same app rewritten in C++ using Cpp/WinRT (with Qt6 for GUI) has the same problems.
And the same happened in Rust with the windows-rs crate. See comment.

This clearly points towards a problem in the library.

Expected behavior

The InputInjector.InitializeGamepadInjection() docs say:

Calling this method is analogous to connecting a physical gamepad, which also triggers a GamepadAdded event.

A physical gamepad is assigned a persistent unique ID (see NonRoamableId) that does not change when the device is connected and disconnected. Similarly, a virtual gamepad created with InitializeGamepadInjection is also assigned a unique ID that persists across calls to UninitializeGamepadInjection and InitializeGamepadInjection for the same InputInjector instance.

A virtual reconnect should behave adequately then.

Creating and initializing multiple InputInjector instances should work without crashing. It is equivalent to connecting multiple devices in reality.

Screenshots

No response

NuGet package version

None

Windows version

Windows 11 (22H2): Build 22621

Additional context

No response

kitswas commented 1 year ago

You may use this website (not mine) to test the Gamepad Input.
It works. (At least till the apps crash)