{"record":{"id":"c441976c4a080d8e","repo":"dotnet/wpf","slug":"sr-textpenaltymodulehasbeendisposed","errorCode":null,"errorMessage":"SR.TextPenaltyModuleHasBeenDisposed","messagePattern":"SR\\.TextPenaltyModuleHasBeenDisposed","errorType":"exception","errorClass":"ObjectDisposedException","httpStatus":null,"severity":"error","filePath":"src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/TextFormatting/TextPenaltyModule.cs","lineNumber":76,"sourceCode":"            {\n                UnsafeNativeMethods.LoDisposePenaltyModule(_ploPenaltyModule);\n                _ploPenaltyModule = IntPtr.Zero;\n                _isDisposed = true;\n                GC.KeepAlive(this);\n            }\n        }\n\n\n        /// <summary>\n        /// This method should only be called by Framework to authorize direct access to\n        /// unsafe LS penalty module for exclusive use of PTS during optimal paragraph \n        /// penalty calculation.\n        /// </summary>\n        internal IntPtr DangerousGetHandle()\n        {\n            if (_isDisposed)\n            {\n                throw new ObjectDisposedException(SR.TextPenaltyModuleHasBeenDisposed);\n            }\n\n            IntPtr penaltyModuleInternalHandle;\n            LsErr lserr = UnsafeNativeMethods.LoGetPenaltyModuleInternalHandle(_ploPenaltyModule, out penaltyModuleInternalHandle);\n\n            if (lserr != LsErr.None)\n                TextFormatterContext.ThrowExceptionFromLsError(SR.Format(SR.GetPenaltyModuleHandleFailure, lserr), lserr);\n\n            GC.KeepAlive(this);\n            return penaltyModuleInternalHandle;\n        }\n    }\n}\n\n","sourceCodeStart":58,"sourceCodeEnd":91,"githubUrl":"https://github.com/dotnet/wpf/blob/81131a70a4c573cd62748a5c36908fc4d662daa9/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/TextFormatting/TextPenaltyModule.cs#L58-L91","documentation":"TextPenaltyModule.DangerousGetHandle throws ObjectDisposedException when the penalty module's native handle has already been disposed. Once disposed, the underlying native penalty module cannot be queried.","triggerScenarios":"Calling DangerousGetHandle (via ptsPenaltyModule) after Dispose() was called on the TextPenaltyModule, e.g. using the handle after a formatting session finished.","commonSituations":"Holding a TextPenaltyModule beyond the scope of its owner formatter; double-dispose patterns; reusing cached penalty modules across FormatLine calls after cleanup.","solutions":["Ensure DangerousGetHandle is only called while the TextPenaltyModule is alive, inside its using scope","Check disposal state before use; re-create a new TextPenaltyModule if disposed","Fix object lifetime so the module is not disposed before the ptsPenaltyModule callback uses it"],"exampleFix":"// before\nvar module = new TextPenaltyModule();\nmodule.Dispose();\nvar h = module.DangerousGetHandle();\n// after\nvar module = new TextPenaltyModule();\nvar h = module.DangerousGetHandle(); // before dispose\nmodule.Dispose();","handlingStrategy":"validation","validationCode":"if (module.IsDisposed) throw new InvalidOperationException(\"TextPenaltyModule already disposed\");","typeGuard":"bool Usable(TextPenaltyModule m) => m != null && !m.IsDisposed;","tryCatchPattern":"try { handle = module.DangerousGetHandle(); } catch (ObjectDisposedException) { module = new TextPenaltyModule(); handle = module.DangerousGetHandle(); }","preventionTips":["Keep all handle usage inside the module's using scope","Never cache the native handle beyond the module's lifetime"],"tags":["wpf","text-formatting","object-disposed","native-handle"],"backgroundTag":"invalid-state-transition","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"}