dotnet/wpf · warning · ObjectDisposedException

NLGSpellerInterop.SpellerSegment

Error message

NLGSpellerInterop.SpellerSegment

What it means

NLGSpellerInterop.SpellerSegment.Dispose(bool) throws ObjectDisposedException (message "NLGSpellerInterop.SpellerSegment") when Dispose is called on an already-disposed segment. Like the outer interop class, this nested type's Dispose is not idempotent.

Solutions

  1. Dispose each SpellerSegment exactly once; let SpellerSentence own and dispose its segments.
  2. Add an ownership convention: only the parent (sentence) disposes segments.
  3. Catch ObjectDisposedException in bulk cleanup loops.
  4. Null out references after disposal to prevent accidental re-disposal.

Example fix

// before
foreach (var seg in sentence.Segments) seg.Dispose();
sentence.Dispose(); // may double-dispose segments
// after
sentence.Dispose(); // sentence disposes its own segments once
Defensive patterns

Strategy: try-catch

Try / catch

try { segment.Dispose(); }
catch (ObjectDisposedException) { /* ignore in cleanup */ }

Prevention

When it happens

Trigger: Disposing a SpellerSegment twice - e.g. iterating subSegments and disposing each, then disposing parent segments that dispose their children again, or explicit plus finalizer cleanup.

Common situations: Manual spell-check span management where both the sentence loop and segment loop release the same child segments; repeated teardown after spell-check cancellation.

Related errors


AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/ed08431a0a502f8f. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/NLGSpellerInterop.cs:743

                }
            }


            #endregion SpellerInteropBase.ISpellerSegment

            #region IDisposable

            public void Dispose()
            {
                Dispose(true);
                GC.SuppressFinalize(this);
            }

            protected virtual void Dispose(bool disposing)
            {
                if (_disposed)
                {
                    throw new ObjectDisposedException("NLGSpellerInterop.SpellerSegment");
                }

                if (_subSegments != null)
                {
                    foreach (SpellerSegment subSegment in _subSegments)
                    {
                        // Don't call Dispose(disposing) here. That will 
                        // fail to suppress finalization of subsegment objects.
                        subSegment.Dispose();
                    }
                    _subSegments = null;
                }

                if (_textSegment != null)
                {
                    Marshal.ReleaseComObject(_textSegment);
                    _textSegment = null;
                }

View on GitHub (pinned to 81131a70a4)