直接将字节数组发送到打印机

时间:2010-08-17 15:31:56

标签: c# api printing

我的服务将报告转换为字节数组,并使用以下代码传递给客户端(wpf app):

byte[] bytes = renderer.ServerReport.Render("PDF", deviceInfo, out mimeType, out encoding, out extension, out streamids, out warnings);

其中renderer是Microsoft.Reporting.Webforms.ReportViewer的一个实例。这里有一个问题:编码输出参数返回null,因此无法找到有关编码的信息。

UI必须以静默方式将此字节数组打印到打印机。可以将此字节数组直接发送到打印机,而不是将其转换回UI中的PDF文件然后打印吗?

在查看msdn链接之后,我实际上尝试了类似下面的内容,但是当实际报告只是一页或两页时,这会在很多页面上打印出奇怪的符号。关于winspool dll在线功能的信息较少,所以不确定我哪里出错了。

任何想法都受到高度赞赏。

public class RawPrintHelper
{
     //Structure and API declarions:
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    public class DOCINFOA
    {
        [MarshalAs(UnmanagedType.LPStr)]
        public string pDocName;
        [MarshalAs(UnmanagedType.LPStr)]
        public string pOutputFile;
        [MarshalAs(UnmanagedType.LPStr)]
        public string pDataType;
    }
    [DllImport("winspool.Drv", EntryPoint = "OpenPrinter", SetLastError = true, CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter, out IntPtr hPrinter, IntPtr pd);

    [DllImport("winspool.Drv", EntryPoint = "ClosePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern bool ClosePrinter(IntPtr hPrinter);

    [DllImport("winspool.Drv", EntryPoint = "StartDocPrinter", SetLastError = true, CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern bool StartDocPrinter(IntPtr hPrinter, Int32 level, [In, MarshalAs(UnmanagedType.LPStruct)] DOCINFOA di);

    [DllImport("winspool.Drv", EntryPoint = "EndDocPrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern bool EndDocPrinter(IntPtr hPrinter);

    [DllImport("winspool.Drv", EntryPoint = "StartPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern bool StartPagePrinter(IntPtr hPrinter);

    [DllImport("winspool.Drv", EntryPoint = "EndPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern bool EndPagePrinter(IntPtr hPrinter);

    [DllImport("winspool.Drv", EntryPoint = "WritePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern bool WritePrinter(IntPtr hPrinter, IntPtr pBytes, Int32 dwCount, out Int32 dwWritten);

    // SendBytesToPrinter()
    // When the function is given a printer name and an unmanaged array
    // of bytes, the function sends those bytes to the print queue.
    // Returns true on success, false on failure.
    public static bool SendBytesToPrinter(string szPrinterName, IntPtr pBytes, Int32 dwCount)
    {
        Int32 dwError = 0, dwWritten = 0;
        IntPtr hPrinter = new IntPtr(0);
        DOCINFOA di = new DOCINFOA();
        bool bSuccess = false; // Assume failure unless you specifically succeed.

        di.pDocName = "My C#.NET RAW Document";
        di.pDataType = "RAW";

        // Open the printer.
        if (OpenPrinter(szPrinterName.Normalize(), out hPrinter, IntPtr.Zero))
        {
            // Start a document.
            if (StartDocPrinter(hPrinter, 1, di))
            {
                // Start a page.
                if (StartPagePrinter(hPrinter))
                {
                    // Write your bytes.
                    bSuccess = WritePrinter(hPrinter, pBytes, dwCount, out dwWritten);
                    EndPagePrinter(hPrinter);
                }
                EndDocPrinter(hPrinter);
            }
            ClosePrinter(hPrinter);
        }
        // If you did not succeed, GetLastError may give more information
        // about why not.
        if (bSuccess == false)
        {
            dwError = Marshal.GetLastWin32Error();
        }
        return bSuccess;
    }

}

3 个答案:

答案 0 :(得分:2)

是的,这不行。您使用此代码绕过打印机驱动程序,您必须使用您使用的特定打印机的打印机控制语言生成打印命令。 PCL和Postscript是常见的打印机控制语言。但无论如何,制造商通常会创建自己的定制品种。

看起来您正在发送PDF,我不知道任何使用PDF作为其本机控制语言的打印机。也许这样的打印机存在,显然你所拥有的打印机不存在。我无法猜到为什么你在服务中使用Webforms类,很难提供替代方案。从服务打印通常是一个坏主意,打印机驱动程序非常乐意提出“尽快更换碳粉”提示。在隐形桌面上显示,您无法找出文档无法打印的原因。

答案 1 :(得分:1)

我保留了初始实现,我将PDF文件发送到打印机,因为直接向打印机发送字节不起作用。

答案 2 :(得分:0)

我在这里玩游戏有点晚了,但是自从第一次提出这个问题以来已经过去了一段时间,说明您无法将 PDF 直接发送到打印机的回答现在不太正确。很多情况下可以使用System.IO文件拷贝将PDF文件直接发送到UNC打印机队列(只要打印机支持)

示例:File.Copy(pathToAPDFFileYouWantPrinted, @"\\fserverprint\printerName");

我知道这很管用,因为我一直在使用它。它非常快。问题是您需要先将字节数组写入文件,然后再将其复制到打印机。

虽然这不能满足 OP 的字节数组方法,但是一旦您将字节数组作为 PDF 格式使用字节数组/发送到打印机方法,实际上只需两行代码。

    File.WriteAllBytes(pathToPDFFileYouWantPrinted, output);
    File.Copy(pathToAPDFFileYouWantPrinted, @"\\fserverprint\printerName");

我最近尝试了字节数组方法,但它不起作用。你会得到这个错误:System.NotSupportedException: 'FileStream was asked to open a device that was not a file. For support for devices like 'com1:' or 'lpt1:', call CreateFile, then use the FileStream constructors that take an OS handle as an IntPtr.'