Skip to content

Regression in .NET 10: controls no longer raise MSAA WinEvents (inverted NoClientNotifications check in Control.AccessibilityNotifyClients) #15176

Description

@kastwey

.NET version

.NET 10 (10.0.11 runtime, SDK 10.0.303). Still present in main (checked at 75072a6) and in release/10.0 (f8eab87).

Did it work in .NET Framework?

Yes

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

Yes. It works in .NET 8 and .NET 9 (verified with 9.0.19).

Issue description

Control.AccessibilityNotifyClients(AccessibleEvents, int, int) is the method every control uses to raise MSAA WinEvents (NotifyWinEvent). Its guard lost the negation between .NET 9 and .NET 10:

// .NET 8
if (IsHandleCreated && AccessibleObject.CanNotifyClients)

// .NET 9
if (IsHandleCreated && !LocalAppContextSwitches.NoClientNotifications)

// .NET 10 and main
if (IsHandleCreated && AppContextSwitches.NoClientNotifications)

The negation was dropped in #12840 ("More OLE related refactoring", 620b123), which renamed LocalAppContextSwitches to AppContextSwitches across 81 files. That PR touched seven lines that use this switch; in the other six the rename kept the expression as it was (four !AppContextSwitches.NoClientNotifications, two without negation that had none before). Only this one lost its !, so it looks like a slip in a mechanical rename rather than a decision; the PR description does not mention accessibility and there are no review comments.

NoClientNotifications means "do not notify accessibility clients" and is false by default, so since .NET 10 NotifyWinEvent is only called when the application has asked for no notifications, that is, never by default. Every other use of the switch in the code base is negated (AccessibleObject.cs, Control.ControlAccessibleObject.cs, MessageBox.cs).

The result is that assistive technologies that rely on MSAA WinEvents stop receiving everything raised through this method: SystemMenuPopupStart/SystemMenuPopupEnd of a ContextMenuStrip, SystemMenuStart/SystemMenuEnd of a MenuStrip, the Focus event of ToolStrip items, and the state, selection, value and name change events raised by the controls.

I am a blind developer and I found this while investigating why JAWS and NVDA behave differently with a WinForms app after moving it to .NET 10. What a JAWS 2026 user gets today when opening a ContextMenuStrip with Shift+F10: "Menu" and nothing else; the arrow keys read nothing for a while, and only after going round the menu a few times do they start reading the items. The menu bar is affected too: the first Alt+F is silent. With the one-character fix applied to a local build of release/10.0, JAWS says "Context menu, Open" on opening and the arrows read every item from the first time; Alt+F says "New, 1 of 3". NVDA is not affected because it uses UIA for these windows.

Measurements: the WinEvents seen from another process on .NET Framework 4.8, .NET 9, .NET 10 and .NET 10 with the fix

I listened to the WinEvents of the repro app below from another process (SetWinEventHook, out of context) while opening a context menu with Shift+F10 and pressing Down twice. For every event I called AccessibleObjectFromEvent.

.NET Framework 4.8 raises the same events (MENUPOPUPSTART idObject=-4 name="DropDown" role=MENUPOPUP, then FOCUS idObject=-4 idChild=1 name="Open" role=MENUITEM...), so .NET 10 is the first version in which they are missing.

.NET 9.0.19, stock — real MSAA events, resolvable:

MENUPOPUPSTART idObject=-4 idChild=0 name=""     role=MENUPOPUP
FOCUS          idObject=-4 idChild=1 name="Open" role=MENUITEM
FOCUS          idObject=-4 idChild=2 name="Edit" role=MENUITEM

.NET 10.0.11, stock — none of them. The only WinEvents left are the ones Windows bridges from the UIA events, which carry a positive idObject and cannot be resolved (AccessibleObjectFromEvent returns E_FAIL):

FOCUS          idObject=1 idChild=0  (AccessibleObjectFromEvent hr=0x80004005)
FOCUS          idObject=2 idChild=0  (AccessibleObjectFromEvent hr=0x80004005)

.NET 10 with the fix — back to the .NET 9 behaviour:

MENUPOPUPSTART idObject=-4 idChild=0 role=MENUPOPUP
FOCUS          idObject=-4 idChild=1 name="Open" role=MENUITEM
FOCUS          idObject=-4 idChild=2 name="Edit" role=MENUITEM

The existing unit tests of AccessibilityNotifyClients only check that the call does not throw, which is why the regression went unnoticed.

Steps to reproduce

  1. Create a WinForms app targeting net10.0-windows with this in the form:

    Button button = new() { Text = "Button with a ContextMenuStrip", AutoSize = true };
    ContextMenuStrip menu = new();
    menu.Items.Add("&Open");
    menu.Items.Add("&Edit");
    button.ContextMenuStrip = menu;
    Controls.Add(button);
  2. From another process, install a WinEvent hook for the app (or use Accessible Event Watcher / AccEvent from the Windows SDK, in WinEvents mode).

  3. Focus the button, press Shift+F10, then Down.

  4. No EVENT_SYSTEM_MENUPOPUPSTART and no EVENT_OBJECT_FOCUS with idObject = OBJID_CLIENT arrive. The same app targeting net9.0-windows raises them.

I have a fix with unit tests ready and will open a pull request.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

💥 regression-releaseRegression from a public releasetenet-accessibilityMAS violation, UIA issue; problems with accessibility standards

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions