gpt4 book ai didi

c# - 将 GetKeyNameText 与特殊键一起使用

转载 作者:太空宇宙 更新时间:2023-11-03 15:17:16 25 4
gpt4 key购买 nike

我为 WPF 编写了一个热键控件,并希望向用户显示友好名称。为此,我正在使用 GetKeyNameText .

但是,例如使用 Key.MediaNextTrack 时作为输入,GetKeyNameText 返回 P,这显然看起来不对。任何人都可以帮助我获得这些深奥的 key 的正确名称吗?

我的代码执行以下操作:

  1. 调用KeyInterop.VirtualKeyFromKey获取 Win32 虚拟 key
  2. 调用MapVirtualKey将虚拟键翻译成扫码
  3. 调用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);
}
}

最佳答案

这些媒体键的名称不包含在键盘布局 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"}, // 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);

关于c# - 将 GetKeyNameText 与特殊键一起使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38584015/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com