How do I get the char for a keypress knowing only the ConsoleKey enum and Modifiers?

c#, console, keypress, powershell

Solution

You probably don't need to create a ConsoleKeyInfo just for PSReadline.

Occasionally you may need to pass a ConsoleKeyInfo to a method in PSConsoleReadLine, but most methods in PSConsoleReadLine that accept a ConsoleKeyInfo don't even look at the argument. That's why the parameter is Nullable.

This doesn't actually answer your question though. JaredPar is absolutely correct that in general, it's not possible to convert a ConsoleKey/ConsoleModifiers pair to a char. If we don't care about full generality (and currently PSReadline doesn't), you can use code similar to what PSReadline uses:

internal static char GetCharFromConsoleKey(ConsoleKey key, ConsoleModifiers modifiers)
{
    // default for unprintables and unhandled
    char keyChar = '\u0000';

    // emulate GetKeyboardState bitmap - set high order bit for relevant modifier virtual keys
    var state = new byte[256];
    state[NativeMethods.VK_SHIFT] = (byte)(((modifiers & ConsoleModifiers.Shift) != 0) ? 0x80 : 0);
    state[NativeMethods.VK_CONTROL] = (byte)(((modifiers & ConsoleModifiers.Control) != 0) ? 0x80 : 0);
    state[NativeMethods.VK_ALT] = (byte)(((modifiers & ConsoleModifiers.Alt) != 0) ? 0x80 : 0);

    // a ConsoleKey enum's value is a virtual key code
    uint virtualKey = (uint)key;

    // get corresponding scan code
    uint scanCode = NativeMethods.MapVirtualKey(virtualKey, NativeMethods.MAPVK_VK_TO_VSC);

    // get corresponding character  - maybe be 0, 1 or 2 in length (diacriticals)
    var chars = new char[2];
    int charCount = NativeMethods.ToUnicode(
        virtualKey, scanCode, state, chars, chars.Length, NativeMethods.MENU_IS_INACTIVE);

    // TODO: support diacriticals (charCount == 2)
    if (charCount == 1)
    {
        keyChar = chars[0];
    }

    return keyChar;
}

Problem

I want to create a `ConsoleKeyInfo` instance of the matching closing brace of any open brace typed in a PowerShell session (I'm using PSReadline to do the key handling). For your convenience, here are the properties of all the keys involved ``` PS> while($true){ [System.Console]::ReadKey($true) } KeyChar Key Modifiers ------- --- --------- [ Oem4 0 ] Oem6 0 { Oem4 Shift } Oem6 Shift ``` In the key handler, I am given the `ConsoleKeyInfo` for the "chord" that was pressed (and PSReadline does filtering, so I already know I'm receiving only an `Oem4` or `Shift+Oem4`). I want to generate the matching `ConsoleKeyInfo` so I can send the pair printing to the console. The `ConsoleKeyInfo` constructor takes - a `char` - a `System.ConsoleKey` - a `bool` each for Shift, Alt, and Control I can get the correct `ConsoleKey` by casting it to an `int` and moving up two... ``` PS> [System.ConsoleKey]([int]$key.Key + 2) Oem6 ``` And I can map from the pressed key's `Modifiers` by testing it bitwise... ``` PS> ($key.Modifiers -band [System.ConsoleModifiers]::Shift) -ne 0 False ``` But, I have no idea how to get the literal `char` for this console key. How does the console get the char from the keyboard key? Can this only be done with a live console/keyboard? I'd rather not maintain a map of the key pairs nor split the handlers, one for each "chord", and hardcode the matching key char. :(

Original source