1
votes

I'm need hook my keyboard and found the following code (reference here) that i want use in a Delphi Console project. Happens that after the first test i noted that LowLevelKeyboardProc() function never is executed (hello never is displayed on console window). Already SetWindowsHookEx() is working fine and returning <> 0.

Then someone can help me to solve this problem please?

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  Windows,
  Messages,
  SysUtils;

function ToUnicodeEx(wVirtKey, wScanCode: UINT; lpKeyState: PByte; pwszBuff: PWideChar; cchBuff: Integer; wFlags: UINT; dwhkl: HKL): Integer; stdcall; external 'user32.dll';

const
  LLKHF_ALTDOWN = KF_ALTDOWN shr 8;
  WH_KEYBOARD_LL = 13;

type
  PKBDLLHOOKSTRUCT = ^TKBDLLHOOKSTRUCT;

  TKBDLLHOOKSTRUCT = packed record
    vkCode: DWORD;
    scanCode: DWORD;
    flags: DWORD;
    time: DWORD;
    dwExtraInfo: DWORD;
  end;

var
  llKeyboardHook: HHOOK = 0;
  AltDown, ShiftDown, CtrlDown: Boolean;
  KeyBoardState: TKeyboardState;
  KeyBoardLayOut: HKL;

function TranslateVirtualKey(VirtualKey: integer): WideString;
begin
  Result := '';
  {$Region 'Translate VirtualKey'}
  case VirtualKey of
    VK_RETURN:   Result := sLineBreak;
    VK_TAB:      Result := '     ';
    VK_BACK:     Result := '[BackSpace]';
    VK_SHIFT:    Result := '[Shift]';
    VK_CONTROL:  Result := '[Ctrl]';
    VK_MENU:     Result := '[Alt]';
    VK_ESCAPE:   Result := '[Esc]';
    VK_PAUSE:    Result := '[Pause]';
    VK_CAPITAL:  Result := '[Caps Lock]';
    VK_PRIOR:    Result := '[Page Up]';
    VK_NEXT:     Result := '[Page Down]';
    VK_END:      Result := '[End]';
    VK_HOME:     Result := '[Home]';
    VK_LEFT:     Result := '[Left Arrow]';
    VK_UP:   Result := '[Up Arrow]';
    VK_RIGHT:    Result := '[Right Arrow]';
    VK_DOWN:     Result := '[Down Arrow]';
    VK_SELECT:   Result := '[Select]';
    VK_PRINT:    Result := '[Print Screen]';
    VK_EXECUTE:  Result := '[Execute]';
    VK_SNAPSHOT: Result := '[Print]';
    VK_INSERT:   Result := '[Ins]';
    VK_DELETE:   Result := '[Del]';
    VK_HELP:     Result := '[Help]';
    VK_F1:       Result := '[F1]';
    VK_F2:   Result := '[F2]';
    VK_F3:   Result := '[F3]';
    VK_F4:   Result := '[F4]';
    VK_F5:   Result := '[F5]';
    VK_F6:   Result := '[F6]';
    VK_F7:   Result := '[F7]';
    VK_F8:   Result := '[F8]';
    VK_F9:   Result := '[F9]';
    VK_F10:      Result := '[F10]';
    VK_F11:      Result := '[F11]';
    VK_F12:      Result := '[F12]';
    VK_NUMPAD0:  Result := '0';
    VK_NUMPAD1:  Result := '1';
    VK_NUMPAD2:  Result := '2';
    VK_NUMPAD3:  Result := '3';
    VK_NUMPAD4:  Result := '4';
    VK_NUMPAD5:  Result := '5';
    VK_NUMPAD6:  Result := '6';
    VK_NUMPAD7:  Result := '7';
    VK_NUMPAD8:  Result := '8';
    VK_NUMPAD9:  Result := '9';
    VK_SEPARATOR:Result := '+';
    VK_SUBTRACT: Result := '-';
    VK_DECIMAL:  Result := '.';
    VK_DIVIDE:   Result := '/';
    VK_NUMLOCK:  Result := '[Num Lock]';
    VK_SCROLL:   Result := '[Scroll Lock]';
    VK_PLAY:     Result := '[Play]';
    VK_ZOOM:     Result := '[Zoom]';
    VK_LWIN,
    VK_RWIN:     Result := '[Win Key]';
    VK_APPS:     Result := '[Menu]';
  end;
   {$EndRegion}
end;

function LowLevelKeyboardProc(nCode: Integer; wParam: wParam; lParam: lParam): HRESULT; stdcall;
var
  pkbhs: PKBDLLHOOKSTRUCT;
  AChr: array[0..1] of WideChar;
  VirtualKey: integer;
  ScanCode: integer;
  ConvRes: integer;
  ActiveWindow: HWND;
  ActiveThreadID: DWord;
  Str: widestring;
begin
  pkbhs := PKBDLLHOOKSTRUCT(Pointer(lParam));
  if nCode = HC_ACTION then
  begin
    VirtualKey := pkbhs^.vkCode;

    Str := '';
    if LongBool(pkbhs^.flags and LLKHF_ALTDOWN) and (not AltDown) then
    begin
      Str := '[Alt]';
      AltDown := True;
    end;
    if (not LongBool(pkbhs^.flags and LLKHF_ALTDOWN)) and (AltDown) then
      AltDown := False;

    if (WordBool(GetAsyncKeyState(VK_CONTROL) and $8000)) and (not CtrlDown) then
    begin
      Str := '[Ctrl]';
      CtrlDown := True;
    end;
    if (not WordBool(GetAsyncKeyState(VK_CONTROL) and $8000)) and (CtrlDown) then
      CtrlDown := False;

    if ((VirtualKey = VK_LSHIFT) or (VirtualKey = VK_RSHIFT)) and (not ShiftDown) then
    begin
      Str := '[Shift]';
      ShiftDown := True;
    end;
    if (wParam = WM_KEYUP) and ((VirtualKey = VK_LSHIFT) or (VirtualKey = VK_RSHIFT)) then
      ShiftDown := False;

    if (wParam = WM_KEYDOWN) and ((VirtualKey <> VK_LMENU) and (VirtualKey <> VK_RMENU)) and (VirtualKey <> VK_LSHIFT) and (VirtualKey <> VK_RSHIFT) and (VirtualKey <> VK_LCONTROL) and (VirtualKey <> VK_RCONTROL) then
    begin
      Str := TranslateVirtualKey(VirtualKey);
      if Str = '' then
      begin
        ActiveWindow := GetForegroundWindow;
        ActiveThreadID := GetWindowThreadProcessId(ActiveWindow, nil);
        GetKeyboardState(KeyBoardState);
        KeyBoardLayOut := GetKeyboardLayout(ActiveThreadID);
        ScanCode := MapVirtualKeyEx(VirtualKey, 0, KeyBoardLayOut);
        if ScanCode <> 0 then
        begin
          ConvRes := ToUnicodeEx(VirtualKey, ScanCode, @KeyBoardState, @AChr, SizeOf(AChr), 0, KeyBoardLayOut);
          if ConvRes > 0 then
            Str := AChr;
        end;
      end;
    end;
    if Str <> '' then
      writeln('hello!');
  end;
  Result := CallNextHookEx(llKeyboardHook, nCode, wParam, lParam);
end;

begin
  try
    llKeyboardHook := SetWindowsHookEx(WH_KEYBOARD_LL, @LowLevelKeyboardProc, HInstance, 0);
    Writeln(llKeyboardHook);
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;
end.
1
Well your code technically starts at the very end, where you create the hook, and write the handle out to the user. The very next thing is ReadLn where it waits for the user to input anything. Once the user types something, the code exits and terminates. So yeah, this hook is never called, or by the time it gets called, it's too late and the app is already terminated. I presume you should be doing some sort of waiting to actually give it a chance to trigger. For example, a message loop. - Jerry Dodge
@JerryDodge, thank you very much. Solved after insert a message loop. - BrowJr

1 Answers

3
votes

Your code is not giving the hook a chance to trigger. Your code's actual execution begins with assigning a hook:

llKeyboardHook := SetWindowsHookEx(WH_KEYBOARD_LL, @LowLevelKeyboardProc, HInstance, 0);

Then you write out that handle to the user:

Writeln(llKeyboardHook);

Assuming no exception was raised, the very next thing is waiting for user input:

Readln;

At this point, suppose a user presses a key on the keyboard and presses enter. That causes ReadLn to exit, thus exiting the code and terminating the application. So the hook never has a chance to actually fire.

You should be doing some sort of wait after creating the hook, such as processing Windows messages.