2

I am building an multimedia console based on an old computer running Win7.

I want to control the players with a numeric keyboard. I can't use the common media control devices because they respond only to windows media player. I will use the KVM Player, Winamp and others. So each one has it's own set of keyboard shortcuts for play, pause, foward, volume etc.

For that I am thinking of building a Delphi application that detects the foreground application and gets from a database the shortcuts this application uses.

When I use the numeric keyboard (the size of a regular remote control) and press 5 for play, my application could detect it and send to the OS the P key if I am using Winamp or Space if I am using Media Player Classic.

What functions should I use to first grab the pressed key and then send a different key?

NaN
  • 8,596
  • 20
  • 79
  • 153
  • 1
    As far as I remember, [`Winamp has its own API`](http://wiki.winamp.com/wiki/SDK_Contents). I wrote some tiny applications using that a long time ago. Unfortunately they're all gone after a disk failure. Windows Media Player has also API, don't know how about KMPlayer (if you meant KMPlayer and it's a typo in your question). I'm pointing this out since I think that will be much better to implement those APIs to be notified about playback status changes instead of this voodoo style of key redirection. – TLama Dec 18 '12 at 20:09
  • `WH_KEYBOARD_LL` keyboard hook: http://msdn.microsoft.com/en-gb/library/windows/desktop/ms644959(v=vs.85).aspx#wh_keyboard_llhook – David Heffernan Dec 18 '12 at 20:11
  • 1
    Monitoring [Raw Input](http://msdn.microsoft.com/en-us/library/ms645536.aspx) is more efficient and preferred over `WH_KEYBOARD_LL` hooks. MSDN says as much. – Remy Lebeau Dec 18 '12 at 21:27
  • @Remy What are the advantages of raw input for this use case? – David Heffernan Dec 18 '12 at 23:16
  • @DavidHeffernan: it doesn't invoke a context switch on every keystroke, and it doesn't suffer from a OS-imposed timeout that could kill the hook if it runs too long. – Remy Lebeau Dec 18 '12 at 23:46
  • 1
    @DavidHeffernan: MSDN also states: "Debug hooks cannot track this type of low level keyboard hooks. If the application must use low level hooks, it should run the hooks on a dedicated thread that passes the work off to a worker thread and then immediately returns. In most cases where the application needs to use low level hooks, it should monitor raw input instead. This is because raw input can asynchronously monitor mouse and keyboard messages that are targeted for other threads more effectively than low level hooks can." – Remy Lebeau Dec 18 '12 at 23:46
  • @RemyLebeau Using raw input may or may not cause international characters to be typed wrong? Like if the user types ã but then the software may interpret as two different presses ~a – NaN Mar 27 '13 at 03:24

1 Answers1

3

Part of the solution can be use a Keyboard Hook (WH_KEYBOARD_LL) to capture a special key combination or a single key and then use the keybd_event function to send (replace) another keystroke.

Try this sample code which intercepts the VK_UP key and send a S

var
 hhk: HHOOK;


function CBT_FUNC(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;


type
  PKBDLLHOOKSTRUCT = ^TKBDLLHOOKSTRUCT;
  TKBDLLHOOKSTRUCT = record
    vkCode: cardinal;
    scanCode: cardinal;
    flags: cardinal;
    time: cardinal;
    dwExtraInfo: Cardinal;
  end;
  PKeyboardLowLevelHookStruct = ^TKeyboardLowLevelHookStruct;
  TKeyboardLowLevelHookStruct = TKBDLLHOOKSTRUCT;

var
 LKBDLLHOOKSTRUCT: PKeyboardLowLevelHookStruct;
begin
   case nCode of
     HC_ACTION:
     begin
       LKBDLLHOOKSTRUCT := PKeyboardLowLevelHookStruct(lParam);
        if (LKBDLLHOOKSTRUCT^.vkCode = VK_UP)  then
        begin

          if (wParam=WM_KEYUP) or (wParam=WM_SYSKEYUP)then
           keybd_event( Ord('S'), 0, KEYEVENTF_KEYUP, 0)
          else
           keybd_event( Ord('S'), 0, 0, 0);

          Exit(1); //eat the key
        end;
     end;
   end;
  Result := CallNextHookEx(hhk, nCode, wParam, lParam);
end;

Procedure InitHook();
begin
  hhk := SetWindowsHookEx(WH_KEYBOARD_LL, @CBT_FUNC, 0, 0);
  if hhk=0 then RaiseLastOSError;
end;


Procedure KillHook();
begin
  if (hhk <> 0) then
    UnhookWindowsHookEx(hhk);
end;


initialization
  InitHook();

finalization
  KillHook();
end.

Before to use this kind of hook remember read the documentation, specifically the remarks section.

RRUZ
  • 134,889
  • 20
  • 356
  • 483
  • wow it worked in delphi 7 after a little modification (put it in a unit, add some uses clauses, make it a dll, replace Exit(1) with Result := 1; Exit; syntax) – nurettin Feb 05 '17 at 18:30