12

我有一个使用“打印到文件”生成的 PCL 文件。

在 C# 中以编程方式打印此文件的最佳方法是什么?

(当然,鉴于我要打印的打印机支持 PCL。)

我知道我可以通过从提示中调用来打印:

copy filename.pcl //location/printername

所以我想我也可以以编程方式(使用复制)做同样的事情......我想知道是否有更清洁的方法来做到这一点,比如使用 PrintDocument。

请注意,当我使用 PrintDocument 时:

var pd = new PrintDocument
         {
             DocumentName = @"filename.pcl";
             PrinterSettings = {PrinterName = @"\\location\printername"}
         };

pd.Print();

我总是打印空白页。

4

3 回答 3

4

很抱歉这么晚才回答这个问题,但我有一些代码可以完成这项工作。原来不是我写的。我从另一个程序员的帮助站点收到了代码,但我不记得是哪一个了。它工作得很好。这是创建 dll 的项目。要自己做,请选择 Create:/Project/Class Library(在项目类型下选择 Visual C#)。

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.IO;

namespace PrintRaw
{
   public class RawFilePrint
   {
      // Structure and API declarions:
      [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
      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 = "OpenPrinterA", SetLastError = true, CharSet = CharSet.Ansi, 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 = "StartDocPrinterA", SetLastError = true, CharSet = CharSet.Ansi, 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 = "RAW Document";
         di.pDataType = "RAW";

         if (OpenPrinter(szPrinterName.Normalize(), out hPrinter, IntPtr.Zero))
         {
            if (StartDocPrinter(hPrinter, 1, di))
            {
               if (StartPagePrinter(hPrinter))
               {
                  bSuccess = WritePrinter(hPrinter, pBytes, dwCount, out dwWritten);
                  EndPagePrinter(hPrinter);
               }
               EndDocPrinter(hPrinter);
            }
            ClosePrinter(hPrinter);
         }
         if (!bSuccess)
         {
            dwError = Marshal.GetLastWin32Error();
         }
         return bSuccess;
      }

      public static bool SendFileToPrinter(string szPrinterName, string szFileName)
      {
         FileStream fs = new FileStream(szFileName, FileMode.Open);
         BinaryReader br = new BinaryReader(fs);
         Byte[] bytes = new Byte[fs.Length];
         bool bSuccess = false;
         IntPtr pUnmanagedBytes = new IntPtr(0);
         int nLength;

         nLength = Convert.ToInt32(fs.Length);
         bytes = br.ReadBytes(nLength);
         pUnmanagedBytes = Marshal.AllocCoTaskMem(nLength);
         Marshal.Copy(bytes, 0, pUnmanagedBytes, nLength);
         bSuccess = SendBytesToPrinter(szPrinterName, pUnmanagedBytes, nLength);
         Marshal.FreeCoTaskMem(pUnmanagedBytes);
         return bSuccess;
      }

      public static bool SendStringToPrinter(string szPrinterName, string szString)
      {
         IntPtr pBytes;
         Int32 dwCount;
         dwCount = szString.Length;
         // Assume that the printer is expecting ANSI text, and then convert
         // the string to ANSI text.
         pBytes = Marshal.StringToCoTaskMemAnsi(szString);
         SendBytesToPrinter(szPrinterName, pBytes, dwCount);
         Marshal.FreeCoTaskMem(pBytes);
         return true;
      }
   }
}

现在,要使用此代码,请将生成的 dll 添加为项目的引用,然后根据需要调用函数。这是我今天使用的一些代码:

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace PclFontTest
{
    class Program
    {
        static void Main(string[] args)
        {

            string szPrinterName = @"\\printserver\LaserJet 2420";

            StreamReader sr = new StreamReader(@"C:\Fonts\US20HP.FNT");
            string line = (char)27 + "*c32545D";
            line += sr.ReadToEnd();
            line += (char)27 + "*c5F";

            PrintRaw.RawFilePrint.SendStringToPrinter(szPrinterName, line);


        }
    }
}

该程序从文件中读取 PCL 字体。它用代码包装字体,为其分配一个 32545 的字体 ID,然后调用 dll 函数 SendStringToPrinter。

于 2010-09-21T19:51:27.017 回答
2

这篇文章适用于 Visual Basic,但我认为它很容易理解以便能够将其应用到 C# 中。否则,我很乐意为您解决任何有问题的部分。

如果打印机在网络上,这里有一个有趣的例子来说明如何直接与它对话。如果您只是将 PCL 的字节发送到打印机,不确定它是否也有效。

于 2010-08-17T21:42:30.127 回答
2

我们对每个 DLL 导入使用以下方法:

    [DllImport("winspool.Drv", EntryPoint = "OpenPrinterA", SetLastError = true, CharSet = CharSet.Ansi, 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 = "StartDocPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern bool StartDocPrinter(IntPtr hPrinter, Int32 level, [In, MarshalAs(UnmanagedType.LPStruct)] RawPrinter 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);
于 2010-08-17T21:43:15.937 回答