如何仅凭ConsoleKey枚举和Modifiers获取按键的字符?

4
我想要创建一个 ConsoleKeyInfo 实例,以匹配 PowerShell 会话中键入的任何开放大括号的相应闭合大括号(我正在使用 PSReadline 进行键处理)。为了方便起见,这里列出了所有涉及到的按键属性。
PS> while($true){ [System.Console]::ReadKey($true) }

    KeyChar     Key    Modifiers
    -------     ---    ---------
          [    Oem4            0
          ]    Oem6            0  
          {    Oem4        Shift
          }    Oem6        Shift

在键处理程序中,我会收到按下的“和弦”的ConsoleKeyInfo(并且PSReadline进行过滤,因此我已经知道我只接收到Oem4Shift+Oem4)。我想生成匹配的ConsoleKeyInfo,以便将成对打印发送到控制台。 ConsoleKeyInfo构造函数需要以下参数:
  • 一个char
  • 一个System.ConsoleKey
  • 每个Shift、Alt和Control的bool
我可以通过将其转换为int并向上移动两个来获取正确的ConsoleKey...
PS> [System.ConsoleKey]([int]$key.Key + 2)
Oem6

我可以通过按下的键的Modifiers进行位测试来进行映射...

PS> ($key.Modifiers -band [System.ConsoleModifiers]::Shift) -ne 0
False

但是,我不知道如何获取此控制台键的文字char。控制台如何从键盘键获取字符?这只能使用实时控制台/键盘完成吗? 我不想维护键对映射表,也不想拆分处理程序,每个“和弦”都要硬编码匹配键字符。:(

2
请查看我的“InsertPairedBraces”示例,链接在此处:https://github.com/lzybkr/PSReadLine/blob/master/PSReadLine/SamplePSReadlineProfile.ps1 - Jason Shirk
3个回答

5

对于PSReadline而言,您可能不需要创建一个ConsoleKeyInfo。

偶尔,您可能需要将ConsoleKeyInfo传递给PSConsoleReadLine中的方法,但大多数接受ConsoleKeyInfo参数的PSConsoleReadLine方法甚至都不会查看该参数。这就是为什么该参数是可为空的原因。

然而,这并没有真正回答您的问题。JaredPar是绝对正确的,一般来说,将ConsoleKey/ConsoleModifiers对转换为char是不可能的。如果我们不关心完全泛化(当前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;
}

3
如果你只是想插入右括号,那么你应该能够像这个处理程序一样执行 - 它对单引号和双引号都是一样的:
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)
    }
}

据我所知,这种方法可能并不适用于所有的键盘,但是我猜测你只想让这个方法在你的键盘上生效。 :-)


3
很遗憾,你无法真正做到这一点。键盘输入并不像是一个键盘按键+修饰符=一个字符那么简单。相反,它更像是一系列的键盘按键+修饰符值=一个字符。在英文键盘中,这个序列往往只有长度为1,但在其他语言中,这个序列可能会更长。这意味着,一般来说,这不是一个可解决的问题,因为一个键不能确定地产生一个给定的字符。
Michael Kaplan有一系列精彩的博客文章详细介绍了这个问题。

http://www.siao2.com/2006/04/13/575500.aspx


1
PSReadline目前忽略了这个问题,没有任何投诉 - 无法为不能由ConsoleKeyInfo表示的键提供自定义绑定。 - Jason Shirk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接