diff --git a/src/System.Windows.Forms/System/Windows/Forms/Control.cs b/src/System.Windows.Forms/System/Windows/Forms/Control.cs
index cb67f146d3e..e500ea5e2d1 100644
--- a/src/System.Windows.Forms/System/Windows/Forms/Control.cs
+++ b/src/System.Windows.Forms/System/Windows/Forms/Control.cs
@@ -4438,7 +4438,7 @@ protected internal void AccessibilityNotifyClients(AccessibleEvents accEvent, in
[EditorBrowsable(EditorBrowsableState.Advanced)]
protected void AccessibilityNotifyClients(AccessibleEvents accEvent, int objectID, int childID)
{
- if (IsHandleCreated && AppContextSwitches.NoClientNotifications)
+ if (IsHandleCreated && !AppContextSwitches.NoClientNotifications)
{
PInvoke.NotifyWinEvent((uint)accEvent, this, objectID, childID + 1);
}
diff --git a/src/test/unit/System.Windows.Forms/System/Windows/Forms/ControlTests.AccessibilityNotifyClients.cs b/src/test/unit/System.Windows.Forms/System/Windows/Forms/ControlTests.AccessibilityNotifyClients.cs
new file mode 100644
index 00000000000..c21fcdd9239
--- /dev/null
+++ b/src/test/unit/System.Windows.Forms/System/Windows/Forms/ControlTests.AccessibilityNotifyClients.cs
@@ -0,0 +1,108 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Windows.Win32.UI.Accessibility;
+
+namespace System.Windows.Forms.Tests;
+
+public partial class ControlTests
+{
+ [WinFormsFact]
+ public void Control_AccessibilityNotifyClients_WithHandle_RaisesWinEvent()
+ {
+ using NoClientNotificationsScope scope = new(enable: false);
+ using SubControl control = new();
+ Assert.NotEqual(IntPtr.Zero, control.Handle);
+
+ using WinEventListener listener = new(AccessibleEvents.Focus);
+ control.AccessibilityNotifyClients(AccessibleEvents.Focus, 2);
+
+ WinEventListener.WinEvent winEvent = Assert.Single(listener.EventsOf(control));
+ Assert.Equal((uint)AccessibleEvents.Focus, winEvent.Event);
+ Assert.Equal((int)OBJECT_IDENTIFIER.OBJID_CLIENT, winEvent.ObjectId);
+
+ // MSAA child ids are 1-based.
+ Assert.Equal(3, winEvent.ChildId);
+ }
+
+ [WinFormsFact]
+ public void Control_AccessibilityNotifyClients_ObjectId_WithHandle_RaisesWinEvent()
+ {
+ using NoClientNotificationsScope scope = new(enable: false);
+ using SubControl control = new();
+ Assert.NotEqual(IntPtr.Zero, control.Handle);
+
+ using WinEventListener listener = new(AccessibleEvents.SystemMenuStart);
+ control.AccessibilityNotifyClients(AccessibleEvents.SystemMenuStart, (int)OBJECT_IDENTIFIER.OBJID_MENU, -1);
+
+ WinEventListener.WinEvent winEvent = Assert.Single(listener.EventsOf(control));
+ Assert.Equal((int)OBJECT_IDENTIFIER.OBJID_MENU, winEvent.ObjectId);
+ Assert.Equal(0, winEvent.ChildId);
+ }
+
+ [WinFormsFact]
+ public void Control_AccessibilityNotifyClients_NoClientNotifications_DoesNotRaiseWinEvent()
+ {
+ using NoClientNotificationsScope scope = new(enable: true);
+ using SubControl control = new();
+ Assert.NotEqual(IntPtr.Zero, control.Handle);
+
+ using WinEventListener listener = new(AccessibleEvents.Focus);
+ control.AccessibilityNotifyClients(AccessibleEvents.Focus, 2);
+
+ Assert.Empty(listener.EventsOf(control));
+ }
+
+ ///
+ /// Listens, on the current thread, to the WinEvents of one kind raised by this process.
+ /// The hook is in-context, so the callback runs synchronously inside NotifyWinEvent.
+ ///
+ private sealed unsafe class WinEventListener : IDisposable
+ {
+ public readonly record struct WinEvent(uint Event, HWND Hwnd, int ObjectId, int ChildId);
+
+ // The hook is installed for the current thread only, and WinForms tests do not run in parallel.
+ private static List? s_events;
+
+ private readonly HWINEVENTHOOK _hook;
+
+ public WinEventListener(AccessibleEvents accEvent)
+ {
+ s_events = [];
+ _hook = PInvoke.SetWinEventHook(
+ (uint)accEvent,
+ (uint)accEvent,
+ PInvoke.GetModuleHandle((PCWSTR)null),
+ &WinEventProc,
+ (uint)Environment.ProcessId,
+ PInvokeCore.GetCurrentThreadId(),
+ PInvoke.WINEVENT_INCONTEXT);
+
+ Assert.False(_hook.IsNull);
+ }
+
+ public IEnumerable EventsOf(Control control)
+ {
+ HWND hwnd = control.HWND;
+ return s_events!.Where(e => e.Hwnd == hwnd);
+ }
+
+ [UnmanagedCallersOnly(CallConvs = [typeof(CallConvStdcall)])]
+ private static void WinEventProc(
+ HWINEVENTHOOK hook,
+ uint @event,
+ HWND hwnd,
+ int idObject,
+ int idChild,
+ uint idEventThread,
+ uint dwmsEventTime) => s_events?.Add(new(@event, hwnd, idObject, idChild));
+
+ public void Dispose()
+ {
+ PInvoke.UnhookWinEvent(_hook);
+ s_events = null;
+ }
+ }
+}