AccessibleObject answers IRawElementProviderFragment.get_FragmentRoot with S_OK and a null root; UIAutomationCore crashes in TextPattern.RangeFromPoint and the app freezes 15-30 s under JAWS
- Dominant language
- C#
- Stars
- 4.9k
- Forks
- 1.1k
- Avg merge
- 1d 13m
- Merged PRs (30d)
- 85
Description
### .NET version
.NET 10.0.11 (`System.Windows.Forms.dll` from `Microsoft.WindowsDesktop.App\10.0.11`), Windows 11 Pro 26100 (build lab 26100.1.amd64fre.ge_release), `UIAutomationCore.dll` 7.2.26100.9278.
### Did it work in .NET Framework?
Not tested. .NET Framework `TextBox` does not implement the UIA text provider in-process, so the code path does not exist there.
### Did it work in any of the earlier releases of .NET Core or .NET 5+?
Not tested. The relevant code (`AccessibleObject` implementing `IRawElementProviderFragment` with `FragmentRoot => null`) is present on `release/10.0`; I have not bisected older releases.
### Issue description
With the JAWS screen reader running and its *Mouse Echo* feature on, the first time the mouse pointer passes over a WinForms `TextBox` the whole application stops responding for 15–30 seconds. It happens once per process. Turning Mouse Echo off removes it. A Win32 application with a plain `EDIT` control does not reproduce.
A full process dump taken during the freeze (analyzed with `cdb` and public Microsoft symbols) shows the chain:
1. JAWS calls `TextPattern.RangeFromPoint` on the text box under the pointer. It is a cross-process COM call into the application, dispatched on the UI thread via `combase!ThreadWndProc`.
2. `UIAutomationCore!RemotePatternStub::Text_RangeFromPoint` → `ScreenPointToProviderPointHelper` → `UiaUtils::TryGetCoordinateConverterForVisualRelativeRoot` queries the element for `IRawElementProviderFragment` (succeeds) and calls `get_FragmentRoot` (returns `S_OK`).
3. WinForms hands back **`S_OK` with a null `IRawElementProviderFragmentRoot*`**. `AccessibleObject.FragmentRoot` defaults to `null`, `TextBoxBase.TextBoxBaseAccessibleObject` does not override it, and `IRawElementProviderFragment.Interface.get_FragmentRoot` does `*pRetVal = ComHelpers.TryGetComPointer(FragmentRoot); return HRESULT.S_OK;` ([AccessibleObject.cs, release/10.0](https://github.com/dotnet/winforms/blob/release/10.0/src/System.Windows.Forms/System/Windows/Forms/Accessibility/AccessibleObject.cs)).
4. UIAutomationCore dereferences the null root: `mov rax,[rcx]` with `rcx = 0`, `EXCEPTION_ACCESS_VIOLATION` at `UIAutomationCore+0x1e9bb7`.
5. COM's server-side stub filter (`combase!ServerExceptionFilter`) swallows the exception and calls `SilentlyReportExceptions` → `RtlReportException` → `ntdll!WerpWaitForCrashReporting`. The UI thread blocks there until Windows Error Reporting has written a minidump of the process. That wait is the visible freeze. COM reports only the first stub exception per process, hence once per launch.
UIAutomationCore should not dereference the null (separately reported to Windows), but the [UIA contract for `IRawElementProviderFragment::get_FragmentRoot`](https://learn.microsoft.com/windows/win32/api/uiautomationcore/nf-uiautomationcore-irawelementproviderfragment-get_fragmentroot) is that a fragment returns its root, and WinForms exposes the fragment interface on every `AccessibleObject` while returning nothing from it.
Resolved UI-thread stack (newest first, abridged):
```
ntdll!NtWaitForMultipleObjects
ntdll!WerpWaitForCrashReporting
ntdll!RtlReportException
combase!SilentlyReportExceptions
combase!ServerExceptionFilter
combase!AppInvokeExceptionFilterWithMethodAddress
ntdll!KiUserExceptionDispatch
UIAutomationCore!UiaUtils::TryGetCoordinateConverterForVisualRelativeRoot+0x73 <- AV, rcx = 0
UIAutomationCore!`anonymous namespace'::ScreenPointToProviderPointHelper+0x7e
UIAutomationCore!RemotePatternStub::Text_RangeFromPoint+0xca
UIAutomationCore!InvokePatternMethodOnCorrectContext_Callback+0x26f
OneCoreCommonProxyStub!IServiceProvider_RemoteQueryService_Thunk+0x2f
rpcrt4!Ndr64StubWorker+0x862
combase!CStdStubBuffer_Invoke+0x7d
combase!ReentrantSTAInvokeInApartment+0x194
combase!ThreadDispatch+0x3ef
combase!ThreadWndProc+0x177
user32!DispatchMessageWorker+0x1dd
System.Windows.Forms.Application.LightThreadContext.FPushMessageLoop+0x160
System.Windows.Forms.Application.Run+0x6c
```
Disassembly at the fault, showing the successful `get_FragmentRoot` followed by the unchecked dereference:
```
call UIAutomationCore!wil::try_com_query
mov rcx,qword ptr [rbp-28h]
test rcx,rcx
je +0xd4
and qword ptr [rbp+38h],rdi ; out param = null
lea rdx,[rbp+38h]
call UIAutomationCore!ProviderCallouts::get_FragmentRoot
mov rcx,qword ptr [rbp+18h]
test eax,eax
jns +0x6a ; S_OK
and qword ptr [rbp+28h],0
mov rcx,qword ptr [rbp+38h] ; rcx = 0
mov rax,qword ptr [rcx] ; <- access violation
```
Windows Event Log, Application: *Application Error* 1000 / *Windows Error Reporting* 1001, faulting module `UIAutomationCore.DLL 7.2.26100.9278`, exception code `0xc0000005`, fault offset `0x1e9bb7`, recorded for every launch that was touched by the mouse under JAWS.
Managed heap at the time of the dump contained `System.Windows.Forms.TextBox+TextBoxAccessibleObject` (×2) and `System.Windows.Forms.TextBoxBase+TextBoxBaseUiaTextProvider` (×2), matching the two text boxes on the form.
### Steps to reproduce
1. Windows 11, JAWS 2026 (tested with `jhook.dll` 27.5.20) running, *Mouse Echo* enabled (Settings Center → Mouse Echo).
2. Create a WinForms app targeting `net10.0-windows` with a form containing one `TextBox { Multiline = true, Dock = DockStyle.Fill }` (a second control such as a `ListBox` is optional; a `ListBox` plus `TextBox` form reproduced identically).
3. Run it and move the mouse pointer across the text box once.
4. The window stops responding for 15–30 seconds, then recovers. Event Viewer → Application shows an *Application Error* 1000 for the app with faulting module `UIAutomationCore.DLL`, exception `0xc0000005`, offset `0x1e9bb7`. Subsequent mouse passes do not freeze (COM reports only the first exception per process), but a new process freezes again.
Expected: a UIA client calling `TextPattern.RangeFromPoint` on a WinForms text box gets a range or an error `HRESULT`; the application never blocks on WER.
The same call should be reachable without a screen reader by any UIA client invoking `IUIAutomationTextPattern::RangeFromPoint` against the text box's element; I have only verified it via JAWS.
A dump of the freeze from a data-free test application can be provided on request.
Contributor guide
Research direction
Start with src/System.Windows.Forms/System/Windows/Forms/Accessibility/AccessibleObject.cs on release/10.0, then trace TextBoxBase.TextBoxBaseAccessibleObject and the IRawElementProviderFragment get_FragmentRoot path described in the issue. Reproduce with a net10.0-windows TextBox and UIA TextPattern.RangeFromPoint under JAWS, and verify that the call no longer causes the UI thread freeze or UIAutomationCore fault while returning a range or HRESULT.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- accessibility, desktop
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100