在没有预览的情况下打印本地报告 - 超出流大小或 GDI+ C# 中发生一般错误
Printing a Local Report without Preview - Stream size exceeded or A generic error occurred in GDI+ C#
我正在使用 this 文章将我的 rdlc 直接打印到打印机,但是当我尝试通过传递 stream
创建 Metafile
对象时,它给我错误。 (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+)
中发生一般性错误
一般错误 异常是一个非常难诊断的异常。除了 "it did not work",它传达的信息很少。每当 Graphics class 在使用绘图对象或将绘图命令渲染到底层设备上下文时遇到问题时,就会引发异常。在此代码中以及您为排除故障所做的工作中,有一个明确而明显的原因:程序运行内存不足。
图形 class 将其底层设备上下文视为非托管资源,这是您没有得到更明显的 OutOfMemoryException 的基本原因。它通常是,就像当您使用它来渲染到屏幕或打印机时,只是在这种情况下不是因为它渲染到 MemoryStream。您可以在 VS 输出 window 中看到它的第一次机会通知的可能性很小。在任务管理器中添加提交大小列可以提供额外的诊断,当它超过 1 GB 时,问题就开始了。
关于这段代码,特别没有table 的是程序将总是 失败并出现此异常。给它一个页数太多的报告或一个记录太多的数据 table,它注定要失败。不可避免地总是需要太多内存来存储内存流中的图元文件记录。您唯一能做的就是提高程序的内存效率,以便它可以处理生产需求。这里有很多机会。
首先观察到您从 MSDN 代码示例中继承了一些草率。这是常见的,通常需要注意的是,此类示例侧重于演示编码技术。使代码防弹会妨碍任务,未经测试并作为练习留给 reader。 Notable 是它忽略了太多 Dispose() 的需要。提供的 Dispose() 方法实际上并没有完成任何事情,处理内存流只是将其标记为不可读。它没有做的是正确处理 Metafile、LocalReport 和 PrintDocument 对象。使用 using
语句更正这些遗漏。
第二个观察是,对 CreateStream() 方法的添加非常浪费。也是一种糟糕的浪费,它在大对象堆上非常粗糙。不需要 Copy() DataTable,报表不会写入它。无需将 MemoryStream 转换为数组并再次从该数组创建 MemoryStream,第一个 MemoryStream 已经很好了。不要使用使用,将其位置设置为0。这很可能足以解决问题。
如果您仍然遇到问题,那么您应该考虑使用 FileStream 而不是 MemoryStream。它将同样高效,OS 确保它是,必须为文件选择一个名称是唯一的额外负担。这里不是真正的问题,使用 Path.GetTempFileName()。请注意 Dispose() 方法现在如何变得有用和必要,您还需要再次删除该文件。或者更好的是,在打开文件时使用 FileOptions.DeleteOnClose 选项,这样它是自动的。
最后但同样重要的是,您需要利用 OS 功能,现代机器可以提供数 TB 的地址 space 并且 LOH 碎片从来都不是问题。项目 > 属性 > 构建选项卡 > 取消勾选 "Prefer 32-bit" 复选框。重复发布配置。当你遇到内存不足的问题时,你永远不会喜欢它。
在我这边使用与你使用的相同的函数并遇到同样的问题不知道为什么我使用提供的函数但它是 运行 在我这边所以使用这个函数可能会解决你的问题:
private Stream CreateStream(string name, string fileNameExtension, Encoding encoding, string mimeType, bool willSeek)
{
Stream stream = new MemoryStream();
m_streams.Add(stream);
return stream;
}
我正在使用 this 文章将我的 rdlc 直接打印到打印机,但是当我尝试通过传递 stream
创建 Metafile
对象时,它给我错误。 (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+)
中发生一般性错误一般错误 异常是一个非常难诊断的异常。除了 "it did not work",它传达的信息很少。每当 Graphics class 在使用绘图对象或将绘图命令渲染到底层设备上下文时遇到问题时,就会引发异常。在此代码中以及您为排除故障所做的工作中,有一个明确而明显的原因:程序运行内存不足。
图形 class 将其底层设备上下文视为非托管资源,这是您没有得到更明显的 OutOfMemoryException 的基本原因。它通常是,就像当您使用它来渲染到屏幕或打印机时,只是在这种情况下不是因为它渲染到 MemoryStream。您可以在 VS 输出 window 中看到它的第一次机会通知的可能性很小。在任务管理器中添加提交大小列可以提供额外的诊断,当它超过 1 GB 时,问题就开始了。
关于这段代码,特别没有table 的是程序将总是 失败并出现此异常。给它一个页数太多的报告或一个记录太多的数据 table,它注定要失败。不可避免地总是需要太多内存来存储内存流中的图元文件记录。您唯一能做的就是提高程序的内存效率,以便它可以处理生产需求。这里有很多机会。
首先观察到您从 MSDN 代码示例中继承了一些草率。这是常见的,通常需要注意的是,此类示例侧重于演示编码技术。使代码防弹会妨碍任务,未经测试并作为练习留给 reader。 Notable 是它忽略了太多 Dispose() 的需要。提供的 Dispose() 方法实际上并没有完成任何事情,处理内存流只是将其标记为不可读。它没有做的是正确处理 Metafile、LocalReport 和 PrintDocument 对象。使用 using
语句更正这些遗漏。
第二个观察是,对 CreateStream() 方法的添加非常浪费。也是一种糟糕的浪费,它在大对象堆上非常粗糙。不需要 Copy() DataTable,报表不会写入它。无需将 MemoryStream 转换为数组并再次从该数组创建 MemoryStream,第一个 MemoryStream 已经很好了。不要使用使用,将其位置设置为0。这很可能足以解决问题。
如果您仍然遇到问题,那么您应该考虑使用 FileStream 而不是 MemoryStream。它将同样高效,OS 确保它是,必须为文件选择一个名称是唯一的额外负担。这里不是真正的问题,使用 Path.GetTempFileName()。请注意 Dispose() 方法现在如何变得有用和必要,您还需要再次删除该文件。或者更好的是,在打开文件时使用 FileOptions.DeleteOnClose 选项,这样它是自动的。
最后但同样重要的是,您需要利用 OS 功能,现代机器可以提供数 TB 的地址 space 并且 LOH 碎片从来都不是问题。项目 > 属性 > 构建选项卡 > 取消勾选 "Prefer 32-bit" 复选框。重复发布配置。当你遇到内存不足的问题时,你永远不会喜欢它。
在我这边使用与你使用的相同的函数并遇到同样的问题不知道为什么我使用提供的函数但它是 运行 在我这边所以使用这个函数可能会解决你的问题:
private Stream CreateStream(string name, string fileNameExtension, Encoding encoding, string mimeType, bool willSeek)
{
Stream stream = new MemoryStream();
m_streams.Add(stream);
return stream;
}