8

この記事を使用して rdlc をプリンターに直接印刷していMetafileますが、渡してオブジェクトを作成しようとするstreamとエラーが発生します。( GDI+ で一般的なエラーが発生しました)

コード:

 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();
            }
        }
    }

ストリーム サイズが超過した場合、または rdlc 静的コンテンツがそれ以上の場合、エラーが発生します。

ストリームを作成するために使用するデータセットは次のとおりです。 ここに画像の説明を入力

静的コンテンツがストリーム サイズに影響を与えてはならないかどうかはわかりませんが、rdlc から一部のコンテンツを削除してもエラーは発生しませんが、それを追加すると再びエラーが発生します ( GDI+ で一般的なエラーが発生しました) 。

4

2 に答える 2

4

一般的なエラー例外は、診断するにはかなり厄介な例外です。「うまくいかなかった」以外の情報はほとんどありません。Graphics クラスで描画オブジェクトを使用したり、基になるデバイス コンテキストに描画コマンドをレンダリングしたりする際に問題が発生すると、常に例外が発生します。このコードとトラブルシューティングのために行ったことから、明確で明白な理由があります:プログラムがメモリ不足になりました。

Graphics クラスは、基になるデバイス コンテキストをアンマネージ リソースとして扱います。これが、より明白な OutOfMemoryException を取得しない基本的な理由です。通常は、画面やプリンターにレンダリングするために使用する場合と同様ですが、この場合は MemoryStream にレンダリングするため、そうではありません。VS 出力ウィンドウで最初のチャンスの通知を確認できる可能性があります。タスク マネージャーに [コミット サイズ] 列を追加すると、追加の診断が提供されます。1 ギガバイトを超えると問題が発生します。

このコードで特に注目すべき点は、プログラムが常にこの例外で失敗することです。ページ数が多すぎるレポートや、レコード数が多すぎるデータ テーブルを作成すると、失敗する運命にあります。メタファイル レコードをメモリ ストリームに格納するには、必然的に常に大量のメモリが必要になります。これに対してできる唯一のことは、プログラムのメモリ効率を高めて、本番環境の要求に対応できるようにすることです。ここにはたくさんの機会があります。

最初に観察したことは、MSDN コード サンプルからずさんな部分を受け継いでいることです。これは一般的であり、一般的に注意すべきことですが、そのようなサンプルはコーディング手法のデモンストレーションに焦点を当てています。コードを防弾にすることはミッションの邪魔になり、テストされず、読者への演習として残されます。注目すべきは、Dispose() の必要性を無視しすぎていることです。提供されている Dispose() メソッドは、実際には何も達成しません。メモリ ストリームを破棄しても、読み取り不能としてマークするだけです。Metafile、LocalReport、および PrintDocument オブジェクトを適切に破棄することはできません。usingステートメントを使用して、これらの省略を修正します。

2 つ目の注意点は、CreateStream() メソッドへの追加が非常に無駄であるということです。また、ラージ オブジェクト ヒープが非常に粗いという悪い種類の無駄もあります。DataTable を Copy() する必要はありません。レポートは DataTable に書き込みません。MemoryStream を配列に変換し、配列から再度 MemoryStream を作成する必要はありません。最初の MemoryStream はそのままで十分です。using を使用しないでください。Position を 0 に設定してください。問題を解決するには、これで十分な可能性があります。

それでも問題が解決しない場合は、MemoryStream の代わりに FileStream を使用することを検討してください。ファイルの名前を選択する必要があるのは、追加の負担だけです。ここでは実際の問題ではありません。Path.GetTempFileName() を使用してください。Dispose() メソッドがどのように便利になり、必要になったかに注意してください。また、ファイルを再度削除する必要があります。または、ファイルを開くときに FileOptions.DeleteOnClose オプションを使用して、自動化します。

最後になりましたが、OS の機能を利用したいと思うでしょう。最新のマシンは数テラバイトのアドレス空間を提供でき、LOH フラグメンテーションは決して問題ではありません。[プロジェクト] > [プロパティ] > [ビルド] タブ > [32 ビットを優先] チェックボックスのチェックを外します。リリース構成について繰り返します。メモリ不足の問題と戦うときは、決して好まないでしょう。

于 2017-08-16T12:08:49.347 に答える