dotnet / winforms

Windows Forms is a .NET UI framework for building Windows desktop applications.
MIT License
4.36k stars 965 forks source link

System.Windows.Automation.InvokePattern.Invoke blocks on a menu item. #10244

Open lovettchris opened 10 months ago

lovettchris commented 10 months ago

.NET version

7.0.403

Did it work in .NET Framework?

Yes

Did it work in any of the earlier releases of .NET Core or .NET 5+?

Don't know, it works in .NET Framework 4.6.2, but not in latest version of .NET Framework 4.8

Issue description

Run the unit test in the following solution. Notice that the unit test hangs on line 80 of UnitTest1.cs which is the InvokePattern.Invoke call on the FIle/Open... menu item. This makes it impossible to test any menu item using System.Automation?

Steps to reproduce

  1. on Windows 11 22H2 build 22621.2361
  2. Load this solution into VS 2022
  3. Run Test/Debug all tests
  4. wait for windows open file dialog to appear
  5. Notice it never disappears
  6. hit break in the debugger and you will see it is blocked on line 80 of UnitTest1.cs.

WinFormsApp1.zip

elachlan commented 10 months ago

@Olina-Zhang can your team please test this to confirm?

Amy-Li03 commented 10 months ago

@elachlan we can repro this issue on .NET 6.0, 7.0, 8.0 and 9.0. Repro

lovettchris commented 10 months ago

[like] Chris Lovett reacted to your message:

Epica3055 commented 10 months ago

I and @Amy-Li03 discussed this issue, and we think this is not a a11y issue.

merriemcgaw commented 10 months ago

@Epica3055 let's discuss in the next meeting.

Tanya-Solyanik commented 10 months ago

@Amy-Li03 - Could you please verify if this issue reproduces on 4.7.2 and 4.8.1 and if it depends on any AccessibilityImprovements.Level?

lovettchris commented 10 months ago

Attached is a .NET Framework 4.7.2 version of the above test app and the test passes (the invoke does not block) WindowsFormsApp1.zip

If you then change the project properties to target .NET Framework 4.8, the test fails as per my original bug report.

Amy-Li03 commented 10 months ago

@Tanya-Solyanik , this issue reproduces on .NET Framework 4.8 & 4.8.1 and it doesn't repro on 4.7.2 and its previous versions. Also it doesn't depend on AccessibilityImprovements.Level, add following quirk to app.config file, issue still repro.

<runtime>
      <AppContextSwitchOverrides value="Switch.UseLegacyAccessibilityFeatures=false;Switch.UseLegacyAccessibilityFeatures.2=false;Switch.UseLegacyAccessibilityFeatures.3=false;Switch.UseLegacyAccessibilityFeatures.4=false;Switch.UseLegacyAccessibilityFeatures.5=false" />
</runtime>
LeafShi1 commented 5 months ago

.netFramwork4.7 project: The function RawInvokePattern_Invoke(hobj) return 0

UIAutomationClientsideProviders.dll!MS.Internal.AutomationProxies.Accessible.DoDefaultAction() Line 527 C#
UIAutomationClientsideProviders.dll!MS.Internal.AutomationProxies.MsaaNativeProvider.CallDoDefaultAction() Line 820 C#
UIAutomationClientsideProviders.dll!MS.Internal.AutomationProxies.MsaaNativeProvider.System.Windows.Automation.Provider.IInvokeProvider.Invoke() Line 515   C#
[Native to Managed Transition]  
[Managed to Native Transition]  
 UIAutomationClient.dll!MS.Internal.Automation.UiaCoreApi.InvokePattern_Invoke(MS.Internal.Automation.SafePatternHandle hobj) Line 659  C#

netFramwork4.8.1 project: The function RawInvokePattern_Invoke(hobj) return -2146233083

UIAutomationClient.dll!MS.Internal.Automation.UiaCoreApi.CheckError(int hr) Line 1017   C#
UIAutomationClient.dll!MS.Internal.Automation.UiaCoreApi.InvokePattern_Invoke(MS.Internal.Automation.SafePatternHandle hobj) Line 660   C#
UIAutomationClient.dll!System.Windows.Automation.InvokePattern.Invoke() Line 28 C#
TestProject1.dll!TestProject1.UnitTest1.Invoke(System.Windows.Automation.AutomationElement e) Line 99   C#
TestProject1.dll!TestProject1.UnitTest1.InvokeMenuItem(System.Windows.Automation.AutomationElement window, string menuItemName) Line 91 C#
TestProject1.dll!TestProject1.UnitTest1.TestMethod1() Line 35   C#
Tanya-Solyanik commented 5 months ago

@LeafShi1 - does 4.8.1 sample block as well?

LeafShi1 commented 5 months ago

@LeafShi1 - does 4.8.1 sample block as well?

Yes

Tanya-Solyanik commented 5 months ago

@LeafShi1 - what other controls does this reproduce for?

LeafShi1 commented 5 months ago

@LeafShi1 - what other controls does this reproduce for?

So this problem only reproduces in MenuStrip, and the invoke event of menuItem needs to pop up a new windows page.

Tanya-Solyanik commented 5 months ago

@LeafShi1 - do we have any unit tests that call the Invoke method on controls? If not, could you please add some?

LeafShi1 commented 5 months ago

@LeafShi1 - do we have any unit tests that call the Invoke method on controls? If not, could you please add some?

Many controls contain an IsPatternSupported test but the Invoke method on the control is not tested? Do we need to complete all of them? Such as https://github.com/dotnet/winforms/blob/9313f05e84328c3176eb7d80497c9505cd8aaebf/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/AccessibleObjects/HScrollBar.ScrollBarFirstLineButtonAccessibleObjectTests.cs#L128-L135

Tanya-Solyanik commented 5 months ago

Start with the MenuItems mentioned in this bug and buttons, we'll see if we need to add more later.

LeafShi1 commented 5 months ago

Start with the MenuItems mentioned in this bug and buttons, we'll see if we need to add more later.

Try to add following test, the System.Windows.Forms.AccessibleObject.Invoke blocks on Button and MenuItem. But System.Windows.Automation.InvokePattern.Invoke only blocks on MenuItem.

    {
        int callCount = 0;
        using Button button = new()
        {
            AccessibleRole = AccessibleRole.Default
        };

        button.CreateControl();
        ButtonAccessibleObject buttonAccessibleObject = new(button);

        button.Click += (sender, e) =>
        {
            MessageBox.Show("TestDialog");
            callCount++;
        };

        buttonAccessibleObject.Invoke();
        Assert.Equal(1, callCount);

        foreach (Form form in Application.OpenForms)
        {
            if (form.Text == "TestDialog")
            {
                form.Close();
                break;
            }
        }
    }

image

Tanya-Solyanik commented 5 months ago

This is interesting, my test application was blocking on a button. I was doing a cross process access though. I have a driver application and a test application, and the driver is invoking buttons in the test app.

Let's test only for menuitems for now. Going forward we can start replacing code that send clicks to buttons(or other controls) with AccessibleObject.Invoke in tests that excersize control functionality.