dotnet/wpf · error · IOException
Current stream object was closed and disposed. Cannot…
Error message
Current stream object was closed and disposed. Cannot access a closed stream.
What it means
ManagedIStream.Stat reports stream attributes by probing CanRead/CanWrite. A stream that is neither readable nor writable is treated as closed, so it throws IOException with the StreamObjectDisposed message — deliberately IOException (not ObjectDisposedException) because the interop marshaller does not map ObjectDisposedException back to COM HRESULTs correctly.
Solutions
- Keep the underlying managed stream open for as long as the IStream wrapper is used by native code.
- Check stream.CanRead || stream.CanWrite before calling Stat and reopen/re-create the stream if false.
- Catch IOException and treat it as a disposed stream: re-open the source and rebuild the ManagedIStream.
Example fix
// before
pIStream.Stat(out stat, 0); // stream already closed
// after
if (stream.CanRead || stream.CanWrite)
pIStream.Stat(out stat, 0);
else
stream = File.OpenRead(path); // re-open before retrying Defensive patterns
Strategy: try-catch
Validate before calling
bool streamAlive = stream != null && (stream.CanRead || stream.CanWrite);
Type guard
static bool IsOpen(Stream s) => s != null && (s.CanRead || s.CanWrite);
Try / catch
try { pIStream.Stat(out stat, 0); } catch (IOException) when (!stream.CanRead && !stream.CanWrite) { /* stream disposed: re-open and rebuild IStream */ } Prevention
- Keep the managed stream alive while native code holds the IStream wrapper
- Use GC.KeepAlive or extend using-scope across all interop calls
- Check CanRead/CanWrite before Stat and other IStream members
When it happens
Trigger: Calling IStream.Stat (or ManagedIStream.Stat directly) on a Stream that has been closed/disposed, so both CanRead and CanWrite are false.
Common situations: COM clients holding an IStream reference after the managed stream was disposed; disposing a Package/FileStream while unmanaged code still holds the IStream wrapper; finalizer ordering closing the stream before Stat is called.
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
- SR.SeekFailed
- 0x80070057
- ' ' is not a valid value for ' '.
- ArgumentNullException (buffer/sourceBuffer was IntPtr.Zero)
- ArgumentNullException (hwnd)
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/24fc65634071d63c.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/IO/Packaging/ManagedIStream.cs:136
};
if (_ioStream.CanRead && _ioStream.CanWrite)
{
streamStats.grfMode |= NativeMethods.STGM_READWRITE;
}
else if (_ioStream.CanRead)
{
streamStats.grfMode |= NativeMethods.STGM_READ;
}
else if (_ioStream.CanWrite)
{
streamStats.grfMode |= NativeMethods.STGM_WRITE;
}
else
{
// A stream that is neither readable nor writable is a closed stream.
// Note the use of an exception that is known to the interop marshaller
// (unlike ObjectDisposedException).
throw new IOException(SR.StreamObjectDisposed);
}
}
/// <summary>
/// Write at most bufferSize bytes from buffer.
/// </summary>
void IStream.Write(Byte[] buffer, Int32 bufferSize, IntPtr bytesWrittenPtr)
{
_ioStream.Write(buffer, 0, bufferSize);
if (bytesWrittenPtr != IntPtr.Zero)
{
// If fewer than bufferSize bytes had been written, an exception would
// have been thrown, so it can be assumed we wrote bufferSize bytes.
Marshal.WriteInt32(bytesWrittenPtr, bufferSize);
}
}
#region Unimplemented methodsView on GitHub (pinned to 81131a70a4)