我正在使用 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+ 中)
最佳答案
一般错误 异常是一个非常难以诊断的异常。除了“它不起作用”之外,它传达的信息很少。每当 Graphics 类在使用绘图对象或将绘图命令渲染到底层设备上下文时遇到问题,就会引发异常。在这段代码中以及您为排除故障所做的工作中,有一个明确而明显的原因:程序内存不足。
Graphics 类将其底层设备上下文视为非托管资源,这是您没有得到更明显的 OutOfMemoryException 的基本原因。它通常是,就像当您使用它来渲染到屏幕或打印机时,只是在这种情况下不是因为它渲染到 MemoryStream。您可能会在 VS 输出窗口中看到它的第一次机会通知。在任务管理器中添加提交大小列可以提供额外的诊断,当它超过 1 GB 时,问题就开始了。
这段代码特别值得注意的是,程序总是会因为这个异常而失败。给它一个页数太多的报告或一个记录太多的数据表,它就注定要失败。不可避免地总是需要太多内存来存储内存流中的图元文件记录。您唯一能做的就是提高程序的内存效率,以便它可以处理生产需求。这里有很多机会。
首先观察到您从 MSDN 代码示例中继承了一些草率。这是常见的,通常需要注意的是,此类示例侧重于演示编码技术。使代码防弹会妨碍任务,未经测试并留给读者作为练习。值得注意的是,它过多地忽略了 Dispose() 的必要性。提供的 Dispose() 方法实际上并没有完成任何事情,处理内存流只是将其标记为不可读。它没有做的是正确处理 Metafile、LocalReport 和 PrintDocument 对象。使用 using
语句更正这些遗漏。
第二个观察结果是,添加到 CreateStream() 方法非常浪费。也是一种糟糕的浪费,它在大对象堆上非常粗糙。不需要 Copy() DataTable,报表不会写入它。无需将 MemoryStream 转换为数组并再次从该数组创建 MemoryStream,第一个 MemoryStream 已经很好了。不要使用 using,将其 Position 设置为 0。这很可能足以解决问题。
如果您仍然遇到问题,那么您应该考虑使用 FileStream 而不是 MemoryStream。它将同样高效,操作系统确保如此,必须为文件选择一个名称是唯一的额外负担。这里不是真正的问题,使用 Path.GetTempFileName()。请注意 Dispose() 方法现在如何变得有用和必要,您还需要再次删除该文件。或者更好的是,在打开文件时使用 FileOptions.DeleteOnClose 选项,这样它就可以自动打开了。
最后但并非最不重要的一点是,您需要利用操作系统的功能,现代机器可以提供数 TB 的地址空间,而 LOH 碎片从来都不是问题。项目 > 属性 > 构建选项卡 > 取消选中“首选 32 位”复选框。重复发布配置。当您遇到内存不足问题时,您永远不会喜欢它。
关于c# - 在没有预览的情况下打印本地报告 - 超出流大小或 GDI+ C# 中发生一般错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45389632/