14

A friend of mine is blind and I've got an idea of developing a program that would let him use a PC with the help of a blind typing method and audial feedback. The experience would be much richer (making use of more keys for particular functions) and flawless (like preventing occasional focus loss) if my application could forcibly take total control over the whole keyboard input as soon as it is started (I'd put it in start-up for him). I am a WinForms C# .NET developer so I'd like to implement this in an application using this particular framework and language (don't mind wrapped WinAPI calls though).

PS: I don't mind the system keeping control over the Ctrl+Alt+Del combination, but I'd like to take control over all the other keys and combinations, including the Windows logo and standard application launcher buttons.

Pang
  • 9,564
  • 146
  • 81
  • 122
Ivan
  • 63,011
  • 101
  • 250
  • 382
  • 5
    hats off for the effort you are putting into for your blind friend... – Kurubaran Jul 10 '13 at 19:50
  • Sure as hell I'd get a handful of downvotes if I hadn't specified the purpose as people would think I am writing a virus rather than an UI shell for blind people. – Ivan Jul 10 '13 at 20:05
  • I actually want to make a program that will let him write and read (through MS speech API) messages, select audio books and music to listen to, request news, weather data etc. Perhaps he will even be able to write code on something like a BASIC dialect. – Ivan Jul 10 '13 at 20:07
  • I don't quite follow what you are hoping to do. Your friend probably is using JAWS or NVDA, which has various audio output options. By your app eating up all the key presses may make the app less accessible. Assistive Tech has built in hotkeys that allow a user to navigate to various elements provided they are accessible. – Ryan B Jul 11 '13 at 12:49

2 Answers2

11

You can use the low-level keyboard hook implementation posted here. It shouldn't steal focus away from any programs but your program can be notified when keys are pressed. This is the code from the post in case the link stops working.

using System;
using System.Diagnostics;
using System.Windows.Forms;
using System.Runtime.InteropServices;

class InterceptKeys
{
    private const int WH_KEYBOARD_LL = 13;
    private const int WM_KEYDOWN = 0x0100;
    private static LowLevelKeyboardProc _proc = HookCallback;
    private static IntPtr _hookID = IntPtr.Zero;

    public static void Main()
    {
        _hookID = SetHook(_proc);
        Application.Run();
        UnhookWindowsHookEx(_hookID);
    }

    private static IntPtr SetHook(LowLevelKeyboardProc proc)
    {
        using (Process curProcess = Process.GetCurrentProcess())
        using (ProcessModule curModule = curProcess.MainModule)
        {
            return SetWindowsHookEx(WH_KEYBOARD_LL, proc,
                GetModuleHandle(curModule.ModuleName), 0);
        }
    }

    private delegate IntPtr LowLevelKeyboardProc(
        int nCode, IntPtr wParam, IntPtr lParam);

    private static IntPtr HookCallback(
        int nCode, IntPtr wParam, IntPtr lParam)
    {
        if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
        {
            int vkCode = Marshal.ReadInt32(lParam);
            Console.WriteLine((Keys)vkCode);
        }
        return CallNextHookEx(_hookID, nCode, wParam, lParam);
    }

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr SetWindowsHookEx(int idHook,
        LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool UnhookWindowsHookEx(IntPtr hhk);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
        IntPtr wParam, IntPtr lParam);

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr GetModuleHandle(string lpModuleName);
}

You can handle the key presses in the HookCallback event (or further abstract the main parts into a separate class and raise an event).

Pang
  • 9,564
  • 146
  • 81
  • 122
keyboardP
  • 68,824
  • 13
  • 156
  • 205
  • Interesting, but I'd like to prevent the other applications and the system from getting the focus and any input so that an occasional key combination pressed or a shortcut key (like the Windows logo or a multimedia keyboard application launcher button) won't trigger the standard action. – Ivan Jul 10 '13 at 20:49
5

I know this topic is old, but I found a possible solution. You can use the last answer and edit it a little bit:

Instead of calling the next hook:

private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) {
    if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
    {
        int vkCode = Marshal.ReadInt32(lParam);
        Console.WriteLine((Keys)vkCode);
    }
    return CallNextHookEx(_hookID, nCode, wParam, lParam);
}

just return -1 for stopping preceding any handle to other controls:

private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) {
    if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
    {
        int vkCode = Marshal.ReadInt32(lParam);
        Console.WriteLine((Keys)vkCode);
    }
    return -1;
}

This isn't nice but it's working. Be careful with this!

Pang
  • 9,564
  • 146
  • 81
  • 122
Lord_Fry
  • 83
  • 1
  • 5