wpf 将GetKeyNameText与特殊键一起使用

axr492tv  于 2022-12-30  发布在  其他
关注(0)|答案(2)|浏览(156)

我为WPF编写了一个热键控件,希望向用户显示友好的名称。为此,我使用GetKeyNameText
然而,例如,当使用Key.MediaNextTrack作为输入时,GetKeyNameText返回P,这看起来似乎是错误的。有人能帮助我获得这些深奥的键的正确名称吗?
我的代码执行以下操作:
1.调用KeyInterop.VirtualKeyFromKey以获取Win32虚拟密钥
1.通过调用MapVirtualKey将虚拟键翻译为扫码
1.呼叫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);
    }
}
xsuvu9jc

xsuvu9jc1#

这些媒体键的名称不包含在键盘布局dll中,因此无法通过Win32 API获得。
下面是我用C++编写的GetKeyNameTextW API的 Package 器:

// 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"}, // VK_MEDIA_PREV_TRACK
        { 0xe019, "Next Track"}, // VK_MEDIA_NEXT_TRACK
        { 0xe020, "Volume Mute"}, // VK_VOLUME_MUTE
        { 0xe021, "Launch App 2"}, // VK_LAUNCH_APP2
        { 0xe022, "Media Play/Pause"}, // VK_MEDIA_PLAY_PAUSE
        { 0xe024, "Media Stop"},// VK_MEDIA_STOP
        { 0xe02e, "Volume Down"}, // VK_VOLUME_DOWN
        { 0xe030, "Volume Up"}, // VK_VOLUME_UP
        { 0xe032, "Browser Home"}, // VK_BROWSER_HOME
        { 0xe05e, "System Power"}, // System Power (no VK code)
        { 0xe05f, "System Sleep"}, // VK_SLEEP
        { 0xe063, "System Wake"}, // System Wake (no VK code)
        { 0xe065, "Browser Search"}, // VK_BROWSER_SEARCH
        { 0xe066, "Browser Favorites"}, // VK_BROWSER_FAVORITES
        { 0xe067, "Browser Refresh"}, // VK_BROWSER_REFRESH
        { 0xe068, "Browser Stop"}, // VK_BROWSER_STOP
        { 0xe069, "Browser Forward"}, // VK_BROWSER_FORWARD
        { 0xe06a, "Browser Back"}, // VK_BROWSER_BACK
        { 0xe06b, "Launch App 1"}, // VK_LAUNCH_APP1
        { 0xe06c, "Launch Mail"}, // VK_LAUNCH_MAIL
        { 0xe06d, "Launch Media Selector"} // VK_LAUNCH_MEDIA_SELECT
    };

    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

const RAWKEYBOARD& keyboard = input->data.keyboard;

if (keyboard.MakeCode == KEYBOARD_OVERRUN_MAKE_CODE || keyboard.VKey == 0xff/*VK__none_*/)
    return;

bool keyUp = (keyboard.Flags & RI_KEY_BREAK) == RI_KEY_BREAK;

uint16_t scanCode = keyboard.MakeCode;
scanCode |= (keyboard.Flags & RI_KEY_E0) ? 0xe000 : 0;
scanCode |= (keyboard.Flags & RI_KEY_E1) ? 0xe100 : 0;

constexpr uint16_t c_BreakScanCode = 0xe11d; // emitted on Ctrl+NumLock
constexpr uint16_t c_NumLockScanCode = 0xe045;
constexpr uint16_t c_PauseScanCode = 0x0045;

// These are special for historical reasons
// https://en.wikipedia.org/wiki/Break_key#Modern_keyboards
// Without it GetKeyNameTextW API will fail for these keys
if (scanCode == c_BreakScanCode)
    scanCode = c_PauseScanCode;
else if (scanCode == c_PauseScanCode)
    scanCode = c_NumLockScanCode;

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

yfjy0ee72#

1.虚拟关键点不包含修改器,因此无需执行&ffff

  1. MapVirtualKey不能使用扩展密钥,我怀疑这是您的问题。请尝试使用MapVirtualKeyEx

相关问题