2

I am looking for a way to click a button programmatically (Winform). I have a form (say Form2) and button (Button2) of my project. When I click my button it should click Button1 (which is in another project), but both are in same solution. This is what I tried to do:

private void button2(object sender, EventArgs e)
{

    System.IntPtr hwnd;

    System.IntPtr hwndChild = IntPtr.Zero;

    hwnd = FindWindow(null, "form1");

    if (hwnd.Equals(IntPtr.Zero))
    {
     MessageBox.Show("Couldn't find the form");
    }
    else
    {
    hwndChild = FindWindowEx((IntPtr)hwnd, IntPtr.Zero, null, "BUTTON1");

    if(!hwndChild.Equals(IntPtr.Zero))
    SendMessage(hwndChild, BM_CLICK, IntPtr.Zero, IntPtr.Zero);
    }
}

Should I use any other approach like reflection instead of this?

P.S: Button1 is private and no changes can be made to that function/project

Cœur
  • 37,241
  • 25
  • 195
  • 267
sjohnson
  • 315
  • 1
  • 3
  • 7
  • 3
    does `button2(null,null);` work? – maraaaaaaaa Jan 31 '17 at 15:31
  • The button you want to click, is it in another process? Does BUTTON1 belong to a different program than the one that is trying to click it? – 15ee8f99-57ff-4f92-890c-b56153 Jan 31 '17 at 15:34
  • 3
    If its within your process you should instead invoke your business logic rather than going via your UI. If its another process, use Microsoft UIA –  Jan 31 '17 at 15:35
  • Expose the button click event on Form1 to the other project and fire it, rather than this unsafe window pointer method. – Chris Pickford Jan 31 '17 at 15:38
  • Button1 and Button2 are in two different process. I am not supposed to make any changes to the code containing button1. – sjohnson Jan 31 '17 at 15:40
  • 2
    @sjohnson - tell whoever gave you this work to go back to the design phase and walk away – Chris Pickford Jan 31 '17 at 15:43
  • 1
    "If the button is in a dialog box and the dialog box is not active, the BM_CLICK message might fail. To ensure success in this situation, call the SetActiveWindow function to activate the dialog box before sending the BM_CLICK message to the button." Have you checked if you have the correct IDs and if the correct events fire in Spy++? – Luaan Jan 31 '17 at 15:44
  • 1
    I would seriously read up on design patterns to avoid what you are trying to do here. Rather than "clicking" button2 in another project, what you are trying to do is perform the action that that button does. You should seperate your UI code from your business logic. Google "MVC Design Pattern" or "MVVM Design Pattern". Either one will work for what you are trying to do. Your controller action from Project1 can call an Action method on the controller in Project2, without having to fake a button click – Jon Jan 31 '17 at 16:31
  • @Mangist in that case, the so called actions (methods) that are called are private. How can I access it ? can reflection be used? – sjohnson Jan 31 '17 at 20:23
  • @sjohnson you would not call private methods but you can expose an interface (your controller could implement an interface) and those methods would be public. Another option is to use an event broker or publisher/subscription design pattern where your controller in ProjectB would subscribe to a message, and your ProjectA would send the message. ProjectA -> Sends Message -> Received by Subscribers -> ProjectB. Read more on this here https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern That would be more of a loosely coupled solution – Jon Jan 31 '17 at 20:31
  • Adding to Mangist's comment, essentially you _could_ use any number of IPC techniques but why add all that code? Just use Windows UIA. That's what's its for –  Feb 01 '17 at 00:23
  • @MickyD I'm not sure this case is what UIA is for. Where Applicable By using UI Automation and following accessible design practices, developers can make applications running on Windows more accessible to many people with vision, hearing, or motion disabilities. Also, UI Automation is specifically designed to provide robust functionality for automated testing scenarios. .... sounds like it is for UI accessibility and testing. He is trying to have one project communicate with another project in the same solution. If one of the developers changed the UI in ProjectB, UIA would break – Jon Feb 01 '17 at 15:42
  • @Mangist As I said, if you want to go the UI way, use UIA. You are incorrect in thinking it is just for _"testing"_. It is an OO approach for manipulating UIs instead of mucking about with fiddly control handles and sending `BN_CLICK` notifications keeping your fingers crossed in hoping that it works. Otherwise use any number of IPC techniques as I said –  Feb 01 '17 at 16:09

2 Answers2

4

OP:

I am looking for a way to click a button programatically (Winform).

Button1 and Button2 are in two different process. I am not supposed to make any changes to the code containing button1.

Sure, the easiest thing to do that does not require any changes to the target app is to use Windows UI Automation. UIA essentially allows you to automate another app from the GUI perspective (instead of say behind the scenes via COM/Ole Automation) regardless of whether you just want to drive another app, say click a button, or perform full-on automation testing in QA.

MSDN:

Microsoft UI Automation is an accessibility framework that enables Windows applications to provide and consume programmatic information about user interfaces (UIs). more...

...and:

UIA can be used to automate native windows applications , WPF applications as well as Silverlight applications. more...

This code will click a button containing the text "Click Me" in a window captioned "Target Demo". Note I do not make any reference to any particular object type; callback or GUI technology (this works for native; WinForms; WPF and web)

//
// This is the handler in the ButtonClicker app, the app that is doing the clicking
//
private void go_Click(object sender, EventArgs e)
{
    // Look for a top-level window/application called "Target Demo"
    var root = AutomationElement.RootElement;
    var condition1 = new PropertyCondition(AutomationElement.NameProperty, "Target Demo");

    var treeWalker = new TreeWalker(condition1);
    AutomationElement element = treeWalker.GetFirstChild(root);
    
    if (element == null)
    {
        // not found
        return;
    }

    // great!  window found
    var window = element;

    // now look for a button with the text "Click Me"
    var buttonElement =window.FindFirst(TreeScope.Children, 
        new PropertyCondition(AutomationElement.NameProperty, "Click Me"));
    if (buttonElement == null)
    {
        // not found
        return;
    }

    // great! now click the button
    var invokePattern = buttonElement.GetCurrentPattern(InvokePattern.Pattern) as InvokePattern;
    invokePattern?.Invoke();

}

Benefits

  • Zero changes required to target app
  • No mucking about finding window handles
  • No mucking about trying to send BN_CLICK notifications to an app's message pump fingers crossed in hoping that it works
  • works on c++ apps; WinForms; WPF; web
Community
  • 1
  • 1
1

you have a few options like below

1- button1.PerformClick();

2- button1_Click(this, EventArgs.Empty);

3- button1(null, null)

Saif
  • 2,611
  • 3
  • 17
  • 37