Go Premium for a chance to win a PS4. Enter to Win

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 5731
  • Last Modified:

Detect another app's edit box text change?

I've written some code to give me the text of an edit box in AOL. I would like to detect when a change occurs in that edit box.  Currently I would have to poll the the edit box and compare its current text with the last poll.  However, I'm looking for a more elegant way to do it such as capturing a WM message from the edit box or some similar way.  Any thoughts on how I could do this? Working code sample would be appreciated.

Here's the function I use to obtain the edit box text:

function GetURL:string;
var
ie,toolbar,combo,
comboboxex,edit,
worker,toolbarwindow:hwnd;
begin
   ie := FindWindow(pchar('AOL Frame25'),nil);
  worker := FindWindowEx(ie,0,'AOL Toolbar',nil);     toolbar := FindWindowEx(worker,0,'_AOL_Toolbar',nil);
  comboboxex := FindWindowEx(toolbar, 0, '_AOL_Combobox', nil);
    edit := FindWindowEx(comboboxex,0,'Edit',nil);
    EditHWND:=edit;
    result := GetText(edit);
  end;
0
martin_g
Asked:
martin_g
  • 7
  • 3
  • 2
  • +4
1 Solution
 
nestoruaCommented:
HI,
I think you must use windows hooks.
Sincerely,
Nestorua.
0
 
martin_gAuthor Commented:
That's what I was thinking too.  Do you have an example of what I need to do?  I suppose it will need to be a DLL?
0
 
martin_gAuthor Commented:
That's what I was thinking too.  Do you have an example of what I need to do?  I suppose it will need to be a DLL?
0
Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

 
nestoruaCommented:
Win32 Programmer's reference>Hooks.
Here you'll find all you need with examples.
0
 
PikaCommented:
listening...
0
 
SlavakCommented:
Try this:

const
  LastProc : Pointer = nil;

function WindowProc(H : HWND; Msg : Cardinal; wParam, LParam : Cardinal) : Integer; stdcall;
Begin
  If Msg = CM_TEXTCHANGED Then begin
    //  place your code here

  end;

  Result := CallWindowProc(LastProc, H, Msg, wParam, LParam);
End;

procedure HookWindow(H : HWND);
Begin
  LastProc := Pointer(GetWindowLong(H, GWL_WNDPROC));
  SetWindowLong(H, GWL_WNDPROC, Integer(@WindowProc));
End;

procedure UnHookWindow(H : HWND);
Begin
  SetWindowLong(H, GWL_WNDPROC, Integer(LastProc));
End;
0
 
smurffCommented:
listening
0
 
vterekhCommented:
listening
0
 
martin_gAuthor Commented:
Slavak, since this will be detecting an edit box text change in another app (America On Line), should I put this in a library (DLL)?  Also, when using Winsight, I noticed that the complete text is "posted" with the WM_SETTEXT message. Should I use the WM_SETEXT inseatd of CM_TEXTCHANGED?
Thanks for the help!
0
 
Slick812Commented:
hello martin_g, you want to do something that took some effort for me to do. I wanted to hook when an edit in another app had focus and got text input, I also wanted to get the changes in text. First, I have never been able to get

SetWindowLong(H, GWL_WNDPROC, Integer(@WindowProc));

for any window out of my app's process, which is any control in another program. Next, if you try a Keyboard hook, it will not give you the window's handle that the message is going to, nor will a Journal hook. So you can't tell if the keystoke is for your edit or not. I don't think a Shell hook will give child window focus changes. I used a WH_CBT, a computer based traing hook to get the focus change, and then I used a journal hook to get the select start and the charater to go into my app memo. If you want to see some of this code let me know.
0
 
martin_gAuthor Commented:
The code I posted does work fine.  It's just that I must keep polling the edit box to see if the text has changed.  Is it possible to hook the edit box's WM_SETTEXT message if I already know the edit box's handle? For instance could I use Slavak's code and pass it the handle to the edit box? Slavak - should your code be put in a DLL?

Slick812, thanks, I would like to see some code to get me pointed in the right direction.
0
 
Slick812Commented:
why whould you want to get the WM_SETTEXT message?, it is only used if it is sent as a SendMessage( ) or SetWindowText( ). .  charaters are added from the keyboard with a WM_CHAR message.

\\\\\ Some code

First I will show the code for the Library, it uses a WH_CBT hook, (a WH_CBT will reduce the performance of your computer, since it montitors so many messages and waits for a responce from the hook dll). This dll just gets the HCBT_SETFOCUS code and sends the hooking app a WM_USER message with the control (or app) handle that's getting focus and the Handle that is losing focus.


library Focus44Hook;

uses
  SysUtils, Windows, Messages;

{$R *.RES}

var
Hooked: Boolean;
hKeyHook, hMemFile, hApp: HWND;
PhApp: PInteger;

function CBTFunc(Code, wParam, lParam: Integer): Integer; stdcall;
begin
Result := 0;
CallNextHookEx(hKeyHook, Code, wParam, lParam);
if Code < 0 then
Exit;

if Code = HCBT_SETFOCUS then
  begin
  {this only get's the SETFOCUS notification and sends
  the wParam and lParam to the Hooking Program, hApp}
  if not IsWindow(hApp) then
  begin
  hMemFile := OpenFileMapping(FILE_MAP_WRITE, False, 'Global8v3k');
  PhApp := MapViewOfFile(hMemFile, FILE_MAP_WRITE, 0, 0, 0);
    if PhApp <> nil then
      hApp := PhApp^;
  end;
  PostMessage(hApp, WM_USER+543, wParam, lParam);
  end;
end;

function StartHook(AppHandle: HWND) : Byte; export;
begin
Result := 0;
if Hooked then
  begin
  Result := 1;
  Exit;
  end;
if not IsWindow(AppHandle) then
  begin
  Result := 3;
  Exit;
  end;
hKeyHook := SetWindowsHookEx(WH_CBT, @CBTFunc, hInstance, 0);
if hKeyHook > 0 then
  begin
  {I use a Memory File mapping to get the hApp Handle to all the
  Dlls attatched to all the processes running}
  hMemFile := CreateFileMapping($FFFFFFFF, // $FFFFFFFF gets a page memory file
                nil,                // no security attributes
                PAGE_READWRITE,      // read/write access
                0,                   // size: high 32-bits
                SizeOf(Integer),           // size: low 32-bits
                'Global8v3k');    // name of map object
  PhApp := MapViewOfFile(hMemFile, FILE_MAP_WRITE, 0, 0, 0);
  hApp := AppHandle;
  PhApp^ := AppHandle;
  Hooked := True;
  end else
  Result := 2;
end;

function StopHook: Boolean; export;
begin
if Hooked then
Result := UnhookWindowsHookEx(hKeyHook) else
Result := True;
Hooked := False;
if PhApp <> nil then
  begin
  UnmapViewOfFile(PhApp);
  PhApp := nil;
  end;
CloseHandle(hMemFile);
end;

procedure EntryProc(dwReason : DWORD);
begin
 if (dwReason = Dll_Process_Detach) then
 begin
 UnhookWindowsHookEx(hKeyHook);
 if PhApp <> nil then
   begin
   UnmapViewOfFile(PhApp);
   end;
 CloseHandle(hMemFile);
 end;
end;

exports
  StartHook,
  StopHook;

begin
PhApp := nil;
Hooked := False;
hMemFile := 0;
hKeyHook := 0;
hApp := 0;
DLLProc := @EntryProc;
EntryProc(Dll_Process_Attach);
end.
 


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

Nwxt, the Hooking Program Code. In the hooking Program, I need to get the keystrokes with a Journal Hook. I start the journal hook whenever the hEdit control gets the focus, and the program is notified with a WM_USER message.


private
    { Private declarations }
    hLib3: THandle;
    procedure FocusMessage(var Msg : TMessage); message WM_USER+543;


var
  Form1: TForm1;
  JHook1: THandle;
  Hooked1: Boolean;
  hEdit, Start1, End1, Rus: Integer;



function JorHookProc(Code, wParam: Integer; var EventStrut: TEVENTMSG): Integer; stdcall;
var
KeyState1: TKeyBoardState;
AryChar: Array[0..1] of Char;
Count: Integer;
VirtKey, ScanCode: Cardinal;
begin
Result := CallNextHookEx(JHook1, Code, wParam, Integer(@EventStrut));
if Code < 0 then Exit;
if Code = HC_ACTION then
  begin
  if (EventStrut.paramL = 17923) then {17923 is for Ctrl+Break}
    begin
    Hooked1 := False;
    UnhookWindowsHookEx(JHook1);
    JHook1 := 0;
    end;

  if (EventStrut.message = WM_KEYDOWN) then
    begin
    Rus := SendMessage(hEdit, EM_GETSEL, Start1, End1);
    SendMessage(Form1.Memo1.Handle, EM_SETSEL, LOWORD(Rus), HIWORD(Rus));
    VirtKey := LOBYTE(LOWORD(EventStrut.paramL));
    ScanCode := HIBYTE(LOWORD(EventStrut.paramL));
    GetKeyboardState(KeyState1);
    Count := ToAscii(VirtKey,ScanCode, KeyState1, AryChar, 0);
    if Count = 1 then
    Form1.Memo1.Perform(WM_CHAR, Ord(AryChar[0]), 0);
    end;
  end;
end;


procedure TForm1.ApplicationEvents1Message(var Msg: tagMSG;
  var Handled: Boolean);
begin
{if the Ctrl+Alt+Del  keys are used then the journal hook is canceled
and you need to restart it}
Handled := False;
if (Msg.message = WM_CANCELJOURNAL) and Hooked1 then
  JHook1 := SetWindowsHookEx(WH_JOURNALRECORD , @JorHookProc, hInstance, 0);
end;


procedure TForm1.FormCreate(Sender: TObject);
begin
Hooked1 := False;
end;

procedure TForm1.FocusMessage(var Msg : TMessage);
begin
if (Msg.wParam = hEdit) and not Hooked1 then
  begin
{the wParam has the handle of window to get focus}
  JHook1 := SetWindowsHookEx(WH_JOURNALRECORD , @JorHookProc, hInstance, 0);
  if JHook1 > 0 then
    begin
    Hooked1 := True;
    end else
    begin
    ShowMessage('No Journal Hook availible');
    Hooked := False;
    end;
  end;


if Msg.lParam = hEdit then
  begin
{lParam has handle to lose focus}
  Hooked := False;
  UnhookWindowsHookEx(JHook1);
  JHook1 := 0;
  end;


procedure TForm1.Button_StartFocusHookClick(Sender: TObject);
type
TStartHook = function(AppHandle: HWND): Byte;
hForm, worker, toolbar, comboboxex, hEdit: Cardinal;

var
StartHook1: TStartHook;
SHresult: Byte;
PText: PChar;
Len: Integer;

begin
hForm := FindWindow('AOL Frame25',nil);
if hForm < 32 then
  begin
  ShowMessage('No AOL Frame25, no Hook');
  Exit;
  end;
worker := FindWindowEx(ie,0,'AOL Toolbar',nil);
toolbar := FindWindowEx(worker,0,'_AOL_Toolbar',nil);
comboboxex := FindWindowEx(toolbar, 0, '_AOL_Combobox', nil);
hEdit := FindWindowEx(comboboxex,0,'Edit',nil);

if hEdit < 32 then
  begin
  ShowMessage('No hEdit, no Hook');
  Exit;
  end else
  begin
  Len := GetWindowTextLength(hEdit);
  if Len > 0 then
    begin
    GetMem(PText, Len+1);
    try
      GetWindowText(hEdit, PText, Len+1);
      Memo1.Text := PText;
      finally
      FreeMem(PText);
      end;
    end else
    Memo1.Clear;
  end;
hLib3:=LoadLibrary('Focus44Hook.dll');
@StartHook1 := GetProcAddress(hLib3, 'StartHook');
if @StartHook1 = nil then Exit;
SHresult := StartHook1(Handle);
if SHresult = 0 then ShowMessage('the Key Hook was Started, good');
if SHresult = 1 then ShowMessage('the Key Hook was already Started');
if SHresult = 2 then ShowMessage('the Key Hook can NOT be Started, bad');
if SHresult = 3 then ShowMessage('AppHandle is incorrect');
end;

procedure TForm1.Button_StopFocusHookClick(Sender: TObject);
type
TStopHook = function: Boolean;
var
StopHook1: TStopHook;
begin
@StopHook1 := GetProcAddress(hLib3, 'StopHook');
if @StopHook1 = nil then
    begin
    ShowMessage('Stop Hook Addy not found');
    Exit;
    end;
  if StopHook1 then
  ShowMessage('Hook was stoped');
  FreeLibrary(hLib3);
  FreeLibrary(hLib3);
  if Hooked1 then
  UnhookWindowsHookEx(JHook1);
end;

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

if you haven't done hooks before this may be more than you want to deal with, ask questions if you need more info
0
 
martin_gAuthor Commented:
The WM_SETTTEXT is sent when a user clicks on a link in a webpage or enters text.  When entering text, it occurs when they press the ENTER key, i.e. - when they are finished typing. With this message,hoefully I won't need to track keystrokes.  
0
 
martin_gAuthor Commented:
Thanks for the code Slick812!
Is there a way to avoid tracking keystrokes?  Right now I poll the edit box which is degrades performance.  It looks like tracking keystrokes would be even more of a perfomance hit.
0
 
Slick812Commented:
There's probally hundreds of different ways to do what you want, I gave you some code I used to track keystrokes in another programs edit, and I have no Idea about the relative performance hit's, you can do all the perfomance testing you want, I don't have time to do that. I see that you are dealing with a ComboBox Edit, which is a different set up than the Edit box I was monitoring with mine. If you want to get the "Enter" key, then you could test for

LOBYTE(LOWORD(EventStrut.paramL)) = 13

in the JorHookProc

You might try the WH_CALLWNDPROCRET hook to get the WM_SETTTEXT message. I have not tried to monitor a combo box edit, but it seems that might be a difficult thing to do, if you want all the changes that can happen there.
0
 
martin_gAuthor Commented:
Slick812 - Thanks a  lot for all the advice. I appreciate the extensive source code!  I think I have enough to get started in the right direction.
0

Featured Post

Hire Technology Freelancers with Gigs

Work with freelancers specializing in everything from database administration to programming, who have proven themselves as experts in their field. Hire the best, collaborate easily, pay securely, and get projects done right.

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