Skip to content

Fix FailFast crash in LabelEditNativeWindow due to garbage collected WinEvent hook delegate#14663

Open
LeafShi1 wants to merge 2 commits into
dotnet:mainfrom
LeafShi1:Fix_14657_FailFast_crash_in_LabelEditNativeWindow
Open

Fix FailFast crash in LabelEditNativeWindow due to garbage collected WinEvent hook delegate#14663
LeafShi1 wants to merge 2 commits into
dotnet:mainfrom
LeafShi1:Fix_14657_FailFast_crash_in_LabelEditNativeWindow

Conversation

@LeafShi1

@LeafShi1 LeafShi1 commented Jun 22, 2026

Copy link
Copy Markdown
Member

Fixes #14657

Root Cause

Window destroyed → hooks not unregistered → object garbage collected → hook callback invokes freed memory → crash (FailFast).

The original code skipped cleanup when handle was gone, leaving orphaned hooks running.

Proposed changes

This prevents callbacks from invoking garbage-collected delegates.

  • Added UnhookWinEventHooks() method to centralize hook cleanup
  • Call unhook when handle is destroyed in OnHandleChange()
  • Added _isReleasing flag to prevent reentry during cleanup
  • Set _winEventProcCallback = null after unhooking to allow GC

Customer Impact

  • Applications using screen readers (e.g., Narrator) no longer crash when editing PropertyGrid or TreeView with accessibility enabled.

Regression?

  • No

Risk

  • Minimal: only defensive guards added, no behavior changes.

Screenshots

Before

protected override void OnHandleChange()
{
    base.OnHandleChange();
    
    if (!IsHandleCreated)
    {
        return;  // ← Hooks remain registered even after window destruction
    }
    
    InstallWinEventHooks();
}

When native window is destroyed, WinEvent hooks are not unregistered. This orphaned hook persists after the managed object is garbage collected, causing late callbacks to invoke freed delegates → FailFast crash.

After

protected override void OnHandleChange()
{
    base.OnHandleChange();
    
    if (!IsHandleCreated)
    {
        UnhookWinEventHooks();  // ← Properly clean up hooks
        return;
    }
    
    InstallWinEventHooks();
}

private void UnhookWinEventHooks()
{
    if (_winEventHooksInstalled)
    {
        PInvoke.UnhookWinEvent(_valueChangeHook);
        PInvoke.UnhookWinEvent(_textSelectionChangedHook);
        _winEventHooksInstalled = false;
        _winEventProcCallback = null;  // Allow GC
    }
}

Hooks are properly unregistered when the native window handle is destroyed, preventing orphaned callbacks and GC-related crashes.

Test methodology

  • Manually

Test environment(s)

  • .net 11.0.0-preview.6.26310.110
Microsoft Reviewers: Open in CodeFlow

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a GC-induced FailFast crash in LabelEditNativeWindow accessibility scenarios by ensuring WinEvent hooks are reliably unregistered when the underlying native handle is destroyed, preventing callbacks from invoking a collected delegate.

Changes:

  • Centralized WinEvent hook cleanup into UnhookWinEventHooks() and invoked it when the handle is destroyed (OnHandleChange when !IsHandleCreated).
  • Added a _isReleasing reentrancy guard to prevent hook install/uninstall cycles during teardown.
  • Added callback-side guards and cleared the stored delegate reference after unhooking to avoid stale callbacks and enable GC.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@LeafShi1 LeafShi1 requested a review from SimonZhao888 June 23, 2026 07:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Problem with LabelEditNativeWindow

2 participants