Search code examples
c#windowsaccessibilitymouse

How to enable "mouse keys" accessibility feature via c# code?


I'm trying to enable the mouse accessibility feature "mouse keys" using user32.dll method and I can't figure it out what I'm doing wrong.

I'm using the code below to enable it but it isn't working.

PS: I tried to use SPI_GETMOUSEKEYS (0x0036) and I was able to retrieve the mouse accessibility settings into the MOUSEKEYS struct, but I'm not being able to use SPI_SETMOUSEKEYS to change the current settings

class Program
{
    const int SPI_SETMOUSEKEYS = 0x0037;
    const int SPIF_SENDCHANGE = 0x002;

    [DllImport("user32.dll", SetLastError = true)]
   // [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool SystemParametersInfo(int uiAction, int uiParam, IntPtr pvParam, int fWinIni);

    [StructLayout(LayoutKind.Sequential)]
    public struct MOUSEKEYS
    {
        public uint cbSize;
        public uint dwFlags;
        public uint iMaxSpeed;
        public uint iTimeToMaxSpeed;
        public uint iCtrlSpeed;
        public uint dwReserved1;
        public uint dwReserved2;
    }

    static void Main()
    {

        try
        {
            // Enable Mouse Keys
            EnableMouseKeys();

            Console.WriteLine("Mouse Keys enabled.");
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

    public static void EnableMouseKeys()
    {
        MOUSEKEYS mk = new MOUSEKEYS();
        mk.cbSize = (uint)Marshal.SizeOf(typeof(MOUSEKEYS));
        mk.dwFlags = 0x00000001;
        bool retVal;
        IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(mk));

        Marshal.StructureToPtr(mk, ptr, true);

        retVal = SystemParametersInfo(SPI_SETMOUSEKEYS, Marshal.SizeOf(typeof(MOUSEKEYS)), ptr, SPIF_SENDCHANGE);
    }

Solution

  • When you create a MOUSEKEYS struct from scratch, you have to specify all parameters.
    Better read the current state of the Mouse Keys feature, then change only the necessary flags: MKF_MOUSEKEYSON and, eventually, MKF_REPLACENUMBERS in this case.

    You can pass a MOUSEKEYS struct by ref here, no need to bother Marshal.AllocHGlobal().
    You also need to verify that the MKF_AVAILABLE flags is set, otherwise the feature is not available.

    When you change the current parameters, instruct to save the change and also to broadcast the notification to top level windows. I.e., set both SPIF_UPDATEINIFILE and SPIF_SENDCHANGE.

    Call the EnableMouseKeys() method, passing true or false to enable / disable the feature.

    public static bool EnableMouseKeys(bool enabled) {
        if (GetMouseKeys(out MOUSEKEYS mouseKeys)) {
            if (!mouseKeys.dwFlags.HasFlag(MouseKeysFlags.AVAILABLE)) {
                throw new InvalidOperationException("Mouse Keys feature not available");
            }
            mouseKeys.dwFlags = enabled ? 
                mouseKeys.dwFlags | MouseKeysFlags.MOUSEKEYSON : 
                mouseKeys.dwFlags &~MouseKeysFlags.MOUSEKEYSON;
            return SetMouseKeys(mouseKeys);;
        }
        return false;
    }
    
    public static bool GetMouseKeys(out MOUSEKEYS mouseKeys) {
        mouseKeys = new MOUSEKEYS();
        return SystemParametersInfo(SPI.GETMOUSEKEYS, 
            (uint)Marshal.SizeOf<MOUSEKEYS>(), ref mouseKeys, SPIF.NONE);
    }
    
    public static bool SetMouseKeys(MOUSEKEYS mouseKeys) {
        return SystemParametersInfo(SPI.SETMOUSEKEYS, 
            (uint)Marshal.SizeOf<MOUSEKEYS>(), ref mouseKeys, SPIF.UPDATEANDSENDCHANGE);
    }
    

    Declarations:

    public enum MouseKeysFlags : uint {
        MOUSEKEYSON = 0x00000001,
        AVAILABLE = 0x00000002
    }
    
    public enum SPI : uint {
        GETMOUSEKEYS = 0x0036,
        SETMOUSEKEYS = 0x0037
    }
    
    public enum SPIF : uint {
        NONE = 0x00,
        UPDATEINIFILE = 0x01,
        SENDCHANGE = 0x02,
        SENDWININICHANGE = 0x02,
        UPDATEANDSENDCHANGE = UPDATEINIFILE | SENDCHANGE
    }
    
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    public struct MOUSEKEYS {
        public MOUSEKEYS() { }
        public uint cbSize = (uint)Marshal.SizeOf<MOUSEKEYS>();
        public MouseKeysFlags dwFlags;
        public int iMaxSpeed;
        public int iTimeToMaxSpeed;
        public int iCtrlSpeed;
        public int dwReserved1;
        public int dwReserved2;
    }
    
    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    internal static extern bool SystemParametersInfo(SPI uiAction, uint uiParam, ref MOUSEKEYS pvParam, SPIF fWinIni);