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

2

8

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: enter image description here

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+)

Addle answered 29/7, 2017 at 13:4 Comment(17)
The code is producing html from an xml file. The xml may contain special characters (en.wikipedia.org/wiki/…), So you may need to use System.Net.WebUtility.HtmlEncode()Oligopoly
@Oligopoly If I say I have passed the list of stream from byte array to stream and than creating the Metafile in Print event handler should I have to do as you said sir? Please see the question I have update it. Please see CreateStream function.Addle
Actually this code works and printing as well but if I add more content either it is static or from dataset in rdlc it gives me error A generic error occurred in GDI+ C#.Addle
The rdlc is html which is ascii. So I would look at text being sent to the printer in notepad and compare the working file with the non-working file.Oligopoly
@jsweng Can you please explain bit more what to do because sent file is a stream file and what should I find to check the difference with working and non working. The main thing I can say working RDLC has less content compare to non working RDLC.Addle
For testing, you can write to a file using code like this : byte[] buffer = new byte[stream.Length]; stream.Write(buffer, 0, (int)stream.Length); File.WriteAllBytes("filename",buffer );Oligopoly
How can I write sir the problem is with Metafile because it is used for drawing in printer how can I write that in paper sir!Addle
Not sure why you are using the binary serialization. Printer probably want an html stream.Oligopoly
Can I send direct Html stream to printer without preview for print in place of Metafile?Addle
What method causes an exception? Have you tried converting metafiles to bitmaps? What is the size of your metafile?Hilde
@Hilde how to find the size of metafile? and how to convert metafile to bitmaps? Will printer will get the bitmaps data?Addle
@3rules You can determine binary size by dumping content of the stream into some binary file - as jdweng suggested. Then you should be able to view metafile size and try to determine what fails - maybe your report is just too complex for Metafile class implementation.Hilde
@Hilde ok let me check sirAddle
@3rules btw this might be related and this might be a solution.Hilde
@Hilde ohhh thanks I will look into that and applyAddle
Share your stack inner exception please?Airel
@Airel gives me this error A generic error occurred in GDI+ C# nothing elseAddle
B
1

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;
        }
Blab answered 1/3, 2018 at 9:1 Comment(4)
woow it's running thanks but I am confused if we are just creating the blank stream in the function than return it than there is no use of this function right?Addle
@3rules I also don't know the exact reason but when you are calling report.Render("Image", deviceInfo, CreateStream,out warnings); here you must have to define callback function so as I did it and it is running. But as I will know the reason I will update answer for sure.Blab
You always welcome by the way answer by Hans Passant have the same solution read it carefully and can make it as selected answerBlab
I don't understand it so I tell him to explain in more easy way so will do it after get it and also your answer is just the exact answer what I am finding so people will directly use it and than can read it why this happen answer by Hans Passant sir.Addle
I
4

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.

Inexhaustible answered 29/7, 2017 at 13:5 Comment(3)
thanks for the answer but not able to understand it clearly sir could you please give me an easy explanation so I can understand it by the way I got the solution as per my question but still I want to know why this happen!Addle
Your program runs out of memory. Provide more memory.Inexhaustible
Ohhh but how sir?Addle
B
1

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;
        }
Blab answered 1/3, 2018 at 9:1 Comment(4)
woow it's running thanks but I am confused if we are just creating the blank stream in the function than return it than there is no use of this function right?Addle
@3rules I also don't know the exact reason but when you are calling report.Render("Image", deviceInfo, CreateStream,out warnings); here you must have to define callback function so as I did it and it is running. But as I will know the reason I will update answer for sure.Blab
You always welcome by the way answer by Hans Passant have the same solution read it carefully and can make it as selected answerBlab
I don't understand it so I tell him to explain in more easy way so will do it after get it and also your answer is just the exact answer what I am finding so people will directly use it and than can read it why this happen answer by Hans Passant sir.Addle

© 2022 - 2024 — McMap. All rights reserved.