dotnet/wpf · error · ObjectDisposedException
SR.CryptoProviderDisposed
Error message
SR.CryptoProviderDisposed
What it means
CryptoProvider.CheckDisposed throws ObjectDisposedException with message SR.CryptoProviderDisposed when any member (Encrypt, Decrypt, BlockSize, CanMergeBlocks, BoundGrants, CanEncrypt, CanDecrypt, etc.) is used after Dispose was called. The provider wraps an unmanaged DRM handle that is released on Dispose, so further calls are invalid.
Solutions
- Keep the CryptoProvider alive for the whole usage window; extend the using scope or dispose only after last use
- Re-create the provider from the UseLicense and SecureEnvironment when the old one was disposed
- Check _disposed via a public wrapper or track disposal in your own flag before calling members
- Centralize ownership so exactly one component disposes the provider
Example fix
// before
CryptoProvider provider;
using (var tmp = useLicense.Bind(env)) { provider = tmp; } // disposed here
provider.Encrypt(data); // ObjectDisposedException
// after
using (var provider = useLicense.Bind(env))
{
var cipher = provider.Encrypt(data); // used inside lifetime
} Defensive patterns
Strategy: try-catch
Validate before calling
bool usable = provider != null && !isDisposedFlag; // track disposal in a wrapper class
Type guard
bool IsUsable(CryptoProvider p) => p != null && !isDisposedTracker.Contains(p);
Try / catch
try { blockSize = provider.BlockSize; }
catch (ObjectDisposedException)
{ provider = useLicense.Bind(env); blockSize = provider.BlockSize; } Prevention
- Scope using blocks to cover the entire provider usage window
- Never cache a provider beyond its owning using/dispose scope
- Assign ownership of Dispose to a single component
When it happens
Trigger: Calling Encrypt/Decrypt/BlockSize/CanMergeBlocks/BoundGrants/CanEncrypt on a CryptoProvider after its Dispose() ran — commonly due to a using block, deterministic finalization, or caching the provider beyond the scope in which it was disposed.
Common situations: Storing the CryptoProvider in a long-lived field while wrapping creation in using; reusing a provider across requests after a request-scoped dispose; double-dispose then late access.
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/3213c3cbc63d462e.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Security/RightsManagement/CryptoProvider.cs:373
else
{
return new UnsignedPublishLicense(BoundLicenseOwnerViewRightsHandle, serializedPublishLicense);
}
}
//------------------------------------------------------
//
// Private Methods
//
//------------------------------------------------------
/// <summary>
/// Call this before accepting any API call
/// </summary>
private void CheckDisposed()
{
if (_disposed)
throw new ObjectDisposedException(null, SR.CryptoProviderDisposed);
}
private int QueryBlockSize()
{
uint attributeSize = 0;
byte[] dataBuffer = null;
uint encodingType;
int hr = SafeNativeMethods.DRMGetInfo(DecryptorHandle,
NativeConstants.QUERY_BLOCKSIZE,
out encodingType,
ref attributeSize,
null);
Errors.ThrowOnErrorCode(hr);
// it must return 4 bytes for block size View on GitHub (pinned to 81131a70a4)