- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我为 WPF 编写了一个热键控件,并希望向用户显示友好名称。为此,我正在使用 GetKeyNameText
.
但是,例如使用 Key.MediaNextTrack
时作为输入,GetKeyNameText
返回 P
,这显然看起来不对。任何人都可以帮助我获得这些深奥的 key 的正确名称吗?
我的代码执行以下操作:
KeyInterop.VirtualKeyFromKey
获取 Win32 虚拟 key MapVirtualKey
将虚拟键翻译成扫码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/
我正在尝试使用 GetKeyNameText 获取按键的名称,使用原始输入给出的生成/扫描代码和扩展键标志: std::wstring GetKeyName(const RAWKEYBOARD& in
我们可以使用GetKeyNameText()检索表示键名的字符串。有什么方法可以做相反的事情,即获取给定键名的扫描码或虚拟键? 我想将键名写入配置文件,以便用户可以轻松编辑它们。当我读入配置文件时,我
Win32 函数 GetKeyNameText将提供当前输入区域设置中键盘按键的名称。 来自 MSDN: The key name is translated according to the lay
我为 WPF 编写了一个热键控件,并希望向用户显示友好名称。为此,我正在使用 GetKeyNameText . 但是,例如使用 Key.MediaNextTrack 时作为输入,GetKeyNameT
我是一名优秀的程序员,十分优秀!