fix(skia): Keep Win32 UIA providers alive on removal#23613
fix(skia): Keep Win32 UIA providers alive on removal#23613morning4coffe-dev wants to merge 2 commits into
Conversation
Fixes NoClickablePointException / 0x80070002 (FileNotFound) raised to out-of-proc UIA clients (FlaUI, WinAppDriver, Narrator) when calling into an element whose subtree was removed. - Port CUIAWrapper::Invalidate: providers now expose Invalidate(), which disconnects from UIA, cascades to virtual (DataGrid) children, and makes every entry point return UIA_E_ELEMENTNOTAVAILABLE via ThrowIfDisconnected, instead of the COM-callable wrapper being GC'd out from under a cached client proxy. A short-lived strong tombstone keeps the wrapper alive until UIA processes the disconnect. - Wire UIA_ClickablePointPropertyId to AutomationPeer.GetClickablePoint, matching CUIAWrapper (VT_EMPTY when (0,0), else client-to-screen). Fixes #494
There was a problem hiding this comment.
Pull request overview
This PR adjusts the Win32 (Skia) UI Automation (UIA) provider lifecycle so that providers are explicitly disconnected/invalidated when their subtree is removed, and are kept alive briefly to ensure out-of-proc UIA clients observe the expected UIA_E_ELEMENTNOTAVAILABLE behavior rather than failures caused by prematurely collected COM-callable wrappers.
Changes:
- Add an explicit provider invalidation path (
Win32RawElementProvider.Invalidate) that disconnects from UIA and makes subsequent calls fail withUIA_E_ELEMENTNOTAVAILABLE. - Add “tombstoning” in
Win32Accessibilityto keep just-disconnected providers strongly referenced until the coalesced structure-changed flush completes. - Expose
UIA_ClickablePointPropertyIdviaGetPropertyValue(with coordinate conversion) and introduce theUIA_E_ELEMENTNOTAVAILABLEHRESULT constant.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/Uno.UI.Runtime.Skia.Win32/Accessibility/Win32UIAutomationInterop.cs | Adds the UIA_E_ELEMENTNOTAVAILABLE HRESULT constant for consistent COM error reporting after disconnect. |
| src/Uno.UI.Runtime.Skia.Win32/Accessibility/Win32RawElementProvider.cs | Implements provider invalidation + disconnection behavior and expands property/pattern surface (incl. ClickablePoint). |
| src/Uno.UI.Runtime.Skia.Win32/Accessibility/Win32Accessibility.cs | Keeps disconnected providers alive until structure-change notifications flush, preventing stale proxies from hitting severed CCWs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
🤖 Your WebAssembly Skia Sample App stage site is ready! Visit it here: https://unowasmprstaging.z20.web.core.windows.net/pr-23613/wasm-skia-net9/index.html |
|
|
|
Claude finished @morning4coffe-dev's task in 3m 44s —— View job PR Review
Verdict: ship — all blocking findings from the prior review are resolved; 1 low nit (inline comment posted). What changed since last reviewCommit Core fix — confirmed correctThe tombstone/Invalidate design is sound:
Other observations
|
| // delivering the structure-changed notification, so an out-of-proc client | ||
| // that still holds the proxy observes UIA_E_ELEMENTNOTAVAILABLE rather than a | ||
| // severed-CCW 0x80070002. Drained once the structure-change flush completes. | ||
| private readonly HashSet<Win32RawElementProvider> _disconnectedProviders = new(ReferenceEqualityComparer.Instance); |
There was a problem hiding this comment.
_pendingStructureChanges (declared just above) holds the same reference-typed Win32RawElementProvider values but is constructed without an explicit comparer, while this field spells out ReferenceEqualityComparer.Instance. Since Win32RawElementProvider doesn't override Equals/GetHashCode the behaviour is identical, but for clarity and consistency it's worth aligning them:
| private readonly HashSet<Win32RawElementProvider> _disconnectedProviders = new(ReferenceEqualityComparer.Instance); | |
| private readonly HashSet<Win32RawElementProvider> _pendingStructureChanges = new(ReferenceEqualityComparer.Instance); | |
| // Strong references to just-invalidated providers. Keeps their COM-callable | |
| // wrappers alive across the window between UiaDisconnectProvider and UIA | |
| // delivering the structure-changed notification, so an out-of-proc client | |
| // that still holds the proxy observes UIA_E_ELEMENTNOTAVAILABLE rather than a | |
| // severed-CCW 0x80070002. Drained once the structure-change flush completes. | |
| private readonly HashSet<Win32RawElementProvider> _disconnectedProviders = new(ReferenceEqualityComparer.Instance); |
|
🤖 Your WebAssembly Skia Sample App stage site is ready! Visit it here: https://unowasmprstaging.z20.web.core.windows.net/pr-23613/wasm-skia-net9/index.html |

Fixes #494
GitHub Issue: closes https://github.com/unoplatform/kahua-private/issues/494
PR Type:
🐞 Bugfix
What changed? 🚀
PR Checklist ✅
Screenshots Compare Test Runresults.