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 .