How to print PCL Directly to the printer? Vb.net VS 2010

Posted by Justin on Stack Overflow See other posts from Stack Overflow or by Justin
Published on 2010-06-17T15:00:58Z Indexed on 2010/06/17 15:03 UTC
Read the original article Hit count: 882

Filed under:
|
|
|
|

Good day, I've been trying to upgrade an old Print Program that prints raw pcl directly to the printer. The print program takes a PCL Mask and a PCL Batch Spool File (just pcl with page turn commands, I think) merges them and sends them off to the printer. I'm able to send to the Printer a file stream of PCL but I get mixed results and i do not understand why printing is so difficult under .net. I mean yes there is the PrintDocument class, but to print PCL... Let's just say I'm ready to detach my printer from the network and burn it a live.

Here is my class PrintDirect (rather a hybrid class)

Imports System
Imports System.Text
Imports System.Runtime.InteropServices

<StructLayout(LayoutKind.Sequential)> _ 
 Public Structure DOCINFO
<MarshalAs(UnmanagedType.LPWStr)> _
Public pDocName As String
<MarshalAs(UnmanagedType.LPWStr)> _
Public pOutputFile As String
<MarshalAs(UnmanagedType.LPWStr)> _
Public pDataType As String
End Structure 'DOCINFO

Public Class PrintDirect
<DllImport("winspool.drv", CharSet:=CharSet.Unicode, ExactSpelling:=False, CallingConvention:=CallingConvention.StdCall)> _
Public Shared Function OpenPrinter(ByVal pPrinterName As String, ByRef phPrinter As IntPtr, ByVal pDefault As Integer) As Long
End Function

<DllImport("winspool.drv", CharSet:=CharSet.Unicode, ExactSpelling:=False, CallingConvention:=CallingConvention.StdCall)> _
Public Shared Function StartDocPrinter(ByVal hPrinter As IntPtr, ByVal Level As Integer, ByRef pDocInfo As DOCINFO) As Long
End Function

<DllImport("winspool.drv", CharSet:=CharSet.Unicode, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
Public Shared Function StartPagePrinter(ByVal hPrinter As IntPtr) As Long
End Function

<DllImport("winspool.drv", CharSet:=CharSet.Ansi, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
Public Shared Function WritePrinter(ByVal hPrinter As IntPtr, ByVal data As String, ByVal buf As Integer, ByRef pcWritten As Integer) As Long
End Function

<DllImport("winspool.drv", CharSet:=CharSet.Unicode, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
Public Shared Function EndPagePrinter(ByVal hPrinter As IntPtr) As Long
End Function

<DllImport("winspool.drv", CharSet:=CharSet.Unicode, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
Public Shared Function EndDocPrinter(ByVal hPrinter As IntPtr) As Long
End Function

<DllImport("winspool.drv", CharSet:=CharSet.Unicode, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
Public Shared Function ClosePrinter(ByVal hPrinter As IntPtr) As Long
End Function

'Helps uses to work with the printer
Public Class PrintJob

    ''' <summary>
    ''' The address of the printer to print to.
    ''' </summary>
    ''' <remarks></remarks>
    Public PrinterName As String = ""

    Dim lhPrinter As New System.IntPtr()

    ''' <summary>
    ''' The object deriving from the Win32 API, Winspool.drv.
    ''' Use this object to overide the settings defined in the PrintJob Class.
    ''' </summary>
    ''' <remarks>Only use this when absolutly nessacary.</remarks>
    Public OverideDocInfo As New DOCINFO()

    ''' <summary>
    ''' The PCL Control Character or the ascii escape character. \x1b
    ''' </summary>
    ''' <remarks>ChrW(27)</remarks>
    Public Const PCL_Control_Character As Char = ChrW(27)

    ''' <summary>
    ''' Opens a connection to a printer, if false the connection could not be established. 
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function OpenPrint() As Boolean
        Dim rtn_val As Boolean = False

        PrintDirect.OpenPrinter(PrinterName, lhPrinter, 0)

        If Not lhPrinter = 0 Then
            rtn_val = True
        End If

        Return rtn_val
    End Function

    ''' <summary>
    ''' The name of the Print Document.
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property DocumentName As String
        Get
            Return OverideDocInfo.pDocName
        End Get
        Set(ByVal value As String)
            OverideDocInfo.pDocName = value
        End Set
    End Property

    ''' <summary>
    ''' The type of Data that will be sent to the printer.
    ''' </summary>
    ''' <value>Send the print data type, usually "RAW" or "LPR". To overide use the OverideDocInfo Object</value>
    ''' <returns>The DataType as it was provided, if it is not part of the enumeration it is set to "UNKNOWN".</returns>
    ''' <remarks></remarks>
    Public Property DocumentDataType As PrintDataType
        Get
            Select Case OverideDocInfo.pDataType.ToUpper
                Case "RAW"
                    Return PrintDataType.RAW
                Case "LPR"
                    Return PrintDataType.LPR
                Case Else
                    Return PrintDataType.UNKOWN
            End Select
        End Get
        Set(ByVal value As PrintDataType)
            OverideDocInfo.pDataType = [Enum].GetName(GetType(PrintDataType), value)
        End Set
    End Property

    Public Property DocumentOutputFile As String
        Get
            Return OverideDocInfo.pOutputFile
        End Get
        Set(ByVal value As String)
            OverideDocInfo.pOutputFile = value
        End Set
    End Property

    Enum PrintDataType
        UNKOWN = 0
        RAW = 1
        LPR = 2
    End Enum

    ''' <summary>
    ''' Initializes the printing matrix
    ''' </summary>
    ''' <param name="PrintLevel">I have no idea what the hack this is...</param>
    ''' <remarks></remarks>
    Public Sub OpenDocument(Optional ByVal PrintLevel As Integer = 1)

        PrintDirect.StartDocPrinter(lhPrinter, PrintLevel, OverideDocInfo)

    End Sub

    ''' <summary>
    ''' Starts the next page.
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub StartPage()
        PrintDirect.StartPagePrinter(lhPrinter)
    End Sub

    ''' <summary>
    ''' Writes a string to the printer, can be used to write out a section of the document at a time.
    ''' </summary>
    ''' <param name="data">The String to Send out to the Printer.</param>
    ''' <returns>The pcWritten as an integer, 0 may mean the writter did not write out anything.</returns>
    ''' <remarks>Warning the buffer is automatically created by the lenegth of the string.</remarks>
    Public Function WriteToPrinter(ByVal data As String) As Integer
        Dim pcWritten As Integer = 0
        PrintDirect.WritePrinter(lhPrinter, data, data.Length - 1, pcWritten)
        Return pcWritten
    End Function

    Public Sub EndPage()
        PrintDirect.EndPagePrinter(lhPrinter)
    End Sub

    Public Sub CloseDocument()
        PrintDirect.EndDocPrinter(lhPrinter)
    End Sub

    Public Sub ClosePrint()
        PrintDirect.ClosePrinter(lhPrinter)
    End Sub

    ''' <summary>
    ''' Opens a connection to a printer and starts a new document.
    ''' </summary>
    ''' <returns>If false the connection could not be established. </returns>
    ''' <remarks></remarks>
    Public Function Open() As Boolean
        Dim rtn_val As Boolean = False

        rtn_val = OpenPrint()

        If rtn_val Then
            OpenDocument()
        End If

        Return rtn_val
    End Function

    ''' <summary>
    ''' Closes the document and closes the connection to the printer.
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub Close()
        CloseDocument()
        ClosePrint()
    End Sub


End Class


End Class 'PrintDirect

Here is how I print my file. I'm simple printing the PCL Masks, to show proof of concept. But I can't even do that. I can effectively create PCL and send it the printer without reading the file and it works fine... Plus I get mixed results with different stream reader text encoding as well.

    Dim pJob As New PrintDirect.PrintJob
    pJob.DocumentName = " test doc"
    pJob.DocumentDataType = PrintDirect.PrintJob.PrintDataType.RAW
    pJob.PrinterName = sPrinter.GetDevicePath '//This is where you'd stick your device name, sPrinter stands for Selected Printer from a dropdown (combobox).
    'pJob.DocumentOutputFile = "C:\temp\test-spool.txt"
    If Not pJob.OpenPrint() Then

        MsgBox("Unable to connect to " & pJob.PrinterName, MsgBoxStyle.OkOnly, "Print Error")
        Exit Sub

    End If

    pJob.OpenDocument()
    pJob.StartPage()
    Dim sr As New IO.StreamReader(Me.txtFile.Text, Text.Encoding.ASCII) '//I've got best results with ASCII before, but only mized. 
    Dim line As String = sr.ReadLine

    'Fix for fly code on first run
    'If line = 27 Then line = PrintDirect.PrintJob.PCL_Control_Character

    Do While (Not line Is Nothing)
        pJob.WriteToPrinter(line)
        line = sr.ReadLine
    Loop

    'pJob.WriteToPrinter(ControlChars.FormFeed)

    pJob.EndPage()
    pJob.CloseDocument()

    sr.Close()
    sr.Dispose()
    sr = Nothing

    pJob.Close()

I was able to get to print the mask, in the morning... now I'm getting strange printer characters scattered through 5 pages... I'm totally clueless. I must have changed something or the printer is at fault.

You can access my test Check Mask, here http://kscserver.com/so/chk_mask.zip .

© Stack Overflow or respective owner

Related posts about vb.net

Related posts about printer