C# - Cannot access a closed stream - c#

My code for PDF creation with iTextSharp version 5.5.13.2 is returning error, "Cannot access a closed stream."
I am unsure how this error could arise as I have my code encapsulated within reaches of Using statement. Debugging results in app going to break state.
PdfWriter writer = PdfWriter.GetInstance(doc, ms);

Looking at the source code for the (deprecated) iTextSharp 5.5.13.2 here, I can find the source for the DocWriter (base class of PdfWriter) and it's Close method here
public virtual void Close() {
open = false;
os.Flush();
if (closeStream)
os.Close();
}
os in this case is whatever was passed as the second argument to PdfWriter.GetInstance (ms in your case). Using Ctrl + F I can find the source for closeStream, which happens to be a property exposes as CloseStream here
public virtual bool CloseStream {
get {
return closeStream;
}
set {
closeStream = value;
}
}
And all together Close is automatically called by the Dispose method of DocWriter
public virtual void Dispose() {
Close();
}
So, if you don't want the PdfWriter to close your ms, you'll need to set writer.CloseStream = false; before your PdfWriter gets closed

Related

Printing a Local Report without Preview - Stream size exceeded or A generic error occurred in GDI+ C#

I am using this article to print my rdlc directly to printer but when I am trying to create Metafile object by passing stream it gives me error. (A generic error occurred in GDI+)
Code:
using System;
using System.IO;
using System.Data;
using System.Text;
using System.Drawing.Imaging;
using System.Drawing.Printing;
using System.Collections.Generic;
using System.Windows.Forms;
using Microsoft.Reporting.WinForms;
public class Demo : IDisposable
{
private int m_currentPageIndex;
private IList<Stream> m_streams;
// Routine to provide to the report renderer, in order to
// save an image for each page of the report.
private Stream CreateStream(string name, string fileNameExtension, Encoding encoding, string mimeType, bool willSeek)
{
DataSet ds = new DataSet();
ds.Tables.Add(dsData.Tables[0].Copy());
using (MemoryStream stream = new MemoryStream())
{
IFormatter bf = new BinaryFormatter();
ds.RemotingFormat = SerializationFormat.Binary;
bf.Serialize(stream, ds);
data = stream.ToArray();
}
Stream stream1 = new MemoryStream(data);
m_streams.Add(stream1);
return stream1;
}
// Export the given report as an EMF (Enhanced Metafile) file.
private void Export(LocalReport report)
{
string deviceInfo =
#"<DeviceInfo>
<OutputFormat>EMF</OutputFormat>
<PageWidth>8.5in</PageWidth>
<PageHeight>11in</PageHeight>
<MarginTop>0.25in</MarginTop>
<MarginLeft>0.25in</MarginLeft>
<MarginRight>0.25in</MarginRight>
<MarginBottom>0.25in</MarginBottom>
</DeviceInfo>";
Warning[] warnings;
m_streams = new List<Stream>();
report.Render("Image", deviceInfo, CreateStream,
out warnings);
foreach (Stream stream in m_streams)
stream.Position = 0;
}
// Handler for PrintPageEvents
private void PrintPage(object sender, PrintPageEventArgs ev)
{
Metafile pageImage = new
Metafile(m_streams[m_currentPageIndex]);
// Adjust rectangular area with printer margins.
Rectangle adjustedRect = new Rectangle(
ev.PageBounds.Left - (int)ev.PageSettings.HardMarginX,
ev.PageBounds.Top - (int)ev.PageSettings.HardMarginY,
ev.PageBounds.Width,
ev.PageBounds.Height);
// Draw a white background for the report
ev.Graphics.FillRectangle(Brushes.White, adjustedRect);
// Draw the report content
ev.Graphics.DrawImage(pageImage, adjustedRect);
// Prepare for the next page. Make sure we haven't hit the end.
m_currentPageIndex++;
ev.HasMorePages = (m_currentPageIndex < m_streams.Count);
}
private void Print()
{
if (m_streams == null || m_streams.Count == 0)
throw new Exception("Error: no stream to print.");
PrintDocument printDoc = new PrintDocument();
if (!printDoc.PrinterSettings.IsValid)
{
throw new Exception("Error: cannot find the default printer.");
}
else
{
printDoc.PrintPage += new PrintPageEventHandler(PrintPage);
m_currentPageIndex = 0;
printDoc.Print();
}
}
// Create a local report for Report.rdlc, load the data,
// export the report to an .emf file, and print it.
private void Run()
{
LocalReport report = new LocalReport();
LocalReport report = new LocalReport();
report.ReportPath = #"Reports\InvoiceReportTest.rdlc";
report.DataSources.Add(
new ReportDataSource("DataSet1", dsPrintDetails));
Export(report);
Print();
}
public void Dispose()
{
if (m_streams != null)
{
foreach (Stream stream in m_streams)
stream.Close();
m_streams = null;
}
}
public static void Main(string[] args)
{
using (Demo demo = new Demo())
{
demo.Run();
}
}
}
It gives me error when stream size exceed or rdlc static content is more.
My dataset that I use to create stream of it is:
I don't know whether static content should not affect stream size or not but it is not giving me any error if I remove some content from rdlc but when I add that it again throw error (A generic error occurred in GDI+)
A generic error exception is a pretty lousy exception to diagnose. It conveys little info beyond "it did not work". The exception is raised whenever the Graphics class runs into trouble using drawing objects or rendering the drawing commands to the underlying device context. There is a clear and obvious reason for that in this code and from the things you did to troubleshoot it: the program ran out of memory.
The Graphics class treats its underlying device context as unmanaged resource, the basic reason why you don't get the more obvious OutOfMemoryException. It usually is, like when you use it to render to the screen or a printer, just not in this case because it renders to a MemoryStream. Some odds that you can see the first-chance notification for it in the VS Output window. Adding the Commit Size column in Task Manager can provide an additional diagnostic, trouble starts when it heads north of a gigabyte.
What is especially notable about this code that the program will always fail with this exception. Give it a report with too many pages or a data table with too many records and it is doomed. It will inevitably always require too much memory to store the metafile records in the memory streams. The only thing you can do about it is to make the program more memory-efficient so it can deal with production demands. Lots of opportunities here.
First observation is that you inherited some sloppiness from the MSDN code sample. Which is common and something in general to beware of, such samples focus on demonstrating coding techniques. Making the code bullet-proof gets in the way of the mission, untested and left as an exercise to the reader. Notable is that it ignores the need to Dispose() too much. The provided Dispose() method does not actually accomplish anything, disposing a memory stream merely marks it as unreadable. What it does not do is properly dispose the Metafile, LocalReport and PrintDocument objects. Use the using statement to correct these omissions.
Second observation is that the addition to the CreateStream() method is hugely wasteful. Also the bad kind of waste, it is very rough on the Large Object Heap. There is no need to Copy() the DataTable, the report doesn't write to it. There is no need to convert the MemoryStream to an array and create a MemoryStream from the array again, the first MemoryStream is already good as-is. Don't use using, set its Position to 0. This is pretty likely good enough to solve the problem.
If you still have trouble then you should consider using a FileStream instead of a MemoryStream. It will be just as efficient, the OS ensures it is, having to pick a name for the file is the only additional burden. Not a real issue here, use Path.GetTempFileName(). Note how the Dispose() method now becomes useful and necessary, you'll also want to delete the file again. Or better, use the FileOptions.DeleteOnClose option when you open the file so it is automagic.
And last but not least, you'll want to take advantage of the OS capabilities, modern machines can provide terabytes of address space and LOH fragmentation is never a problem. Project > Properties > Build tab > untick the "Prefer 32-bit" checkbox. Repeat for the Release configuration. You never prefer it when you battle out-of-memory problems.
At my end using the same functions as you are using and getting the same problem don't know why I use the provided function but it's running at my end so use this function may solve your problem:
private Stream CreateStream(string name, string fileNameExtension, Encoding encoding, string mimeType, bool willSeek)
{
Stream stream = new MemoryStream();
m_streams.Add(stream);
return stream;
}

Explorer Wont Release Files After Using My Thumbnail Provider

I've set up a thumbnail provider for a file type.
The project is made with
C#
.NET 4.5
And I am running Windows x64
My provider successfully generates the thumbnail as expected and I can move, delete, copy, ect, the file. The issue of the locking seems to be caused by the file being placed in a folder. At that point, moving, deleting, ect, the folder all show the error, "File In Use".
I have confirmed Explore locking the file using Sysinternal's Process Explorer, if you are familiar with it.
I've tried 2 approaches to try to resolve this...
implemented IThumbnailProvider and IInitializeWithStream myself.
used 3rd party Sharpshell
Both suffer from this same issue, the file not being released.
On Sharpshell's github, an issue has been started specifying this too.
https://github.com/dwmkerr/sharpshell/issues/78
I associate the file type in the registry like so
HKEY_CLASSES_ROOT
---- .qb
----shellex
----{e357fccd-a995-4576-b01f-234630154e96} : my CLSID...
I have also tried instead...
HKEY_CLASSES_ROOT
---- .qb
-----PersistentHandler : my CLSID...
Both result in this issue being created.
If I was to implement IExtractImage instead... will I see the same issue?
I know C# isn't "officially" supported to do this, is that where my issue lies? If I was to implement this in C++ would I wind up with the same issue?
EDIT:
I'd like to mention the file after around 1 minute seems to get freed, and things go back to normal.
Thumbnail Creation
Some bytes are read into a buffer... then then image is generated from that.
public void GetThumbnail(int cx, out IntPtr hBitmap, out WTS_ALPHATYPE bitmapType)
{
... bunch of other code
using (MemoryStream steam = new MemoryStream(buffer))
using (var image = new Bitmap(steam))
using (var scaled = new Bitmap(image, cx, cx))
{
hBitmap = scaled.GetHbitmap();
hBitmap = (IntPtr)(hBitmap.ToInt64());
}
}
EDIT 2:
Doing some more testing, I called DeleteObject(hBitmap), (even though this destroys the thumbnail), and the file is still locked. I even removed all the code from GetThumbnail... just gives the same result, file locked. There has to be something more going on?
Turns out you need to release the COM IStream object that you get from IInitializeWithStream.
I came to this conclusion by reading more about disposing COM objects.
Proper way of releasing COM objects?
I followed MS's example on how to wrap IStream
https://msdn.microsoft.com/en-us/library/jj200585%28v=vs.85%29.aspx
public class StreamWrapper : Stream
{
private IStream m_stream;
// initialize the wrapper with the COM IStream
public StreamWrapper(IStream stream)
{
if (stream == null)
{
throw new ArgumentNullException();
}
m_stream = stream;
}
// .... bunch of other code
protected override void Dispose(bool disposing)
{
if (m_stream != null)
{
Marshal.ReleaseComObject(m_stream); // releases the file
m_stream = null;
}
}
}
Here's a sample.
Follow the link above to see StreamWrapper's implementation...
[ComVisible(true), ClassInterface(ClassInterfaceType.None)]
[ProgId("mythumbnailer.provider"), Guid("insert-your-guid-here")]
public class QBThumbnailProvider : IThumbnailProvider, IInitializeWithStream
{
#region IInitializeWithStream
private StreamWrapper stream{ get; set; }
public void Initialize(IStream stream, int grfMode)
{
// IStream passed to our wrapper which handles our clean up
this.stream = new StreamWrapper(stream);
}
#endregion
#region IThumbnailProvider
public void GetThumbnail(int cx, out IntPtr hBitmap, out WTS_ALPHATYPE bitmapType)
{
hBitmap = IntPtr.Zero;
bitmapType = WTS_ALPHATYPE.WTSAT_ARGB;
try
{
//... bunch of other code
// set the hBitmap somehow
using (MemoryStream stream = new MemoryStream(buffer))
using (var image = new Bitmap(stream))
using (var scaled = new Bitmap(image, cx, cx))
{
hBitmap = scaled.GetHbitmap();
}
}
catch (Exception ex)
{
}
// release the IStream COM object
stream.Dispose();
}
#endregion
}
Basically it comes down to two lines of code
Marshal.ReleaseComObject(your_istream); // releases the file
your_istream = null;
Side Note
The GDI Bitmap created with scaled.GetHbitmap(); probably needs to disposed of, but I can't find a way to do it without loosing the created thumbnail.

How should I wrap a MemoryStream to have access to the data that was written to it even after it is closed

I am attempting to use a ThirdParty library to write some data to a MemoryStream so that I can compare the output as part of some unit tests. Unfortunately the Third Party library closes the MemoryStream during execution of it Save() method.
Thus I have the following code:
byte[] expected = LoadExpectedResult("Test1");
using (var memoryStream = new MemoryStream()) {
ThirdPartyLibrary.Save(memoryStream);
var result = memoryStream.ToArray();
ConfirmBinaryBlobsAreSufficentlyEqual(expected, result);
}
Unfortunately it appears that the memoryStream.ToArray() function is only returning the last 3398 bytes that were the last loaded into the buffer as it has been disposed as part of the save process.
Is there anything I can wrap the MemoryStream in so that as data is written to it, it gets read out or written to another memory stream so that when it is disposed of I can still have access to the data.
Update
For clarity the Save() method also does the writing out so before it is called the MemoryStream is empty. I think the writers of the library expected you to only pass in FileStreams.
You can try with:
public class MyMemoryStream : MemoryStream
{
public bool CanDispose { get; set; }
public override void Close()
{
if (!CanDispose)
{
return;
}
base.Close();
}
}
In the Stream class, the Dispose() calls Close() that then calls Dispose(bool disposing). Close() is virtual, so I overrode it.
After using the stream, set CanDispose = true and then let it be disposed normally.
byte[] expected = LoadExpectedResult("Test1");
using (var memoryStream = new MyMemoryStream()) {
// implicitly memoryStream.CanDispose == false;
ThirdPartyLibrary.Save(memoryStream);
var result = memoryStream.ToArray();
ConfirmBinaryBlobsAreSufficentlyEqual(expected, result);
memoryStream.CanDispose = true;
}
To circumvent the bug/behaviour: you could either copy the memorystream to a second instance or write it to some temp-file before calling ThirdPartyLibrary.Save.

PdfStamper being disposed

The PdfStamper I'm passing in to this method is being disposed of at the end of the method - why, and how do I stop it? I'm trying to create a page object from the template, which I can then add to the PdfStamper X number of times.
//real code
public void DoSpecialAction(PdfStamper pdfStamper)
{
using (var pdfTemplate = new PdfReader(_extraPageTemplatePath))
using (var pdfReader = new PdfReader(pdfTemplate))
{
PdfImportedPage page = pdfStamper.GetImportedPage(pdfReader, 1);
pdfStamper.InsertPage(3, pdfReader.GetPageSize(1));
PdfContentByte pb = pdfStamper.GetUnderContent(3);
pb.AddTemplate(page, 0, 0);
}
}
the program structure is as follows:
//psuedocode
class PrintFieldsToPdf {
foreach (normalfield) {
PrintNormalFields();
}
foreach (specialaction) {
DoSpecialAction(pdfStamper);
}
pdfStamper.Close(); //at this point the object has been deallocated
}
Throwing the following exception:
An exception of type 'System.ObjectDisposedException' occurred in mscorlib.dll but was not handled in user code
Additional information: Cannot access a closed file.
The OP eventually commented:
I have a hunch it may be that the page object never actually gets copied until the PdfStamper calls Close and writes the file, and therefore the PdfReader I'm using to read the extra page template is causing the issue, as it is disposed of at the end of my method, before PdfStamper is closed.
His hunch was correct: The copying of at least certain parts of the original page is delayed until the PdfStamper is being closed. This allows for certain optimizations in case multiple pages from the same PdfReader instance are imported in separate calls.
The use case of imports from many different PdfReaders had also been on the mind of the iText(Sharp) developers. So they provided a way to tell the PdfStamper to copy everything required from a given PdfReader at the time the user is sure he won't copy anything else from it:
public void DoSpecialAction(PdfStamper pdfStamper)
{
using (var pdfTemplate = new PdfReader(_extraPageTemplatePath))
using (var pdfReader = new PdfReader(pdfTemplate))
{
PdfImportedPage page = pdfStamper.GetImportedPage(pdfReader, 1);
pdfStamper.InsertPage(3, pdfReader.GetPageSize(1));
PdfContentByte pb = pdfStamper.GetUnderContent(3);
pb.AddTemplate(page, 0, 0);
// Copy everything required from the PdfReader
pdfStamper.Writer.FreeReader(pdfReader);
}
}

Disposing of object multiple times

I have the following code, which uses a stream to open and modify an Open XML document, and then save the new binary representation of that stream:
MemoryStream stream = null;
try
{
stream = new MemoryStream();
stream.Write(this.GetBinaryRepresentation(), 0, this.GetBinaryRepresentation().Length);
using (WordprocessingDocument document = WordprocessingDocument.Open(stream, true))
{
OfficeDocument.ModifyDocument(document);
this.SetBinaryRepresentation(stream.ToArray());
stream = null;
}
}
finally
{
if (stream != null)
{
stream.Dispose();
}
}
I had originally used two using blocks (one for the MemoryStream and the second for the WordprocessingDocument), but received warning CA2202: "Object 'stream' can be disposed more than once in method..." Per the MSDN article, I modified the code to above (converting the outer using to a try), but I am still receiving this warning.
I'm unsure of how I can structure this method to ensure that Dispose is called exactly once on the stream. I would prefer not to simply suppress this warning since the MSDN article states that you shouldn't rely on Dispose being safely callable multiple times.
Disposing of an object multiple times should always be safe. From the documentation for Dispose:
If an object's Dispose method is called more than once, the object must ignore all calls after the first one. The object must not throw an exception if its Dispose method is called multiple times.
That being said, a using statement is definitely the way to go here. The only reason you'd receive that method was if you were explicitly disposing of the object, which would not be required, as the using statement should always dispose the object exactly once.
The reason that the example from the MSDN article did not work for you is that they set the stream to null as soon as they enter the using block, whereas you use the stream inside your using block and set the stream to null after. If an exception is thrown before your stream = null statement, stream would be disposed of as the using block is exited, and then again in your finally block.
Unfortunately, since you need to access your stream after document has updated it, I don't see a clean way to use their example of setting stream = null within your using statement to avoid the multiple Dispose() calls. An alternative would be to you could declare both stream and document outside of the try block, and then clean both of them up inside your finally, like so:
MemoryStream stream = null;
WordprocessingDocument document = null;
try
{
stream = new MemoryStream();
stream.Write(this.GetBinaryRepresentation(), 0, this.GetBinaryRepresentation().Length);
document = WordprocessingDocument.Open(stream, true));
OfficeDocument.ModifyDocument(document);
this.SetBinaryRepresentation(stream.ToArray());
}
finally
{
if( document != null)
{
document.Dispose();
}
// Catch the case where an error occurred before document was defined.
else
{
stream.Dispose();
}
}
The stream may still be disposed twice if an exception is thrown in the using block before stream is set to null. Try this:
MemoryStream stream = null;
MemoryStream streamToDispose = null;
try
{
streamToDispose = stream = new MemoryStream();
stream.Write(this.GetBinaryRepresentation(), 0, this.GetBinaryRepresentation().Length);
using (WordprocessingDocument document = WordprocessingDocument.Open(stream, true))
{
streamToDispose = null;
OfficeDocument.ModifyDocument(document);
this.SetBinaryRepresentation(stream.ToArray());
}
}
finally
{
if (streamToDispose != null)
{
streamToDispose.Dispose();
}
}
The using statement disposes the object - so essentially you are calling dispose twice
When your code leaves the using block around the WordProcessingDocument, it will call dispose.
using (WordprocessingDocument document = WordprocessingDocument.Open(stream, true))
Since the WordProcessingDocument takes an instance of stream in its constructor, it will call dispose on that stream instance when WordProcessingDocument.Dispose is called. You then enter the finally block where you call stream.Dispose() - you have now called Dispose() on the stream instance twice.

Categories

Resources