Discussion:
Remote control of Delphi app
(too old to reply)
Marc Hillman
2010-03-22 07:28:23 UTC
Permalink
I'm trying to control a Delphi app by remote control from another app
(VB.NET), and I've hit a problem.

I can send keystrokes to the application, but I can only access some of the
program features - those that have a menu item.

I need to press a button on a toolbar, which presents me with two problems
a) how to locate the button, and b) how to simulate a mouse click on it.

I can locate the handle of the window that contains toolbar, and I know the
x-y position of the button in question within this window, but it is a
customisable toolbar, so the position, indeed presence, of the button is
unpredictable. How can I locate the location of a specific button within
this window? The window class of the toolbar is "tdpnpanel"

What messages do I need to send to simulate a mouseclick? I assumed a
WM_LBUTTONOWN followed by a WM_LBUTTONUP would be sufficient, but below is
the entire sequence of messages that occur when I click the button. Are all
these really necessary? Is there a simpler way to simulate a mouse click?


<00095> 000508B8 S WM_NCHITTEST xPos:190 yPos:96
<00096> 000508B8 R WM_NCHITTEST nHittest:HTCLIENT
<00097> 000508B8 S WM_MOUSEACTIVATE hwndTopLevel:00060830 nHittest:HTCLIENT
uMsg:WM_LBUTTONDOWN
<00098> 000508B8 R WM_MOUSEACTIVATE fuActivate:MA_ACTIVATE
<00099> 000508B8 S WM_SETCURSOR hwnd:000508B8 nHittest:HTCLIENT
wMouseMsg:WM_LBUTTONDOWN
<00100> 000508B8 R WM_SETCURSOR fHaltProcessing:False
<00101> 000508B8 P WM_LBUTTONDOWN fwKeys:MK_LBUTTON xPos:190 yPos:12
<00102> 000508B8 P WM_MOUSEMOVE fwKeys:MK_LBUTTON xPos:190 yPos:12
<00103> 000508B8 S WM_NCHITTEST xPos:190 yPos:96
<00104> 000508B8 R WM_NCHITTEST nHittest:HTCLIENT
<00105> 000508B8 P WM_LBUTTONUP fwKeys:0000 xPos:190 yPos:12
<00106> 000508B8 S WM_CAPTURECHANGED hwndNewCapture:00000000
<00107> 000508B8 R WM_CAPTURECHANGED
<00108> 000508B8 P WM_PAINT hdc:00000000
<00109> 000508B8 S WM_PAINT hdc:00000000
<00110> 000508B8 R WM_PAINT
<00111> 000508B8 S WM_PAINT hdc:00000000
<00112> 000508B8 R WM_PAINT
Maarten Wiltink
2010-03-22 08:33:31 UTC
Permalink
I'm trying to control a Delphi app by remote control ...
Your own? If so, you can add something better to it than you're doing now.

Groetjes,
Maarten Wiltink
Marc Hillman
2010-03-22 10:08:07 UTC
Permalink
No - it's not my own app. I have no source.
Post by Maarten Wiltink
I'm trying to control a Delphi app by remote control ...
Your own? If so, you can add something better to it than you're doing now.
Groetjes,
Maarten Wiltink
Skybuck Flying
2010-03-22 17:14:07 UTC
Permalink
Here is whacky idea:

Insert DLL into app.

Replacing a commonly called routine with a stub.

Then when the routine gets executed by the application, push your own
instruction pointer onto the stack.
(As a return address)

Then simply return from the routine...

The retun address will end up in the instruction pointer for the
application.

The trick is to set the return address to the routine that must be executed
inside the application.

This might be sufficient... depends on what the routine actually does and if
it interacts with the events and it's data..
if not then it might work.

So what you would need to do is:

1. Inject DLL.
2. Reverse engineer/disable the application, figure out where the routine
is.
3. Try to figure out where it is in virtual memory... should always be on
the same offset.
4. Place a stub on same routine
5. Put the offset on the stack.
6. Return from routine naturally.

Bye,
Skybuck.
Post by Marc Hillman
I'm trying to control a Delphi app by remote control from another app
(VB.NET), and I've hit a problem.
I can send keystrokes to the application, but I can only access some of
the program features - those that have a menu item.
I need to press a button on a toolbar, which presents me with two problems
a) how to locate the button, and b) how to simulate a mouse click on it.
I can locate the handle of the window that contains toolbar, and I know
the x-y position of the button in question within this window, but it is a
customisable toolbar, so the position, indeed presence, of the button is
unpredictable. How can I locate the location of a specific button within
this window? The window class of the toolbar is "tdpnpanel"
What messages do I need to send to simulate a mouseclick? I assumed a
WM_LBUTTONOWN followed by a WM_LBUTTONUP would be sufficient, but below is
the entire sequence of messages that occur when I click the button. Are
all these really necessary? Is there a simpler way to simulate a mouse
click?
<00095> 000508B8 S WM_NCHITTEST xPos:190 yPos:96
<00096> 000508B8 R WM_NCHITTEST nHittest:HTCLIENT
<00097> 000508B8 S WM_MOUSEACTIVATE hwndTopLevel:00060830
nHittest:HTCLIENT uMsg:WM_LBUTTONDOWN
<00098> 000508B8 R WM_MOUSEACTIVATE fuActivate:MA_ACTIVATE
<00099> 000508B8 S WM_SETCURSOR hwnd:000508B8 nHittest:HTCLIENT
wMouseMsg:WM_LBUTTONDOWN
<00100> 000508B8 R WM_SETCURSOR fHaltProcessing:False
<00101> 000508B8 P WM_LBUTTONDOWN fwKeys:MK_LBUTTON xPos:190 yPos:12
<00102> 000508B8 P WM_MOUSEMOVE fwKeys:MK_LBUTTON xPos:190 yPos:12
<00103> 000508B8 S WM_NCHITTEST xPos:190 yPos:96
<00104> 000508B8 R WM_NCHITTEST nHittest:HTCLIENT
<00105> 000508B8 P WM_LBUTTONUP fwKeys:0000 xPos:190 yPos:12
<00106> 000508B8 S WM_CAPTURECHANGED hwndNewCapture:00000000
<00107> 000508B8 R WM_CAPTURECHANGED
<00108> 000508B8 P WM_PAINT hdc:00000000
<00109> 000508B8 S WM_PAINT hdc:00000000
<00110> 000508B8 R WM_PAINT
<00111> 000508B8 S WM_PAINT hdc:00000000
<00112> 000508B8 R WM_PAINT
Jamie
2010-03-23 00:02:24 UTC
Permalink
Post by Marc Hillman
I'm trying to control a Delphi app by remote control from another app
(VB.NET), and I've hit a problem.
I can send keystrokes to the application, but I can only access some of
the program features - those that have a menu item.
I need to press a button on a toolbar, which presents me with two
problems a) how to locate the button, and b) how to simulate a mouse
click on it.
I can locate the handle of the window that contains toolbar, and I know
the x-y position of the button in question within this window, but it is
a customisable toolbar, so the position, indeed presence, of the button
is unpredictable. How can I locate the location of a specific button
within this window? The window class of the toolbar is "tdpnpanel"
What messages do I need to send to simulate a mouseclick? I assumed a
WM_LBUTTONOWN followed by a WM_LBUTTONUP would be sufficient, but below
is the entire sequence of messages that occur when I click the button.
Are all these really necessary? Is there a simpler way to simulate a
mouse click?
<00095> 000508B8 S WM_NCHITTEST xPos:190 yPos:96
<00096> 000508B8 R WM_NCHITTEST nHittest:HTCLIENT
<00097> 000508B8 S WM_MOUSEACTIVATE hwndTopLevel:00060830
nHittest:HTCLIENT uMsg:WM_LBUTTONDOWN
<00098> 000508B8 R WM_MOUSEACTIVATE fuActivate:MA_ACTIVATE
<00099> 000508B8 S WM_SETCURSOR hwnd:000508B8 nHittest:HTCLIENT
wMouseMsg:WM_LBUTTONDOWN
<00100> 000508B8 R WM_SETCURSOR fHaltProcessing:False
<00101> 000508B8 P WM_LBUTTONDOWN fwKeys:MK_LBUTTON xPos:190 yPos:12
<00102> 000508B8 P WM_MOUSEMOVE fwKeys:MK_LBUTTON xPos:190 yPos:12
<00103> 000508B8 S WM_NCHITTEST xPos:190 yPos:96
<00104> 000508B8 R WM_NCHITTEST nHittest:HTCLIENT
<00105> 000508B8 P WM_LBUTTONUP fwKeys:0000 xPos:190 yPos:12
<00106> 000508B8 S WM_CAPTURECHANGED hwndNewCapture:00000000
<00107> 000508B8 R WM_CAPTURECHANGED
<00108> 000508B8 P WM_PAINT hdc:00000000
<00109> 000508B8 S WM_PAINT hdc:00000000
<00110> 000508B8 R WM_PAINT
<00111> 000508B8 S WM_PAINT hdc:00000000
<00112> 000508B8 R WM_PAINT
You need to learn a little more to do this..

Some of the operations require you to be in the same process space..

for that, You need the process handle, create some virtual space, open
the process so that you can actually get items from it's space and not
yours, move the info into virtual space, close the process from your
space and now you have it..

If you already have the Toolbar handle, then look at the TB_xxxx
messages. Also, the TBBUTTON maybe of some interest to you. For that
contains the CommandID of the button you need. From there, you can
simply send the CommandID via the TB_PRESSBUTTON with the CommandID in
it. You do not need to know where the button is. YOu may want to be
careful with that because in some cases, you will not have a button
visible because its not in the proper time sequence. In other words, not
all menus/buttons are visible and for good reason..

Have fun with the fault generators!
Marc Hillman
2010-03-23 09:21:48 UTC
Permalink
Thanks Jamie - I was unaware of the TB_BUTTON message, but I'm not out of
the woods yet.

I cannot see TB_BUTTON messages in any of the several application windows.
They are all WM_ type. Also I have no idea how to extract the command ID
from the application.

It's a Delphi app, and I have no source code. I understood most of what you
said, but when you said open the process space you started to lose me. Is
there a tool that will allow me to extract the command id's?

I have the toolbar handle, but from the fragment of the windows messages
below
Post by Jamie
Post by Marc Hillman
I'm trying to control a Delphi app by remote control from another app
(VB.NET), and I've hit a problem.
I can send keystrokes to the application, but I can only access some of
the program features - those that have a menu item.
I need to press a button on a toolbar, which presents me with two
problems a) how to locate the button, and b) how to simulate a mouse
click on it.
I can locate the handle of the window that contains toolbar, and I know
the x-y position of the button in question within this window, but it is
a customisable toolbar, so the position, indeed presence, of the button
is unpredictable. How can I locate the location of a specific button
within this window? The window class of the toolbar is "tdpnpanel"
What messages do I need to send to simulate a mouseclick? I assumed a
WM_LBUTTONOWN followed by a WM_LBUTTONUP would be sufficient, but below
is the entire sequence of messages that occur when I click the button.
Are all these really necessary? Is there a simpler way to simulate a
mouse click?
<00095> 000508B8 S WM_NCHITTEST xPos:190 yPos:96
<00096> 000508B8 R WM_NCHITTEST nHittest:HTCLIENT
<00097> 000508B8 S WM_MOUSEACTIVATE hwndTopLevel:00060830
nHittest:HTCLIENT uMsg:WM_LBUTTONDOWN
<00098> 000508B8 R WM_MOUSEACTIVATE fuActivate:MA_ACTIVATE
<00099> 000508B8 S WM_SETCURSOR hwnd:000508B8 nHittest:HTCLIENT
wMouseMsg:WM_LBUTTONDOWN
<00100> 000508B8 R WM_SETCURSOR fHaltProcessing:False
<00101> 000508B8 P WM_LBUTTONDOWN fwKeys:MK_LBUTTON xPos:190 yPos:12
<00102> 000508B8 P WM_MOUSEMOVE fwKeys:MK_LBUTTON xPos:190 yPos:12
<00103> 000508B8 S WM_NCHITTEST xPos:190 yPos:96
<00104> 000508B8 R WM_NCHITTEST nHittest:HTCLIENT
<00105> 000508B8 P WM_LBUTTONUP fwKeys:0000 xPos:190 yPos:12
<00106> 000508B8 S WM_CAPTURECHANGED hwndNewCapture:00000000
<00107> 000508B8 R WM_CAPTURECHANGED
<00108> 000508B8 P WM_PAINT hdc:00000000
<00109> 000508B8 S WM_PAINT hdc:00000000
<00110> 000508B8 R WM_PAINT
<00111> 000508B8 S WM_PAINT hdc:00000000
<00112> 000508B8 R WM_PAINT
You need to learn a little more to do this..
Some of the operations require you to be in the same process space..
for that, You need the process handle, create some virtual space, open
the process so that you can actually get items from it's space and not
yours, move the info into virtual space, close the process from your space
and now you have it..
If you already have the Toolbar handle, then look at the TB_xxxx
messages. Also, the TBBUTTON maybe of some interest to you. For that
contains the CommandID of the button you need. From there, you can simply
send the CommandID via the TB_PRESSBUTTON with the CommandID in it. You do
not need to know where the button is. YOu may want to be careful with that
because in some cases, you will not have a button
visible because its not in the proper time sequence. In other words, not
all menus/buttons are visible and for good reason..
Have fun with the fault generators!
Jamie
2010-03-24 01:35:34 UTC
Permalink
Post by Marc Hillman
Thanks Jamie - I was unaware of the TB_BUTTON message, but I'm not out
of the woods yet.
I cannot see TB_BUTTON messages in any of the several application
windows. They are all WM_ type. Also I have no idea how to extract the
command ID from the application.
It's a Delphi app, and I have no source code. I understood most of what
you said, but when you said open the process space you started to lose
me. Is there a tool that will allow me to extract the command id's?
I have the toolbar handle, but from the fragment of the windows messages
below
First of all, TB_BUTTON does not exist, it's not a message, it's a
Struct/Record object that contains information about a button.

This Record needs to be filled via one of the other TB_xxxxx messages
that gets sent directly to the Toolbar via a
"SendMessage(TollBarBHandle,TB_GETBUTTON,IndexOfBUtton, LpTBBUTTON)";

If you use the TB_BUTTONCOUNT, which is a message you send directly to
the contol, it'll report the number of buttons in the control..

By enumerating through the buttons to locate the desire button via
the text of it, for example, you can then find the IDcommand of that button.

All of this is find and dandy, how ever, you need to jump into the
process space of that app and transfer these returned items that have
pointer information, into a virtual piece of memory. This memory you can
access from your own app

First you need to get the processID>.

GetWindowThreadProcessId(H,@PID); //H is the handle of the window
that contains the control
//Open the process so that memory moves ends on your turf.

Ph := OpenProcess(PROCESS_VM_OPERATION or PROCESS_VM_READ or
PROCESS_VM_WRITE,false, PID); //PID is a cardinal/unsigned 32.

//Create a Virtual Page that the remote process can write too.

GM:=
VirtualAllocEx(PH,Pointer(0),$0400,MEM_COMMIT,PAGE_READWRITE);//$0400 is
the size of the page. should be on event boundaries. but It may not
matter these days.

// Now use the "SendMessage" messages to obtain the information you
need..
Example
SizeOfSTring :=
SendMessage(TheToolBarHandle,TB_GETBUTTONTEXT,IndexOfBUtton,
Integer(GM); // pass the virtual pointer as the destination memory chunk.

//To get this information into your app for use..

ReadProcessMemory(PH, GM, Pointer(Char String Var), SizeOFstring,Var
forNumberOFBytesRead);

//Now the "Char String VAR" will contain the text of the button.

// Use this method for obtaining the info you need via the TB_xxxx
messages to get the BUtton COUnt, and ButtinIDS (TBBUTTON) that you can
then directly send the ButtonID to press the button.

When done with this search and seek code which you only need to obtain
the CommandID's on inital start up, you do this.

VirtualFreeEx(PH, GM,0,MEM_RELEASE);

That will release the Virtual memory made for the remote process (PH)
referred too via the GM handle....
// next , close the process

CloseHandle(PH);


I know that's slopping but it's the best I could do with my current
notes I have here hacked from apps I've wrote and things off the top of
my head and 3 beers in me!

Have a good day!
Marc Hillman
2010-03-24 07:14:10 UTC
Permalink
Many thanks Jamie. A "ButtonCount = SendMessage(oziZoomWindow,
TB_BUTTONCOUNT, 0, 0)" returns 0, so I suspect there aren't any buttons
there. The class of the 'toolbar' is tdpnpanel, which I don't recognise as a
standard Delphi one, so I suspect the developer has hand-crafted something.
I might be screwed.
Post by Jamie
Post by Marc Hillman
Thanks Jamie - I was unaware of the TB_BUTTON message, but I'm not out of
the woods yet.
I cannot see TB_BUTTON messages in any of the several application
windows. They are all WM_ type. Also I have no idea how to extract the
command ID from the application.
It's a Delphi app, and I have no source code. I understood most of what
you said, but when you said open the process space you started to lose
me. Is there a tool that will allow me to extract the command id's?
I have the toolbar handle, but from the fragment of the windows messages
below
First of all, TB_BUTTON does not exist, it's not a message, it's a
Struct/Record object that contains information about a button.
This Record needs to be filled via one of the other TB_xxxxx messages
that gets sent directly to the Toolbar via a
"SendMessage(TollBarBHandle,TB_GETBUTTON,IndexOfBUtton, LpTBBUTTON)";
If you use the TB_BUTTONCOUNT, which is a message you send directly to
the contol, it'll report the number of buttons in the control..
By enumerating through the buttons to locate the desire button via the
text of it, for example, you can then find the IDcommand of that button.
All of this is find and dandy, how ever, you need to jump into the
process space of that app and transfer these returned items that have
pointer information, into a virtual piece of memory. This memory you can
access from your own app
First you need to get the processID>.
that contains the control
//Open the process so that memory moves ends on your turf.
Ph := OpenProcess(PROCESS_VM_OPERATION or PROCESS_VM_READ or
PROCESS_VM_WRITE,false, PID); //PID is a cardinal/unsigned 32.
//Create a Virtual Page that the remote process can write too.
GM:=
VirtualAllocEx(PH,Pointer(0),$0400,MEM_COMMIT,PAGE_READWRITE);//$0400 is
the size of the page. should be on event boundaries. but It may not matter
these days.
// Now use the "SendMessage" messages to obtain the information you
need..
Example
SizeOfSTring :=
SendMessage(TheToolBarHandle,TB_GETBUTTONTEXT,IndexOfBUtton, Integer(GM);
// pass the virtual pointer as the destination memory chunk.
//To get this information into your app for use..
ReadProcessMemory(PH, GM, Pointer(Char String Var), SizeOFstring,Var
forNumberOFBytesRead);
//Now the "Char String VAR" will contain the text of the button.
// Use this method for obtaining the info you need via the TB_xxxx
messages to get the BUtton COUnt, and ButtinIDS (TBBUTTON) that you can
then directly send the ButtonID to press the button.
When done with this search and seek code which you only need to obtain
the CommandID's on inital start up, you do this.
VirtualFreeEx(PH, GM,0,MEM_RELEASE);
That will release the Virtual memory made for the remote process (PH)
referred too via the GM handle....
// next , close the process
CloseHandle(PH);
I know that's slopping but it's the best I could do with my current
notes I have here hacked from apps I've wrote and things off the top of my
head and 3 beers in me!
Have a good day!
Jamie
2010-03-26 00:48:07 UTC
Permalink
Post by Marc Hillman
Many thanks Jamie. A "ButtonCount = SendMessage(oziZoomWindow,
TB_BUTTONCOUNT, 0, 0)" returns 0, so I suspect there aren't any buttons
there. The class of the 'toolbar' is tdpnpanel, which I don't recognise
as a standard Delphi one, so I suspect the developer has hand-crafted
something. I might be screwed.
I suppose that could be true..

Have you tried using the EnumChildWIndows with in that Menu panel?

You may find hidden buttons or at least get the class to come other
controls.
Marc Hillman
2010-03-26 12:52:53 UTC
Permalink
No child windows.

Another quick question - Is there a default font in Delphi? What is the most
likely proportional font for a developer to use in Delphi?
Post by Jamie
Post by Marc Hillman
Many thanks Jamie. A "ButtonCount = SendMessage(oziZoomWindow,
TB_BUTTONCOUNT, 0, 0)" returns 0, so I suspect there aren't any buttons
there. The class of the 'toolbar' is tdpnpanel, which I don't recognise
as a standard Delphi one, so I suspect the developer has hand-crafted
something. I might be screwed.
I suppose that could be true..
Have you tried using the EnumChildWIndows with in that Menu panel?
You may find hidden buttons or at least get the class to come other
controls.
Jamie
2010-03-26 22:01:22 UTC
Permalink
Post by Marc Hillman
No child windows.
Another quick question - Is there a default font in Delphi? What is the
most likely proportional font for a developer to use in Delphi?
The System font, most of the time, is what's used.

And if they were to change it, I can't read the minds of others.

I tend to use some kind of RASTER font for things that need to be
formatted in a list and such!

The GUI preferences aren't any different than in VB, I would think?
Continue reading on narkive:
Loading...