Как прочитать нажатия клавиш Ctrl, Alt, Shift?

Для функции ReadKey эти клавиши не генерируют никаких кодов. Однако, информация о нажатии подобных клавиш все-таки имеется и располагается в памяти (область данных BIOS) по адресу:

Seg0040:$17 - Keyboard Status Flags #1 (основные флаги спец.клавиш)

Ячейка Seg0040:$0017: <-+---- номера битов Њ7+6+5+4+3+2+1+0| |i|c|n|s|A|^|S|S| Бит Знач. Назначение бита Іs+s+s+s+-+-+L+R| N | | | | | | | +-. 0: 01h нажат Right-shift | | | | | | +---. 1: 02h нажат Left-shift | | | | | +-----. 2: 04h нажат Ctrl (любой) | | | | +-------. 3: 08h нажат Alt (любой) | | | +---------. 4: 10h состяние ScrollLock | | +-----------. 5: 20h состяние NumLock | +-------------. 6: 40h состяние CapsLock +---------------. 7: 80h состяние Insert

В этих ячейках каждый бит отвечает за одну конкретную спец.клавишу - если бит установлен, то клавиша нажата, если сброшен - то не нажата. Исключение составляют клавиши ScrollLock, NumLock, CapsLock, Insert - при первом нажатии соответствующий бит устанавливается в 1, а при следующем - сбрасывается в 0.

Вот вам функция для вытаскивания этой информации из байтов Seg040:$17.

Пример вызова: if GetLockKey(Ctrl) then {нажат Ctrl}

Type
  Keytype=(Ins, Caps, Num, Scroll, Ctrl, Alt, LShift, RShift);
 
function GetLockKey(lock:Keytype):Boolean;
{Проверяет, нажата ли спец.клавиша}
var b:word;
begin
  case lock of
    Ins    : b:=$0080;
    Caps   : b:=$0040;
    Num    : b:=$0020;
    Scroll : b:=$0010;
    Alt    : b:=$0008;
    Ctrl   : b:=$0004;
    LShift : b:=$0002;
    RShift : b:=$0001;
  end;
  if (mem[0:$417] and b)=b then GetLockKey:=true
                         else GetLockKey:=false;
end;

Аналогично можно анализировать и байт по адресу Seg040:$18 (Keyboard Status Flags #2)

Ячейка Seg0040:$0018: <-+---- номера битов Њ7+6+5+4+3+2+1+0| |i|c|n|s|p|q|A|^| Бит Знач. Назначение бита Іd+d+d+d+-+d+l+l| N | | | | | | | +-. 0: 01h нажат левый Ctrl | | | | | | +---. 1: 02h нажат левый Alt | | | | | +-----. 2: 04h SysReq DOWN | | | | +-------. 3: 08h hold/pause state | | | +---------. 4: 10h нажат ScrollLock | | +-----------. 5: 20h нажат NumLock | +-------------. 6: 40h нажат CapsLock +---------------. 7: 80h нажат Insert

Посмотрите на прилагаемый тест, попробуйте нажать несколько клавиш сразу, обратите внимание на то, что статус клавиатуры изменяется как при нажатии на клавиши, так и при отпускании тоже!

Uses CRT;
Const
  RightShift    = $0001;
  LeftShift     = $0002;
  AnyCtrl       = $0004;
  AnyAlt        = $0008;
  ScrollActive  = $0010;
  NumLockActive = $0020;
  CapsLockActive= $0040;
  InsActive     = $0080;
  LeftCtrl      = $0100;
  LeftAlt       = $0200;
  SysReq        = $0400;
  PauseKey      = $0800;
  ScrollLock    = $1000;
  NumLock       = $2000;
  CapsLock      = $4000;
  Insert        = $8000;
 
const hex_num:array [0..15] of char='0123456789ABCDEF';
 
var
  key:char;                {код нажатой клавиши}
  flags:word;              {флаги состояния клавиатуры}
  newflags:word;
 
function word2hex(w:word):string;
{перевод в 16-ричное число}
var
 b:array[1..2] of byte absolute w;
begin
  word2hex:=hex_num[b[2] shr 4]+hex_num[b[2] and $0F] +
            hex_num[b[1] shr 4]+hex_num[b[1] and $0F]
end;
 
function GetFlags:Word;
{Считывает состояние флагов спец.клавиш}
begin
  GetFlags:=memW[0:$417];
end;
 
function AnyKeyEvent:boolean;
begin
  AnyKeyEvent:= (KeyPressed or (newflags<>flags));
end;
 
function Pressed(lock:word):Boolean;
{Проверяет, нажата ли спец.клавиша с кодом LOCK}
begin
  if (flags and word(lock))<>0 then Pressed:=true
                         else Pressed:=false;
end;
 
Procedure WriteKeyCode;
begin
  TextAttr:=White;
  If KeyPressed then begin
    key:= ReadKey;                  {читаем код              }
    if Key = #0 then begin          {код оказался расширенным}
       Write(Ord(Key):3,',');       {печатаем нулевой код    }
       key:= ReadKey;               {читаем расширенный код  }
    end;
    Write(Ord(Key):3);              {печатаем основной код   }
  end
  else write('       ');
end;
 
Procedure WriteFlags;
begin
    TextAttr:=LightGray;
    Write(' Flags:',word2hex(memW[Seg0040:$17]));
    {Теперь печатаем флаги спец.клавиш}
    TextAttr:=Cyan;
    If Pressed(RightShift)     then Write(' RightShift');
    If Pressed(LeftShift )     then Write(' LeftShift');
    If Pressed(AnyAlt    )     then Write(' AnyAlt');
    If Pressed(AnyCtrl   )     then Write(' AnyCtrl');
    If Pressed(LeftCtrl  )     then Write(' LeftCtrl');
    If Pressed(LeftAlt   )     then Write(' LeftAlt');
    If Pressed(SysReq    )     then Write(' SysReq');
    If Pressed(PauseKey  )     then Write(' Pause');
    If Pressed(ScrollLock)     then Write(' ScrollLock');
    If Pressed(NumLock   )     then Write(' NumLock');
    If Pressed(CapsLock  )     then Write(' CapsLock');
    If Pressed(Insert    )     then Write(' Insert');
 
    {Теперь печатаем состояние переключателей}
    TextAttr:=Yellow;
    If Pressed(ScrollActive  ) then Write(' ScrollLockActive');
    If Pressed(NumLockActive ) then Write(' NumLockActive');
    If Pressed(CapsLockActive) then Write(' CapsLockActive');
    If Pressed(InsActive     ) then Write(' InsActive');
    Writeln;
    TextAttr:=LightGray;
end;
 
begin
  while keypressed do readkey;      {Очистить буфер клавиатуры}
  flags:=GetFlags;                  {начальное состояние флагов}
  repeat
    newflags:=GetFlags;             {новое состояние флагов}
    If AnyKeyEvent then begin       {если чего-нибудь нажато}
       WriteKeyCode;
       flags:=newflags;             {запомнить состояние флагов}
       WriteFlags;
    end;
  until Key = #27;                  {Цикл, пока не нажмем Esc}
  while keypressed do readkey;      {Очистить буфер клавиатуры}
end.