Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

Операционная система Microsoft Windows 3.1 для программиста

© Александр Фролов, Григорий Фролов
Том 11, М.: Диалог-МИФИ, 1993, 269 стр.

[Назад] [Содеожание] [Дальше]

5.2. Параметры клавиатурных сообщений

Сообщения WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN, WM_SYSKEYUP передают информацию о нажатой клавише через параметры lParam и wParam.

Параметр lParam для этих сообщений содержит информацию низкого уровня, такую, как скан-код, счетчик повторов, флаг предыдущего состояния клавиши и т. п. Эта информация редко используется приложениями Windows.

Параметр wParam содержит код виртуальной клавиши, соответствующей нажатой физической клавише. Именно этот параметр используется приложениями для идентификации нажатой клавиши.

Приведем описание отдельных бит парамера lParam.

Бит Описание
0-15 Счетчик повторов. Если нажать клавишу и держать ее в нажатом состоянии, несколько сообщений WM_KEYDOWN и WM_SYSKEYDOWN будут слиты в одно. Количество объединенных таким образом сообщений
16-23 OEM скан-код клавиши. Изготовители аппаратуры (OEM - Original Equipment Manufacturer) могут заложить в своей клавиатуре различное соответствие скан-кодов и обозначений клавиш. Скан-код генерируется клавиатурным контроллером. Это тот самый код, который получают в регистре AH программы MS-DOS, вызывая прерывание INT16h
24 Флаг расширенной клавиатуры. Этот бит установлен в 1, если сообщение соответствует клавише, имеющейся только на расширенной 101- или 102-клавишной клавиатуре. Это может быть одна из следующих клавиш: <Home>, <End>, <PgUp>, <PgDn>, <Insert>, <Delete>, клавиши дополнительной клавиатуры.
25-26 Не используются
27-28 Зарезервированы для использования Windows
29 Код контекста. Этот бит равен 1, если сообщение соответствует комбинации клавиши <Alt> с любой другой, и 0 в противном случае
30 Предыдущее состояние клавиши. Если перед приходом сообщения клавиша, соответствующая сообщению, была в нажатом состоянии, этот бит равен 1. В противном случае бит равен 0
31 Флаг изменения состояния клавиши (transition state). Если клавиша была нажата, бит равен 0, если отпущена - 1

Если нажать клавишу и оставить ее в нажатом состоянии, функция окна может получить подряд несколько сообщений WM_KEYDOWN, прежде чем придет сообщение WM_KEYUP. В этом случае бит предыдущего состояния клавиши (бит 30) можно использовать для обнаружения сообщений, возникших в результате включения автоповтора клавиатуры. Приложение может игнорировать такие сообщения, исключая эффект накопления, когда приложение не может обрабатывать сообщения с такой скоростью, с какой они поступают. Например, если вы будете долго держать клавишу пролистывания страниц в текстовом редакторе, то после того, как вы ее отпустите, текстовый редактор будет еще долго листать ваш документ.

Для сообщений WM_KEYDOWN и WM_KEYUP значение кода контекста (бит 29) и флага изменения состояния (бит 31) всегда равно 0.

Для сообщений WM_SYSKEYUP и WM_SYSKEYDOWN бит 31 равен 1. Но есть два исключения.

Во-первых, если активное окно свернуто в пиктограмму, все сообщения от клавиатуры преобразовываются в системные и, если клавиша <Alt> не нажата, код контекста равен 0.

Во-вторых, на некоторых клавиатурах для ввода символов национального языка могут использоваться комбинации с участием клавиш <Alt>, <Control>, <Shift> и т. п. В этом случае код контекста может быть равен 1, хотя сообщение не является системным.

Обработчик клавиатурного сообщения должен возвратить значение 0 для всех перехваченных сообщений.

Теперь мы расскажем вам о параметре wParam.

Как мы уже говорили, этот параметр содержит код виртуальной клавиши, соответствующей нажатой физической клавише. Код виртуальной клавиши не зависит от аппаратной реализации клавиатуры.

Многие коды виртуальных клавиш имеют символьное обозначение, определенное в файле windows.h. Приведем полный список кодов виртуальных клавиш. Те из них, которые определены в файле windows.h, имеют префикс VK_ (от слов Virtual Key).

Символическое имя Код виртуальной клавиши Клавиша, которой соответствует данный код Клавиша на клавиатуре IBM PC
Не определено 0x0    
VK_LBUTTON 0x1 Левая клавиша мыши  
VK_RBUTTON 0x2 Правая клавиша мыши  
VK_CANCEL 0x3 <Control + Break> <Control + Break>
VK_MBUTTON 0x4 Средняя клавиша мыши  
Не определено 0x5 - 0x7 Не определено  
VK_BACK 0x8 Клавиша забоя Клавиша забоя <Backspace>
VK_TAB 0x9 Клавиша табулятора <Tab>
Не определено 0xa - 0xb Не определено  
VK_CLEAR 0xc CLEAR Соответствует клавише <5> дополнительной клавиатуры при выключенном режиме <Num Lock>
VK_RETURN 0xd RETURN <Enter>
Не определено 0xe - 0xf Не определено  
VK_SHIFT 0x10 SHIFT <Shift>
VK_CONTROL 0x11 CONTROL <Control>
VK_MENU 0x12 MENU <Alt>
VK_PAUSE 0x13 PAUSE <Pause>
VK_CAPITAL 0x14 CAPITAL <Caps Lock>
Не определено 0x15 - 0x19 Зарезервировано для систем Kanji  
Не определено 0x1a Не определено  
VK_ESCAPE 1b ESCAPE <Esc>
Не определено 0x1c - 0x1f Не определено  
VK_SPACE 0x20 Клавиша пробела SPACEBAR Клавиша пробела
VK_PRIOR 0x21 PAGE UP <PgUp>
VK_NEXT 0x22 PAGE DOWN <PgDn>
VK_END 0x23 END <End>
VK_HOME 0x24 HOME <Home>
VK_LEFT 0x25 Перемещение курсора влево LEFT ARROW Клавиша перемещения курсора влево <Left>
VK_UP 0x26 Перемещение курсора вверх UP ARROW Клавиша перемещения курсора вверх <Up>
VK_RIGHT 0x27 Перемещение курсора вправо RIGHT ARROW Клавиша перемещения курсора вправо <Right>
VK_DOWN 0x28 Перемещение курсора вниз DOWN ARROW Клавиша перемещения курсора вниз <Down>
VK_SELECT 0x29 SELECT  
VK_PRINT 0x2a Зависит от изготовителя клавиатуры  
VK_EXECUTE 0x2b EXECUTE  
VK_SNAPSHOT 0x2c PRINTSCREEN <PrtSc>
VK_INSERT 0x2d INSERT <Insert>
VK_DELETE 0x2e DELETE <Delete>
VK_HELP 0x2f HELP  
Не определено 0x30 0 <0>
Не определено 0x31 1 <1>
Не определено 0x32 2 <2>
Не определено 0x33 3 <3>
Не определено 0x34 4 <4>
Не определено 0x35 5 <5>
Не определено 0x36 6 <6>
Не определено 0x37 7 <7>
Не определено 0x38 8 <8>
Не определено 0x39 9 <9>
Не определено 0x3a - 0x40 Не определено  
Не определено 0x41 A <A>
Не определено 0x42 B <B>
Не определено 0x43 C <C>
Не определено 0x44 D <D>
Не определено 0x45 E <E>
Не определено 0x46 F <F>
Не определено 0x47 G <G>
Не определено 0x48 H <H>
Не определено 0x49 I <I>
Не определено 0x4a J <J>
Не определено 0x4b K <K>
Не определено 0x4c L <L>
Не определено 0x4d M <M>
Не определено 0x4e N <N>
Не определено 0x4f O <O>
Не определено 0x50 P <P>
Не определено 0x51 Q <Q>
Не определено 0x52 R <R>
Не определено 0x53 S <S>
Не определено 0x54 T <T>
Не определено 0x55 U <U>
Не определено 0x56 V <V>
Не определено 0x57 W <W>
Не определено 0x58 X <X>
Не определено 0x59 Y <Y>
Не определено 0x5a Z <Z>
Не определено 0x5b - 0x5f Не определено  
VK_NUMPAD0 0x60 0 на цифровой клавиатуре <0> на цифровой клавиатуре
VK_NUMPAD1 0x61 1 на цифровой клавиатуре <1> на цифровой клавиатуре
VK_NUMPAD2 0x62 2 на цифровой клавиатуре <2> на цифровой клавиатуре
VK_NUMPAD3 0x63 3 на цифровой клавиатуре <3> на цифровой клавиатуре
VK_NUMPAD4 0x64 4 на цифровой клавиатуре <4> на цифровой клавиатуре
VK_NUMPAD5 0x65 5 на цифровой клавиатуре <5> на цифровой клавиатуре
VK_NUMPAD6 0x66 6 на цифровой клавиатуре <6> на цифровой клавиатуре
VK_NUMPAD7 0x67 7 на цифровой клавиатуре <7> на цифровой клавиатуре
VK_NUMPAD8 0x68 8 на цифровой клавиатуре <8> на цифровой клавиатуре
VK_NUMPAD9 0x69 9 на цифровой клавиатуре <9> на цифровой клавиатуре
VK_MULTIPLAY 0x6a Клавиша умножения <*> на цифровой клавиатуре
VK_ADD 0x6b Клавиша сложения <+> на цифровой клавиатуре
VK_SEPARATOR 0x6c Клавиша разделения  
VK_SUBTRACT 0x6d Клавиша вычитания <-> на цифровой клавиатуре
VK_DECIMAL 0x6e Клавиша десятичной точки <.> на цифровой клавиатуре
VK_DIVIDE 0x6f Клавиша деления </> на цифровой клавиатуре
VK_F1 0x70 F1 <F1>
VK_F2 0x71 F2 <F2>
VK_F3 0x72 F3 <F3>
VK_F4 0x73 F4 <F4>
VK_F5 0x74 F5 <F5>
VK_F6 0x75 F6 <F6>
VK_F7 0x76 F7 <F7>
VK_F8 0x77 F8 <F8>
VK_F9 0x78 F9 <F9>
VK_F10 0x79 F10 <F10>
VK_F11 0x7a F11 <F11>
VK_F12 0x7b F12 <F12>
VK_F13 0x7c F13  
VK_F14 0x7d F14  
VK_F15 0x7e F15  
VK_F16 0x7f F16  
Не определено 0x80 - 0x87 Зависит от изготовителя клавиатуры  
Не определено 0x88 - 0x8f Не определено  
VK_NUMLOCK 0x90 NUM LOCK <Num Lock>
VK_SCROLL 0x91 SCROLL LOCK <Scroll Lock>
Не определено 0x92 - 0xb9 Не определено  
Не определено 0xba Клавиша знака пунктуации ;
Не определено 0xbb Плюс + =
Не определено 0xbc Запятая , <
Не определено 0xbd Минус - _
Не определено 0xbe Точка . >
Не определено 0xbf Клавиша знака пунктуации / ?
Не определено 0xc0 Клавиша знака пунктуации ` ~
Не определено 0xc1 - 0xda Не определено  
Не определено 0xdb Клавиша знака пунктуации [ {
Не определено 0xdc Клавиша знака пунктуации \ |
Не определено 0xdd Клавиша знака пунктуации ] }
Не определено 0xde Клавиша знака пунктуации ' "
Не определено 0xdf Клавиша знака пунктуации  
Не определено 0xe0 - 0xe1 Зависит от изготовителя клавиатуры  
Не определено 0xe2 Знак неравенства  
Не определено 0xe3 - 0xe4 Зависит от изготовителя клавиатуры  
Не определено 0xe5 Не определено  
Не определено 0xe6 Зависит от изготовителя клавиатуры  
Не определено 0xe7 - 0xe8 Не определено  
Не определено 0xe9 - 0xf5 Зависит от изготовителя клавиатуры  
Не определено 0xf6 - 0xff Не определено  

Рассматривая приведенную выше таблицу, нетрудно заметить, что в ней есть коды виртуальных клавиш, которые невозможно получить в компьютере с IBM-совместимой клавиатурой. Кроме того, используя только эти коды, невозможно различить строчные и прописные буквы.

Для определения состояния клавиш <Shift>, <Caps Lock>, <Control>, <Num Lock> сразу после получения сообщения функция окна должна вызвать функцию с именем GetKeyState, которая входит в программный интерфейс Windows. Приведем прототип этой функции:

int WINAPI GetKeyState(int vkey);

Параметр функции vkey должен указывать код виртуальной клавиши, для которой необходимо вернуть состояние.

Старший бит возвращаемого значения, установленный в 1, говорит о том, что указанная клавиша была нажата. Если этот бит равен 0, клавиша не была нажата.

Младший бит возвращаемого значения указывает состояние переключения. Если он равен 1, клавиша (такая, как <Caps Lock> или <Num Lock>) находится во включенном состоянии, то есть она была нажата нечетное число раз после включения компьютера. Учтите, что при помощи программы установки параметров компьютера SETUP, расположенной в BIOS, вы можете задать произвольное состояние переключающих клавиш после запуска системы.

Функция GetKeyState возвращает состояние клавиши на момент извлечения сообщения из очереди приложения функцией GetMessage.

Для того чтобы узнать состояние клавиш в любой произвольный момент времени, можно воспользоваться функцией GetAsyncKeyState:
int WINAPI GetAsyncKeyState (int vkey);

Параметр функции vkey должен указывать код виртуальной клавиши, для которой необходимо вернуть состояние.

Старший бит возвращаемого значения, установленный в 1, говорит о том, что указанная клавиша была нажата в момент вызова функции. Если этот бит равен 0, клавиша не была нажата.

Младший бит возвращаемого значения установлен в 1, если указанная клавиша была нажата с момента последнего вызова функции GetAsyncKeyState.

Если для функции в качестве параметра задать значения VK_LBUTTON или VK_RBUTTON, можно узнать состояние клавиш, расположенных на корпусе мыши.

Есть возможность определить и изменить состояние для всех клавиш одновременно.

Для определения состояния клавиш воспользуйтесь функцией GetKeyboardState:

void WINAPI GetKeyboardState (BYTE FAR* lpbKeyState);

Единственный параметр lpbKeyState этой функции - дальний указатель на массив из 256 байт. После вызова функции этот массив будет заполнен информацией о состоянии всех виртуальных клавиш в момент генерации клавиатурного сообщения. В этом смысле функция аналогична функции GetKeyState.

Для любого байта массива установленный в 1 старший бит означает, что соответствующая клавиша была нажата. Если этот бит равен 0, клавиша не была нажата. Младший бит, установленный в 1, означает, что клавиша была переключена. Если младший бит равен 0, клавиша не была переключена.

После вызова функции GetKeyboardState вы можете изменить содержимое массива и вызвать функцию SetKeyboardState, изменяющую состояние клавиатуры:

void WINAPI SetKeyboardState(BYTE FAR* lpbKeyState);

Функция GetKeyboardType позволит вам определить тип клавиатуры и количество имеющихся на ней функциональных клавиш:

int WINAPI GetKeyboardType(int fnKeybInfo);

В зависимости от значения параметра fnKeybInfo функция может возвращать различную информацию о клавиатуре.

Если задать значение параметра fnKeybInfo, равное 0, функция вернет код типа клавиатуры:

Код Тип клавиатуры Количество функциональных клавиш
1 Клавиатура IBM PC/XT или совместимая, 83-клавишная 10
2 Клавиатура Olivetti "ICO", 102-клавишная 12 (иногда 18)
3 Клавиатура IBM AT или аналогичная, 84-клавишная 10
4 Клавиатура IBM Enhanced (улучшенная), 101- или 102-клавишная 12
5 Клавиатура Nokia 1050 или аналогичная 10
6 Клавиатура Nokia 9140 или аналогичная 24
7 Японская клавиатура Зависит от аппаратуры

Если задать значение параметра, равное 1, функция вернет код подтипа клавиатуры.

И наконец, если задать значение параметра, равное 2, функция вернет количество функциональных клавиш, имеющихся на клавиатуре.

Интересна также функция GetKeyNameText, возвращающая для заданного кода виртуальной клавиши название соответствующей клавиши в виде текстовой строки. Названия виртуальных клавиш определены в драйвере клавиатуры.

Приведем прототип функции GetKeyNameText:

int WINAPI GetKeyNameText(LONG lParam, 
   LPSTR lpszBuffer, int cbMaxKey);

Первый параметр функции lParam должен определять клавишу в формате компоненты lParam клавиатурного сообщения. Вы можете использовать в качестве этого параметра значение lParam, полученное функцией окна вместе с любым клавиатурным сообщением, таким, как WM_KEYDOWN или WM_SYSKEYDOWN.

Второй параметр - lpszBuffer является указателем на буфер, в который будет записано название клавиши.

Третий параметр - cbMaxKey должен быть равен длине буфера, уменьшенной на 1.

Приведем исходный текст приложения KBTYPE, определяющего тип и подтип клавиатуры, а также количество функциональных клавиш (листинг 5.1).


Листинг 5.1. Файл kbtype\kbtype.cpp


// ----------------------------------------
// Определение типа клавиатуры
// ----------------------------------------

#define STRICT
#include <windows.h>

#pragma argsused
int PASCAL
WinMain(HINSTANCE hInstance,
        HINSTANCE hPrevInstance,
        LPSTR     lpszCmdLine,
        int       nCmdShow)
{
  // Рабочий буфер
  char szBuf[80];

  // Рабочие переменные
  int type, subtype, nfkeys, size;

  // Типы клавиатур
  char *apszKbTypes[] =
  {
    "IBM PX/XT",
    "Olivetti ICO",
    "IBM AT",
    "IBM Enhanced",
    "Nokia 1050",
    "Nokia 9140",
    "Японская",
  };

  // Определяем тип клавиатуры
  type = GetKeyboardType(0);

  // Он должен лежать в интервале от
  // 1 до 7. Если это не так, завершаем
  // работу приложения с сообщением об ошибке
  if (type == 0 || type > 7)
  {
    MessageBox(NULL, "Ошибка в типе клавиатуры",
      "KBTYPE Application", MB_ICONSTOP);
    return 0;
  }

  // Определяем подтип клавиатуры
  subtype = GetKeyboardType(1);

  // Определяем количество функциональных
  // клавиш
  nfkeys  = GetKeyboardType(2);

  // Подготавливаем буфер и выводим его
  size = wsprintf(szBuf,
    "Клавиатура %s,\nподтип %d,\n",
    (LPSTR)apszKbTypes[type-1], subtype);

  wsprintf(szBuf + size,
    " %d функциональных клавиш",
    nfkeys);

  MessageBox(NULL, szBuf,
    "KBTYPE Application", MB_OK | MB_ICONINFORMATION);
  return 0;
}

Приложение использует файл определения модуля, приведенный в листинге 5.2.


Листинг 5.2. Файл kbtype\kbtype.def


; =============================
; Файл определения модуля
; =============================
NAME        KBTYPE
DESCRIPTION 'Приложение KBTYPE, (C) 1994, Frolov A.V.'
EXETYPE     windows
STUB        'winstub.exe'
STACKSIZE   5120
HEAPSIZE    1024
CODE        preload moveable discardable
DATA        preload moveable multiple

Работа приложения KBTYPE понятна без дополнительных комментариев. Единственное, на чем нам хотелось бы остановиться, так это на использовании для подготовки текстового буфера функции wsprintf.

Функция wsprintf входит в ядро Windows и используется аналогично функции sprintf. Эта функция определена в файле windows.h следующим образом:

int FAR CDECL wsprintf(LPSTR lpszOut, LPCSTR lpszFmt, ...);

Первый параметр функции является дальним указателем на буфер, в который будет записана сформированная текстовая строка, закрытая двоичным нулем.

Второй параметр - указатель на строку формата, определяющую формат строки, которая будет записана в буфер. Допустимо использовать следующие спецификаторы форматов вывода:

Спецификатор Формат
c Один символ
d, i Целое число со знаком
ld, li Двойное целое число со знаком
u Целое число без знака
lu Двойное целое число без знака
lx, lX Двойное целое число без знака в шестнадцатеричном формате строчными или прописными буквами
s Текстовая строка

Далее следует произвольное число переменных, описанных в строке формата. Так как функции передается переменное число параметров, она (в отличие от подавляющего большинства функций программного интерфейса Windows) использует для передачи параметров соглашение языка Си, а не Паскаль.

Для вывода текстовых строк необходимо использовать явное преобразование типа, как это сделано в нашем примере:

size = wsprintf(szBuf,
  "Клавиатура %s,\nподтип %d,\n",
  (LPSTR)apszKbTypes[type-1], subtype);

Функция возвращает количество байт, записанных в выходной буфер, без учета двоичного нуля, закрывающего текстовую строку.

На рис. 5.1 представлено сообщение, которое было выведено при запуске приложения KBTYPE на компьютере одного из авторов этой книги.

Рис. 5.1. Сообщение приложения KBTYPE

Прежде чем перейти к следующему разделу, приведем исходные тексты еще одного приложения, демонстрирующего использование функций GetKeyboardState и SetKeyboardState для изменения состояния клавиш <Num Lock>, <Caps Lock>, <Scroll Lock>. Это приложение называется KBLED. Исходный текст основного файла приложения приведен в листинге 5.3.


Листинг 5.3. Файл kbled\kbled.cpp


// ----------------------------------------
// Переключение состояния виртуальных
// клавиш
// ----------------------------------------

#define STRICT
#include <windows.h>

#pragma argsused
int PASCAL
WinMain(HINSTANCE hInstance,
        HINSTANCE hPrevInstance,
        LPSTR     lpszCmdLine,
        int       nCmdShow)
{
  // Буфер для записи состояния клавиш
  BYTE aKBState[256];

  // Определяем состояние клавиш
  GetKeyboardState(aKBState);

  MessageBox(NULL, "Нажмите 'OK' для"
    " переключения клавиш <NumLock>, "
    "<ScrollLock>, <CapsLock>",
    "KBLED Application", MB_OK | MB_ICONINFORMATION);

  // Инвертируем текущее состояние клавиш
  // <NumLock>, <ScrollLock>, <CapsLock>
  aKBState[VK_NUMLOCK] ^= 1;
  aKBState[VK_SCROLL]  ^= 1;
  aKBState[VK_CAPITAL] ^= 1;

  // Устанавливаем новое состояние клавиш
  SetKeyboardState(aKBState);

  MessageBox(NULL, "Нажмите 'OK' для обратного"
    " переключения",
    "KBLED Application", MB_OK | MB_ICONINFORMATION);

  // Возвращаем исходное состояние клавиш
  // <NumLock>, <ScrollLock>, <CapsLock>
  aKBState[VK_NUMLOCK] ^= 1;
  aKBState[VK_SCROLL]  ^= 1;
  aKBState[VK_CAPITAL] ^= 1;

  // Устанавливаем новое состояние клавиш
  SetKeyboardState(aKBState);

  return 0;
}

Это простое приложение выводит на экран сообщение, в котором говорится, что для переключения состояния виртуальных клавиш надо нажать кнопку "OK". Когда вы нажмете эту кнопку, состояние трех виртуальных клавиш изменится на противоположное. Это нетрудно проконтролировать при помощи светодиодов, расположенных на клавиатуре: все они должны изменить свое состояние на противоположное.

Когда вы ответите на второе сообщение, приложение возвратит исходное состояние клавиш (и светодиодов).

Файл определения модуля для приложения KBLED приведен в листинге 5.4.


Листинг 5.4. Файл kbled\kbled.def


; =============================
; Файл определения модуля
; =============================
NAME        KBLED
DESCRIPTION 'Приложение KBLED, (C) 1994, Frolov A.V.'
EXETYPE     windows
STUB        'winstub.exe'
STACKSIZE   5120
HEAPSIZE    1024
CODE        preload moveable discardable
DATA        preload moveable multiple

[Назад] [Содеожание] [Дальше]