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

Hooking on a programs window textbox.

Hey guys n gals,

I know a bit about this keyhooking thing. I understand most of what is going on with all the keylogging examples on this forum. But I just can't seem to find a way to place a global or local hook on another program, and then on its textbox. And that when the textbox is being changed the hook will notify the program with the text and stuff. Better would be if in case of a messenger that it's possible to set a hook on the textbox window and intercept messages before they are being displayed, for example to gap out bad words. But an example with an onchange hook on notepad textbox for example would great too.

Thnx!
0
JimmyJJ
Asked:
JimmyJJ
  • 3
  • 3
  • 2
  • +1
1 Solution
 
Imthiyaz_phCommented:
In this site http://delphi.about.com/library/bluc/text/uc063001a.htm, you will find an article to create a global keyboard hook. A little modification on that might be the solution u r looking for.

The problem with the keyboard hook is that its hooking a thread, not a window. So instead of the keyboard hook (WH_KEYBOARD), try to monitor the messages sent (WH_MSGFILTER) along with MessageProc procedure. The lParam of this procedure points to an MSG structure, which contains the handle of the window. Filter out the messages for WM_KEYDOWN or WM_KEYUP and to that particular window using the MSG param. You can event prevent the message from going to the particular window by returning a non-zero value for your MessageProc procedure.

The key issue here is to get the handle of the window (edit box, memo, etc.) to monitor.
I m not sure how to get that. Either use FindWindowEx or WindowFromPoint to get the handle of the window.
0
 
carlp42Commented:
It depends exactly what you're trying to do.  As I understand it you want to trap keystroke messages being sent to a textbox in a seperate executable being run on the same system, for instance you are running a program written in delphi that will trap keystroke messages being sent to a textbox in a seperate accounting system that may or may not have been written in delphi but is running on the same desktop.  Is that right?


If so I think you'll need to trap things at an API level.  If I wanted to do this then I'd probably create a new class a bit like this...

uses Windows, Messages;

TKeystrokeInterceptor = class
  FPreviousWndProc : Longint;
  FObjectInstance : Pointer;
protected
  procedure ScanMessage(var Message: TMessage);
public
  constructor Create(WindowToTrap: HWND);
  destructor Destroy; override;
end;

implementation

uses Classes;

procedure TKeystrokeInterceptor.ScanMessage(var Message: TMessage);
begin
  if (Message.Msg = WM_KEYDOWN) then
  begin
    <process the message here>
  end
  else
  begin
    asm
       PUSH FPreviousWndProc
       RET
    end;
  end;
end;

constructor Create(WindowToTrap: HWND);
begin
  FPreviousWndProc := GetWindowLong(WindowToTrap, GWL_WNDPROC);
  FObjectInstance := Classes.MakeObjectInstance(ScanMessage);
  SetWindowLong(WindowToTrap, GWL_WNDPROC, FObjectInstance);
end;

destructor Destroy;
begin
  if FObjectInstance <> nil then Classes.FreeObjectInstance(FObjectInstance);  
end;


I don't know if I'm barking up the wrong tree here.  This code probably won't compile as I've written it here, in particular the assembler will probably not work!  However I didn't want to spend too much effort until I was sure I was answering the right question!

Carl
0
 
JimmyJJAuthor Commented:
Hey thnx for you replies. I think you are going the right way :P. Let me clarify what I want... For example, in your normal delphi program you could add a textbox. That textbox has an onchange event so that it is possible to execute code whenever the textbox content is being changed. This is exactly what I want but then for textboxes outside of my program, so for textboxes like notepad or whatever and I guess that is being done by setting a hook on that textbox and trapping the right messages.

Thnx!
0
Technology Partners: 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!

 
carlp42Commented:
I worked on this tonight to perfect the technique.  Here is the improved code...

interface

uses Windows, Messages;

type
  TKeystrokeInterceptor = class
  protected
    FPreviousWndProc: Pointer;
    FObjectInstance: Pointer;
    FTrappedWindow: HWND;
    procedure ScanMessage(var Message: TMessage);
  public
    constructor Create(WindowToTrap: HWND);
    destructor Destroy; override;
  end;

implementation

procedure TKeystrokeInterceptor.ScanMessage(var Message: TMessage);
begin
  if (Message.Msg = WM_CHAR) then
  begin
    if (Message.WParam = Integer('T')) then
      ShowMessage('T sniffed');
    //Message.Result := 0;
  end;
  CallWindowProc(FPreviousWndProc, FTrappedWindow, Message.Msg, Message.WParam, Message.LParam);
end;

constructor TKeystrokeInterceptor.Create(WindowToTrap: HWND);
var
  ErrorCode: Integer;
  Buf: array[Byte] of Char;
begin
  FTrappedWindow := WindowToTrap;
  FPreviousWndProc := Pointer(GetWindowLong(WindowToTrap, GWL_WNDPROC));
  if (FPreviousWndProc = nil) then
  begin
    ErrorCode := GetLastError;
    FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, nil,ErrorCode, LOCALE_USER_DEFAULT, Buf, sizeof(Buf), nil);
    raise Exception.Create(Buf);
  end;
  FObjectInstance := Classes.MakeObjectInstance(ScanMessage);
  if (SetWindowLong(WindowToTrap, GWL_WNDPROC, Longint(FObjectInstance)) = 0) then
  begin
    ErrorCode := GetLastError;
    FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, nil,ErrorCode, LOCALE_USER_DEFAULT, Buf, sizeof(Buf), nil);
    raise Exception.Create(Buf);
  end;
end;

destructor TKeystrokeInterceptor.Destroy;
begin
  SetWindowLong(FTrappedWindow, GWL_WNDPROC, Longint(FPreviousWndProc));
  if FObjectInstance <> nil then Classes.FreeObjectInstance(FObjectInstance);
end;

Apart from correcting syntax mistakes what I added was the destructor resetting the window procedure (oops!), error checking in the constructor and instead of assembler to call the old window proc. I used an API function that I found (much simpler).  And it worked!  Sort of...

To use it you create an instance of the object, passing the window handle to trap.  I created a quick form to test it and used Microsoft Spy++ to find the window handle of edit boxes, etc. that I wanted to test this with.  In my test code I just check for a capital T being pressed.

I was going to turn it into a component, get it to enumerate top-level windows when a method was called, etc. and have an event for handling the key checking so more inexperienced programmers would have a drop in and use component, however I hit a snag...


Windows NT/2000/XP (and we *do* all use those nowadays don't we??) has a security feature where it will not allow you to mess with these callback procedures on any window that is not owned by the process the code is running in.  That means that you can't have a Delphi app trapping keypress messages sent to a textbox in Notepad.exe for instance.

In practice I can trap keys typed into any given edit box, text box, button, etc. in my application but not in another program (tested).

(I suppose the restriction seems sensible really as a security measure, if nothing else.)

Might work on win95/98 but i dont think so.


Anyway I think that means that this method will be no use to you, JimmyJJ, interesting though it was!



I think you will have to follow up the lead from Imthiyaz_ph instead.

Carl
0
 
DragonSlayerCommented:
To be able to use the above callback in WinNT/2k/XP machines, you would need to "inject" your DLL into the process.

Madshi's components (www.madshi.net) will ease this injection. Even if you do not want to use his component, his side offers some good read on this topic: http://help.madshi.net/madCodeHook.htm
0
 
JimmyJJAuthor Commented:
Hey Carl thnx for the code but like you said it was not really what I was looking for. It hooks the keyboard but not the program itself. I already had something to hook the keyboard and then just see what program it is (first post), but that's not the right way.

DragonSlayer, I think that that is what I want. Have you tried installing it though? It's an automatic install and everying is a succes (installed everything), but then when I run delphi and want to compile a demo program it says madcodehook.pas not found.
When I try to install it manually by adding the .bpl in the component window then I see the other components installed (madbasic, maddisasm) but when i add the madecokdehook .bpl it says can't find requested module. Any ideas? Delphi 7.

Thnx!
0
 
DragonSlayerCommented:
Not sure... the auto install works for me (I have the registered version), but the installer is the same for both versions. Try to see if madcodehook.dcu is in your delphi's search path?
0
 
JimmyJJAuthor Commented:
Hey,

where would that be listed?

Thnx.
0
 
DragonSlayerCommented:
Project -> Options -> Directories/Conditionals

At the Search path, enter the directory where the madcode is installed... usually C:\Program Files\madshi\madCodeHook\Delphi X (where X is the version of your Delphi).
0

Featured Post

Industry Leaders: 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!

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