Fix InvalidCastException in Control.OnHandleDestroyed for non-ControlAccessibleObject#14295
Open
LeafShi1 wants to merge 4 commits intodotnet:mainfrom
Open
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes a regression in Control.OnHandleDestroyed where PropertyStore.TryGetValue<T> could throw InvalidCastException if a control’s accessibility object stored in the property store is not a ControlAccessibleObject.
Changes:
- Update
OnHandleDestroyedto retrieveAccessibleObjectinstances from thePropertyStoreinstead ofControlAccessibleObject. - Use type pattern matching to only clear the handle when the stored object is actually a
ControlAccessibleObject(including derived types).
SimonZhao888
previously approved these changes
Feb 12, 2026
SimonZhao888
previously approved these changes
Feb 12, 2026
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #14295 +/- ##
===================================================
+ Coverage 77.18395% 77.24652% +0.06257%
===================================================
Files 3279 3279
Lines 645138 645119 -19
Branches 47730 47731 +1
===================================================
+ Hits 497943 498332 +389
+ Misses 143503 143094 -409
- Partials 3692 3693 +1
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
src/test/unit/System.Windows.Forms/System/Windows/Forms/ControlTests.Methods.cs
Show resolved
Hide resolved
Member
ricardobossan
left a comment
There was a problem hiding this comment.
Other than a small comment, all LGTM!
…leObject_DoesNotThrow1"
ricardobossan
approved these changes
Feb 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #14291
Root Cause
Control.OnHandleDestroyedusedProperties.TryGetValue<ControlAccessibleObject>for both AccessibilityObject and NcAccessibilityObject, assuming the stored instance was always a ControlAccessibleObject. When a control overridesCreateAccessibleObject()to return a different AccessibleObject type, the genericTryGetValue<T>attempted an invalid cast and threwInvalidCastExceptionduring handle destruction.Proposed changes
Control.OnHandleDestroyedfors_accessibilityPropertyands_ncAccessibilityPropertyfromTryGetValue<ControlAccessibleObject>toTryGetValue<AccessibleObject>, then use type pattern matching (accObj is ControlAccessibleObject controlAccObj) before resettingcontrolAccObj.Handle = IntPtr.Zero, so only trueControlAccessibleObjectinstances are manipulated and other AccessibleObject types no longer cause invalid casts.Customer Impact
InvalidCastExceptioncrashes when controls with custom accessible objects are destroyedRegression?
Risk
Screenshots
Before
Sample project:
WinFormsApp15.zip
On teardown, If a custom overrode CreateAccessibleObject() and returned a different AccessibleObject type, TryGetValue threw an InvalidCastException when the handle was destroyed, potentially crashing the app.
BeforeChanges.mp4
After
Custom accessible objects that are not
ControlAccessibleObjectare left untouched during handle destruction, so noInvalidCastExceptionis thrown and the app stays stable.AfterChanges.mp4
Test methodology
Test environment(s)
Microsoft Reviewers: Open in CodeFlow