dotnet/wpf · error · ObjectDisposedException

SR.TextPenaltyModuleHasBeenDisposed

Error message

SR.TextPenaltyModuleHasBeenDisposed

What it means

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.

Solutions

  1. Ensure DangerousGetHandle is only called while the TextPenaltyModule is alive, inside its using scope
  2. Check disposal state before use; re-create a new TextPenaltyModule if disposed
  3. Fix object lifetime so the module is not disposed before the ptsPenaltyModule callback uses it

Example fix

// before
var module = new TextPenaltyModule();
module.Dispose();
var h = module.DangerousGetHandle();
// after
var module = new TextPenaltyModule();
var h = module.DangerousGetHandle(); // before dispose
module.Dispose();
Defensive patterns

Strategy: validation

Validate before calling

if (module.IsDisposed) throw new InvalidOperationException("TextPenaltyModule already disposed");

Type guard

bool Usable(TextPenaltyModule m) => m != null && !m.IsDisposed;

Try / catch

try { handle = module.DangerousGetHandle(); } catch (ObjectDisposedException) { module = new TextPenaltyModule(); handle = module.DangerousGetHandle(); }

Prevention

When it happens

Trigger: Calling DangerousGetHandle (via ptsPenaltyModule) after Dispose() was called on the TextPenaltyModule, e.g. using the handle after a formatting session finished.

Common situations: Holding a TextPenaltyModule beyond the scope of its owner formatter; double-dispose patterns; reusing cached penalty modules across FormatLine calls after cleanup.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/TextFormatting/TextPenaltyModule.cs:76

            {
                UnsafeNativeMethods.LoDisposePenaltyModule(_ploPenaltyModule);
                _ploPenaltyModule = IntPtr.Zero;
                _isDisposed = true;
                GC.KeepAlive(this);
            }
        }


        /// <summary>
        /// This method should only be called by Framework to authorize direct access to
        /// unsafe LS penalty module for exclusive use of PTS during optimal paragraph 
        /// penalty calculation.
        /// </summary>
        internal IntPtr DangerousGetHandle()
        {
            if (_isDisposed)
            {
                throw new ObjectDisposedException(SR.TextPenaltyModuleHasBeenDisposed);
            }

            IntPtr penaltyModuleInternalHandle;
            LsErr lserr = UnsafeNativeMethods.LoGetPenaltyModuleInternalHandle(_ploPenaltyModule, out penaltyModuleInternalHandle);

            if (lserr != LsErr.None)
                TextFormatterContext.ThrowExceptionFromLsError(SR.Format(SR.GetPenaltyModuleHandleFailure, lserr), lserr);

            GC.KeepAlive(this);
            return penaltyModuleInternalHandle;
        }
    }
}

View on GitHub (pinned to 81131a70a4)