?
Solved

Getting text from a richedit control in another application

Posted on 2005-03-17
21
Medium Priority
?
529 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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
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!

 

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

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

Question has a verified solution.

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

This article explains how to create forms/units independent of other forms/units object names in a delphi project. Have you ever created a form for user input in a Delphi project and then had the need to have that same form in a other Delphi proj…
In my programming career I have only very rarely run into situations where operator overloading would be of any use in my work.  Normally those situations involved math with either overly large numbers (hundreds of thousands of digits or accuracy re…
NetCrunch network monitor is a highly extensive platform for network monitoring and alert generation. In this video you'll see a live demo of NetCrunch with most notable features explained in a walk-through manner. You'll also get to know the philos…
Visualize your data even better in Access queries. Given a date and a value, this lesson shows how to compare that value with the previous value, calculate the difference, and display a circle if the value is the same, an up triangle if it increased…
Suggested Courses

764 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