Link to home
Start Free TrialLog in
Avatar of fibdev
fibdev

asked on

System Wide Hooks (Hot Keys)

Hi,

I think the title of this question sais it all.

I need to create a system wide hook to trap keyboard events and execute a procedure.  I hope someone has a answer for me that isn't too complex... I'm sorta' a newbie when it comes to Delphi.

Thanks
Avatar of mhervais
mhervais

listening


                     Here is some code that shows you how to do what you want using Hot Keys - it is from my PAQ (credit to ITugay). Note
                     that it is easy to modify it to only register the the particular hotkey you want:

                     This code hooks all possible keys and then resend it to recepient. Result will be shown in the Listbox1 in format "[Shift] KEY
                     ACTIVE_FORM_NAME", if KEY not in "!".."Z" then scancode will be shown.

                     The working sample is bellow:

                     ------------------


                     type
                       TForm1 = class(TForm)
                         Listbox1: TListBox;
                         procedure FormCreate(Sender: TObject);
                         procedure FormDestroy(Sender: TObject);
                       public
                         procedure WMHotKey(var Msg : TWMHotKey); message WM_HOTKEY;
                         procedure RegKeys;
                         procedure UnRegKeys;
                       end;

                     .........

                     procedure TForm1.RegKeys;
                     var I : integer;
                     begin
                        for I:=0 to 255 do
                        if I<>16 then // skip [Shift]
                        begin
                          RegisterHotKey(Handle,I,0,I);
                          RegisterHotKey(Handle,I+256,MOD_SHIFT,I);
                        end;
                     end;

                     procedure TForm1.UnRegKeys;
                     var I : integer;
                     begin
                       for I:=0 to 511 do
                       UnregisterHotKey(Handle,I);
                     end;

                     procedure TForm1.WMHotKey(var Msg : TWMHotKey);
                     var NameBuf  : array[0..1000] of char;
                         FormName : string;
                         Key      : string;
                         Ch       : char;
                     begin
                        SetLength(FormName,GetWindowText(GetForegroundWindow,@NameBuf,1000));
                        Move(NameBuf,pointer(FormName)^,length(FormName));

                        if MSG.HotKey > 255 then
                        begin
                          MSG.HotKey:=MSG.HotKey-256;
                          Key:='[Shift]+';
                        end else Key:='';
                         
                        Ch:=Char(MapVirtualKey(MSG.HotKey,2));
                        if Ch = ' ' then Key:=Key+'[ ]' else
                        if Ch in [#33..#127]
                           then Key:=Key+Ch
                           else Key:=Key+'['+IntToStr(Msg.HotKey)+']';
                        Listbox1.Items.Add(Key+' '+FormName);
                        Listbox1.ItemIndex:=Listbox1.Items.Count-1;
                        UnregKeys; // avoid recursion
                        keybd_event(MSG.HotKey,MSG.HotKey,0,0);
                        RegKeys;
                     end;


                     procedure TForm1.FormCreate(Sender: TObject);
                     begin                                      
                        RegKeys;
                     end;

                     procedure TForm1.FormDestroy(Sender: TObject);
                     begin
                        UnregKeys;
                     end;

                     There are also hotkey components availbale from DSP (http://sunsite.icm.edu.pl/delphi) and Torries (www.torry.ru)

                     Cheers,

                     Raymond.
If you need SYSTEM WIDE, let me know, I have some code
Avatar of fibdev

ASKER

Ok, let me be specific...

Here is what I want to do:

I want Ctrl + Alt + W to fire procedure TForm1.Window1Click(Sender: TObject);

I want Ctrl + Alt + S to fire procedure TForm1.Screen1Click(Sender: TObject);

I want Ctrl + Alt + A to fire procedure TForm1.SelectedArea1Click(Sender: TObject);

Explain how to set this up.  Keep in find I'm a newbie so explain it like you would a 5 year old.

Thanks
Avatar of fibdev

ASKER

errr .... keep in mind, not keep in find :)
I can give an answer when question is unlocked...although 50 pts is a it low... rwilson???
Avatar of fibdev

ASKER

Ok, sorry rwilson, I just need more input.

As far as the points go ...  I don't have many more (25 pts left to spend) If I get a good answer I'll come off of these left over points.
The code presented below shows how to create a keyboard, mouse and shell event loop. (unfortunately, I was asked to create a computer activity monitor at work) Hopefully, you will make better use of it....

In order to create a system wide hook, you need a DLL to handle the hook.
This LIBRARY project will create a DLL to do just this. (more code and explanation after this unit)
=====================================================
library HookLib;

uses Windows;

//this should really be in an include file! [MY]
const
    MSG_MY_KEY_EVENT   =   'MY_KEY_EVENT_HOOK';
    MSG_MY_SHELL_EVENT =   'MY_SHELL_EVENT_HOOK';
    MSG_MY_MOUSE_EVENT =   'MY_MOUSE_EVENT_HOOK';
    HookMemFileName     =   'MYHook.DTA';
    HookMutexName       =   'MYHookMutex';

type TShared=record
    ShellHook: HHook;
    ShellCount: integer; //# of shell hooks
    KeyHook: HHOOK;
    KeyCount: integer;//# of keyboard hooks
    MouseHook: HHOOK;
    MouseCount: integer; //# of mouse hooks
    Receiver:Integer;//app currently processing a hook in this DLL. Only one at a time for now [MY]
    AttachCount:Integer;//how many apps are attached to this DLL
  end;
  PShared=^TShared;
  TWatchType=(wtSHELL,wtKEYBOARD,wtMOUSE);

var
    MemFile, HookMutex: THandle;
    Shared: PShared;
    SHELL_EVENT,MOUSE_EVENT,KEY_EVENT: integer;

function GetShellHook( Code: Integer;wParam: WPARAM;lParam:LPARAM): LRESULT; stdcall;
begin
    if Code>=0 then PostMessage(Shared^.Receiver,SHELL_EVENT,wParam,Code);
    Result := CallNextHookEx(Shared^.ShellHook, Code, wParam, lParam);
end;

function GetKeyHook( Code: Integer;wParam: WPARAM;lParam:LPARAM): LRESULT; stdcall;
begin
    if code=HC_ACTION then PostMessage(Shared^.Receiver,KEY_EVENT,wParam,lParam);
    Result := CallNextHookEx(Shared^.KeyHook, Code, wParam, lParam);
end;

function GetMouseHook( Code: Integer;wParam: WPARAM;lParam:LPARAM): LRESULT; stdcall;
begin
    if code=HC_ACTION then PostMessage(Shared^.Receiver,MOUSE_EVENT,wParam,lParam);
    Result := CallNextHookEx(Shared^.MouseHook, Code, wParam, lParam);
end;

procedure StartWatching(WatchType:TWatchType); stdcall;
begin
    try
        WaitForSingleObject(HookMutex,INFINITE);
        with Shared^ do
        begin
            case WatchType of
            wtSHELL:
                begin
                    if (ShellCount=0) and (ShellHook=0) then
                    ShellHook:=SetWindowsHookEx(WH_SHELL, @GetShellHook, HInstance,0);
                    inc(ShellCount);
                end;
            wtKEYBOARD:
                begin
                    if (KeyCount=0) and (KeyHook=0) then
                    KeyHook:=SetWindowsHookEx(WH_KEYBOARD, @GetKeyHook, HInstance , 0);
                    inc(KeyCount);
                end;
            wtMouse:
                begin
                    if (MouseCount=0) and (MouseHook=0) then
                    MouseHook:=SetWindowsHookEx(WH_Mouse, @GetMouseHook, HInstance , 0);
                    inc(MouseCount);
                end;
            end;
        end;
    finally
        ReleaseMutex(HookMutex);
    end;
end;

procedure StopWatching(WatchType:TWatchType);  stdcall;
begin
    try
        WaitForSingleObject(HookMutex,INFINITE);
        with Shared^ do
        begin
            case WatchType of
            wtSHELL:
                begin
                    dec(ShellCount);
                    if (ShellCount<=0) and (ShellHook<>0) then
                    begin
                        UnhookWindowsHookEx(ShellHook);
                        ShellHook:=0;
                        ShellCount:=0;
                    end;
                end;
            wtKEYBOARD:
                begin
                    dec(KeyCount);
                    if (KeyCount<=0) and (KeyHook<>0) then
                    begin
                        UnhookWindowsHookEx(KeyHook);
                        KeyHook:=0;
                        KeyCount:=0;
                    end;
                end;
            wtMouse:
                begin
                    dec(MouseCount);
                    if (MouseCount<=0) and (MouseHook<>0) then
                    begin
                        UnhookWindowsHookEx(MouseHook);
                        MouseHook:=0;
                        MouseCount:=0;
                    end;
                end;
            end;
        end;
    finally
        ReleaseMutex(HookMutex);
    end;
end;

procedure StopAll;stdcall;//stops all hook activity
    var alreadyStopped:Boolean;
begin
    try
        WaitForSingleObject(HookMutex,INFINITE);
        with Shared^ do
        begin
            alreadyStopped:=((ShellCount=0) and (KeyCount=0) and (MouseCount=0));
            ShellCount:=0;
            KeyCount:=0;
            MouseCount:=0;
        end;
    finally
        ReleaseMutex(HookMutex);
    end;
    if not alreadyStopped then
    begin
        StopWatching(wtSHELL);
        StopWatching(wtKEYBOARD);
        StopWatching(wtMouse);
    end;
end;

//This needs to be rewritten for multiple receivers! [MY]
procedure SetReceiver(R:Integer); stdcall;
begin
    try
        WaitForSingleObject(HookMutex,INFINITE);
        Shared^.Receiver:=r;
    finally
        ReleaseMutex(HookMutex);
    end;
end;

function StillWatching:Boolean; stdcall;
begin
    try
        WaitForSingleObject(HookMutex,INFINITE);
        result:=(Shared^.ShellCount>0) or (Shared^.KeyCount>0);
    finally
        ReleaseMutex(HookMutex);
    end;
end;

procedure StartUp;
begin
    SHELL_EVENT:=RegisterWindowMessage(PChar(MSG_MY_SHELL_EVENT));
    KEY_EVENT:=RegisterWindowMessage(PChar(MSG_MY_KEY_EVENT));
    MOUSE_EVENT:=RegisterWindowMessage(PChar(MSG_MY_MOUSE_EVENT));
    HookMutex:=CreateMutex(nil,True,HookMutexName);
    MemFile:=OpenFileMapping(FILE_MAP_WRITE,False,HookMemFileName);
    if MemFile=0 then MemFile:=CreateFileMapping($FFFFFFFF,nil,
      PAGE_READWRITE,0,SizeOf(TShared),HookMemFileName);
    Shared:=MapViewOfFile(MemFile,FILE_MAP_WRITE,0,0,0);
    if MemFile=0 then FillChar(Shared^,SizeOf(TShared),0);
    Inc(Shared^.AttachCount);
    ReleaseMutex(HookMutex);
end;

procedure DLLEntryPoint(reason:integer);
var
    Unused: Boolean;
begin
    case reason of
    DLL_PROCESS_DETACH:
        begin
            try
                WaitForSingleObject(HookMutex,INFINITE);
                dec(Shared^.AttachCount);
                Unused:=(Shared^.AttachCount=0);
            finally
                ReleaseMutex(HookMutex);
            end;
            if Unused then
            begin
                StopAll;
                UnmapViewOfFile(Shared);
                CloseHandle(MemFile);
                CloseHandle(HookMutex);
            end;
        end;
    DLL_PROCESS_ATTACH: Startup;
    end;
end;

exports
 StartWatching,
 StopWatching,
 StopAll,
 StillWatching,
 SetReceiver;

begin
    Startup;
    DLLProc:=@DLLEntryPoint;
end.

=====================================================
This component encapsulates the interface with the DLL (after this code, an example of using this component)
=====================================================
unit HookLibIntf;

interface

uses Windows,Classes,Messages,Forms,Dialogs;

const
    MSG_MY_KEY_EVENT   =   'MY_KEY_EVENT_HOOK';
    MSG_MY_SHELL_EVENT =   'MY_SHELL_EVENT_HOOK';
    MSG_MY_MOUSE_EVENT =   'MY_MOUSE_EVENT_HOOK';

type
    TWatchType=(wtSHELL,wtKEYBOARD,wtMOUSE);
    TWatching=procedure(WatchType:TWatchType); stdcall;
    TStopAll=procedure; stdcall;
    TStillWatching=function:Boolean; stdcall;
    TSetReceiver=procedure(r:Integer); stdcall;
    TShellEvent=procedure(Sender: TObject;wParam: WPARAM;code: integer) of object;
    TMouseKeyEvent=procedure(Sender: TObject;wParam: WPARAM;lParam: LPARAM) of object;

type THookLib=class(TComponent)
  private
    HWNDHandle:THandle;
    LibHandle:THandle;
    SHELL_EVENT: Cardinal;
    Mouse_EVENT: Cardinal;
    KEY_EVENT: Cardinal;
    StartWatching:TWatching;
    StopWatching:TWatching;
    StopAll:TStopAll;
    StillWatching:TStillWatching;
    SetReceiver:TSetReceiver;
    FOnKeyEvent: TMouseKeyEvent;
    FOnMouseEvent: TMouseKeyEvent;
    FOnShellEvent: TShellEvent;
    procedure LoadLib;
    procedure SetOnKeyEvent(const Value: TMouseKeyEvent);
    procedure SetOnMouseEvent(const Value: TMouseKeyEvent);
    procedure SetOnShellEvent(const Value: TShellEvent);
    procedure UnloadLib;
  protected
    procedure OnShellHook(nCode: Integer; wParam: Longint);virtual;
    procedure OnMouseHook(wParam,lParam: Longint);virtual;
    procedure OnKeyHook(wParam, lParam: Integer);virtual;
  public
    procedure OnHWNDEvent(var Message: TMessage);
    constructor Create(AOwner:TComponent); override;
    destructor Destroy; override;
    procedure Start(WatchType:TWatchType);
    procedure Stop(WatchType:TWatchType);
  published
    property OnShellEvent: TShellEvent read FOnShellEvent write SetOnShellEvent;
    property OnMouseEvent: TMouseKeyEvent read FOnMouseEvent write SetOnMouseEvent;
    property OnKeyEvent: TMouseKeyEvent read FOnKeyEvent write SetOnKeyEvent;
  end;

procedure Register;

implementation

procedure Register;
begin
    RegisterComponents('System',[THookLib]);
end;

{ THookLib }

constructor THookLib.Create(AOwner: TComponent);
begin
    inherited Create(AOwner);
    // get unique Message IDs for the Broadcast of the dll
    SHELL_EVENT:=RegisterWindowMessage(PChar(MSG_MY_SHELL_EVENT));
    KEY_EVENT:=RegisterWindowMessage(PChar(MSG_MY_KEY_EVENT));
    MOUSE_EVENT:=RegisterWindowMessage(PChar(MSG_MY_MOUSE_EVENT));
    LibHandle:=0;
    StartWatching:=nil;
    StopWatching:=nil;
    StopAll:=nil;
    StillWatching:=nil;
    HWNDHandle:=0;
end;

destructor THookLib.Destroy;
begin
    if assigned(StopAll) then StopAll;
    UnLoadLib;
    inherited Destroy;
end;

procedure THookLib.LoadLib;
begin
    if (LibHandle=0) and (not (csDesigning in ComponentState)) then
    begin
        LibHandle:=LoadLibrary('hooklib.dll');
        if LibHandle<>0 then
        begin
            HWNDHandle:=AllocateHwnd(OnHWNDEvent);
            @StartWatching:=GetProcAddress(LibHandle, 'StartWatching');
            @StopWatching:=GetProcAddress(LibHandle, 'StopWatching');
            @StillWatching:=GetProcAddress(LibHandle, 'StillWatching');
            @StopAll:=GetProcAddress(LibHandle, 'StopAll');
            @SetReceiver:=GetProcAddress(LibHandle, 'SetReceiver');
            if assigned(SetReceiver) then SetReceiver(HWNDHandle);
        end;
        if (LibHandle=0) or (not assigned(StartWatching))
        or (not assigned(StopWatching)) or (not assigned(StillWatching))
        or (not assigned(StopAll)) then
        begin
            if LibHandle=0 then MessageDlg('hooklib.dll not found!',mtError,[mbOK],0)
            else  MessageDlg('hooklib.dll is not valid',mtError,[mbOK],0);
            LibHandle:=0;
            StartWatching:=nil;
            StopWatching:=nil;
            StopAll:=nil;
            StillWatching:=nil;
        end;
    end;
end;

procedure THookLib.OnHWNDEvent(var Message: TMessage);
begin
    if Message.Msg=SHELL_EVENT then OnShellHook(Message.lParam,Message.wParam)
    else if Message.Msg=Mouse_EVENT then OnMouseHook(Message.wParam,Message.lParam)
    else if Message.Msg=KEY_EVENT then OnKeyHook(Message.wParam,Message.lParam)
    else Message.Result:= DefWindowProc(HWNDHandle, Message.Msg, Message.wParam, Message.lParam);
end;

procedure THookLib.OnKeyHook(wParam, lParam: Integer);
begin
    if Assigned(FOnKeyEvent) then FOnKeyEvent(self,wParam,lParam);
end;

procedure THookLib.OnMouseHook(wParam, lParam: Integer);
begin
    if Assigned(FOnMouseEvent) then FOnMouseEvent(self,wParam,lParam);
end;

procedure THookLib.OnShellHook(nCode, wParam: Integer);
begin
    if Assigned(FOnShellEvent) then FOnShellEvent(self,wParam,nCode);
end;

procedure THookLib.SetOnKeyEvent(const Value: TMouseKeyEvent);
begin
    FOnKeyEvent := Value;
end;

procedure THookLib.SetOnMouseEvent(const Value: TMouseKeyEvent);
begin
    FOnMouseEvent := Value;
end;

procedure THookLib.SetOnShellEvent(const Value: TShellEvent);
begin
    FOnShellEvent := Value;
end;

procedure THookLib.Start(WatchType: TWatchType);
begin
    LoadLib;
    if assigned(StartWatching) then StartWatching(WatchType);
end;

procedure THookLib.Stop(WatchType: TWatchType);
begin
    if (libHandle<>0) and assigned(StopWatching) then
    begin
        StopWatching(WatchType);
        if not StillWatching then UnLoadLib;
    end;
end;

procedure THookLib.UnLoadLib;
begin
    if HWNDHandle<>0 then DeallocateHwnd(HWNDHandle);
    if LibHandle<>0 then FreeLibrary(LibHandle);
    LibHandle:=0;
    StartWatching:=nil;
    StopWatching:=nil;
    StopAll:=nil;
    StillWatching:=nil;
    HWNDHandle:=0;
end;

end.
=====================================================
An example of using the component and the DLL.
=====================================================
unit main;

interface

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

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Button1: TButton;
    HookLib1: THookLib;
    Label1: TLabel;
    Label2: TLabel;
    procedure HookLib1KeyEvent(Sender: TObject; wParam, lParam: Integer);
    procedure HookLib1MouseEvent(Sender: TObject; wParam, lParam: Integer);
    procedure HookLib1ShellEvent(Sender: TObject; wParam, code: Integer);
    procedure FormShow(Sender: TObject);
  private
    MouseCount: integer;
    KeyboardCount: integer;
    LastTitle: string;
  public
    { Public declarations }
  end;

var Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormShow(Sender: TObject);
begin
    HookLib1.Start(wtSHELL);
    HookLib1.Start(wtKEYBOARD);
    HookLib1.Start(wtMouse);
    MouseCount:=0;
    KeyboardCount:=0;
    LastTitle:='dasilhljkasdfh';
end;

procedure TForm1.HookLib1KeyEvent(Sender: TObject; wParam,
  lParam: Integer);
begin
    Inc(KeyboardCount);
    Label2.Caption:=Format('%d',[KeyboardCount]);
end;

procedure TForm1.HookLib1MouseEvent(Sender: TObject; wParam,
  lParam: Integer);
begin
    Inc(MouseCount);
    Label1.Caption:=Format('%d',[MouseCount]);
end;

procedure TForm1.HookLib1ShellEvent(Sender: TObject; wParam,
  code: Integer);
var
    Title: array[0..300] of Char;
begin
    case code of
    HSHELL_ACTIVATESHELLWINDOW: Memo1.Lines.Add(Format('%s DESKTOP',[FormatDateTime('c',Now)]));
    HSHELL_WINDOWACTIVATED,HSHELL_REDRAW:
        begin
            if GetWindowText(wParam,Title,299)=0 then Title[0]:=#0;
            if LastTitle<>Title then
            begin
                Memo1.Lines.Add(Format('%s %s',[FormatDateTime('c',Now),Title]));
                LastTitle:=Title;
            end;
        end;
    end;
end;

ASKER CERTIFIED SOLUTION
Avatar of orascript
orascript

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of fibdev

ASKER

Ok...

How do I change your example so it executes the procedures when the above listed hotkeys are pressed?

So far I've been able to incorperate it into my project and create the dll and component.  All I need help with now is the example.

Thanks for your help!
This is the routine of interest:

procedure TForm1.HookLib1KeyEvent(Sender: TObject; wParam,
  lParam: Integer);
begin
    Inc(KeyboardCount);
    Label2.Caption:=Format('%d',[KeyboardCount]);
end;

In my case, I was only interested in counting keystrokes. In your case, you want to interpret wParam and lParam to see what key was pressed.

wParam

Specifies the virtual-key code of the key that generated the keystroke message.

lParam

Specifies the repeat count, scan code, extended-key flag, context code, previous key-state flag, and transition-state flag. This parameter can be a combination of the following values:

Value      Description
0-15      Specifies the repeat count. The value is the number of times the keystroke is repeated as a result of the user's holding down the key.
16-23      Specifies the scan code. The value depends on the original equipment manufacturer (OEM).
24      Specifies whether the key is an extended key, such as a function key or a key on the numeric keypad. The value is 1 if the key is an extended key; otherwise, it is 0.
25-28      Reserved.
29      Specifies the context code. The value is 1 if the ALT key is down; otherwise, it is 0.
30      Specifies the previous key state. The value is 1 if the key is down before the message is sent; it is 0 if the key is up.
31      Specifies the transition state. The value is 0 if the key is being pressed and 1 if it is being released.

example:

case wParam of
VK_F9: --the F9 key was pressed (in any application)
end;






Avatar of fibdev

ASKER

Adjusted points from 50 to 75
Avatar of fibdev

ASKER

Could you post an example of how this should look If I want to trap:
 Alt + Ctrl + W and fire a procedure

Thank you for your time and I will give you the points.

I'm not very good at this stuff and I need to be led along, sorry for that...
Let me show you how to find out (as I suspect you need more key codes)

procedure TForm1.HookLib1KeyEvent(Sender: TObject; wParam,
  lParam: Integer);
begin
    case wParam of
    VK_F9: ShowMessage('F9 pressed');
    end;
end;

1) place a break point at the line that reads case wParam of
2) run the program
3) press CTRL+ALT+W
4) the program will break at the specified line. note the value of wParam and lParam.
5) repeat step 3

warning:

first you will get scan code for Alt, then Ctrl+Alt and finally Ctrl+Alt+W


Avatar of fibdev

ASKER

Crud!

I've been having this problem, but it didn't interfere until now.

When I try to run this project in the delphi enviorment I get the CPU debugger and I have no clue about what to do with it or how to resolve the problem.  Until now, I've just been compiling the project and using the run command to execute the exe file.

Here is a screenshot of the cpu window

http://fibdev.com/temp/cpu.jpg

I don't know if you know what this means, but is there another way to get this information, other than by way of breakpoints?

1) are you compiling with debug information.
2) just hit F9 when you get the CPU window to keep running
3) there is no way, although I can guess
  VK_ALT    VK_CTRL   Ord('W')
you will get these three events (one for each key) in ANY order.

4) try with 1 key first, to see how it works. use my VK_F9 example

Tony
Avatar of fibdev

ASKER

Tony...

I must be doing something wronge because nothing happens when I fallow your instruction  :(

This is so frustrating, thanks for your help
1) clear all break points.

2) do this in your code

procedure TForm1.HookLib1KeyEvent(Sender: TObject; wParam,
  lParam: Integer);
begin
    case wParam of
    VK_F9: ShowMessage('F9 pressed');
    end;
end;

3) run program

4) press F9 in any program

5) does message box appear?

Check:
1)
    HookLib1.Start(wtSHELL);
===>    HookLib1.Start(wtKEYBOARD);
    HookLib1.Start(wtMouse);

this must be called to set hooks.    

2) the DLL must be in same directory or on path

you do have the event handler from the component assigned to the code , don't you?
Avatar of fibdev

ASKER

I don't understand this...

I'm doing everything you say, but I'm not having any luck. I know I'm doing something wrong... I just don't know what!

Does this question pertain to the component code, or the example?

Please bare with me because I am so lame at this.  :)

"you do have the event handler from the component assigned to the code , don't you?"
Reyou three source code files
1) the DLL. I assume you built this
2) the component. I assume you installed the component.
3) a form on your program. You put the component on it and assign the OnKeyEvent event. This event will be triggered whenever a key is pressed in any program on the system.
try the code I gave above in this event handler
4) somewhere in your code, you must call the HookLib1.Start(wtKEYBOARD);
(assuming HookLib1 is the name of the component)

Avatar of fibdev

ASKER

I don't get it...

I did all those things, but when I try to press F9, nothing happens!

What could I be doing wrong?
Avatar of fibdev

ASKER

Ok,

I got it working, but it looks like for every time I press F9 I get two message boxes?
email me at ads@houston.rr.com
I will send you the project and executable (I just tested it again, it works like I said)
one for key down, one for key up

look at lParam, bit 31:

lParam

Specifies the repeat count, scan code, extended-key flag, context code, previous key-state flag, and transition-state flag. This parameter can be a combination of the following values:

Value Description
0-15 Specifies the repeat count. The value is the number of times the keystroke is repeated as a result of the user's holding down the key.
16-23 Specifies the scan code. The value depends on the original equipment manufacturer (OEM).
24 Specifies whether the key is an extended key, such as a function key or a key on the numeric keypad. The value is 1 if the key is an extended key; otherwise, it is 0.
25-28 Reserved.
29 Specifies the context code. The value is 1 if the ALT key is down; otherwise, it is 0.
30 Specifies the previous key state. The value is 1 if the key is down before the message is sent; it is 0 if the key is up.
31 Specifies the transition state. The value is 0 if the key is being pressed and 1 if it is being released.

If you got this to work from another app, such as notepad, then I believe I have answered your original question. You now have code for global keyboard hook (and mouse and shell too)

nice piece of work orascript
Avatar of fibdev

ASKER

Answer accepted
Avatar of fibdev

ASKER

Well...

I'm still at a loss with all of this.  I don't have the hok keys I asked for, but I do have an F9 key that fires on press and release.  Thanks anyway, you tried really hard.
Avatar of fibdev

ASKER

How do I use this lParam?

I know you've helped me so much, but I'm still at a bit of a loss here.
what is this" thanks anyway...."
you asked for a system wide key hook, you got a system wide key hook, not just for F9, but for any key.
I really do not understand the grading, this is a very difficult quesytion with very limited points, the least you could have done is award an A for the effort I have put in...
OK, I am p*ssed off.

Orascript: You should know what a hot key is - it is a system wide key handler. It is NOT necessary to write a global system keyboard hook to implement one. Your comment is pretty damn blatant and not acceptable. Unfortunately the weekend intervened and I didn't get back to EE until now, only to discover this cr*p.

fibdev: If you want to get what you want to work, then go back to my original answer and look at the API help for registerhotkey, this documents the flags you pass to request CTRL/ALT etc.

Cheers,

Raymond.

rwilson, sorry, I should have researched your answer more before posting, no I was not familiar with RegisterHotKey.

I have asked community support to reassign the points to you

https://www.experts-exchange.com/jsp/qShow.jsp?ta=commspt&qid=10481138 
orascript: You are a gentleman. Sorry if I was a bit strong... I was miffed...

Cheers,

Raymond.
no, you are right, I am sometimes hasty in my judgement. If EE does not come through, let me know, I will give you the points myself, worth it, I learned something today (RegisterHotKey)...
Hello everyone,

rwilson, please post an answer at:
https://www.experts-exchange.com/jsp/qShow.jsp?ta=delphi&qid=10490698 

darinw
Customer Service
Avatar of fibdev

ASKER

Sorry to cause hard fealings.  I do appreciate all the work that was put into this and I'm sorry that I don't have many points to work with.

My original posting:

Hi,

I think the title of this question sais it all.

I need to create a system wide hook to trap keyboard events and execute a procedure.  I hope someone has a answer for me that isn't too complex... I'm sorta' a newbie when it comes to Delphi.

Thanks

Clearly states that I needed to capture a keyboard event and... execute a function.  So, clearly my question wasn't answered in it's entirety.  I felt as if I was being pushed to award the points, and so I did.

At this point, I even clarified my needs:

Ok, let me be specific...

Here is what I want to do:

I want Ctrl + Alt + W to fire procedure TForm1.Window1Click(Sender: TObject);

I want Ctrl + Alt + S to fire procedure TForm1.Screen1Click(Sender: TObject);

I want Ctrl + Alt + A to fire procedure TForm1.SelectedArea1Click(Sender: TObject);

Explain how to set this up.  Keep in mind I'm a newbie so explain it like you would a 5 year old.

Thanks

Again, sorry to upset you, but my question was not answered.  I do acknowlage that you worked very hard to resolve this for me, but I gave up my points and still don't have a usable solution.  I have been on vacation for the past week, or I would have addressed this sooner!

OK, here are the goods:
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
    procedure WMHotKey(var Msg : TWMHotKey); message WM_HOTKEY;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.WMHotKey(var Msg : TWMHotKey);
begin
  case Msg.HotKey of
    1: ShowMessage('Call TForm1.Window1Click(Sender: TObject);');
    2: ShowMessage('Call TForm1.Screen1Click(Sender: TObject);');
    3: ShowMessage('Call TForm1.SelectedArea1Click(Sender: TObject);');
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  RegisterHotKey(Handle,1,MOD_CONTROL + MOD_ALT, ord('W'));
  RegisterHotKey(Handle,2,MOD_CONTROL + MOD_ALT, ord('S'));
  RegisterHotKey(Handle,3,MOD_CONTROL + MOD_ALT, ord('A'));
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
 UnregisterHotKey(Handle,1);
 UnregisterHotKey(Handle,1);
 UnregisterHotKey(Handle,1);
end;

end.

Cheers,

Raymond.
Oops, FormClose should be:

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
 UnregisterHotKey(Handle,1);
 UnregisterHotKey(Handle,2);
 UnregisterHotKey(Handle,3);
end;
Avatar of fibdev

ASKER

Thank you Raymond...

It works perfectly!

Could you tell me how to set it up so that it executes the procedure in stead of displaying the message.  I know this is a topic far more advanced than my skills (or lack thereof).  I've only got 30 points, but you can have them.
Avatar of fibdev

ASKER

If possible, I want to set focus to my application before executing the function.  I don't know it that will make the process more difficult or easier.

Thank you
Well, you would do it like this:

procedure TForm1.WMHotKey(var Msg : TWMHotKey);
begin
  SetForegroundWindow(Handle);
  case Msg.HotKey of
    1: Window1Click(Self);
    2: Screen1Click(Self);
    3: SelectedArea1Click(Self);
  end;
end;

You will need to add the window, screen and selectedarea (menu items) and create the click events for this to compile.

Don't worry abvout the points, keep them for your next Q.

Cheers,

Raymond.
Best (Amster)damned 7 points I ever spent.