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

3 rules
  • 1,359
  • 3
  • 26
  • 54
  • The code is producing html from an xml file. The xml may contain special characters (https://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references), So you may need to use System.Net.WebUtility.HtmlEncode() – jdweng Jul 29 '17 at 13:30
  • @jdweng 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. – 3 rules Jul 29 '17 at 13:35
  • 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#. – 3 rules Jul 29 '17 at 13:42
  • 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. – jdweng Jul 29 '17 at 13:54
  • @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. – 3 rules Jul 29 '17 at 14:04
  • 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 ); – jdweng Jul 29 '17 at 16:27
  • 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! – 3 rules Jul 31 '17 at 07:03
  • Not sure why you are using the binary serialization. Printer probably want an html stream. – jdweng Jul 31 '17 at 07:23
  • Can I send direct Html stream to printer without preview for print in place of Metafile? – 3 rules Jul 31 '17 at 07:24
  • What method causes an exception? Have you tried converting metafiles to bitmaps? What is the size of your metafile? – vasek Aug 16 '17 at 05:43
  • @vasek how to find the size of metafile? and how to convert metafile to bitmaps? Will printer will get the bitmaps data? – 3 rules Aug 16 '17 at 05:46
  • @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. – vasek Aug 16 '17 at 05:54
  • @vasek ok let me check sir – 3 rules Aug 16 '17 at 05:57
  • @3rules btw [this](https://stackoverflow.com/questions/22426019/save-metafile-to-stream-and-back-again) might be related and [this](https://social.msdn.microsoft.com/Forums/vstudio/en-US/b15357f1-ad9d-4c80-9ec1-92c786cca4e6/bitmapsave-a-generic-error-occurred-in-gdi?forum=netfxbcl) might be a solution. – vasek Aug 16 '17 at 06:01
  • @vasek ohhh thanks I will look into that and apply – 3 rules Aug 16 '17 at 06:17
  • Share your stack inner exception please? – Ramankingdom Aug 21 '17 at 08:22
  • @Ramankingdom gives me this error A generic error occurred in GDI+ C# nothing else – 3 rules Aug 23 '17 at 05:08

2 Answers2

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.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
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;
        }
Mahavirsinh Padhiyar
  • 1,299
  • 11
  • 33
  • 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? – 3 rules Mar 01 '18 at 09:03
  • @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. – Mahavirsinh Padhiyar Mar 01 '18 at 09:06
  • You always welcome by the way answer by Hans Passant have the same solution read it carefully and can make it as selected answer – Mahavirsinh Padhiyar Mar 01 '18 at 09:07
  • 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. – 3 rules Mar 01 '18 at 09:11