Link to home
Start Free TrialLog in
Avatar of xo310
xo310

asked on

Getting text from a richedit control in another application

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?

ASKER CERTIFIED SOLUTION
Avatar of ZhaawZ
ZhaawZ
Flag of Latvia image

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
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)
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;
Avatar of xo310
xo310

ASKER

zhaawz thank you. Let me try your method and feed you back very soon.
thanks
Avatar of xo310

ASKER

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:-)
About mouse - that's needed only 1 time - to get parents of a control you need. Next time you'll not need a mouse.
Is that game downloadable somewhere? If I had it I could try to get needed handle/text...
Avatar of xo310

ASKER

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
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;
Avatar of xo310

ASKER

wow, this is great, it is working:-). Am very grateful. Thank you
;) 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;
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.
Avatar of xo310

ASKER

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
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.
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;
Avatar of xo310

ASKER

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
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
Avatar of xo310

ASKER

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?
Avatar of xo310

ASKER

oh ok. found it i think

$ff is 255 and $00 is 0
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.