winform PrintDocument调用打印提示 System.ComponentModel.Win32Exception (0x80004005): 句柄无效?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了winform PrintDocument调用打印提示 System.ComponentModel.Win32Exception (0x80004005): 句柄无效?相关的知识,希望对你有一定的参考价值。

System.ComponentModel.Win32Exception (0x80004005): 句柄无效。
at System.Drawing.Printing.StandardPrintController.OnStartPrint(PrintDocument document, PrintEventArgs e)
at System.Drawing.Printing.PrintController.Print(PrintDocument document)
at System.Drawing.Printing.PrintDocument.Print()

你可以新建一个项目, 窗体上放一个按钮, 双击按钮, 然后复制以下代码到替换Form1,可以运行:

using System.Drawing.Printing; //把这一行添加到头

public partial class Form1 : Form//替换你创建的Form1

public Form1()

InitializeComponent();

private void button1_Click(object sender, EventArgs e)

var printDocument = new PrintDocument();

//指定打印机

printDocument.PrinterSettings.PrinterName = "Microsoft XPS Document Writer";

//设置页边距

printDocument.PrinterSettings.DefaultPageSettings.Margins.Left = 0;

printDocument.PrinterSettings.DefaultPageSettings.Margins.Top = 0;

printDocument.PrinterSettings.DefaultPageSettings.Margins.Right = 0;

printDocument.PrinterSettings.DefaultPageSettings.Margins.Bottom = 0;

//设置尺寸大小,如不设置默认是A4纸

//A4纸的尺寸是210mm×297mm,

//当你设定的分辨率是72像素/英寸时,A4纸的尺寸的图像的像素是595×842

//当你设定的分辨率是150像素/英寸时,A4纸的尺寸的图像的像素是1240×1754

//当你设定的分辨率是300像素/英寸时,A4纸的尺寸的图像的像素是2479×3508,

printDocument.DefaultPageSettings.PaperSize = new PaperSize("A4", 595, 842);

printDocument.PrintPage += new PrintPageEventHandler(printDocument_PrintPage);

try

printDocument.Print();

catch (InvalidPrinterException)

finally

printDocument.Dispose();

void printDocument_PrintPage(object sender, PrintPageEventArgs e)

var printContent = "打印测试";

var printFont = new Font("宋体", 12, System.Drawing.FontStyle.Regular);

var printColor = System.Drawing.Brushes.Black;

var pointY = 10f;

//画字符串

e.Graphics.DrawString(printContent, printFont, printColor, 10f, pointY);

//如何打印带粗体,倾斜,字体中带横线,下划线的字符串,设置字体的FontStyle(粗体,倾斜,字体中带横线,下划线)

printFont = new Font("宋体", 12, System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic);

e.Graphics.DrawString(printContent, printFont, printColor, 10f, pointY += 20f);

printFont = new Font("宋体", 12, System.Drawing.FontStyle.Regular);

//画图像

//e.Graphics.DrawImage(Image, 10, 50);

//设置坐标系缩放

//设置打印坐标系X值为原值的0.6倍打印

e.Graphics.ScaleTransform(0.6f, 1.0f);

e.Graphics.DrawString(printContent, printFont, printColor, 10f, pointY += 20f);

//恢复坐标系缩放

e.Graphics.ScaleTransform(1 / 0.6f, 1.0f);

e.Graphics.DrawString(printContent, printFont, printColor, 10f, pointY += 20f);

//绘画的设置保存与恢复

var status = e.Graphics.Save();

e.Graphics.ScaleTransform(0.6f, 1.0f);

e.Graphics.DrawString(printContent, printFont, printColor, 10f, pointY += 20f);

e.Graphics.Restore(status);

e.Graphics.DrawString(printContent, printFont, printColor, 10f, pointY += 20f);

//如果打印还有下一页,将HasMorePages值置为true;

e.HasMorePages = false;

追问

这个太基础了,没到点子上

参考技术A 默认打印机正常工作吗追问

正常的,是调用着一段时间就出现这样的

追答

这就有点麻烦了
搜了下也没什么有用信息,有的说加这段就好点的(vb.net 你自己转C#)
Private Sub ReportViewer1_PrintingBegin(sender As Object, e As Microsoft.Reporting.WinForms.ReportPrintEventArgs) Handles ReportViewer1.PrintingBegin
ReportViewer1.PrinterSettings.PrinterName = e.PrinterSettings.PrinterName
ReportViewer1.PrinterSettings.FromPage = e.PrinterSettings.FromPage
ReportViewer1.PrinterSettings.ToPage = e.PrinterSettings.ToPage
End Sub

如何使用 PrintDocument 在热敏打印机上打印文本文件?

【中文标题】如何使用 PrintDocument 在热敏打印机上打印文本文件?【英文标题】:How to print a text file on thermal printer using PrintDocument? 【发布时间】:2017-02-03 05:05:41 【问题描述】:

我正在使用带有 Winforms 的 C# 创建应用程序,现在我需要在热敏打印机上打印销售收据。为此,我正在创建一个文本文件并使用PrintDocument 读取它以进行打印,但我不能这样做,因为我不知道如何配置纸张大小、对齐纸张上的文本中心以及其他配置。当我打印时,文本文件被打印出来了,但是很乱,并且在打印结束之后,纸张并没有停止。

我该怎么做?

正在尝试。

private PrintDocument printDocument = new PrintDocument();
private static String RECEIPT = Environment.CurrentDirectory + @"\comprovantes\comprovante.txt";
private String stringToPrint = "";

private void button1_Click(object sender, EventArgs e)

    generateReceipt();
    printReceipt();


private void generateReceipt()

    FileStream fs = new FileStream(COMPROVANTE, FileMode.Create);
    StreamWriter writer = new StreamWriter(fs);
    writer.WriteLine("==========================================");
    writer.WriteLine("          NOME DA EMPRESA AQUI            ");
    writer.WriteLine("==========================================");
    writer.Close();
    fs.Close();


private void printReceipt()

    FileStream fs = new FileStream(COMPROVANTE, FileMode.Open);
    StreamReader sr = new StreamReader(fs);
    stringToPrint = sr.ReadToEnd();
    printDocument.PrinterSettings.PrinterName = DefaultPrinter.GetDefaultPrinterName();
    printDocument.PrintPage += new PrintPageEventHandler(printPage);
    printDocument.Print();
    sr.Close();
    fs.Close();


private void printPage(object sender, PrintPageEventArgs e)

    int charactersOnPage = 0;
    int linesPerPage = 0;
    Graphics graphics = e.Graphics;

    // Sets the value of charactersOnPage to the number of characters 
    // of stringToPrint that will fit within the bounds of the page.
    graphics.MeasureString(stringToPrint, this.Font,
        e.MarginBounds.Size, StringFormat.GenericTypographic,
        out charactersOnPage, out linesPerPage);

    // Draws the string within the bounds of the page
    graphics.DrawString(stringToPrint, this.Font, Brushes.Black,
        e.MarginBounds, StringFormat.GenericTypographic);

    // Remove the portion of the string that has been printed.
    stringToPrint = stringToPrint.Substring(charactersOnPage);

    // Check to see if more pages are to be printed.
    e.HasMorePages = (stringToPrint.Length > 0);

我正在尝试创建这种收据模型。

使用 RDLC,但开始打印很慢。我关注这个example

//works but slow
private void Run() 
    LocalReport report = new LocalReport();
    report.ReportPath = @"..\..\reports\ReciboDeVenda.rdlc";
    Export(report);
    Print();


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;


private Stream CreateStream(string name, string fileNameExtension, 
                            Encoding encoding, string mimeType, bool willSeek) 
        Stream stream = new MemoryStream();
        m_streams.Add(stream);
        return stream;


private void Print() 
    if (m_streams == null || m_streams.Count == 0)
        throw new Exception("Error: no stream to print.");
    PrintDocument printDoc = new PrintDocument();
    printDoc.PrinterSettings.PrinterName = DefaultPrinter.GetDefaultPrinterName();
    if (!printDoc.PrinterSettings.IsValid) 
        throw new Exception("Error: cannot find the default printer.");
    else 
        printDoc.PrintPage += new PrintPageEventHandler(PrintPage);
        m_currentPageIndex = 0;
        printDoc.Print();
    


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

【问题讨论】:

为什么不使用 RDLC 报告? @RezaAghaei 我试过了,但是使用 RDLC 打印很慢,例如开始打印需要 10 秒,我需要按时开始打印。 尝试在没有报告引擎的情况下自己创建报告是在重新发明*** IMO,但您更了解自己的要求 :)。也许有些东西使报告/打印如此缓慢。我在使用 RDLC 报告时没有看到这种延迟,但在托管在服务器上的 RDL 报告中看到了这种延迟,并且由于服务器唤醒以及网络和数据库服务器延迟,延迟是第一步。 @RezaAghaei 我理解你。我将发布如何使用 RDLC 打印此报告,如果您能给出一个想法,我会很高兴。 我没有检查您代码中的性能问题,但我分享了一个有用的选项。 【参考方案1】:

如果我想自己创建报告,而不是尝试创建文本文件,我将使用 HTML 进行呈现。 另外,为了使渲染逻辑和混合 html 标签和数据更简单,我会使用T4 Run-time Text Templates。然后我可以将数据传递给 html 模板并简单地呈现报告。然后将输出字符串分配给WebBrowserDocumentText 属性并调用其Print 方法就足够了。

下载

您可以从 r-aghaei/HtmlUsingRuntimeT4 克隆或下载一个工作示例。

为什么选择 HTML?

因为格式简单灵活。您可以使用 html 标签和 css 样式的所有功能来格式化文本。真的比使用DrawString好。

为什么选择运行时文本模板?

因为它使混合数据和 html 变得非常容易,并且您可以使用 C# 语言执行一些任务,例如计算总和、迭代模型记录等。它使用T4 模板引擎,让您可以在运行时使用模板并将数据提供给模板。

示例

1- 将模型添加到项目中:

using System;
using System.Collections.Generic;

namespace Sample

    public class ReportModel
    
        public string CustomerName  get; set; 
        public DateTime Date  get; set; 
        public List<OrderItem> OrderItems  get; set; 
    
    public class OrderItem
    
        public string Name  get; set; 
        public int Price  get; set; 
        public int Count  get; set; 
    

2- 将运行时文本模板(也称为预处理文本模板)添加到项目中,并将其命名为 ReportTemplate.tt。打开它并添加这样的代码:

<#@ template language="C#"#>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ parameter name="Model" type="Sample.ReportModel"#>
<html>
<head>
    <title></title>
    <style type="text/css">
        body  font-family: Calibri;width:400px;
        table  text-align:center; 
        .container width:400px; height:100%;
    </style>
</head>
<body>
<div class="container">
<h1 style="text-align:center;">Order</h1>
<hr/>
<table style="width:100%">
    <tr>
        <td>Customer: <#=Model.CustomerName#></td>
        <td>Order Date: <#=Model.Date#></td>
    </tr>
</table>
<hr/>
<table style="width:100%">
    <tr><th>Index</th><th>Name</th><th>Price</th><th>Count</th><th>Sum</th></tr>
    <#
    int index =1;
    foreach(var item in Model.OrderItems) 
    
    #>
    <tr>
        <td><#=index#></td>
        <td><#=item.Name#></td>
        <td><#=item.Price#></td>
        <td><#=item.Count#></td>
        <td><#=item.Count * item.Price#></td>
    </tr>
    <#
        index++;
    
    var total= Model.OrderItems.Sum(x=>x.Count * x.Price);
    #>
    <tr><td></td><td></td><td></td><th>Total:</th><th><#=total#></th></tr>
</table>
<div>
</body>
</html>

3- 在表单上放置一个WebBrowser 控件,然后在要生成报告的地方编写这样的代码:

var rpt = new ReportTemplate();
rpt.Session = new Dictionary<string, object>();
rpt.Session["Model"] = new Sample.ReportModel

    CustomerName = "Reza",
    Date = DateTime.Now,
    OrderItems = new List<Sample.OrderItem>()
    
        new Sample.OrderItem()Name="Product 1", Price =100, Count=2 ,
        new Sample.OrderItem()Name="Product 2", Price =200, Count=3 ,
        new Sample.OrderItem()Name="Product 3", Price =50, Count=1 ,
    
;
rpt.Initialize();
this.webBrowser1.DocumentText= rpt.TransformText();

4-如果要打印报告,可以调用:

this.webBrowser1.Print();

您应该在文档完成后致电Print。所以如果你想直接打印而不向用户显示输出,你可以处理DocumentCompleted事件并在那里调用Print

结果如下:

【讨论】:

这个想法对于您想要创建自定义报告模板、电子邮件模板等的情况非常有用。这种技术比使用Graphics.DrawStringTextRenderer.DrawText 要好得多。它更灵活、更简单。 很好的解决方案。由于到处都有额外的&lt;tr&gt;&lt;td&gt; 标签,看起来比 XML 更复杂 @codefrenzy 感谢您的反馈,对于熟悉html的人来说,解决方案非常友好。例如,使用 WebForms 视图引擎或 Razor 视图引擎,您可以在 ASP.NET MVC 中为视图编写代码。 很好。这似乎非常用户友好,易于根据需要进行自定义。 @RezaAghaei 你试过在热敏打印机上打印它吗?它工作正常吗?【参考方案2】:

如果您向printPage(object sender, PrintEventArgs e) 方法发送纯字符串,它只会以看起来“杂乱”的所有相同字体打印纯文本(正如您命名的那样) 如果您希望使用不同的字体(粗体、常规)以良好的格式打印它,您必须手动完成所有操作:

List<string> itemList = new List<string>()

    "201", //fill from somewhere in your code
    "202"
;

private void printPage( object sender, PrintPageEventArgs e )

    Graphics graphics = e.Graphics;

    Font regular = new Font( FontFamily.GenericSansSerif, 10.0f, FontStyle.Regular );
    Font bold = new Font( FontFamily.GenericSansSerif, 10.0f, FontStyle.Bold );

    //print header
    graphics.DrawString( "FERREIRA MATERIALS PARA CONSTRUCAO LTDA", bold, Brushes.Black, 20, 10 );
    graphics.DrawString( "EST ENGENHEIRO MARCILAC, 116, SAO PAOLO - SP", regular, Brushes.Black, 30, 30 );
    graphics.DrawString( "Telefone: (11)5921-3826", regular, Brushes.Black, 110, 50 );
    graphics.DrawLine( Pens.Black, 80, 70, 320, 70 );
    graphics.DrawString( "CUPOM NAO FISCAL", bold, Brushes.Black, 110, 80 );
    graphics.DrawLine( Pens.Black, 80, 100, 320, 100 );

    //print items
    graphics.DrawString( "COD | DESCRICAO                      | QTY | X | Vir Unit | Vir Total |", bold, Brushes.Black, 10, 120 );
    graphics.DrawLine( Pens.Black, 10, 140, 430, 140 );

    for( int i = 0; i < itemList.Count; i++ )
    
        graphics.DrawString( itemList[i].ToString(), regular, Brushes.Black, 20, 150 + i * 20 );
    

    //print footer
    //...

    regular.Dispose();
    bold.Dispose();

    // Check to see if more pages are to be printed.
    e.HasMorePages = ( itemList.Count > 20 );

我的例子的可能改进:

使用graphics.MeasureString() 可以更好地使标题字符串居中。 项目列表最好是使用纯字符串的业务类列表

因为这一切工作量很大,所以您真的应该考虑使用 RDLC 或一些第三方软件来设计您的文档。

【讨论】:

感谢您的关注和您的榜样。我想使用 RDLC,但正如我之前所说,它非常慢。 e.HasMorePages = ( itemList.Count > 20 ); !我认为如果 itemList.Count > 20 对,这将永远不会退出循环!有没有其他方法可以处理热敏打印机的分页?【参考方案3】:

您的热敏打印机支持 OPOS 吗?

如果是这样,我建议您尝试使用Microsoft Point of Service

【讨论】:

以上是关于winform PrintDocument调用打印提示 System.ComponentModel.Win32Exception (0x80004005): 句柄无效?的主要内容,如果未能解决你的问题,请参考以下文章

winform使用PrintDocument控件打印

在C#,winform下怎样实现使用打印控件printDocument来打印datagridview里的内容?

winform简单打印

C#-WinForm-打印控件

如何使用 PrintDocument 在热敏打印机上打印文本文件?

c# winform如何设置打印纸张大小