Want to protect your cyber security and still get fast solutions? Ask a secure question today.Go Premium

x
?
Solved

Getting text from a richedit control in another application

Posted on 2005-03-17
21
Medium Priority
?
555 Views
Last Modified: 2013-11-22
Hi, Please take a look at this image to see the richedit I am talking about.
http://geocities.com/oalawneh/daimage.jpg
It is my first time playing with api so I was stuck several times. Nevertheless it is still a source of fun:-).
Now I could get the handle of the parent client and consequently its titlebar text. like this:
--------------------------------------------------------------------------------------------------------
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Button1: TButton;
    Label1: TLabel;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
  DeskTopHwnd, WindowHwnd: Hwnd;
  Buff : array[0..255] of char;
begin
  label1.update;
  DeskTopHwnd := GetDeskTopWindow;
  WindowHwnd := GetWindow(DeskTopHwnd,GW_CHILD);
  While WindowHwnd <> 0 do
  begin
  GetWindowText(WindowHwnd,Buff,255);
  if (Buff <> '') AND (IsWindowVisible(WindowHwnd) <> False) and
  (AnsiContainsText(Buff, 'holdem')=true)
  then
begin
  label1.Caption:=buff;
end;
WindowHwnd := GetWindow(WindowHwnd,GW_HWNDNEXT);
end;
end;
end.
----------------------------------------------------------------------------------------------
Now this works for the parent window. But could not figure out how to get the handle of the richedit in the application client window and consequently getting its text  to place in a memo or a richedit.
Which functions should i use?

0
Comment
Question by:xo310
  • 12
  • 9
21 Comments
 
LVL 11

Accepted Solution

by:
ZhaawZ earned 2000 total points
ID: 13572965
You'll have to get a handle of richedit (through all of it's parents). Can you run a delphi app when a mouse cursor is above that richedit? If yes, here's way how to get all parents (I tried this with clock in taskbar):

procedure TForm1.FormCreate(Sender: TObject);
var
  wnd : hwnd;
  txt : pchar;
begin
wnd := WindowFromPoint(mouse.CursorPos);
GetMem(txt, 256);
repeat
  GetClassName(wnd, txt, 256);
  Memo1.Lines.Append(txt);
  wnd := GetParent(wnd);
until wnd = 0;
FreeMem(txt);
end;



This adds to memo1 all classnames you need. For me it returned:

TrayClockWClass
TrayNotifyWnd
Shell_TrayWnd

This meens that mouse was on TrayClockWClass, its parents are TrayNotifyWnd and Shell_TrayWnd. Now to get TrayClockWClass (and it's text), I use:

var
  wnd : hwnd;
  txt : pchar;
begin
//notice the order of getting handles
wnd := FindWindow('Shell_TrayWnd', nil);
wnd := FindWindowEx(wnd, 0, 'TrayNotifyWnd', nil);
wnd := FindWindowEx(wnd, 0, 'TrayClockWClass', nil);
GetMem(txt, 256);
GetWindowText(wnd, txt, 256);
memo1.Text := txt;
FreeMem(txt);



Try this way and say if it works :)
0
 
LVL 11

Expert Comment

by:ZhaawZ
ID: 13572976
Btw, in that picture it looks like a ListBox, not RichText. For ListBox'es there's some other technique of getting text. You'll have to get every line (probably with SendMessage() function with LB_GETTEXT message to get text of item and LB_GETCOUNT message to get count of items)
0
 
LVL 11

Expert Comment

by:ZhaawZ
ID: 13572994
Here it is:

var
  wnd : hwnd;
  txt : pchar;
  n : integer;
  cnt : integer;
  len : integer;
begin
.....
get a handle of listbox, just the same way as in my first post
.....
cnt := SendMessage(wnd, lb_getcount, 0, 0);
for n := 0 to cnt - 1 do begin
  len := SendMessage(wnd, lb_gettextlen, n, 0);
  GetMem(txt, len + 1);
  SendMessage(wnd, lb_gettext, n, integer(txt));
  Memo1.Lines.Append(txt);
  FreeMem(txt);
end;
end;
0
Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

 

Author Comment

by:xo310
ID: 13573010
zhaawz thank you. Let me try your method and feed you back very soon.
thanks
0
 

Author Comment

by:xo310
ID: 13573132
Zhaawz, I used Greatis WinDowse to know the class name of that control and it said richedit.
I saved the first method in my notes for future use thank you. But what I need is a little bit different.

Once the handle is known, <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/indivcontrol.asp" target="_blank">I know how to get text from it</a>.

to summarize, We know there is a string "dealer:" in the control and it is not present anywhere else in the client window. Can we find the class name and handle of the control without using the mouse? Just as i did with the client window?
Basically, my code detected all running windows then chose the one with the word "holdem" as it is seen from my code. But I could not do the same for the control.

Thanks for your patience:-)
0
 
LVL 11

Expert Comment

by:ZhaawZ
ID: 13573264
About mouse - that's needed only 1 time - to get parents of a control you need. Next time you'll not need a mouse.
0
 
LVL 11

Expert Comment

by:ZhaawZ
ID: 13573292
Is that game downloadable somewhere? If I had it I could try to get needed handle/text...
0
 

Author Comment

by:xo310
ID: 13573312
Here is it:
www.partypoker.com
about 3 MB and you dont have to register to watch tables.
I think your code above was very useful so I am giving the points in advance. Thanks
0
 
LVL 11

Expert Comment

by:ZhaawZ
ID: 13573753
Getting handle of this richedit is not that easy as I though at the beginning :) However, it is not impossible. Here's how I did it :)


var
  wnd : cardinal;
  txt : pchar;
  len : integer;
begin
wnd := 0;
GetMem(txt, 256);
repeat
  wnd := FindWindowEx(0, wnd, '#32770', nil);
  GetWindowText(wnd, txt, 256);
  len := lstrlen(txt);
  if len > 16 then begin
    inc(txt, len - 16);
    if txt = '.  Good Luck  ! ' then begin
      dec(txt, len - 16);
      break;
    end;
    dec(txt, len - 16);
  end;
  if lstrcpyn(txt, txt, 7) = 'Table ' then break;
until wnd = 0;
FreeMem(txt);
if wnd <> 0 then begin
  wnd := FindWindowEx(wnd, 0, 'RICHEDIT', nil);
  //now we have a handle we need :)
end else begin
  ShowMessage('Can''t find a window');
end;
end;
0
 

Author Comment

by:xo310
ID: 13573795
wow, this is great, it is working:-). Am very grateful. Thank you
0
 
LVL 11

Expert Comment

by:ZhaawZ
ID: 13573804
;) Now comes a problem - I couldn't get a text from it... Tried to do like in MSDN is written, but something goes wrong - a game just crashes (I don't know why). Here's what I'm doing:



var
  wnd : cardinal;
  txt : pchar;
  textlen : record {gettextlengthex structure}
    flags : cardinal;
    codepage : cardinal;
  end;
  text : record {gettexthex structure}
    cb : cardinal;
    flags : cardinal;
    codepage : cardinal;
    lpDefaultChar : pchar;
    lpUsedDefChar : cardinal;
  end;
  len : integer;
begin
wnd := 0;
GetMem(txt, 256);
repeat
  wnd := FindWindowEx(0, wnd, '#32770', nil);
  GetWindowText(wnd, txt, 256);
  len := lstrlen(txt);
  if len > 16 then begin
    inc(txt, len - 16);
    if txt = '.  Good Luck  ! ' then begin
      dec(txt, len - 16);
      break;
    end;
    dec(txt, len - 16);
  end;
  if lstrcpyn(txt, txt, 7) = 'Table ' then break;
until wnd = 0;
FreeMem(txt);
if wnd <> 0 then begin
  wnd := FindWindowEx(wnd, 0, 'RICHEDIT', nil);
  textlen.flags := 0 {gtl_default};
  textlen.codepage := cp_acp;
  len := SendMessage(wnd, wm_user + 95 {em_gettextlengthex}, integer(@textlen), 0);
  GetMem(txt, len + 1);
  text.cb := len;
  text.flags := 0 {gt_default};
  text.codepage := cp_acp;
  text.lpDefaultChar := nil;
  text.lpUsedDefChar := 0;
  SendMessage(wnd, wm_user + 94 {em_gettextex}, integer(@text), integer(txt));
  memo1.Text := txt;
  FreeMem(txt);
end else begin
  ShowMessage('Can''t find a window');
end;
end;
0
 
LVL 11

Expert Comment

by:ZhaawZ
ID: 13573819
I think that there's something wrong with using em_gettextex in my source. There are no problems with em_gettextlengthex - it gets the right length (I think) ;)

Btw, getting handle was a little tricky for me ;) have never done it this way before.
0
 

Author Comment

by:xo310
ID: 13573861
tricky but realtively fast:-)
about the em_gettextex crash, I hope it is not delibrately done by the client for security purposes.
They have been trying to make programming bots harder. You know they consider it cheating. However mine is a simple calculator not a bot. I prefered dealing with the rich edit where there was another option which is screen scraping. that would be harder imho. I will try to figure it out and will definitely feed you back on this.
thank you
0
 
LVL 11

Expert Comment

by:ZhaawZ
ID: 13573886
I don't know, maybe they have written their own message processing and are using wm_user+94 message. When I changed text.codepage from cp_acp to 1200 (from ANSI to UNICODE), program didn't crash, but... It restarted :) Don't know why.
0
 
LVL 11

Expert Comment

by:ZhaawZ
ID: 13573940
Found a way :)


var
  wnd : cardinal;
  txt : pchar;
  len : integer;
  loop, lines : integer;
begin
wnd := 0;
GetMem(txt, 256);
repeat
  wnd := FindWindowEx(0, wnd, '#32770', nil);
  GetWindowText(wnd, txt, 256);
  len := lstrlen(txt);
  if len > 16 then begin
    inc(txt, len - 16);
    if txt = '.  Good Luck  ! ' then begin
      dec(txt, len - 16);
      break;
    end;
    dec(txt, len - 16);
  end;
  if lstrcpyn(txt, txt, 7) = 'Table ' then break;
until wnd = 0;
FreeMem(txt);
if wnd <> 0 then begin
  wnd := FindWindowEx(wnd, 0, 'RICHEDIT', nil);
  GetMem(txt, $ff);
  lines := SendMessage(wnd, em_getlinecount, 0, 0);
  for loop := 0 to lines - 1 do begin
    move(#$ff#$00, txt^, 2);
    SendMessage(wnd, em_getline, loop, integer(txt));
    Memo1.Lines.Append(trim(txt));
  end;
  FreeMem(txt);
end else begin
  ShowMessage('Can''t find a window');
end;
end;
0
 

Author Comment

by:xo310
ID: 13578779
man, how can I add more point?:-)
This is working very smoothly. Great job and this thread was a very nice lesson in in api programmin also. I am getting the text now. thank you
0
 
LVL 11

Expert Comment

by:ZhaawZ
ID: 13578995
You can't add points :)
Actually 500 points is also a good "payment" :) Even more - I just answered on your question.

This was "a nice lesson" for me too :) AAArgh! Just love ex-ex because I can learn by answering, heh
0
 

Author Comment

by:xo310
ID: 13580952
the lesson is not finished yet:-) kidding. everything works great.
I just want to learn more about what you did in the move procedure.
what does this mean  GetMem(txt, $ff); ? I mean the $ff
and this move(#$ff#$00, txt^, 2); I mean the #$ff#$00 ?

Should I open a new question for this?
0
 

Author Comment

by:xo310
ID: 13581064
oh ok. found it i think

$ff is 255 and $00 is 0
0
 
LVL 11

Expert Comment

by:ZhaawZ
ID: 13581410
About FreeMem and GetMem... txt is PChar. It's pointer to memory, so we need to allocate memory to work with it. That's why GetMem is needed. FreeMem frees memory when it's not needed anymore.

move() procedure copies data to specified destination. You may also use MoveMemory() to do this. Why did I do that? MSDN said that it's needed :)
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/editcontrols/editcontrolreference/editcontrolmessages/em_getline.asp
Read about lParam parameter.

About #$FF#$00 - that's something like chr(255) + chr(0). If you add $ symbol to some number, you may write it as hexidecimal. If you add # character, it turns to CHAR.
$61 = 97
#97 = #$61 = 'a'
That's if using it with strings.
0

Featured Post

VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Introduction Raise your hands if you were as upset with FireMonkey as I was when I discovered that there was no TListview.  I use TListView in almost all of my applications I've written, and I was not going to compromise by resorting to TStringGrid…
This is an update to some code that someone else posted on Experts Exchange. It is an alternate approach, I think a little easier to use, & makes sure that things like the Task Bar will update.
In a question here at Experts Exchange (https://www.experts-exchange.com/questions/29062564/Adobe-acrobat-reader-DC.html), a member asked how to create a signature in Adobe Acrobat Reader DC (the free Reader product, not the paid, full Acrobat produ…
Screencast - Getting to Know the Pipeline
Suggested Courses

564 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question