Search code examples
c#pinvokeui-automation

GUI-Test automation: Finding WinForms buttons via pinvoke with c#


I'm building a small GUI-Test automation tool in C# for a application. One of the functions in the test tool is to close dialogs pops up from the tested application.

The trouble that I have is to find the button to click on without giving the full class name. I have used the FindWindowEx method to get the dialog box and the button that I want to click on. I know the Caption of the button, but the trouble is that I also need to specify the class name for the button. The class name is not always the same, but it looks something like this: "WindowsForms10.BUTTON.app.0.3ce0bb8". For instance is the part in the end "3ce0bb8" different if you start the application locally or via click-once.

So, my question is: How can I find the button with just specifying the first part (that is always the same) of the class like this ""WindowsForms10.BUTTON.app." Or could I solve this in some other way?

The dll import looks like this:

[DllImport("user32.dll", SetLastError = true)]
    static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string pszWindow);

My code looks something like this when trying to click the button:

private void SendDialogButtonClick(IntPtr windowHandle, ApplicationStartType applicationStartType)
    {
        if (applicationStartType == ApplicationStartType.Localy)
            buttonClassName = "WindowsForms10.BUTTON.app.0.3ce0bb8";
        else if (applicationStartType == ApplicationStartType.ClickOnce)
            buttonClassName = "WindowsForms10.BUTTON.app.0.3d893c";

        // Find the "&No"-button
        IntPtr buttonAndNoHandle = FindWindowEx(windowHandle, IntPtr.Zero, buttonClassName, "&No");

        // Send the button click event to the appropriate button found on the dialog
        if (buttonAndNoHandle.ToInt64() != 0)
        {
            SendMessage(new HandleRef(null, buttonAndNoHandle), WM_CLICK, IntPtr.Zero, IntPtr.Zero);
        }
    }

Solution

  • Yes, that's difficult, the class names are auto-generated. You can't use FindWindowEx(), you have to iterate the controls with EnumChildWindows() and GetClassName().

    You could adapt the source code for the Managed Spy tool to make all this a lot easier and cleaner.