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
char
System.ConsoleKey
bool
each for Shift, Alt, and ControlI 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. :(
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;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With