Results 1 to 13 of 13

Thread: Invoking external application form's button event.

  1. #1

    Thread Starter
    Frenzied Member
    Join Date
    Feb 2008
    Location
    Texas
    Posts
    1,288

    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
    You down with OOP? Yeah you know me!
    MCAD and MCMICKEYMOUSE (vb.net)

    ----

    If it even kinda helps... rate it : )

    Edit a Multi-page .tif file and save.

  2. #2
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    39,041

    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

  3. #3

    Thread Starter
    Frenzied Member
    Join Date
    Feb 2008
    Location
    Texas
    Posts
    1,288

    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.
    You down with OOP? Yeah you know me!
    MCAD and MCMICKEYMOUSE (vb.net)

    ----

    If it even kinda helps... rate it : )

    Edit a Multi-page .tif file and save.

  4. #4

    Thread Starter
    Frenzied Member
    Join Date
    Feb 2008
    Location
    Texas
    Posts
    1,288

    Re: Invoking external application form's button event.

    Ok so here is my code:

    C# Code:
    1. IntPtr hwnd = FindWindow(null, "Decal Agent 2.9.7.5");
    2.             List<IntPtr> list = Windows.GetChildWindows(hwnd);
    3.  
    4.             IntPtr button = IntPtr.Zero;
    5.             foreach (IntPtr wnd in list)
    6.             {
    7.                 IntPtr closebutton = FindWindowEx(hwnd, IntPtr.Zero, "Button", "Close");
    8.                 if (closebutton != IntPtr.Zero)
    9.                 {
    10.                     button = closebutton;
    11.                     break;
    12.                 }
    13.             }
    14.  
    15.  
    16.             /*Public Const WM_LBUTTONDOWN = &H201
    17.             Public Const WM_LBUTTONUP = &H202*/
    18.  
    19.             const int WM_LBUTTONDOWN = 0x201;
    20.             const int WM_LBUTTONUP   = 0x202;
    21.             const int BM_CLICK = 0x00F5;
    22.  
    23.             // this didn't work either.
    24.             SendMessage(button, WM_LBUTTONDOWN , IntPtr.Zero, IntPtr.Zero);
    25.             System.Threading.Thread.Sleep(250);
    26.             SendMessage(button, WM_LBUTTONUP, IntPtr.Zero, IntPtr.Zero);

    Here is the GetChildWindows method:

    C# Code:
    1. class Windows
    2.     {
    3.  
    4.         [DllImport("user32")]
    5.         [return: MarshalAs(UnmanagedType.Bool)]
    6.         public static extern bool EnumChildWindows(IntPtr window, EnumWindowProc callback, IntPtr i);
    7.  
    8.         /// <summary>
    9.         /// Returns a list of child windows
    10.         /// </summary>
    11.         /// <param name="parent">Parent of the windows to return</param>
    12.         /// <returns>List of child windows</returns>
    13.         public static List<IntPtr> GetChildWindows(IntPtr parent)
    14.         {
    15.             List<IntPtr> result = new List<IntPtr>();
    16.             GCHandle listHandle = GCHandle.Alloc(result);
    17.             try
    18.             {
    19.                 EnumWindowProc childProc = new EnumWindowProc(EnumWindow);
    20.                 EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(listHandle));
    21.             }
    22.             finally
    23.             {
    24.                 if (listHandle.IsAllocated)
    25.                     listHandle.Free();
    26.             }
    27.             return result;
    28.         }
    29.  
    30.         /// <summary>
    31.         /// Callback method to be used when enumerating windows.
    32.         /// </summary>
    33.         /// <param name="handle">Handle of the next window</param>
    34.         /// <param name="pointer">Pointer to a GCHandle that holds a reference to the list to fill</param>
    35.         /// <returns>True to continue the enumeration, false to bail</returns>
    36.         private static bool EnumWindow(IntPtr handle, IntPtr pointer)
    37.         {
    38.             GCHandle gch = GCHandle.FromIntPtr(pointer);
    39.             List<IntPtr> list = gch.Target as List<IntPtr>;
    40.             if (list == null)
    41.             {
    42.                 throw new InvalidCastException("GCHandle Target could not be cast as List<IntPtr>");
    43.             }
    44.             list.Add(handle);
    45.             //  You can modify this to check to see if you want to cancel the operation, then return a null here
    46.             return true;
    47.         }
    48.  
    49.         /// <summary>
    50.         /// Delegate for the EnumChildWindows method
    51.         /// </summary>
    52.         /// <param name="hWnd">Window handle</param>
    53.         /// <param name="parameter">Caller-defined variable; we use it for a pointer to our list</param>
    54.         /// <returns>True to continue enumerating, false to bail.</returns>
    55.         public delegate bool EnumWindowProc(IntPtr hWnd, IntPtr parameter);
    56.  
    57.     }
    You down with OOP? Yeah you know me!
    MCAD and MCMICKEYMOUSE (vb.net)

    ----

    If it even kinda helps... rate it : )

    Edit a Multi-page .tif file and save.

  5. #5

    Thread Starter
    Frenzied Member
    Join Date
    Feb 2008
    Location
    Texas
    Posts
    1,288

    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.
    You down with OOP? Yeah you know me!
    MCAD and MCMICKEYMOUSE (vb.net)

    ----

    If it even kinda helps... rate it : )

    Edit a Multi-page .tif file and save.

  6. #6

    Thread Starter
    Frenzied Member
    Join Date
    Feb 2008
    Location
    Texas
    Posts
    1,288

    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
    You down with OOP? Yeah you know me!
    MCAD and MCMICKEYMOUSE (vb.net)

    ----

    If it even kinda helps... rate it : )

    Edit a Multi-page .tif file and save.

  7. #7

    Thread Starter
    Frenzied Member
    Join Date
    Feb 2008
    Location
    Texas
    Posts
    1,288

    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.
    You down with OOP? Yeah you know me!
    MCAD and MCMICKEYMOUSE (vb.net)

    ----

    If it even kinda helps... rate it : )

    Edit a Multi-page .tif file and save.

  8. #8
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    39,041

    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

  9. #9
    Software Carpenter dee-u's Avatar
    Join Date
    Feb 2005
    Location
    Pinas
    Posts
    11,123

    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.
    Regards,


    As a gesture of gratitude please consider rating helpful posts. c",)

    Some stuffs: Mouse Hotkey | Compress file using SQL Server! | WPF - Rounded Combobox | WPF - Notify Icon and Balloon | NetVerser - a WPF chatting system

  10. #10
    PowerPoster dunfiddlin's Avatar
    Join Date
    Jun 2012
    Posts
    8,245

    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!

  11. #11

    Thread Starter
    Frenzied Member
    Join Date
    Feb 2008
    Location
    Texas
    Posts
    1,288

    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
    You down with OOP? Yeah you know me!
    MCAD and MCMICKEYMOUSE (vb.net)

    ----

    If it even kinda helps... rate it : )

    Edit a Multi-page .tif file and save.

  12. #12
    PowerPoster dunfiddlin's Avatar
    Join Date
    Jun 2012
    Posts
    8,245

    Re: Invoking external application form's button event.

    New slogan?
    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!

  13. #13

    Thread Starter
    Frenzied Member
    Join Date
    Feb 2008
    Location
    Texas
    Posts
    1,288

    Re: Invoking external application form's button event.

    Quote Originally Posted by dunfiddlin View Post
    Slogan, perhaps, but hardly new! Why I wuz usin' Spy++ before you wuz out of diapers, sonny!
    Haha
    You down with OOP? Yeah you know me!
    MCAD and MCMICKEYMOUSE (vb.net)

    ----

    If it even kinda helps... rate it : )

    Edit a Multi-page .tif file and save.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width