C# How to translate virtual keycode to char?
Asked Answered
O

7

34

I am trying to map a virtual keycode to a char.

My code uses ProcessCmdKey to listen to WM_KEYDOWN which gives me access to the key pressed. For example, when I press single quote I get a key of 222 which I want to have it mapped to keychar 39 which represents... you guessed it... single quote.

My dev context is: - .net Framework 2.0 - UserControl placed in a lot of places

Do you know the answer to the question?

Oho answered 25/11, 2008 at 20:44 Comment(1)
MapVirtualKey() ?Michaeu
T
48

Isn't that what the System.Windows.Form.KeysConverter class is for?

KeysConverter kc = new KeysConverter();
string keyChar = kc.ConvertToString(keyData);
Tesch answered 26/11, 2008 at 14:40 Comment(7)
Forgot to mention the context of my question: - .net Framework 2.0 - UserControl So the answer to your answer is no.Oho
Just noticed I never replied to that... KeysConverter exists in .NET 2.0.Tesch
Hm, but that works only partially. As an example where it won't work: (new KeysConverter()).ConvertToString(Keys.OemQuestion) (Spoiler: it would return OemQuestion instead of ?). Besides, it isn't clear how to handle Shift key. Is there a working solution?Ruzich
@Ruzich Make sure you're matching the correct KeysConverter to the correct Keys enum. System.Windows.Forms.KeyConverter is for WinForms, System.Windows.Input.KeyConverter is for WPF.Tesch
As for modifier keys, the Keys enum actually consists of flags, so D would actually be Keys.Shift | Keys.DTesch
Real answer (at least for me and I guess for some more), is further below.Bigley
Nah, this is just the same with the Keys.ToString()Optimistic
O
29

Yes, I did use the MapVirtualKey method. But I was expecting more details on how to use it: what DllImport directive to use, what enum is specific for mapping to characters, etc.

I don't like these answers where you google for like 5 seconds and then just suggest a solution: the real challenge is to put all the pieces together and not have to waste your time with tons of sample-less MSDN pages or other coding forums in order to get your answer. No offense plinth, but your answer (even good) was worhtless since I had this answer even before posting my question on the forum!

So there you go, I am going to post what I was looking for - an out-of-the-box C# solution:

1- Place this directive inside your class:

[DllImport("user32.dll")]
static extern int MapVirtualKey(uint uCode, uint uMapType);

2- Retrieve your char like this:

  protected override bool ProcessCmdKey(ref Message msg, Keys keyData)      
  {
     const int WM_KEYDOWN = 0x100;

     if (msg.Msg == WM_KEYDOWN)
     {            
        // 2 is used to translate into an unshifted character value 
        int nonVirtualKey = MapVirtualKey((uint)keyData, 2);

        char mappedChar = Convert.ToChar(nonVirtualKey);
     }

     return base.ProcessCmdKey(ref msg, keyData);
  }

Thanks for caring... and enjoy!

Oho answered 26/11, 2008 at 14:29 Comment(5)
Thanks for following up. If you had already looked at MapVirtualKey() perhaps you should've included that in your question (ie, "I've looked at MapVirtualKey() but I don't know how to call it from C#). For your future needs, you might find pinvoke.net useful.Michaeu
And in the case of MapVirtualKey, here's the entry from pinvoke.net: pinvoke.net/default.aspx/user32/MapVirtualKey.htmlMichaeu
Minor correction: MapVirtualKey actually returns a uint, not an int. But thank you for that bit of code, it was exactly what I needed for my own project.Polycotyledon
Very helpful Q&A. I was searching for this. Thank you.Etalon
THIS should be the top-voted answer. The currently top-voted doesn't for for chars that aren't alphanumeric.Irvin
C
26

I was looking for something similar but I needed the character mapped to the current keyboard layout. Since none of the above answers fulfilled my requirements, here is what I came up with.


        public string KeyCodeToUnicode(Keys key)
        {
            byte[] keyboardState = new byte[255];
            bool keyboardStateStatus = GetKeyboardState(keyboardState);

            if (!keyboardStateStatus)
            {
                return "";
            }

            uint virtualKeyCode = (uint)key;
            uint scanCode = MapVirtualKey(virtualKeyCode, 0);
            IntPtr inputLocaleIdentifier = GetKeyboardLayout(0);

            StringBuilder result = new StringBuilder();
            ToUnicodeEx(virtualKeyCode, scanCode, keyboardState, result, (int)5, (uint)0, inputLocaleIdentifier);

            return result.ToString();
        }

        [DllImport("user32.dll")]
        static extern bool GetKeyboardState(byte[] lpKeyState);

        [DllImport("user32.dll")]
        static extern uint MapVirtualKey(uint uCode, uint uMapType);

        [DllImport("user32.dll")]
        static extern IntPtr GetKeyboardLayout(uint idThread);

        [DllImport("user32.dll")]
        static extern int ToUnicodeEx(uint wVirtKey, uint wScanCode, byte[] lpKeyState, [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszBuff, int cchBuff, uint wFlags, IntPtr dwhkl);

Ciapha answered 5/8, 2016 at 10:44 Comment(10)
Excellent. It works perfectly with non English characters. I use it from KeyDown event and call it with e.Keycode or e.KeyData and both works. ThanksPamela
The hero we need, but not the hero we deserve!Alake
This is great. Works as expected. On the other side, such an obvious task require a lot of code. Well done.Gladine
I noticed unexpected results when holding a modifier-key while calling the function and started wondering... Why get the current keyboard state? Works as expected whithout the part related to current keyboard state - and I don't see why the current state would be relevant anyway... am I missing something?Misery
Looks like this was lifted (without attribution) from PInvoke.net (pinvoke.net/default.aspx/user32.tounicodeex)Sashenka
@Sashenka given the "quality" articles pinvoke.net/default.aspx/user32.hello%20from%20spws and pinvoke.net/default.aspx/… on pinvoke.net, it's probably safe to say the lifting was done the other way around.Dunois
So which is safer for Me to Lift into my own code (with attribution, of course): byte[] keyboardState = new byte[255]; from this answer or byte[] bKeyState = new byte[256]; from pinvoke.net?Ymir
new byte[255] or `new byte[256]' ? See nf-winuser-tounicodeex on learn.microsoft.com for the answer.Ymir
Yes, as @Ymir has figured it out, the Virtual-Key Codes article shows that codes go from 0x01 to 0xFE or from 1 to 255.Ciapha
Worked great for me! Tip for others in my situation: If you have a VirtualKey instead of a Keys, you can use this solution with ((Keys)myVirtualKey), and it works for most use cases.Depict
H
7

After reading and testing some of the answers provided, I thought I'd suggest an alternative.

As mentioned by MM, System.Windows.KeysConverter does not provide a character representation of the key but rather the enum's name, e.g. "Enter" instead of '\n'.

The MapVirtualKey method suggested by Horas, in answer to his own question, is a good starting point, but still does not support either capslock, or characters entered with the shift key, e.g. '!', '$' and '>'.

An alternative to the MapVirtualKey method, that I am using, is an extension method for the Keys class:

public static char ToChar(this Keys key)
{
    char c = '\0';
    if((key >= Keys.A) && (key <= Keys.Z))
    {
        c = (char)((int)'a' + (int)(key - Keys.A));
    }

    else if((key >= Keys.D0) && (key <= Keys.D9))
    {
        c = (char)((int)'0' + (int)(key - Keys.D0));
    }

    return c;
}

The method shown above will provide support for alphanumeric characters. Support for additional characters could be implemented with either a switch statement or lookup table.

Halfsole answered 27/2, 2015 at 8:56 Comment(2)
Does this use System.Windows.Forms or System.Windows.Input? Key is an enumeration in System.Windows.Input, but is not used here, and it is unclear where Keys comes from.Indreetloire
'Keys' is part of 'System.Windows.Forms'Systemize
H
6

I've just written an improvement of Ivan Petrov answer to display a string representation of pressed key combinations in WPF, see my code below:

public static string GetKeyString(Key key, ModifierKeys modifiers)
{
    string result = "";
    if (key != Key.None)
    {
        // Setup modifiers
        if (modifiers.HasFlag(ModifierKeys.Control))
            result += "Ctrl + ";
        if (modifiers.HasFlag(ModifierKeys.Alt))
            result += "Alt + ";
        if (modifiers.HasFlag(ModifierKeys.Shift))
            result += "Shift + ";
        // Get string representation
        string keyStr = key.ToString();
        int keyInt = (int)key;
        // Numeric keys are returned without the 'D'
        if (key >= Key.D0 && key <= Key.D9)
            keyStr = char.ToString((char)(key - Key.D0 + '0'));
        // Char keys are returned directly
        else if (key >= Key.A && key <= Key.Z)
            keyStr = char.ToString((char)(key - Key.A + 'A'));
        // If the key is a keypad operation (Add, Multiply, ...) or an 'Oem' key, P/Invoke
        else if ((keyInt >= 84 && keyInt <= 89) || keyInt >= 140)
            keyStr = KeyCodeToUnicode(key);
        result += keyStr;
    }
    return result;
}

private static string KeyCodeToUnicode(Key key)
{
    byte[] keyboardState = new byte[255];
    bool keyboardStateStatus = GetKeyboardState(keyboardState);

    if (!keyboardStateStatus)
    {
        return "";
    }

    uint virtualKeyCode = (uint)KeyInterop.VirtualKeyFromKey(key);
    uint scanCode = MapVirtualKey(virtualKeyCode, 0);
    IntPtr inputLocaleIdentifier = GetKeyboardLayout(0);

    StringBuilder result = new StringBuilder();
    ToUnicodeEx(virtualKeyCode, scanCode, new byte[255], result, (int)5, (uint)0, inputLocaleIdentifier);

    return result.ToString();
}

[DllImport("user32.dll")]
static extern bool GetKeyboardState(byte[] lpKeyState);

[DllImport("user32.dll")]
static extern uint MapVirtualKey(uint uCode, uint uMapType);

[DllImport("user32.dll")]
static extern IntPtr GetKeyboardLayout(uint idThread);

[DllImport("user32.dll")]
static extern int ToUnicodeEx(uint wVirtKey, uint wScanCode, byte[] lpKeyState, [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszBuff, int cchBuff, uint wFlags, IntPtr dwhkl);
Hubie answered 12/7, 2017 at 10:51 Comment(2)
Hmm, this works, but if a non-alphanumeric keys is pressed, the return result is an empty string. Similarly, if the user presses space or TAB, the result will be invisible, making the user think that no result was returned. Is there a list somewhere of non-alphanumeric key codes and their corresponding names so that we can return the name of the key pressed?Octennial
Maybe put more conditions in the GetKeyString method.Hubie
C
1

KeysConverter gets the key name not the keys "text" ex: "Num2" instead of "2" MapVirtualKey will work for english but for non-english chars documentation states using MapVirtualKeyEx but that requires a locale Identifier that identifier is loaded by LoadKeyBoardLayout which requires a culture id constant but then after finding the correct id values it didn't work as i tried it so finally i dumped the whole thing

Centro answered 24/10, 2009 at 12:25 Comment(1)
MapVirtualKey works also for non-english layouts. it uses the actual keyboard-layout.Glabrous
S
-1

If you are using WPF (I didn't try it with WinForms), there is a very simple solution that returns directly the pressed char, the TextInput Event of Window.

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        TextInput += MainWindow_TextInput;
    }

    private void MainWindow_TextInput(object sender, System.Windows.Input.TextCompositionEventArgs e)
    {
        txtInput.Text += e.Text;
    }
}
Send answered 13/5, 2020 at 10:27 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.