Introduction
An EPOS printer is a line oriented device, manufacturers provide Windows drivers for the devices but the Windows printing system is page oriented and often does
not expose the full functionality of an EPOS printer. To control the printer requires the ability to send escape sequences to the device which if sent via the Windows driver will simply be printed out, not actioned.
From the perspective of the EOPS line printer, the other disadvantage to the page orientation is having to build the page first, send the page and then it prints, which takes time and is slow. Printing a single short transaction is not very responsive, but if you are going to print an entire audit roll from a till for a day it is too slow.
There is an article on Microsoft.com describing how to open and write to a printer directly using the Windows drivers using the RawPrinterHelper
class. The advantage using this method is that you do not have to manage the multiple different port types and communication control to the device.
Using the RawPrinterHelper
meant not having to re-write too much of the code used to write direct to the COM port but RawPrinterHelper
is page oriented and slower than the direct method, a few changes and virtually back at full speed only opening the printer added to the time taken to print, not sure what the impact is on holding the printer open for the run life of the application.
The Microsoft article and source can be found at the link below:
Background
Writing code for old equipment was easy, the old printers were attached via parallel or serial ports which could be opened and written to directly. When a printer is network or USB attached, it is not so easy to code for unless the USB devices exposes itself as a COM or LPT port. Using the Windows drivers to print for the USB attached devices meant losing control over opening the till drawer, cutting the receipts and made barcode printing less easy.
Using the Code
Two parts to the testing program, the modified
RawPrinterHelper
code and a form with two buttons, you could add a drop down to select a printer and have it fill in the PrinterName
string.
First set the
PrinterName
string to the name of your printer which you can find in the printer properties dialog.
The form code is broken into separate functions; print header for your receipt, print the body of the receipt which is usually dynamically generated, finally print the footer, cut the paper and open the drawer.
The escape sequences are for EPSON EPOS receipt printers any other manufacture and you will need to replace them with the correct sequences.
More code can be added to handle exceptions, for my purposes if the printer is not attached or working, the code just consumes the writes. The
OpenPrint
function returns a boolean and can be tested and display messages as appropriate.
The printer driver I used for the testing is the EPSON Advanced Printer Driver 4.
The modified RawPrinterHelper
and simple test form with print and exit buttons:
Imports System
Imports System.Drawing
Imports System.Drawing.Printing
Imports System.Windows.Forms
Imports System.Runtime.InteropServices
Imports System.IO
Public Class RawPrinterHelper
<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)> _
szPrinter As String, ByRef hPrinter As IntPtr, pd As IntPtr) As Boolean
End Function
<DllImport("winspool.Drv", EntryPoint:="ClosePrinter", _
SetLastError:=True, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
Public Shared Function ClosePrinter(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(hPrinter As IntPtr, level As Int32, _
<[In](), MarshalAs(UnmanagedType.LPStruct)> di As DOCINFOA) As Boolean
End Function
<DllImport("winspool.Drv", EntryPoint:="EndDocPrinter", _
SetLastError:=True, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
Public Shared Function EndDocPrinter(hPrinter As IntPtr) As Boolean
End Function
<DllImport("winspool.Drv", EntryPoint:="StartPagePrinter", _
SetLastError:=True, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
Public Shared Function StartPagePrinter(hPrinter As IntPtr) As Boolean
End Function
<DllImport("winspool.Drv", EntryPoint:="EndPagePrinter", _
SetLastError:=True, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
Public Shared Function EndPagePrinter(hPrinter As IntPtr) As Boolean
End Function
<DllImport("winspool.Drv", EntryPoint:="WritePrinter", _
SetLastError:=True, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
Public Shared Function WritePrinter(hPrinter As IntPtr, pBytes As IntPtr, _
dwCount As Int32, ByRef dwWritten As Int32) As Boolean
End Function
Private hPrinter As New IntPtr(0)
Private di As New DOCINFOA()
Private PrinterOpen As Boolean = False
Public ReadOnly Property PrinterIsOpen As Boolean
Get
PrinterIsOpen = PrinterOpen
End Get
End Property
Public Function OpenPrint(szPrinterName As String) As Boolean
If PrinterOpen = False Then
di.pDocName = ".NET RAW Document"
di.pDataType = "RAW"
If OpenPrinter(szPrinterName.Normalize(), hPrinter, IntPtr.Zero) Then
If StartDocPrinter(hPrinter, 1, di) Then
If StartPagePrinter(hPrinter) Then
PrinterOpen = True
End If
End If
End If
End If
OpenPrint = PrinterOpen
End Function
Public Sub ClosePrint()
If PrinterOpen Then
EndPagePrinter(hPrinter)
EndDocPrinter(hPrinter)
ClosePrinter(hPrinter)
PrinterOpen = False
End If
End Sub
Public Function SendStringToPrinter(szPrinterName As String, szString As String) As Boolean
If PrinterOpen Then
Dim pBytes As IntPtr
Dim dwCount As Int32
Dim dwWritten As Int32 = 0
dwCount = szString.Length
pBytes = Marshal.StringToCoTaskMemAnsi(szString)
SendStringToPrinter = WritePrinter(hPrinter, pBytes, dwCount, dwWritten)
Marshal.FreeCoTaskMem(pBytes)
Else
SendStringToPrinter = False
End If
End Function
End Class
Public Class Form1
Public Const eClear As String = Chr(27) + "@"
Public Const eCentre As String = Chr(27) + Chr(97) + "1"
Public Const eLeft As String = Chr(27) + Chr(97) + "0"
Public Const eRight As String = Chr(27) + Chr(97) + "2"
Public Const eDrawer As String = eClear + Chr(27) + "p" + Chr(0) + ".}"
Public Const eCut As String = Chr(27) + "i" + vbCrLf
Public Const eSmlText As String = Chr(27) + "!" + Chr(1)
Public Const eNmlText As String = Chr(27) + "!" + Chr(0)
Public Const eInit As String = eNmlText + Chr(13) + Chr(27) + _
"c6" + Chr(1) + Chr(27) + "R3" + vbCrLf
Public Const eBigCharOn As String = Chr(27) + "!" + Chr(56)
Public Const eBigCharOff As String = Chr(27) + "!" + Chr(0)
Private prn As New RawPrinterHelper
Private PrinterName As String = "EPSON TM-T20 Receipt"
Public Sub StartPrint()
prn.OpenPrint(PrinterName)
End Sub
Public Sub PrintHeader()
Print(eInit + eCentre + "My Shop")
Print("Tel:0123 456 7890")
Print("Web: www.????.com")
Print("sales@????.com")
Print("VAT Reg No:123 4567 89" + eLeft)
PrintDashes()
End Sub
Public Sub PrintBody()
Print(eSmlText + "Tea T1 1.30")
PrintDashes()
Print(eSmlText + " Total: 1.30")
Print(" Paid Card: 1.30")
End Sub
Public Sub PrintFooter()
Print(eCentre + "Thank You For Your Support!" + eLeft)
Print(vbLf + vbLf + vbLf + vbLf + vbLf + eCut + eDrawer)
End Sub
Public Sub Print(Line As String)
prn.SendStringToPrinter(PrinterName, Line + vbLf)
End Sub
Public Sub PrintDashes()
Print(eLeft + eNmlText + "-".PadRight(42, "-"))
End Sub
Public Sub EndPrint()
prn.ClosePrint()
End Sub
Private Sub bnExit_Click(sender As System.Object, e As System.EventArgs) _
Handles bnExit.Click
prn.ClosePrint()
Me.Close()
End Sub
Private Sub bnPrint_Click(sender As System.Object, e As System.EventArgs) _
Handles bnPrint.Click
StartPrint()
If prn.PrinterIsOpen = True Then
PrintHeader()
PrintBody()
PrintFooter()
EndPrint()
End If
End Sub
End Class