Friday, December 9, 2011

Bypass Windows Printer Drivers

Zebra printers are best known for their Label printing capabilities.
They make it very easy to create labels for any industry.
Currently, I am working in Healthcare and we use Zebra printers to print labels / wristbands for patients.

Also Barcodes are very popular lately, you can do a lot with barcodes. Zebra printers can create barcodes very easily, and I have already gave some information about that in my blog earlier.

I wrote an application that create labels by using Zebra printers and ZPL Programming, I had a challenge when I was writing the code.
Everything was working fine except when my application tries to send the ZPL command to the printer, Windows was coming up with Printer Windows


My application supposed to create the labels and send them directly to the printer. For that I had to get rid off Windows printer drivers and send the ZPL commands as RAW data to the printers. I googled the issue, and It took forever to find a solution for this problem, I had the same issue with PDFs before, there was just no easy way to auto print a PDF document without PDF Print Window popup.
At the end, I found an article about WinSpool.drv in MSDN
You can do a lot with WinSpool.drv if you use it right, I am going to try to explain Printing in this article.

After playing with the code I found in MSDN, here is the code I came up with which helped me a lot in this an many other projects.

public struct DOCINFO
    {
        [MarshalAs(UnmanagedType.LPWStr)] public string PDocName;
        [MarshalAs(UnmanagedType.LPWStr)] public string POutputFile;
        [MarshalAs(UnmanagedType.LPWStr)] public string PDataType;
    }
 
public class PrintDirect
{
 [DllImport("winspool.drv", CharSet = CharSet.Unicode, ExactSpelling = false,
        CallingConvention = CallingConvention.StdCall)]
 public static extern long OpenPrinter(string pPrinterName, ref IntPtr phPrinter, int pDefault);
 [DllImport("winspool.drv", CharSet = CharSet.Unicode, ExactSpelling = false,
        CallingConvention = CallingConvention.StdCall)]
 public static extern long StartDocPrinter(IntPtr hPrinter, int level, ref DOCINFO pDocInfo);
 
 [DllImport("winspool.drv", CharSet = CharSet.Unicode, ExactSpelling = true,
        CallingConvention = CallingConvention.StdCall)]
 public static extern long StartPagePrinter(IntPtr hPrinter);
 
 [DllImport("winspool.drv", CharSet = CharSet.Ansi, ExactSpelling = true,
        CallingConvention = CallingConvention.StdCall)]
 public static extern long WritePrinter(IntPtr hPrinter, string data, int buf, ref int pcWritten);
 [DllImport("winspool.drv", CharSet = CharSet.Unicode, ExactSpelling = true,
        CallingConvention = CallingConvention.StdCall)]
 public static extern long EndPagePrinter(IntPtr hPrinter);
 
 [DllImport("winspool.drv", CharSet = CharSet.Unicode, ExactSpelling = true,
        CallingConvention = CallingConvention.StdCall)]
 public static extern long EndDocPrinter(IntPtr hPrinter);
 
 [DllImport("winspool.drv", CharSet = CharSet.Unicode, ExactSpelling = true,
        CallingConvention = CallingConvention.StdCall)]
 public static extern long ClosePrinter(IntPtr hPrinter);
 
 [DllImport("winspool.drv", CharSet = CharSet.Unicode, ExactSpelling = false,
        CallingConvention = CallingConvention.StdCall)]
 public static extern bool AddPrinterConnection(string pName);
 }

public static bool Print(string zplcommand, string printername)
{
  var lhPrinter = new IntPtr();
  var di = new DOCINFO();
  var pcWritten = 0;
  di.PDocName = "Barcode";
  di.PDataType = "RAW";
  PrintDirect.OpenPrinter(printername, ref lhPrinter, 0);
  PrintDirect.StartDocPrinter(lhPrinter, 1, ref di);
  PrintDirect.StartPagePrinter(lhPrinter);
  try
  {
    PrintDirect.WritePrinter(lhPrinter, zplcommand, zplcommand.Length, ref pcWritten);
  }
  catch (Exception ex)
  {
     PrintDirect.EndPagePrinter(lhPrinter);
     PrintDirect.EndDocPrinter(lhPrinter);
     PrintDirect.ClosePrinter(lhPrinter);
     //Send an email about the error if you like.     return false;
   }
      PrintDirect.EndPagePrinter(lhPrinter);
      PrintDirect.EndDocPrinter(lhPrinter);
      PrintDirect.ClosePrinter(lhPrinter);
      return true;
}

To create label, you need to call the function Print with ZplCommand and PrinterName value and if you ZPL command doesn't have any problem. You label will be printed without any Windows Print windows.

Now you can use this process to print PDFs too. Just create or load your PDF and use Print function to send it to the printer, You will bypass all Print Windows and your PDF will be printed.

Let me know if you have any questions about it...




No comments:

Post a Comment