C# 如何获取仅知道ConsoleKey枚举和修饰符的按键的字符?

C# 如何获取仅知道ConsoleKey枚举和修饰符的按键的字符?,c#,powershell,console,keypress,C#,Powershell,Console,Keypress,我想创建在PowerShell会话中键入的任何打开大括号的匹配右大括号的实例(我正在使用它进行密钥处理)。为方便起见,以下是所有相关密钥的属性 PS> while($true){ [System.Console]::ReadKey($true) } KeyChar Key Modifiers ------- --- --------- [ Oem4 0 ] Oem6

我想创建在PowerShell会话中键入的任何打开大括号的匹配右大括号的实例(我正在使用它进行密钥处理)。为方便起见,以下是所有相关密钥的属性

PS> while($true){ [System.Console]::ReadKey($true) }

    KeyChar     Key    Modifiers
    -------     ---    ---------
          [    Oem4            0
          ]    Oem6            0  
          {    Oem4        Shift
          }    Oem6        Shift
在按键处理程序中,我被赋予按下的“和弦”的
ConsoleKeyInfo
(并且PSReadline进行过滤,因此我已经知道我只收到
Oem4
Shift+Oem4
)。我想生成匹配的
ConsoleKeyInfo
,以便将打印对发送到控制台

拍摄

  • a
    char
  • a
    System.ConsoleKey
  • a
    bool
    分别用于换档、Alt和控制
我可以得到正确的
ConsoleKey
,方法是将其转换为
int
并向上移动两个

PS> [System.ConsoleKey]([int]$key.Key + 2)
Oem6
通过按位测试,我可以从按下的键的
修饰符映射

PS> ($key.Modifiers -band [System.ConsoleModifiers]::Shift) -ne 0
False
但是,我不知道如何获取这个控制台键的文本
char
。控制台如何从键盘键获取字符?这只能通过实时控制台/键盘完成吗


我不希望维护密钥对的映射,也不希望拆分处理程序,每个“和弦”对应一个处理程序,并对匹配的密钥字符进行硬编码:(

不幸的是,你不能真正做到这一点。键盘输入并不像键盘键+修饰语=一个字符那样简单。相反,它更多的是一系列键盘键+修饰语值=一个字符。在英语键盘中,这个序列的长度往往是1,但在其他语言中,这个序列可能更长。这意味着简单地说,这不是一个可解决的问题,因为一个键不能确定地生成给定的字符

迈克尔·卡普兰(MichaelKaplan)有一个精彩的博客系列,详细介绍了这一点


如果您只是尝试插入右大括号,那么您应该能够执行此处理程序所执行的操作-它对单引号和双引号执行相同的操作:

Set-PSReadlineKeyHandler -Chord "Ctrl+'","Ctrl+Shift+'" `
                         -BriefDescription SmartInsertQuote `
                         -Description "Insert paired quotes if not already on a quote" `
                         -ScriptBlock {
    param($key, $arg)

    $line = $null
    $cursor = $null
    [PSConsoleUtilities.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor)

    $keyChar = $key.KeyChar
    if ($key.Key -eq 'Oem7') {
        if ($key.Modifiers -eq 'Control') {
            $keyChar = "`'"
        }
        elseif ($key.Modifiers -eq 'Shift','Control') {
            $keyChar = '"'
        }
    }

    if ($line[$cursor] -eq $keyChar) {
        # Just move the cursor
        [PSConsoleUtilities.PSConsoleReadLine]::SetCursorPosition($cursor + 1)
    }
    else {
        # Insert matching quotes, move cursor to be in between the quotes
        [PSConsoleUtilities.PSConsoleReadLine]::Insert("$keyChar" * 2)
        [PSConsoleUtilities.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor)
        [PSConsoleUtilities.PSConsoleReadLine]::SetCursorPosition($cursor - 1)
    }
}

它可能不适用于所有键盘,但我猜您只对在键盘上使用它感兴趣。:-

您可能不需要仅为PSReadline创建ConsoleKeyInfo

有时,您可能需要将ConsoleKeyInfo传递给PSConsoleReadLine中的方法,但PSConsoleReadLine中接受ConsoleKeyInfo的大多数方法甚至不查看参数。这就是为什么参数可以为null

但这并不能真正回答你的问题。JaredPar完全正确,一般来说,不可能将ConsoleKey/ConsoleModifiers对转换为字符。如果我们不关心完整的通用性(目前PSReadline不关心),您可以使用类似于PSReadline使用的代码:

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;
}

请看我的“InsertPairedBraces”示例,这里PSReadline忽略了这个问题,目前为止没有任何投诉-无法为不能由ConsoleKeyInfo表示的密钥提供自定义绑定。