Click here to Skip to main content
15,884,099 members
Articles / Desktop Programming / Win32
Tip/Trick

Print Direct To Windows Printer (EPOS Receipt)

Rate me:
Please Sign up or sign in to vote.
4.87/5 (6 votes)
2 Jan 2014CPOL3 min read 121K   18   15
Printing To Line Printers (EPOS) Using Windows

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:

VB.NET
Imports System
Imports System.Drawing
Imports System.Drawing.Printing
Imports System.Windows.Forms
Imports System.Runtime.InteropServices
Imports System.IO

Public Class RawPrinterHelper
    ' Structure and API declarations:
    <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
                ' Start a document.
                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 
VB.NET
 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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Retired
United Kingdom United Kingdom
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
Questionwhat programming language did you use to write this code? Pin
Member 1490032427-Jul-20 10:33
Member 1490032427-Jul-20 10:33 
AnswerRe: what programming language did you use to write this code? Pin
Sing Abend27-Oct-20 11:23
professionalSing Abend27-Oct-20 11:23 
QuestionRawprinterhelper Pin
mabadeje18-Jun-19 1:41
mabadeje18-Jun-19 1:41 
GeneralThank you. Pin
Moheeb U.4-Jan-16 2:26
Moheeb U.4-Jan-16 2:26 
QuestionGood job Pin
MarbryH18-May-15 11:16
MarbryH18-May-15 11:16 
QuestionArabic text Pin
asdsuez111126-Oct-14 23:30
asdsuez111126-Oct-14 23:30 
AnswerRe: Arabic text Pin
Michael_Davies13-Nov-14 21:53
Michael_Davies13-Nov-14 21:53 
GeneralRe: Arabic text Pin
Member 1184510710-Apr-19 3:27
Member 1184510710-Apr-19 3:27 
GeneralRe: Arabic text Pin
tkalai28-Sep-19 6:10
tkalai28-Sep-19 6:10 
QuestionHow to print logos (Which I have already uploaded to my device) ? Pin
saqibsabir18-Aug-14 5:19
saqibsabir18-Aug-14 5:19 
AnswerRe: How to print logos (Which I have already uploaded to my device) ? Pin
Michael_Davies13-Nov-14 22:00
Michael_Davies13-Nov-14 22:00 
SuggestionUnicode rather than ANSI Pin
Duncan Edwards Jones10-Jan-14 4:38
professionalDuncan Edwards Jones10-Jan-14 4:38 
GeneralRe: Unicode rather than ANSI Pin
Michael_Davies11-Jan-14 3:32
Michael_Davies11-Jan-14 3:32 
GeneralRe: Unicode rather than ANSI Pin
Mahdi.mpst18-Feb-14 11:21
Mahdi.mpst18-Feb-14 11:21 
GeneralMy vote of 5 Pin
JOE Heart Under Blade8-Jan-14 19:14
JOE Heart Under Blade8-Jan-14 19:14 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.