Не могу понять как работает функция GetKeyboardState
. Вроде как должна определять состояние всех клавиш, но правильность результата зависит от места вызова... Если, например, скрыть окно, либо запускать функцию по таймеру, все значения окажутся равными 0. Если же назначить некоторой кнопке обработчик и при нажатии определять состояние, скажем, LeftShift, функция покажет верное значение.
Ещё мне не ясно как обозначаются состояния клавиш. Понятно, что старший бит говорит о том нажата клавиша или нет, а вот младший - is key toggled. Что ещё за Toggled state?
UPD: Так же, на счёт toggled state, согласно моим наблюдениям вполне возможна ситуация когда клавиша не нажата, а старший бит равен 1 (младший 0). Не могу понять никак как это должно работать...
var keyboardState = new byte[256];
GetKeyboardState(keyboardState);
var keyStateBits = new System.Collections.BitArray(new byte[] { keyboardState[(uint)Keys.LShiftKey] });
Debug.WriteLine("SHIFT key state: down - " + keyStateBits[0] + "; toggled - " + keyStateBits[7]);
UPD2: В самом деле мне нужно сделать работоспособным этот код:
[DllImport("user32.dll")]
static extern int ToUnicodeEx(
uint wVirtKey,
uint wScanCode,
byte[] lpKeyState,
[Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszBuff,
int cchBuff,
uint wFlags,
IntPtr dwhkl);
[DllImport("user32.dll")]
static extern bool GetKeyboardState(byte[] lpKeyState);
[DllImport("user32.dll")]
static extern uint MapVirtualKey(
uint uCode,
uint uMapType);
[DllImport("user32.dll")]
static extern IntPtr GetKeyboardLayout(uint idThread);
public static string VKCodeToUnicode(uint vkCode) {
StringBuilder buf = new StringBuilder();
byte[] keyboardState = new byte[255];
if (!GetKeyboardState(keyboardState)) return string.Empty;
ToUnicodeEx(vkCode, MapVirtualKey(vkCode, 0), keyboardState, buf, 5, 0, GetKeyboardLayout(0));
return buf.ToString();
}
Если окно отображается и активно, всё работает. Но если окно скрыть (ну через ShowWindow SW_HIDE), код выдаёт неправильные значения (без учёта состояния клавиш клавиатуры управляющих регистром и текущего языка раскладки)... Как это решить?
GetKeyboardState
работает только в случаях, когда сообщение о нажатии клавиши дошло до очереди вашего процесса (т.е., когда ваше окно в фокусе). Для получения состояния клавиши напрямую используется функция GetAsyncKeyState. Она не работает для переключаемых клавиш типа Caps Lock - для них нужно использовать GetKeyState
(или, в C# Control.IsKeyLocked
, который ее использует).
if (GetAsyncKeyState(VK_CONTROL) & 0x8000 != 0){
//нажата клавиша control
}
Если нужно в течение длительного времени отслеживать нажатие каких-то клавиш, лучше использовать Keyboard Hook.
Toggled state - состояние Вкл/Выкл для клавиш типа Caps Lock. Для всех остальных клавиш его значение не определено.
C вызовом GetKeyboardLayout(0)
похожая проблема - он возвращает раскладку для текущего потока, которая может не соответствовать реальной системной раскладке, если окно свернуто. Надо найти поток текущего активного окна, и брать его раскладку.
Итоговый пример кода (C#):
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
public class Keyboard
{
public const uint VK_CAPITAL = 0x14;
[DllImport("USER32.dll")]
public static extern short GetKeyState(int vKey);
[DllImport("user32.dll")]
public static extern short GetAsyncKeyState(int vKey);
[DllImport("user32.dll")]
public static extern int ToUnicodeEx(
uint wVirtKey,
uint wScanCode,
byte[] lpKeyState,
[Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszBuff,
int cchBuff,
uint wFlags,
IntPtr dwhkl);
[DllImport("user32.dll")]
public static extern bool GetKeyboardState(byte[] lpKeyState);
[DllImport("user32.dll")]
public static extern uint MapVirtualKey(
uint uCode,
uint uMapType);
[DllImport("user32.dll")]
public static extern IntPtr GetKeyboardLayout(uint idThread);
[DllImport("user32.dll")]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
public static string VKCodeToUnicode(uint vkCode)
{
StringBuilder buf = new StringBuilder();
byte[] keyboardState = new byte[255];
short x;
byte y;
for (int i = 0; i < 255; i++)
{
if (i == VK_CAPITAL)
{
x = GetKeyState(i);
}
else
{
x = GetAsyncKeyState(i);
}
y = 0;
if ((x & 0x8000) != 0) y = (byte)(y | 0x80);
if ((x & 0x0001) != 0) y = (byte)(y | 0x01);
keyboardState[i] = y;
}
ToUnicodeEx(vkCode, MapVirtualKey(vkCode, 0), keyboardState, buf, 5, 0,
GetKeyboardLayout(GetWindowThreadProcessId(GetForegroundWindow(), IntPtr.Zero)));
return buf.ToString();
}
}
Кофе для программистов: как напиток влияет на продуктивность кодеров?
Рекламные вывески: как привлечь внимание и увеличить продажи
Стратегії та тренди в SMM - Технології, що формують майбутнє сьогодні
Выделенный сервер, что это, для чего нужен и какие характеристики важны?
Современные решения для бизнеса: как облачные и виртуальные технологии меняют рынок
Пишу под ASPNET MVC Framework веб-сервис, который должен работать с СУБД Oracle
Делаю 2d тавер-дефенс, решил сделать конструктор для удобного создания башен, но столкнулся с ошибкой NullReferenceException: Object reference not set to an instance of an object...
Доброго вечера всем обитателем портала (форума)К экзамену были даны практические задания (краткие) по классам
Передо мной встала задача - реверс md5 хеша 4 произвольных байтТо есть, другими словами, мне надо написать сервис из 1 метода - метод получает...