2

The project I am working on needs to record global keystrokes on the keyboard with distinguishing key combinations and which control key (left or right) is held down. It seems the only possible way is to use KeyDown and KeyUp events rather than KeyPress. However, I also need to record which character was typed.

I understand if I use KeyPress event, I will always get the correct character, but with KeyDown and KeyUp events, it will only give the KeyCode which regardless to the key combinations. One of the solutions is to use a Dictionary to map the Shift keys, but it is not friendly to the international environment as different localised keyboard has different symbols. For example, Shift-2, gives a @ symbol on US keyboard, but a " symbol on UK keyboard.

I wonder if there is any way to convert the combination automatically without manually mapping all keyboard layouts?

A slight more complex but off-topic question is, how about if the user use some software key mapping program to give keys different output? e.g., swapping a and s key's definition that when typing a it outputs s, and vice versa. What will happen for the global hook? I don't have this kind software installed so I can't test it out, anyone has some experience in handling it?

Tide Gu
  • 815
  • 1
  • 7
  • 21

1 Answers1

0

Interesting question. I managed to get the correct shifted value using the answer from here: Keyboard Mapping in .NET

using System.Runtime.InteropServices;
public static class User32Interop
{
    public static char ToAscii(Keys key, Keys modifiers)
    {
        var outputBuilder = new StringBuilder(2);
        int result = ToAscii((uint)key, 0, GetKeyState(modifiers),
            outputBuilder, 0);
        if (result == 1)
            return outputBuilder[0];
        else
            throw new Exception("Invalid key");
    }

    private const byte HighBit = 0x80;
    private static byte[] GetKeyState(Keys modifiers)
    {
        var keyState = new byte[256];
        foreach (Keys key in Enum.GetValues(typeof(Keys)))
        {
            if ((modifiers & key) == key)
            {
                try
                {
                    keyState[(int) key] = HighBit;
                }
                catch { }
            }
        }
        return keyState;
    }

    [DllImport("user32.dll")]
    private static extern int ToAscii(uint uVirtKey, uint uScanCode,
        byte[] lpKeyState,
        [Out] StringBuilder lpChar,
        uint uFlags);
}

And then using it like this in the KeyDown event:

private void Form1_KeyDown(object sender, KeyEventArgs e)
{
    if (e.KeyCode != Keys.ShiftKey)
    {
        if (e.Modifiers.ToString() == "Shift")
        {
            var c = User32Interop.ToAscii(e.KeyCode, Keys.ShiftKey);
            MessageBox.Show(c.ToString());
        }
    }
}

With German Keyboard layout, this produced " while with US keybaord layout I got @ . So pretty much what you need I guess.

I cannot test this with a third-party key mapper, so you will simply have to try that.

LocEngineer
  • 2,847
  • 1
  • 16
  • 28
  • Thanks! I saw ToAscii() earlier while researching for a solution, but didn't get it right. It seems that I messed the modifier key bit, i.e., `Keys.ShiftKey` and `Keys.Shift`. – Tide Gu Jun 01 '17 at 16:56
  • 1
    Yep, I had the same trouble with Shift vs ShiftKey, which one is contained in the event argument vs what ToAscii needs; hence the slightly awkward construction with ToString() and then passing explicitly ShiftKey. Good luck with your project. – LocEngineer Jun 01 '17 at 17:54