• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 18504
  • Last Modified:

Delphi - detect a left/right mouse click anywhere on the screen.

Hopefully this will be my last posting on this subject :-).

With the help of the Delphi Experts I have put together a keyboard logger that records in a memo box anything the user types on the screen. This is for about 1,000 disabled internet students in and around London so that their remote tutors can see what they are actually typing. It all working swimmingly together with accurate renditions of the Num/Caps/Scroll Lock lights.

But what I am missing is the ability to see what mouse clicks have been pressed?

Any ideas?

Ed

p.s. Here's the code for the key stroke trapper DLL....

IBRARY KeyHandler;
  uses
  Windows,
  Messages;

CONST
    CM_SEND_KEY = WM_USER + $1000;

  VAR
    Keybrd: HHook;
    MemFile: THandle;
    Reciever: ^Integer;

  Function HookCallBack(Code: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
  {This is the CallBack function called by the Hook}
  Begin
  {if a key was pressed/released}
    if (code = HC_ACTION) then begin
    {attempt to map to MemFile}
      MemFile := OpenFileMapping(FILE_MAP_READ, False, 'KeyReciever');
    {if mapping successful, send keypress to receiver application}
      if (MemFile <> 0) then begin
        Reciever := MapViewOfFile(MemFile, FILE_MAP_READ, 0, 0, 0);
        PostMessage(Reciever^, CM_SEND_KEY, wParam, lParam);
        UnmapViewOfFile(Reciever);
        CloseHandle(MemFile);
      end;
    end;
    {call to next hook of the chain}
    Result := CallNextHookEx(Keybrd, Code, wParam, lParam)
  End;

  Procedure HookOn; stdcall;
  {procedure to install the hook}
  Begin
    Keybrd := SetWindowsHookEx(WH_KEYBOARD, @HookCallBack, HInstance, 0);
  End;

  Procedure HookOff; stdcall;
  {procedure to uninstall the hook}
  Begin
    UnhookWindowsHookEx(Keybrd);
  End;

  EXPORTS

  { Export the following Procedures/Functions }

  HookOn,
  HookOff;

BEGIN
END.



0
edhasted
Asked:
edhasted
  • 7
  • 4
  • 4
  • +2
2 Solutions
 
A. Cristian CsikiSenior System AdministratorCommented:
here's a tip:

http://www.experts-exchange.com/Programming/Programming_Languages/Delphi/Q_21828809.html

private
  procedure WMNCRBUTTONDOWN(var msg: TMessage); message WM_NCRBUTTONDOWN;
  procedure WMNCLBUTTONDOWN(var msg: TMessage); message WM_NCLBUTTONDOWN;
  procedure WMNCLBUTTONDBLCLK(var msg: TMessage); message WM_NCLBUTTONDBLCLK;
end;



implementation


procedure TForm1.WMNCRBUTTONDOWN(var msg: TMessage);
begin
  if msg.wParam = HTCAPTION then Caption := 'Right Click!';
  // Message.Result := 0; {to ignore the message}
  inherited;
end;

procedure TForm1.WMNCLBUTTONDOWN(var msg: TMessage);
begin
  if msg.wParam = HTCAPTION then Caption := 'Left Click!';
  // Message.Result := 0; {to ignore the message}
  inherited;
end;

procedure TForm1.WMNCLBUTTONDBLCLK(var msg: TMessage);
begin
  if msg.wParam = HTCAPTION then Caption := 'Double Click!';
  // Message.Result := 0; {to ignore the message}
  inherited;
end;

probably might help. //from torry's page
0
 
edhastedAuthor Commented:
I saw that in your original reply. What I don't understand is where to position the procedures
TForm1.WMNCRBUTTONDOWN(var msg: TMessage); etc
so they trap the mouseclicks and then display the message....

Ed
0
 
2266180Commented:
since you want to trap it anywhere on the screen, you will need some sort of mouse hooks. either use the one comming with windows api or use some 3rd party components (I recommend using madhook from madshi)
0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
ZhaawZSoftware DeveloperCommented:
If you're using Windows NT/2000/XP, you may use WH_KEYBOARD_LL and WH_MOUSE_LL hooks - quite easy to use and no need for external library (i.e., HookProc may be kept inside of .exe file).
0
 
ZhaawZSoftware DeveloperCommented:
object Memo1: TMemo
  Left = 0
  Top = 16
  Width = 209
  Height = 569
  ReadOnly = True
  ScrollBars = ssVertical
  TabOrder = 0
end
object Label1: TLabel
  Left = 0
  Top = 0
  Width = 71
  Height = 13
  Caption = 'Mouse activity:'
end
object Memo2: TMemo
  Left = 224
  Top = 16
  Width = 209
  Height = 569
  ReadOnly = True
  ScrollBars = ssVertical
  TabOrder = 1
end
object Label2: TLabel
  Left = 224
  Top = 0
  Width = 84
  Height = 13
  Caption = 'Keyboard activity:'
end

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

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Label1: TLabel;
    Memo2: TMemo;
    Label2: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  end;
  KeybdLLHookStruct = record
    vkCode      : cardinal;
    scanCode    : cardinal;
    flags       : cardinal;
    time        : cardinal;
    dwExtraInfo : cardinal;
  end;
  MouseLLHookStruct = record
    pt          : TPoint;
    mouseData   : cardinal;
    flags       : cardinal;
    time        : cardinal;
    dwExtraInfo : cardinal;
  end;

var
  Form1 : TForm1;
  mHook : cardinal;
  kHook : cardinal;

implementation

{$R *.dfm}

function LowLevelKeybdHookProc(nCode, wParam, lParam : integer) : integer; stdcall;
// possible wParam values: WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN, WM_SYSKEYUP
var
  info : ^KeybdLLHookStruct absolute lParam;
  lpChar : word;
  kState : TKeyboardState;
begin
result := CallNextHookEx(kHook, nCode, wParam, lParam);
with info^ do
case wParam of
  wm_keydown : begin
    GetKeyboardState(kState);
    if ToAscii(vkCode, scanCode, kState, @lpChar, 0) > 0 then Form1.Memo2.Lines.Append(format('pressed key -- %s', [char(lpChar)]));
  end;
end;
end;

function LowLevelMouseHookProc(nCode, wParam, lParam : integer) : integer; stdcall;
// possible wParam values: WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_RBUTTONDOWN, WM_RBUTTONUP
var
  info : ^MouseLLHookStruct absolute lParam;
begin
result := CallNextHookEx(mHook, nCode, wParam, lParam);
with info^ do
case wParam of
  wm_lbuttondown : Form1.Memo1.Lines.Append(format('pressed left button (%d, %d)'    , [pt.x, pt.y]));
  wm_lbuttonup   : Form1.Memo1.Lines.Append(format('released left button (%d, %d)'   , [pt.x, pt.y]));
  wm_mbuttondown : Form1.Memo1.Lines.Append(format('pressed middle button (%d, %d)'  , [pt.x, pt.y]));
  wm_mbuttonup   : Form1.Memo1.Lines.Append(format('released middle button (%d, %d)' , [pt.x, pt.y]));
  wm_rbuttondown : Form1.Memo1.Lines.Append(format('pressed right button (%d, %d)'   , [pt.x, pt.y]));
  wm_rbuttonup   : Form1.Memo1.Lines.Append(format('released right button (%d, %d)'  , [pt.x, pt.y]));
  wm_mousewheel  : begin
    if smallInt(mouseData shr 16) > 0
    then Form1.Memo1.Lines.Append('scrolled wheel (up)')
    else Form1.Memo1.Lines.Append('scrolled wheel (down)');
  end;
end;
end;

procedure TForm1.FormCreate(Sender: TObject);
const
  wh_keybd_ll = 13;
  wh_mouse_ll = 14;
begin
kHook := SetWindowsHookEx(wh_keybd_ll, @LowLevelKeybdHookProc, hInstance, 0);
mHook := SetWindowsHookEx(wh_mouse_ll, @LowLevelMouseHookProc, hInstance, 0);
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
UnhookWindowsHookEx(kHook);
UnhookWindowsHookEx(mHook);
end;

end.
0
 
edhastedAuthor Commented:
ZhaawZ, the keyboard parts works perfectly.
The mouse bit which is what I'm after didn't work.
I had to remove the "variant" uses - is that because I'm on Delphi5?

Almost there,

Ed
0
 
ZhaawZSoftware DeveloperCommented:
About keyboard part - I just showed you the way of hooking keyboard by using low-level hook without making external .dll file. You also do not need memory-mapped files to work with low-level hooks ;) But as these are low-level hooks, they're supported only in w2k/nt/xp. If you need an example for older windows, just say.

About "variant" - you may safelly remove it (I think it was added in D6, but I've never used it).
0
 
edhastedAuthor Commented:
A lot of the studentsare runing on '98.
So could you let me know how I attack the issue under these older operating systems.

Wtih many thanks,

Ed
0
 
ZhaawZSoftware DeveloperCommented:
by using WH_KEYBOARD hook.

Try this:


uses
  windows;

var
  hook : cardinal;

function HookProc(nCode, wParam, lParam : integer) : integer; stdcall;
var
  lpChar : word;
  kState : TKeyboardState;
begin
result := CallNextHookEx(hook, nCode, wParam, lParam);
GetKeyboardState(kState);
if ToAscii(wParam, (lParam shr 16) and $ff, kState, @lpChar, 0) > 0 then begin
  // lpChar should be ascii value of char - send it to your app
end;
end;

procedure HookOn;
begin
hook := SetWindowsHookEx(wh_mouse, @HookProc, hInstance, 0);
end;

procedure HookOff;
begin
UnhookWindowsHookEx(hook);
end;
0
 
Slick812Commented:
hello edhasted , here is some code for a Dual Hook Library, it will get both the Key and Mouse input from the user, and send a message to the Form Handle that is in the Memory Mapped File.
You already have code for the KeyHook, but I have included my code for that also. I had to pack the WParam and LParam of the MseMsg post, because there is extra message information for mouse messages like the WM_MOUSEWHEEL message.
I have the code used in the TForm below the library code.

     the library code is below -->



Library GetInput;

uses
  Messages, Windows;

{$R *.RES}


const
mapName: PChar = 'k9i:f$d8aR1';
KeyMsg: Integer = WM_USER+1627;
MseMsg: Integer = WM_USER+1628;

var
hKeyHook, hMseHook, hMemFile: THandle;
Hooked: Boolean = False;
pFHandle: PHandle = nil;


procedure CloseMap;
begin
if pFHandle = nil then Exit;
UnmapViewOfFile(pFHandle);
CloseHandle(hMemFile);
pFHandle := nil;
end;


function KeyHookFunc(Code, VirtualKey, KeyStroke: Integer): LRESULT; stdcall;
var
KeyState1: TKeyBoardState;
AryChar: Array[0..1] of Char;
Count: Integer;
begin
// I had trouble with NON-syncronus key values in separate thread message queues
// so I used the GetKeyboardState function
Result := 0;
if Code = HC_NOREMOVE then Exit;
Result := CallNextHookEx(0, Code, VirtualKey, KeyStroke);
if (Code < 0) or (Code <> HC_ACTION) or IsBadCodePtr(pFHandle) then Exit;

if ((KeyStroke and (1 shl 30)) <> 0) then
  begin
  GetKeyboardState(KeyState1);
  Count := ToAscii(VirtualKey,KeyStroke, KeyState1, AryChar, 0);
  if Count = 1 then
    PostMessage(pFHandle^, KeyMsg, Ord(AryChar[0]), KeyStroke);
  end;
end;



function MseHookFunc(Code, mMsg: Integer; var MouseRec: TMOUSEHOOKSTRUCT): Integer; stdcall;
var
Pos: Integer;
reVal: SmallInt;
begin
// to get the message information into 2 Integer values (wParam, lParam), I use the HiWord and LoWord
Result := 0;
if Code = HC_NOREMOVE then Exit;
Result := CallNextHookEx(0, Code, mMsg, Integer(@MouseRec));
if (Code < 0) or (Code <> HC_ACTION) or IsBadCodePtr(pFHandle) then Exit;

reVal := MouseRec.pt.x;
Pos := Word(reVal);
reVal := MouseRec.pt.y;
Pos := Pos or (Word(reVal) shl 16); // 2 SmallInt values in the LParam
mMsg := mMsg or (Integer(MouseRec.dwExtraInfo) shl 16);// 2 Word values in the WParam
PostMessage(pFHandle^, MseMsg, mMsg, Pos);
end;



// you must include the Forms window Handle in the StartHook
// StartHook is succesfull if it returns Zero
function StartHook(FormHandle: THandle): Integer; export;
begin
Result := 1;
if Hooked then Exit;

if not IsWindow(FormHandle) then
  begin
  Result := 2;
  Exit;
  end;

hMemFile := CreateFileMapping($FFFFFFFF, // $FFFFFFFF gets a page memory file
                nil,                // no security attributes
                PAGE_READWRITE,      // read/write access
                0,                   // size: high 32-bits
                SizeOf(THandle),           // size: low 32-bits
                mapName);    // name of map object
pFHandle := MapViewOfFile(hMemFile, FILE_MAP_WRITE, 0, 0, 0);
if pFHandle = nil then
  begin
  CloseHandle(hMemFile);
  Result := 3;
  Exit;
  end;

hKeyHook := SetWindowsHookEx(WH_KEYBOARD, @KeyHookFunc, hInstance, 0);
if hKeyHook = 0 then
  begin
  CloseMap;
  Result := 4;
  Exit;
  end;

hMseHook := SetWindowsHookEx(WH_MOUSE, @MseHookFunc, hInstance, 0);
if hMseHook = 0 then
  begin
  CloseMap;
  UnhookWindowsHookEx(hKeyHook);
  Result := 5;
  Exit;
  end;

Hooked := True;
pFHandle^ := FormHandle;
Result := 0;
end;


function StopHook: Boolean; export; // success if true
begin
if Hooked then
  begin
  Result := UnhookWindowsHookEx(hKeyHook) and UnhookWindowsHookEx(hMseHook);
  end else
  Result := True;
 
if Result then
  begin
  CloseMap;
  hKeyHook := 0;
  hMseHook := 0;
  Hooked := False;
  end;
end;



procedure EntryProc(Reason: Cardinal);
begin
if (Reason = Dll_Process_Detach) then
 begin
 CloseMap;
 if Hooked then
   begin
   UnhookWindowsHookEx(hMseHook);
   UnhookWindowsHookEx(hKeyHook);
   end;
 end;
end;

exports
  StartHook, StopHook;


begin
DLLProc := @EntryProc;
hMemFile := OpenFileMapping(FILE_MAP_WRITE, False, mapName);
if hMemFile <> 0 then
  pFHandle := MapViewOfFile(hMemFile, FILE_MAP_WRITE, 0, 0, 0);

end.



 = = = = = = = = = = = = = = = = = = = = = = = = =
below is code in the TForm -->


  private
    { Private declarations }
    hLibGI: THandle;
    procedure HookMsgKey(var Msg1: TMessage); message WM_USER+1627;
    procedure HookMsgMse(var Msg1: TMessage); message WM_USER+1628;




procedure TForm1.HookMsgKey(var Msg1: TMessage);
begin
Memo1.Perform(WM_CHAR, Msg1.wParam, 0);
end;



procedure TForm1.HookMsgMse(var Msg1: TMessage);
var
Str1: String;
begin
{below are most of the mouse messages
WM_LBUTTONDBLCLK
WM_LBUTTONDOWN
WM_LBUTTONUP
WM_MBUTTONDBLCLK
WM_MBUTTONDOWN
WM_MBUTTONUP
WM_MOUSEACTIVATE
WM_MOUSEMOVE
WM_MOUSEWHEEL
WM_NCLBUTTONDBLCLK
WM_NCLBUTTONDOWN
WM_NCLBUTTONUP
WM_NCMBUTTONDBLCLK
WM_NCMBUTTONDOWN
WM_NCMBUTTONUP
WM_NCMOUSEMOVE
WM_NCRBUTTONDBLCLK
WM_NCRBUTTONDOWN
WM_NCRBUTTONUP
WM_RBUTTONDBLCLK
WM_RBUTTONDOWN
WM_RBUTTONUP }


// because WM_MOUSEWHEEL is different I have it first
if Msg1.wParamLo = WM_MOUSEWHEEL then
  begin
  Str1 := 'mouseWheel - '+IntToStr(SmallInt(Msg1.wParamHi));
  //' at x:'+IntToStr(SmallInt(Msg1.LParamLo))+
  //      ' y:'+IntToStr(SmallInt(Msg1.LParamHi));
  Memo1.Lines.Add(Str1);
  Exit;
  end else
case Msg1.wParamLo of
  WM_LBUTTONDOWN: Str1 := 'Left Button Down';
  WM_LBUTTONUP: Str1 := 'WM_LBUTTONUP';
  WM_MBUTTONDOWN: Str1 := 'WM_MBUTTONDOWN';
  WM_MBUTTONUP: Str1 := 'WM_MBUTTONUP';
  //WM_MOUSEMOVE: Str1 := 'WM_MOUSEMOVE';
  WM_NCLBUTTONDOWN: Str1 := 'WM_NCLBUTTONDOWN';
  WM_RBUTTONDOWN: Str1 := 'WM_RBUTTONDOWN';
  WM_RBUTTONUP: Str1 := 'WM_RBUTTONDOWN';
  else Exit; // Warning, I just exit here to avoid all of the WM_MOUSEMOVE messages in memo
  end;

Str1 := 'mouse - '+Str1+' at x:'+IntToStr(SmallInt(Msg1.LParamLo))+
        ' y:'+IntToStr(SmallInt(Msg1.LParamHi));
Memo1.Lines.Add(Str1);
end;
 


// this is the StartHook button Click
procedure TForm1.but_StartHookClick(Sender: TObject);
var
StartHook: function(FormHandle: THandle): Integer;
Re: Integer;
MsgStr: String;
begin
MsgStr := 'FAILED to Load Library';
hLibGI := LoadLibrary('GetInput.dll');
if hLibGI > 0 then
  begin
  @StartHook := GetProcAddress(hLibGI,'StartHook');
  if @StartHook <> nil then
    begin
    Re := StartHook(Handle);
    if Re = 0 then
      MsgStr := 'Success - Hooks Are Running'
      else
      MsgStr := 'ERROR - Hooks NOT Started, Error code is '+IntToStr(Re);
    end else
    begin
    FreeLibrary(hLibGI);
    MsgStr := 'ERROR - StartHook function NOT in Library';
    end;
  end;


ShowMessage(MsgStr);
end;
0
 
edhastedAuthor Commented:
Slick,

I've compiled the DLL but in the executable I get an "Undeclared Identifier" against the but_StartHookClick

// this is the StartHook button Click
procedure TForm1.but_StartHookClick(Sender: TObject);

What have I missed? If in doubt assume ignorance.

Many thanks,

Ed
0
 
Slick812Commented:
???
I tried to give you the info in the comment -

// this is the StartHook button Click

what about that do you not understand? It is a standard Delphi Button click procedure, , , but that really does NOT matter, you can place the code I gave in ANY place where you want it to run, just have your own procedure or function and use the code above
0
 
Slick812Commented:
OK, , maybe you are not familar with using Delphi ? ?
When you have your Delphi IDE running and you can see your Designing Form (Form1 I guess), click on the "Button" control in the Component Library and then click on your form where you want the button to be, , , now go to the Object inspector (usually ,default, on the left hand side of IDE) and Click the "Events" tab for the the button you just added (Button1) and double click on the "OnClick" event for that button, you should get New Code in your Form .PAS file, like this -

procedure TForm1.Button1Click(Sender: TObject);
begin

end;

now add the code from above, so you have -

procedure TForm1.Button1Click(Sender: TObject);
begin
var
StartHook: function(FormHandle: THandle): Integer;
Re: Integer;
MsgStr: String;
begin
MsgStr := 'FAILED to Load Library';
hLibGI := LoadLibrary('GetInput.dll');
if hLibGI > 0 then
  begin
  @StartHook := GetProcAddress(hLibGI,'StartHook');
  if @StartHook <> nil then
    begin
    Re := StartHook(Handle);
    if Re = 0 then
      MsgStr := 'Success - Hooks Are Running'
      else
      MsgStr := 'ERROR - Hooks NOT Started, Error code is '+IntToStr(Re);
    end else
    begin
    FreeLibrary(hLibGI);
    MsgStr := 'ERROR - StartHook function NOT in Library';
    end;
end;

 = = = = = = = = = = = = = = = = = = =
that's as much as I know how to tell you what to do for this, , , It's just a button click. . .
I guess I should ask, , do you understand any of the code methods used here, or are you just copy and paste, and hope it works?
0
 
edhastedAuthor Commented:
Apologies - I should have looked at the OnClick button syntax.

This works a treat and the mouse co-ordinates are neat and I'll transpose them onto a little grid box.

One final question - with the disabled students we need to know what keys they are pressing. This passes through the letters and punctuation. Can it copy with the other keys, function keys etc and show when they have been pressed.

With many thanks,

Ed
0
 
edhastedAuthor Commented:
Thank you all for a massive amount of input oin a non-one liner solution.

Firstly I have increased to amount of points that need to be awardsed as this has taken a large amount of work. I have decided to split the points giving 200 to Slick812 as his solution gave me a working answer.

I am sure ZhaawZ's would have worked if I had the wit to work my way round it.

To you both many thanks,

Ed
0
 
edhastedAuthor Commented:
One thing I noticed in Slick812's code when running it is that final line in

WM_LBUTTONDOWN: Str1 := 'Left Button Down';
  WM_LBUTTONUP: Str1 := 'WM_LBUTTONUP';
  WM_MBUTTONDOWN: Str1 := 'WM_MBUTTONDOWN';
  WM_MBUTTONUP: Str1 := 'WM_MBUTTONUP';
  //WM_MOUSEMOVE: Str1 := 'WM_MOUSEMOVE';
  WM_NCLBUTTONDOWN: Str1 := 'WM_NCLBUTTONDOWN';
  WM_RBUTTONDOWN: Str1 := 'WM_RBUTTONDOWN';
  WM_RBUTTONUP: Str1 := 'WM_RBUTTONDOWN';

should read

  WM_RBUTTONUP: Str1 := 'WM_RBUTTONUP';
0
 
Slick812Commented:
You asked about also getting the non-ascii key input, , here is some code changes that may get that to happen, at least this works for me, but I have no Idea how you might need the non-ascii key press to be translated, used, or presented.


   All code in DLL is the SAME except I changed some code in the KeyHookFunc shown below -

function KeyHookFunc(Code, VirtualKey, KeyStroke: Integer): Integer; stdcall;
var
KeyState1: TKeyBoardState;
AryChar: Array[0..1] of Char;
Count: Integer;
begin
Result := 0;
if Code = HC_NOREMOVE then Exit;
Result := CallNextHookEx(0, Code, VirtualKey, KeyStroke);
if (Code <> HC_ACTION) or IsBadCodePtr(pFHandle) then Exit;


if ((KeyStroke and (1 shl 30)) <> 0) then
  begin
  GetKeyboardState(KeyState1);
  Count := ToAscii(VirtualKey,KeyStroke, KeyState1, AryChar, 0);

// changes below
  if Count = 1 then
    PostMessage(pFHandle^, KeyMsg, Ord(AryChar[0]), KeyStroke)
    else
    PostMessage(pFHandle^, KeyMsg, -VirtualKey, KeyStroke);
// VirtualKey is made negative to singnal NON ASCII in the program message processing
  end;
end;



 = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

   All code in the program is the same except I changed some code in the HookMsgKey procedure below



procedure TForm1.HookMsgKey(var Msg1: TMessage);
var
Str1: String;
begin
{if the KEY Down is an ASCII charater that can be used in a memo text, then the
 Msg1.wParam is Above Zero, if the Key Down is a NON ASCII key ,
 like F6 or "Page Up" then I have set the VirtualKey to a negative in the GetInput.dll.
 So I can test it here to see how to process this message}

if Msg1.wParam > 0 then // above zero, text Char to memo
  Memo1.Perform(WM_CHAR, Msg1.wParam, 0)
  else
  begin // below zero is NOT text for memo
  SetLength(Str1, 128);
  if GetKeyNameText(Msg1.lParam, PChar(Str1), Length(Str1)) > 0 then
    begin
    { You are not one to give information about how to deal with the output you may need !
     I use the GetKeyNameText function to get some text for reference to name
     the key pressed. . . HOWEVER if you need more specific actions (code functions)
     or key name text you will need to set up your own case or if tests for the
     Msg1.wParam (remember to change it back to a positive number) some values are -
     VK_F1 = 112;
     VK_F24 = 135;
     VK_LEFT = 37;
     VK_DOWN = 40;
     VK_HOME = 36;
     there are many others}

    Str1 := PChar(Str1)+' Key '+IntToStr(Msg1.wParam){+ ' Vur '+IntToStr(Msg1.lParam)};
    if -Msg1.wParam = VK_SHIFT then
      Str1 := Str1+#13#10;
    Memo1.Lines.Add(Str1);
    end;
  end;
end;





good luck,
0

Featured Post

Free Tool: Path Explorer

An intuitive utility to help find the CSS path to UI elements on a webpage. These paths are used frequently in a variety of front-end development and QA automation tasks.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

  • 7
  • 4
  • 4
  • +2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now