Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / Win32

Print Direct To Windows Printer (EPOS Receipt)

4.88/5 (7 votes)
2 Jan 2014CPOL3 min read 124.8K  
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)