-
Aug 9th, 2013, 12:43 PM
#1
Thread Starter
Frenzied Member
Invoking external application form's button event.
Is there a way to A. Determine what controls an external application's current form contains and B. invoke their events?
Thanks,
Justin
-
Aug 9th, 2013, 01:25 PM
#2
Re: Invoking external application form's button event.
Yes, but it involves API and most likely will involve a modest familiarity with Spy++ (which comes with VS, though perhaps not all versions). Spy++ gives you two main tools: A little crosshairs tool that looks really cool and seems really useful....but isn't. It's still fun to play with, and is a good way to start out. The other tool is a window that shows information about all existing windows. Every control is a window. If you have the program up and running when you run Spy++, you can probably identify the groups of nested windows that make up the form you want. Figuring out which of the sub controls is the button you want is a bit trickier. Opening and closing the program while Spy++ is running is a fairly easy way to track down the form you want to see. The crosshairs tool, when moved over a form, shows you a bit of information about the window it is over, but that information often isn't all that useful, though it may help you identify things in the list of windows.
Once you have that information, open and close the program a few more times and see how windows handles change, and how things don't change. That will be the key to the ultimate task: Getting the windows handle for the button. This won't be a constant value, though, so what you'll be doing is getting the relative position of the button in the tree of windows that makes up the form.
The next step I remember, but I don't remember the actual API calls. It's something like GetWindow and FindWindowEx. This step is painful, at first, but it isn't all that hard. One of those returns the first handle that matches the search criteria. If you are looking for the Nth item, you have to call the method N-1 times, each time passing in the return from the previous call so that the search begins right after that point. The result is often a series of loops that drills down through the tree of windows that makes up the form until you get to the right handle. Spy++ gives you the tree of windows, so you can use that in debugging and during design, to see that you are getting the right control window handle in the end. Once you have that, you can read the text of the control.
My usual boring signature: Nothing
-
Aug 9th, 2013, 02:14 PM
#3
Thread Starter
Frenzied Member
Re: Invoking external application form's button event.
So once you have the handle of the button, how would you then PerformClick() on it?
Thanks for the reply : )
Justin
** I tried:
Code:
Button b = (Button)Control.FromHandle(buttonhandle);
b.PerformClick();
But, b is null. (buttonhandle = 5114478)
** also tried:
Code:
const int BM_CLICK = 0x00F5;
SendMessage(button, BM_CLICK, IntPtr.Zero, IntPtr.Zero);
No luck : (.
Last edited by MonkOFox; Aug 9th, 2013 at 02:29 PM.
-
Aug 9th, 2013, 02:42 PM
#4
Thread Starter
Frenzied Member
Re: Invoking external application form's button event.
Ok so here is my code:
C# Code:
IntPtr hwnd = FindWindow(null, "Decal Agent 2.9.7.5");
List<IntPtr> list = Windows.GetChildWindows(hwnd);
IntPtr button = IntPtr.Zero;
foreach (IntPtr wnd in list)
{
IntPtr closebutton = FindWindowEx(hwnd, IntPtr.Zero, "Button", "Close");
if (closebutton != IntPtr.Zero)
{
button = closebutton;
break;
}
}
/*Public Const WM_LBUTTONDOWN = &H201
Public Const WM_LBUTTONUP = &H202*/
const int WM_LBUTTONDOWN = 0x201;
const int WM_LBUTTONUP = 0x202;
const int BM_CLICK = 0x00F5;
// this didn't work either.
SendMessage(button, WM_LBUTTONDOWN , IntPtr.Zero, IntPtr.Zero);
System.Threading.Thread.Sleep(250);
SendMessage(button, WM_LBUTTONUP, IntPtr.Zero, IntPtr.Zero);
Here is the GetChildWindows method:
C# Code:
class Windows
{
[DllImport("user32")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumChildWindows(IntPtr window, EnumWindowProc callback, IntPtr i);
/// <summary>
/// Returns a list of child windows
/// </summary>
/// <param name="parent">Parent of the windows to return</param>
/// <returns>List of child windows</returns>
public static List<IntPtr> GetChildWindows(IntPtr parent)
{
List<IntPtr> result = new List<IntPtr>();
GCHandle listHandle = GCHandle.Alloc(result);
try
{
EnumWindowProc childProc = new EnumWindowProc(EnumWindow);
EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(listHandle));
}
finally
{
if (listHandle.IsAllocated)
listHandle.Free();
}
return result;
}
/// <summary>
/// Callback method to be used when enumerating windows.
/// </summary>
/// <param name="handle">Handle of the next window</param>
/// <param name="pointer">Pointer to a GCHandle that holds a reference to the list to fill</param>
/// <returns>True to continue the enumeration, false to bail</returns>
private static bool EnumWindow(IntPtr handle, IntPtr pointer)
{
GCHandle gch = GCHandle.FromIntPtr(pointer);
List<IntPtr> list = gch.Target as List<IntPtr>;
if (list == null)
{
throw new InvalidCastException("GCHandle Target could not be cast as List<IntPtr>");
}
list.Add(handle);
// You can modify this to check to see if you want to cancel the operation, then return a null here
return true;
}
/// <summary>
/// Delegate for the EnumChildWindows method
/// </summary>
/// <param name="hWnd">Window handle</param>
/// <param name="parameter">Caller-defined variable; we use it for a pointer to our list</param>
/// <returns>True to continue enumerating, false to bail.</returns>
public delegate bool EnumWindowProc(IntPtr hWnd, IntPtr parameter);
}
-
Aug 9th, 2013, 02:45 PM
#5
Thread Starter
Frenzied Member
Re: Invoking external application form's button event.
OK so evidently the most parent window has to be the active form. Let me try that first.
-
Aug 9th, 2013, 03:08 PM
#6
Thread Starter
Frenzied Member
Re: Invoking external application form's button event.
When using spy++ I'm logging the messages for the control but nothing is coming up when I click the button?
Justin
-
Aug 9th, 2013, 03:35 PM
#7
Thread Starter
Frenzied Member
Re: Invoking external application form's button event.
So my handle that I get from GetChildWindows (the first one) is 1115048 in my application. But in Spy++ it's 001103A8. Because when I use FindWindowEx with (hwnd, intptr.zero, "Button", "Close") I get the same return pointer that is in the 0 position of list.
What gives?
SendMessage won't work for the button and SetActiveWindow won't work on hwnd. I'm lost at this point.
-
Aug 9th, 2013, 05:11 PM
#8
Re: Invoking external application form's button event.
1115048 is 001103A8. The first is decimal, the second is hex. So you are getting the same thing both times, just in different representations of the integer value. That doesn't matter.
As for the rest, some of that gets into things I'm not so familiar with, but I was answering what appears to have been the wrong question. That drilling down is what you need to do to read the information from a control. When it comes to SendMessage, that doesn't work the same way. That's for putting a message into the message queue for the process. I don't believe that the HWND argument for SendMessage is the HWND for the button, in that case, but the HWND for the form itself. I haven't dealt with SendMessage, though, or anything particularly close to that.
My usual boring signature: Nothing
-
Aug 10th, 2013, 02:26 AM
#9
Re: Invoking external application form's button event.
Ok, here's a VB.Net that works for the Calculator window.
Code:
Public Class Form1
Private Declare Auto Function FindWindow Lib "user32.dll" ( _
ByVal lpClassName As String, _
ByVal lpWindowName As String _
) As IntPtr
Private Declare Auto Function FindWindowEx Lib "user32.dll" ( _
ByVal hwndParent As IntPtr, _
ByVal hwndChildAfter As IntPtr, _
ByVal lpszClass As String, _
ByVal lpszWindow As String _
) As IntPtr
Declare Auto Function SendMessage Lib "user32.dll" ( _
ByVal hWnd As IntPtr, _
ByVal msg As Integer, _
ByVal wParam As IntPtr, _
ByVal lParam As IntPtr _
) As IntPtr
Private Const BM_CLICK = &HF5
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim hWnd As IntPtr = FindWindow(Nothing, "Calculator")
If hWnd.Equals(IntPtr.Zero) Then
Return
End If
Dim hwndButton As IntPtr = FindWindowEx(hWnd, 0, Nothing, "9")
Dim retval As Long = SendMessage(hwndButton, BM_CLICK, Nothing, Nothing)
End Sub
End Class
Last edited by dee-u; Aug 10th, 2013 at 02:55 AM.
-
Aug 10th, 2013, 09:42 AM
#10
Re: Invoking external application form's button event.
I don't believe that the HWND argument for SendMessage is the HWND for the button, in that case, but the HWND for the form itself.
SendMessage requires the handle of the window you're sending the message to (unsurprisingly) so it would be the button (as illustrated in dee-u's code) as that's what we want to click. Use the form's handle and you get a form click.
I haven't dealt with SendMessage
No sane person would! It's ok for the simple things but parameters, especially those that are structures (which an awful lot are) are a nightmare to handle in VB. And it's not aided by MSDN's failure to give values to message constants which means you have to scrabble about t'Interwebs piecing together lists for yourself. Consider yourself better off out of the chaos!
As the 6-dimensional mathematics professor said to the brain surgeon, "It ain't Rocket Science!"
Reviews: "dunfiddlin likes his DataTables" - jmcilhinney
Please be aware that whilst I will read private messages (one day!) I am unlikely to reply to anything that does not contain offers of cash, fame or marriage!
-
Aug 12th, 2013, 10:52 AM
#11
Thread Starter
Frenzied Member
Re: Invoking external application form's button event.
I figured it out. It was working all along. I just thought it would physically move the mouse but didn't. I'll post the code tonight when I get home. Spy++ is a must. <-- New slogan?
Justin
-
Aug 12th, 2013, 11:29 AM
#12
Re: Invoking external application form's button event.
Slogan, perhaps, but hardly new! Why I wuz usin' Spy++ before you wuz out of diapers, sonny!
As the 6-dimensional mathematics professor said to the brain surgeon, "It ain't Rocket Science!"
Reviews: "dunfiddlin likes his DataTables" - jmcilhinney
Please be aware that whilst I will read private messages (one day!) I am unlikely to reply to anything that does not contain offers of cash, fame or marriage!
-
Aug 12th, 2013, 12:21 PM
#13
Thread Starter
Frenzied Member
Re: Invoking external application form's button event.
Originally Posted by dunfiddlin
Slogan, perhaps, but hardly new! Why I wuz usin' Spy++ before you wuz out of diapers, sonny!
Haha
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|