1

I have a memory stream in a function like so

using var document = new PdfDocument();
var memoryStream = new MemoryStream();
document.Save(memoryStream, new GemBox.Pdf.PdfSaveOptions());
memoryStream.Seek(0, SeekOrigin.Begin);
return memoryStream;

It seems like as soons the function returns, the memoryStream is Disposed, later access will throws

System.ObjectDisposedException: Cannot access a closed Stream.
  • I would like to use this MemoryStream by seeking to 0 and read again and again later. How can I achieve that?

  • If I am using CopyTo, then the new MemoryStream is kept open but is that hugely inefficient? As I understand, the CopyTo will read the whole backing memory and write to the new Stream, basically double the memory usage? The result I received from CopyTo is what I wanted, I am just not sure if that is the right way or if there are better alternatives

      using var document = new PdfDocument();
      var memoryStream = new MemoryStream();
      document.Save(memoryStream, new GemBox.Pdf.PdfSaveOptions());
      memoryStream.Seek(0, SeekOrigin.Begin);
      var newStream = new MemoryStream();
      memoryStream.CopyTo(newStream);
      newStream.Seek(0, SeekOrigin.Begin);
      return newStream;
    

Sample Repro https://dotnetfiddle.net/mCZhug

Callstack at dispose time enter image description here

qkhanhpro
  • 4,371
  • 2
  • 33
  • 45
  • 3
    Use the debugger: Debug > New Breakpoint > Function Breakpoint and type System.IO.Stream.Close(). Once it hits, look at the Debug > Windows > Call Stack window to see how it got there. – Hans Passant Mar 21 '23 at 17:57
  • Can you please show how document is instantiated/created? – Guru Stron Mar 21 '23 at 18:01
  • @GuruStron I updated the question using var document = new PdfDocument() – qkhanhpro Mar 21 '23 at 18:11
  • Was not able to reproduce, can you please post a full [mre]? – Guru Stron Mar 21 '23 at 18:11
  • My [attempt to repro](https://dotnetfiddle.net/luQcBb). – Guru Stron Mar 21 '23 at 18:14
  • @GuruStron I will make a minimal repro but unfortunately it will be in a few hours. Thank you a lot! – qkhanhpro Mar 21 '23 at 18:24
  • @HansPassant Here is the callstack. I do not control the library function https://imgur.com/a/lS6aMl1 – qkhanhpro Mar 22 '23 at 02:50
  • Have you tried playing with the [PdfSaveOptions](https://www.gemboxsoftware.com/pdf/docs/GemBox.Pdf.PdfSaveOptions.html)? I wonder if `CloseInput` or `CloseOutput` have any affect on the memory stream. – Rufus L Mar 22 '23 at 16:06
  • @RufusL they have during `Save` method itself, i.e. if `CloseOutput` is opted in then the stream will be disposed even before the `using` going out of scope. – Guru Stron Mar 22 '23 at 18:56

4 Answers4

2

The reason why it's closed is because the stream ends up being disposed with the PdfDocument.Dispose() call, that is the whole point of that method.

There are two ways how you can resolve your issue, first is to not call PdfDocument.Dispose() (in other words, remove the using statement):

var document = new PdfDocument();
var memoryStream = new MemoryStream();
// ...

The second solution is this:

using var document = new PdfDocument();
document.CloseStream = false;
var memoryStream = new MemoryStream();
// ...

Setting the PdfDocument.CloseStream option to false has the same effect as not calling PdfDocument.Dispose().

Mario Z
  • 4,328
  • 2
  • 24
  • 38
1

PdfDocument has CloseStream property - you set it to false:

document.CloseStream = false;

Though the XML-doc states that:

Setting this option to false has the same effect as not calling Close() nor Dispose().

Based on what I can see it is not all it actually does, though, but it is better to consult with the library authors for actual effect of the setting.

Guru Stron
  • 102,774
  • 10
  • 95
  • 132
1

I often flip these things around and change the signature from MemoryStream Method() to void Method(Action<MemoryStream> action) so that I'm injecting in the code that uses the MemoryStream and then I can let all of the disposes happen.

Something like this:

public void UseMemoryStream(Action<MemoryStream> action)
{
    using var document = new PdfDocument();
    using var memoryStream = new MemoryStream();
    document.Save(memoryStream, new GemBox.Pdf.PdfSaveOptions());
    memoryStream.Seek(0, SeekOrigin.Begin);
    action(memoryStream);
}
Enigmativity
  • 113,464
  • 11
  • 89
  • 172
-1

You could create the MemoryStream in your caller and pass it as an argument to your function. That way the caller has complete control of creation, closing and disposal of the stream.

Lee Boozer
  • 79
  • 6
  • _"That way the caller has complete control of creation, closing and disposal of the stream."_ - that is not completely true, nobody prevents the stream from being disposed inside the method. – Guru Stron Mar 21 '23 at 18:12
  • You're just moving their `MemoryStream` declaration somewhere else, which really isn't changing anything. – LarsTech Mar 21 '23 at 18:14
  • @GuruStron You are correct, of course. Any method that has access to the stream could engage in such ill behavior. – Lee Boozer Mar 21 '23 at 18:17