使用 GetKeyNameText 和特殊键

问题描述 投票:0回答:2

我已经为WPF编写了一个热键控件,并且想要向用户显示友好的名称。为此,我正在使用

GetKeyNameText

但是,例如当使用

Key.MediaNextTrack
作为输入时,
GetKeyNameText
返回
P
,这显然看起来是错误的。谁能帮我得到这些深奥钥匙的正确名称?

我的代码执行以下操作:

  1. 调用
    KeyInterop.VirtualKeyFromKey
    获取Win32虚拟密钥
  2. 通过调用
    MapVirtualKey
  3. 将虚拟键翻译为扫码
  4. 致电
    GetKeyNameText

完整代码是这样的(需要参考WindowsBase):

using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Input;

namespace ConsoleApplication1 {
    class Program {
        static void Main() {
            var key = Key.MediaNextTrack;
            var virtualKeyFromKey = KeyInterop.VirtualKeyFromKey(key);
            var displayString = GetLocalizedKeyStringUnsafe(virtualKeyFromKey);

            Console.WriteLine($"{key}: {displayString}");
        }

        private static string GetLocalizedKeyStringUnsafe(int key) {
            // strip any modifier keys
            long keyCode = key & 0xffff;

            var sb = new StringBuilder(256);

            long scanCode = MapVirtualKey((uint) keyCode, MAPVK_VK_TO_VSC);

            // shift the scancode to the high word
            scanCode = (scanCode << 16); // | (1 << 24);
            if (keyCode == 45 ||
                keyCode == 46 ||
                keyCode == 144 ||
                (33 <= keyCode && keyCode <= 40)) {
                // add the extended key flag
                scanCode |= 0x1000000;
            }

            GetKeyNameText((int) scanCode, sb, 256);
            return sb.ToString();
        }

        private const uint MAPVK_VK_TO_VSC = 0x00;

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

        [DllImport("user32.dll", EntryPoint = "GetKeyNameTextW", CharSet = CharSet.Unicode)]
        private static extern int GetKeyNameText(int lParam, [MarshalAs(UnmanagedType.LPWStr), Out] StringBuilder str, int size);
    }
}
c# .net wpf winapi interop
2个回答
1
投票

这些媒体键的名称不包含在键盘布局 dll 中,因此无法通过 Win32 API 获取。扫描码列表记录在此处

这是我对 C++ 中的

GetKeyNameTextW
API 的包装:

// Clears keyboard buffer
// Needed to avoid side effects on other calls to ToUnicode API
// http://archives.miloush.net/michkap/archive/2007/10/27/5717859.html
inline void ClearKeyboardBuffer(uint16_t vkCode)
{
    std::array<wchar_t, 10> chars{};
    const uint16_t scanCode = LOWORD(::MapVirtualKeyW(vkCode, MAPVK_VK_TO_VSC_EX));
    int count = 0;
    do
    {
        count = ::ToUnicode(vkCode, scanCode, nullptr, chars.data(), static_cast<int>(chars.size()), 0);
    } while (count < 0);
}

std::string GetStringFromKeyPress(uint16_t scanCode)
{
    std::array<wchar_t, 10> chars{};
    const uint16_t vkCode = LOWORD(::MapVirtualKeyW(scanCode, MAPVK_VSC_TO_VK_EX));
    std::array<uint8_t, 256> keyboardState{};

    // Turn on CapsLock to return capital letters
    keyboardState[VK_CAPITAL] = 0b00000001;

    ClearKeyboardBuffer(VK_DECIMAL);

    // For some keyboard layouts ToUnicode() API call can produce multiple chars: UTF-16 surrogate pairs or ligatures.
    // Such layouts are listed here: https://kbdlayout.info/features/ligatures
    int count = ::ToUnicode(vkCode, scanCode, keyboardState.data(), chars.data(), static_cast<int>(chars.size()), 0);

    ClearKeyboardBuffer(VK_DECIMAL);

    return utf8::narrow(chars.data(), std::abs(count));
}

std::string GetScanCodeName(uint16_t scanCode)
{
    static struct
    {
        uint16_t scanCode;
        const char* keyText;
    } mediaKeys[] =
    {
        { 0xe010, "Previous Track"},
        { 0xe019, "Next Track"},
        { 0xe020, "Volume Mute"},
        { 0xe021, "Launch App 2"},
        { 0xe022, "Media Play/Pause"},
        { 0xe024, "Media Stop"},
        { 0xe02e, "Volume Down"},
        { 0xe030, "Volume Up"},
        { 0xe032, "Browser Home"},
        { 0xe05e, "System Power"},
        { 0xe05f, "System Sleep"},
        { 0xe063, "System Wake"},
        { 0xe065, "Browser Search"},
        { 0xe066, "Browser Favorites"},
        { 0xe067, "Browser Refresh"},
        { 0xe068, "Browser Stop"},
        { 0xe069, "Browser Forward"},
        { 0xe06a, "Browser Back"},
        { 0xe06b, "Launch App 1"},
        { 0xe06c, "Launch Mail"},
        { 0xe06d, "Launch Media Selector"}
    };

    auto it = std::find_if(std::begin(mediaKeys), std::end(mediaKeys),
        [scanCode](auto& key) { return key.scanCode == scanCode; });
    if (it != std::end(mediaKeys))
        return it->keyText;

    std::string keyText = GetStringFromKeyPress(scanCode);
    std::wstring keyTextWide = utf8::widen(keyText);
    if (!keyTextWide.empty() && !std::iswblank(keyTextWide[0]) && !std::iswcntrl(keyTextWide[0]))
    {
        return keyText;
    }

    std::array<wchar_t, 128> buffer{};
    const LPARAM lParam = MAKELPARAM(0, ((scanCode & 0xff00) ? KF_EXTENDED : 0) | (scanCode & 0xff));
    int count = ::GetKeyNameTextW(static_cast<LONG>(lParam), buffer.data(), static_cast<int>(buffer.size()));

    return utf8::narrow(buffer.data(), count);
}

在RawInput API中你可以通过这样的代码获取完整的扫描码:

// ...got `RAWINPUT* input` from WM_INPUT message

if (raw->header.dwType == RIM_TYPEKEYBOARD)
{
    RAWKEYBOARD& keyboard = raw->data.keyboard;

    // Ignore key overrun state
    if (keyboard.MakeCode == KEYBOARD_OVERRUN_MAKE_CODE)
        return 0;

    // Ignore keys not mapped to any virtual key code
    if (keyboard.VKey >= UCHAR_MAX)
        return 0;

    WORD scanCode = keyboard.MakeCode;
    BOOL keyUp = keyboard.Flags & RI_KEY_BREAK;
    if (scanCode != 0)
    {
        // Some apps may send wrong make scan codes with
        // high-order bit set (key break code).
        // Strip high-order bit and add extended scan code value.
        scanCode = MAKEWORD(scanCode & 0x7f, ((keyboard.Flags & RI_KEY_E0) ? 0xe0 : ((keyboard.Flags & RI_KEY_E1) ? 0xe1 : 0x00)));
    }
    else
    {
        // Scan codes may be empty for some buttons (like multimedia buttons).
        // Try to map them from the virtual key code.
        scanCode = LOWORD(MapVirtualKey(keyboard.VKey, MAPVK_VK_TO_VSC_EX));
    }

    std::string scanCodeName = GetScanCodeName(scanCode);
}

0
投票
  1. 虚拟键不包含修饰符,因此无需执行
    &ffff
  2. MapVirtualKey 不适用于扩展键,我怀疑这是您的问题。尝试使用 MapVirtualKeyEx
© www.soinside.com 2019 - 2024. All rights reserved.