.NET Class to print directly to windows printer

By Allen Stoner

If you want to print a PCL file directly to an HP printer you need to bypass the Windows printer driver. In the old DOS days you could do something like 'copy file.pcl prn'. This VB.NET class does the same sort of thing. Assuming you have the PCL file in a stream you can print it with this call: RawPrinterHelper.SendStreamToPrinter(_Printer, OutputStream)

Imports System.Collections.Generic
Imports System.Linq
Imports System.Text
Imports System.Drawing.Printing
Imports System.Runtime.InteropServices
Imports System.IO

Public Class RawPrinterHelper
  ' Structure and API declarions:
  <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi)> _
  Public Class DOCINFOA
    <MarshalAs(UnmanagedType.LPStr)> _
    Public pDocName As String
    <MarshalAs(UnmanagedType.LPStr)> _
    Public pOutputFile As String
    <MarshalAs(UnmanagedType.LPStr)> _
    Public pDataType As String
  End Class
  <DllImport("winspool.Drv", EntryPoint:="OpenPrinterA", SetLastError:=True, CharSet:=CharSet.Ansi, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
  Public Shared Function OpenPrinter(<MarshalAs(UnmanagedType.LPStr)> ByVal szPrinter As String, ByRef hPrinter As IntPtr, ByVal pd As IntPtr) As Boolean
  End Function

  <DllImport("winspool.Drv", EntryPoint:="ClosePrinter", SetLastError:=True, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
  Public Shared Function ClosePrinter(ByVal hPrinter As IntPtr) As Boolean
  End Function

  <DllImport("winspool.Drv", EntryPoint:="StartDocPrinterA", SetLastError:=True, CharSet:=CharSet.Ansi, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
  Public Shared Function StartDocPrinter(ByVal hPrinter As IntPtr, ByVal level As Int32, <[In](), MarshalAs(UnmanagedType.LPStruct)> ByVal di As DOCINFOA) As Boolean
  End Function

  <DllImport("winspool.Drv", EntryPoint:="EndDocPrinter", SetLastError:=True, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
  Public Shared Function EndDocPrinter(ByVal hPrinter As IntPtr) As Boolean
  End Function

  <DllImport("winspool.Drv", EntryPoint:="StartPagePrinter", SetLastError:=True, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
  Public Shared Function StartPagePrinter(ByVal hPrinter As IntPtr) As Boolean
  End Function

  <DllImport("winspool.Drv", EntryPoint:="EndPagePrinter", SetLastError:=True, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
  Public Shared Function EndPagePrinter(ByVal hPrinter As IntPtr) As Boolean
  End Function

  <DllImport("winspool.Drv", EntryPoint:="WritePrinter", SetLastError:=True, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
  Public Shared Function WritePrinter(ByVal hPrinter As IntPtr, ByVal pBytes As IntPtr, ByVal dwCount As Int32, ByRef dwWritten As Int32) As Boolean
  End Function

  ' 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 Shared Function SendBytesToPrinter(ByVal szPrinterName As String, ByVal pBytes As IntPtr, ByVal dwCount As Int32, Optional ByVal DocName As String = "") As Boolean
    Dim dwError As Int32 = 0
    Dim dwWritten As Int32 = 0
    Dim hPrinter As New IntPtr(0)
    Dim di As New DOCINFOA()
    Dim bSuccess As Boolean = False
    ' Assume failure unless you specifically succeed.
    di.pDocName = DocName
    di.pDataType = "RAW"

    ' Open the printer.
    If OpenPrinter(szPrinterName.Normalize(), hPrinter, IntPtr.Zero) Then
      ' Start a document.
      If StartDocPrinter(hPrinter, 1, di) Then
        ' Start a page.
        If StartPagePrinter(hPrinter) Then
          ' Write your bytes.
          bSuccess = WritePrinter(hPrinter, pBytes, dwCount, dwWritten)
           EndPagePrinter(hPrinter)
         End If
        EndDocPrinter(hPrinter)
      End If
      ClosePrinter(hPrinter)
     End If
    ' If you did not succeed, GetLastError may give more information
    ' about why not.
    If bSuccess = False Then
      dwError = Marshal.GetLastWin32Error()
    End If
    Return bSuccess
  End Function

  Public Shared Function SendFileToPrinter(ByVal szPrinterName As String, ByVal szFileName As String) As Boolean
    ' Open the file.
    Dim fs As New FileStream(szFileName, FileMode.Open)
    '  Make sure we're at the beginning of the stream
    fs.Seek(0, SeekOrigin.Begin)
    Dim bSuccess As Boolean = False
    bSuccess = SendStreamToPrinter(szPrinterName, fs)
    Return bSuccess
  End Function

  Public Shared Function SendStreamToPrinter(ByVal szPrinterName As String, ByVal fs As Stream) As Boolean
    ' Create a BinaryReader on the file.
    Dim br As New BinaryReader(fs)
    ' Dim an array of bytes big enough to hold the file's contents.
    Dim bytes As [Byte]() = New [Byte](fs.Length - 1) {}
    Dim bSuccess As Boolean = False
    ' Your unmanaged pointer.
    Dim pUnmanagedBytes As New IntPtr(0)
    Dim nLength As Integer

    nLength = Convert.ToInt32(fs.Length)
    ' Read the contents of the file into the array.
    bytes = br.ReadBytes(nLength)
    ' Allocate some unmanaged memory for those bytes.
    pUnmanagedBytes = Marshal.AllocCoTaskMem(nLength)
    ' Copy the managed byte array into the unmanaged array.
   Marshal.Copy(bytes, 0, pUnmanagedBytes, nLength)
    ' Send the unmanaged bytes to the printer.
    bSuccess = SendBytesToPrinter(szPrinterName, pUnmanagedBytes, nLength)
    ' Free the unmanaged memory that you allocated earlier.
   Marshal.FreeCoTaskMem(pUnmanagedBytes)
    Return bSuccess
  End Function

  Public Shared Function SendStringToPrinter(ByVal szPrinterName As String, ByVal szString As String) As Boolean
    Dim pBytes As IntPtr
    Dim dwCount As Int32
    ' How many characters are in the string?
    dwCount = szString.Length
    ' Assume that the printer is expecting ANSI text, and then convert
    ' the string to ANSI text.
    pBytes = Marshal.StringToCoTaskMemAnsi(szString)
    ' Send the converted ANSI string to the printer.
    SendBytesToPrinter(szPrinterName, pBytes, dwCount)
   Marshal.FreeCoTaskMem(pBytes)
    Return True
  End Function
End Class

.NET Class to print directly to windows printer  (1890 Views)