{"record":{"id":"cbea25b29dfc5ccb","repo":"dotnet/wpf","slug":"sr-texteditorspellerinterophasbeendisposed","errorCode":null,"errorMessage":"SR.TextEditorSpellerInteropHasBeenDisposed","messagePattern":"SR\\.TextEditorSpellerInteropHasBeenDisposed","errorType":"exception","errorClass":"ObjectDisposedException","httpStatus":null,"severity":"warning","filePath":"src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/NLGSpellerInterop.cs","lineNumber":116,"sourceCode":"        //  IDispose Methods\n        //\n        //------------------------------------------------------\n\n        #region IDispose Methods\n\n        public override void Dispose()\n        {\n            Dispose(true);\n            GC.SuppressFinalize(this);\n        }\n\n        /// <summary>\n        /// Internal interop resource cleanup\n        /// </summary>\n        protected override void Dispose(bool disposing)\n        {\n            if (_isDisposed)\n                throw new ObjectDisposedException(SR.TextEditorSpellerInteropHasBeenDisposed);\n\n            if (_textChunk != null)\n            {\n                Marshal.ReleaseComObject(_textChunk);\n                _textChunk = null;\n            }\n\n            // Stop the lifetime of Natural Language library\n            UnsafeNlMethods.NlUnload();\n\n            _isDisposed = true;\n        }\n\n        #endregion IDispose Methods\n\n        //------------------------------------------------------\n        //\n        //  Internal Methods","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/dotnet/wpf/blob/81131a70a4c573cd62748a5c36908fc4d662daa9/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/NLGSpellerInterop.cs#L98-L134","documentation":"NLGSpellerInterop.Dispose(bool) guards against double-disposal: if _isDisposed is already true, calling Dispose again throws ObjectDisposedException with SR.TextEditorSpellerInteropHasBeenDisposed rather than silently returning.","triggerScenarios":"Calling Dispose (or using/Dispose pattern completion) a second time on the same NLGSpellerInterop instance - e.g. explicit Dispose followed by a using block or finalizer-driven cleanup.","commonSituations":"Speller interop objects shared between TextEditor components and disposed by more than one owner; manual cleanup plus IDisposable dispose in the same shutdown path.","solutions":["Track disposal in the owner and call Dispose only once (guard with a bool or Interlocked flag).","Wrap the speller interop in a class that implements the standard dispose pattern tolerating multiple Dispose calls.","Catch ObjectDisposedException around cleanup code in shutdown paths.","Note: unlike most .NET types, this Dispose is not idempotent - check _isDisposed-visible state indirectly via calls that throw on use."],"exampleFix":"// before\nspeller.Dispose();\nspeller.Dispose(); // throws\n// after\nif (!disposed)\n{\n    speller.Dispose();\n    disposed = true;\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { speller.Dispose(); }\ncatch (ObjectDisposedException) { /* already disposed */ }","preventionTips":["Single-owner disposal discipline","Guard with your own disposed flag","Avoid mixing explicit Dispose with using blocks on the same instance"],"tags":["dispose","wpf","spellcheck","object-disposed"],"backgroundTag":"object-disposed","analyzedSha":"81131a70a4c573cd62748a5c36908fc4d662daa9","analyzedAt":"2026-09-14T10:12:48.479Z","contentChangedAt":"2026-09-14T10:12:48.479Z","schemaVersion":2},"datasetVersion":"2026-09-22T01:17:13.364Z"}