Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: WinAPI и системное программирование > Глобальный хук


Автор: Poseidon 27.12.2005, 19:31
Я тут порылся по форуму и нашел код Рауса

Код
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  end;

var
  Form1: TForm1;

implementation

var
  hHook: THandle;

{$R *.dfm}

function LowLevelKeyboardProc(nCode: Integer;
  WParam: WPARAM; LParam: LPARAM): LRESULT stdcall;
type
  PKbdDllHookStrukt = ^TKbdDllHookStrukt;
  _KBDLLHOOKSTRUCT = record
    vkCode: DWORD;
    scanCode: DWORD;
    flags: DWORD;
    time: DWORD;
    dwExtraInfo: PDWORD;
  end;
  TKbdDllHookStrukt = _KBDLLHOOKSTRUCT;

const
  RPT_WPARAM_DATA = 'Keyboard message = %s ';
  RPT_LPARAM_DATA = 'scan code = %s [%d]';
var
  StrResult: String;
begin
  StrResult := '';
  if nCode = HC_ACTION then
    Result := CallNextHookEx(hHook, nCode, WParam, LParam);
  case WParam of
    WM_KEYDOWN: StrResult := Format(RPT_WPARAM_DATA, ['WM_KEYDOWN']);
    WM_KEYUP: StrResult := Format(RPT_WPARAM_DATA, ['WM_KEYUP']);
    WM_SYSKEYDOWN: StrResult := Format(RPT_WPARAM_DATA, ['WM_SYSKEYDOWN']);
    WM_SYSKEYUP: StrResult := Format(RPT_WPARAM_DATA, ['WM_SYSKEYUP']);
  end;

  StrResult := StrResult + Format(RPT_LPARAM_DATA, [
    Chr(PKbdDllHookStrukt(LParam)^.vkCode), PKbdDllHookStrukt(LParam)^.vkCode]);
  Form1.Memo1.Lines.Add(StrResult);
end;

procedure TForm1.FormCreate(Sender: TObject);
const
  WH_KEYBOARD_LL = 13;
begin
  hHook := SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, hInstance, 0);
  if hHook = 0 then RaiseLastOSError;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  if not UnhookWindowsHookEx(hHook) then RaiseLastOSError;
end;

end.
Как видно, реализован отлов нажатия клавиши без использования dll. Все вроде ОК, но вот только я что-то не могу понять, как можно подобным способом (не использую dll) отловить нажатие 2 или даже 3 клавиш одновременно?

Автор: JoyEx 27.12.2005, 20:12
Смотрите MSDN:

SetWindowsHookEx
Hook Scope
WH_KEYBOARD_LL Global only


Значит без длл не обойтись

Автор: Poseidon 27.12.2005, 20:23
Цитата(JoyEx @ 27.12.2005, 20:12)
Значит без длл не обойтись

JoyEx, но ведь одну клавишу отловить можно и без dll...

Автор: JoyEx 27.12.2005, 22:29
Poseidon

Это смотря где ловить, если в своём процессе, то тогда можно и без длл

Автор: Poseidon 27.12.2005, 22:37
JoyEx, посмотри код, каторый я привел выше. Ловит клавишу во всех процессах и без dll. Вот только жаль, что одну клавишу...

Автор: JoyEx 27.12.2005, 22:48
Цитата
SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, hInstance, 0);


hInstance - это ведь базовый адрес длл для WH_KEYBOARD_LL. Или нет?

Автор: Albinos_x 27.12.2005, 22:50
по идее клава также генерирует код... я так понимаю ты хочешь отловить Шифт и альт...?
нада по экспериментировать с возвращаемыми значениями... при зажатой какой-нибудь из этих клавиш
один из возвращёных байтов должен иметь приблизительно такой формат:

бит - клавиша

0 - нажата правая шифт
1 - нажата левая шифт
2 - нажата Ctrl
3 - нажата Alt
4 - нажата scroll lock
5 - нажата num lock
6 - нажата caps lock
7 - нажата insert

т.е. к примеру байт равный 32 ($20)(00100000) - соответствует, что включён num lock из всех выше перечисленных.

Автор: Poseidon 27.12.2005, 23:18
Цитата(Albinos_x @ 27.12.2005, 22:50)
я так понимаю ты хочешь отловить Шифт и альт...?
Если честно мне все-равно. Можно и "буквенные" клавиши отлавливать. Вот только как это сделать? Приведенный код отлавливает KeyDown и KeyUp по отдельности каждой клавиши. А вот нажатие нескольких не хочет... smile

Автор: p0s0l 27.12.2005, 23:30
Если нажать одновременно 3 кнопки, сгенерится 3 WM_KEYDOWN'а... Когда отпустишь кнопки - сгенерится 3 WM_KEYUP'а... Что еще нужно - не понятно...
Объясни конкретно, что ты хочешь и в каком виде ?
Ты хочешь, чтобы одним WM_KEYDOWN'ом тебе сообщили, что нажалось 50 кнопок ? smile
И также, то что для тебя кажется "одновременным" на самом деле отнюдь не одновременно...

Автор: Albinos_x 27.12.2005, 23:40
как вариант, можно попробовать установить типа задержки длительностью допустим 250 мс во время задержки считывать нажатые клавиши в строку, а потом уже анализировать, если несколько символов и они отличаются, то зажаты несколько клавиш...

Автор: p0s0l 27.12.2005, 23:44
В WM_KEYDOWN ставим флажок в массиве, в WM_KEYUP убираем, потом можно по массиву определить сколько и какие кнопки зажаты...
Либо проще и надежнее GeyKeyboardState.
Или если нужно проверить нажатие определенных кнопок - GetKeyState...

Автор: Albinos_x 28.12.2005, 00:12
Цитата(Albinos_x @ 27.12.2005, 23:40)
как вариант, можно попробовать установить типа задержки длительностью допустим 250 мс во время задержки считывать нажатые клавиши в строку, а потом уже анализировать, если несколько символов и они отличаются, то зажаты несколько клавиш...

вот приблизительно так:
Код

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Button1: TButton;
    Timer1: TTimer;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

var    
  hHook: THandle;
  StrResult: String;

  function LowLevelKeyboardProc(nCode: Integer;
  WParam: WPARAM; LParam: LPARAM): LRESULT stdcall;
type
  PKbdDllHookStrukt = ^TKbdDllHookStrukt;
  _KBDLLHOOKSTRUCT = record
    vkCode: DWORD;
    scanCode: DWORD;
    flags: DWORD;
    time: DWORD;
    dwExtraInfo: PDWORD;
  end;
  TKbdDllHookStrukt = _KBDLLHOOKSTRUCT;
const
  RPT_WPARAM_DATA = 'Keyboard message = %s ';
  RPT_LPARAM_DATA = 'scan code = %s [%d]';

begin
  if nCode = HC_ACTION then
    Result := CallNextHookEx(hHook, nCode, WParam, LParam);
  StrResult := StrResult + Chr(PKbdDllHookStrukt(LParam)^.vkCode);
end;


procedure TForm1.FormCreate(Sender: TObject);
const
  WH_KEYBOARD_LL = 13;
begin    
  hHook := SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, hInstance, 0);
  Timer1.Enabled:=true;
  if hHook = 0 then RaiseLastOSError;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
if not UnhookWindowsHookEx(hHook) then RaiseLastOSError;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
Memo1.Clear;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
var l,i,j:integer;
    c:char;
    str:string;
begin
str:=StrResult;
l:=length(Str);
if l=0 then exit;
if l>1 then
   begin
   c:=Str[1];
   i:=1;
   j:=2;
   while i<=l do
      begin
      while j<=l do
         begin
         if j=i then break;
        if Str[i]=Str[j] then
            begin
            delete(Str,j,1);
            l:=length(Str);;
            end;
         inc(j);
         end;
      j:=2;   
      inc(i);
      end;
   if l>1 then
      begin
      Memo1.Lines.Add('Íà÷àòû êëàâèøè:');
      for i:=1 to l do
          Memo1.Lines.Add(Str[i]+'  ('+inttostr(ord(Str[i]))+')');
      end;
   end;
StrResult := '';
end;

end.


только в этом коде (не до конца разобрался) глюк с удалением повторных символов (когда долго зажата клавиша)

Автор: Albinos_x 28.12.2005, 00:24
да... ещё... не разобрался пока до конца... почему то иногда лишние симолы вылетают... которые не зажаты..возможно из-за того что после удаления строк (после выхода из цикла) не определяю снова длину строки...

Автор: Poseidon 28.12.2005, 18:25
Цитата(p0s0l @ 27.12.2005, 23:44)
Или если нужно проверить нажатие определенных кнопок - GetKeyState...
smile

Код

function LowLevelKeyboardProc(nCode: Integer;
  WParam: WPARAM; LParam: LPARAM): LRESULT stdcall;
begin
  if nCode = HC_ACTION then
    Result := CallNextHookEx(hHook, nCode, WParam, LParam);

   If ((GetKeyState(VK_CONTROL) AND 128)=128) and
      ((GetKeyState(80)      AND 128)=128) and
      ((GetKeyState(VK_Menu)   AND 128)=128)
   then
       Application.MessageBox('Нажаты Ctrl+Alt+P','Сработало!',MB_ICONWARNING+MB_SYSTEMMODAL);;
end;


Спасибо p0s0l smile


PS. Albinos_x, если честно, то твой вариант с таймором мягко говоря ....

Автор: Albinos_x 28.12.2005, 19:11
Цитата(Poseidon @ 28.12.2005, 18:25)
PS. Albinos_x, если честно, то твой вариант с таймором мягко говоря ....

знаю...

PS: в этот раз колесо получилось прямоугольным smile

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)