Printing Directly to Printer

I have a client that has a process which builds a text file. This text file contains commands that is used by their thermal printer, i.e.:

^XA^CF,0,0,0^PR12^MD30^PW800%^PON
^FO0,147^GB800,4,4^FS
^FO0,401^GB800,4,4^FS
^FO0,746^GB800,4,4^FS
^FO35,12^AdN,0,0^FWN^FH^FDFrom:^FS
^FO35,31^AdN,0,0^FWN^FH^FDAri Rothman^FS
^FO35,51^AdN,0,0^FWN^FH^

They want this to print directly to their printer. I have attempted using the following code to produce the results:

public class Printer
{
[DllImport("winspool.Drv", EntryPoint="GetDefaultPrinter")]
public static extern bool GetDefaultPrinter(
StringBuilder pszBuffer,
// printer name buffer
ref int pcchBuffer // size of name buffer
);
[ DllImport( "winspool.drv",CharSet=CharSet.Unicode,ExactSpelling=
false,
CallingConvention=CallingConvention.StdCall )]
public static extern long OpenPrinter(string pPrinterName,
ref IntPtr phPrinter, int pDefault);
[ DllImport( "winspool.drv",CharSet=CharSet.Unicode,ExactSpelling=
false,
CallingConvention=CallingConvention.StdCall )]
public static extern long StartDocPrinter(IntPtr hPrinter,
int Level, ref DOCINFO pDocInfo);
[ DllImport( "winspool.drv",CharSet=CharSet.Unicode,ExactSpelling=
true,
CallingConvention=CallingConvention.StdCall)]
public static extern long StartPagePrinter(IntPtr hPrinter);
[ DllImport( "winspool.drv",CharSet=CharSet.Ansi,ExactSpelling=
true,
CallingConvention=CallingConvention.StdCall)]
public static extern long WritePrinter(IntPtr hPrinter,string data,
int buf,ref int pcWritten);
[ DllImport( "winspool.drv" ,CharSet=CharSet.Unicode,ExactSpelling=
true,
CallingConvention=CallingConvention.StdCall)]
public static extern long EndPagePrinter(IntPtr hPrinter);
[ DllImport( "winspool.drv" ,CharSet=CharSet.Unicode,ExactSpelling=
true,
CallingConvention=CallingConvention.StdCall)]
public static extern long EndDocPrinter(IntPtr hPrinter);
[ DllImport( "winspool.drv",CharSet=CharSet.Unicode,ExactSpelling=
true,
CallingConvention=CallingConvention.StdCall )]
public static extern long ClosePrinter(IntPtr hPrinter);

public Printer()

{}

public static void SendToPrinter(string jobName, string PCL5Commands, string printerName)

{

System.IntPtr lhPrinter=new System.IntPtr();

DOCINFO di = new DOCINFO();

int pcWritten=0;

di.pDocName=jobName;

di.pDataType="RAW";

OpenPrinter(printerName,ref lhPrinter,0);

StartDocPrinter(lhPrinter,1,ref di);

StartPagePrinter(lhPrinter);

WritePrinter(lhPrinter,PCL5Commands,PCL5Commands.Length,ref pcWritten);

EndPagePrinter(lhPrinter);

EndDocPrinter(lhPrinter);

ClosePrinter(lhPrinter);

}

}

However, this prints the commands as text. But I am using an HP PCL 6 printer for testing purposes, so it may be caused by the printer not recognizing the commands. I am trying to gain opinions on this situation. Also, is there a more direct way to issue printer commands directly, or is this the best way I was thinking maybe opening a socket between the client and the printer and feeding this through directly to the port.

Any thoughts




Answer this question

Printing Directly to Printer

  • Kroneus

    Can I inject a new wrinkle that I am trying to get through. Printing text is pretty straight forward. How does a person add a image to the file that is being printed, like a concert ticket with a logo on it. I tried writing the bytes to text, but the printer goes right past and continues with the rest of the text.

  • C_TO

    Hi. I am using a EPSON printer.

    This is a sample (in old basic).


    PRINT #1, CHR$(&H1B);"@"; 'Initializes the printer (ESC @)
    PRINT #1, CHR$(&H1B);"a";CHR$(1);'Specifies a centered printing position (ESC a)
    PRINT #1, CHR$(&H1B);"!";CHR$(0); 'Specifies font A (ESC !)
    PRINT #1, "January 14, 2002 15:00";
    PRINT #1, CHR$(&H1B);"d";CHR$(3); 'Prints and 3 line feeding (ESC d)
    PRINT #1, CHR$(&H1B);"a";CHR$(0); 'Selects the left print position (ESC a)
    PRINT #1, CHR$(&H1B);"!";CHR$(1); 'Selects font B
    PRINT #1, "TM-U210B $20.00";CHR$(&HA);
    PRINT #1, "TM-U210D $21.00";CHR$(&HA);
    PRINT #1, "PS-170 $17.00";CHR$(&HA);
    PRINT #1, CHR$(&HA); 'Line feeding (LF)

    Now i've been trying for weeks (literally) to port this to c#.

    I am using the following class named rawPrinterHelper on msdn :

    using System;

    using System.Drawing;

    using System.Drawing.Printing;

    using System.Windows.Forms;

    using System.Runtime.InteropServices;

    using System.IO;

    namespace Impression

    {

    public class DirectHelper

    {

    // Structure and API declarions:

    [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]

    public class DOCINFOA

    {

    [MarshalAs(UnmanagedType.LPStr)] public string pDocName;

    [MarshalAs(UnmanagedType.LPStr)] public string pOutputFile;

    [MarshalAs(UnmanagedType.LPStr)] public string pDataType;

    }

    [DllImport("winspool.Drv", EntryPoint="OpenPrinterA", SetLastError=true, CharSet=CharSet.Ansi, ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]

    public static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter, out IntPtr hPrinter, IntPtr pd);

    [DllImport("winspool.Drv", EntryPoint="ClosePrinter", SetLastError=true, ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]

    public static extern bool ClosePrinter(IntPtr hPrinter);

    [DllImport("winspool.Drv", EntryPoint="StartDocPrinterA", SetLastError=true, CharSet=CharSet.Ansi, ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]

    public static extern bool StartDocPrinter( IntPtr hPrinter, Int32 level, [In, MarshalAs(UnmanagedType.LPStruct)] DOCINFOA di);

    [DllImport("winspool.Drv", EntryPoint="EndDocPrinter", SetLastError=true, ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]

    public static extern bool EndDocPrinter(IntPtr hPrinter);

    [DllImport("winspool.Drv", EntryPoint="StartPagePrinter", SetLastError=true, ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]

    public static extern bool StartPagePrinter(IntPtr hPrinter);

    [DllImport("winspool.Drv", EntryPoint="EndPagePrinter", SetLastError=true, ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]

    public static extern bool EndPagePrinter(IntPtr hPrinter);

    [DllImport("winspool.Drv", EntryPoint="WritePrinter", SetLastError=true, ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]

    public static extern bool WritePrinter(IntPtr hPrinter, IntPtr pBytes, Int32 dwCount, out Int32 dwWritten );

    // SendBytesToPrinter()

    // When the function is given a printer name and an unmanaged array

    // of bytes, the function sends those bytes to the print queue.

    // Returns true on success, false on failure.

    public static bool SendBytesToPrinter( string szPrinterName, IntPtr pBytes, Int32 dwCount)

    {

    Int32 dwError = 0, dwWritten = 0;

    IntPtr hPrinter = new IntPtr(0);

    DOCINFOA di = new DOCINFOA();

    bool bSuccess = false; // Assume failure unless you specifically succeed.

    di.pDocName = "My C#.NET RAW Document";

    di.pDataType = "RAW";

    // Open the printer.

    if( OpenPrinter( szPrinterName.Normalize(), out hPrinter, IntPtr.Zero ) )

    {

    // Start a document.

    if( StartDocPrinter(hPrinter, 1, di) )

    {

    // Start a page.

    if( StartPagePrinter(hPrinter) )

    {

    // Write your bytes.

    bSuccess = WritePrinter(hPrinter, pBytes, dwCount, out dwWritten);

    EndPagePrinter(hPrinter);

    }

    EndDocPrinter(hPrinter);

    }

    ClosePrinter(hPrinter);

    }

    // If you did not succeed, GetLastError may give more information

    // about why not.

    if( bSuccess == false )

    {

    dwError = Marshal.GetLastWin32Error();

    }

    return bSuccess;

    }

    public static bool SendFileToPrinter( string szPrinterName, string szFileName )

    {

    // Open the file.

    FileStream fs = new FileStream(szFileName, FileMode.Open);

    // Create a BinaryReader on the file.

    BinaryReader br = new BinaryReader(fs);

    // Dim an array of bytes big enough to hold the file's contents.

    Byte []bytes = new Byte[fs.Length];

    bool bSuccess = false;

    // Your unmanaged pointer.

    IntPtr pUnmanagedBytes = new IntPtr(0);

    int nLength;

    nLength = Convert.ToInt32(fs.Length);

    // Read the contents of the file into the array.

    bytes = br.ReadBytes( nLength );

    // Allocate some unmanaged memory for those bytes.

    pUnmanagedBytes = Marshal.AllocCoTaskMem(nLength);

    // Copy the managed byte array into the unmanaged array.

    Marshal.Copy(bytes, 0, pUnmanagedBytes, nLength);

    MessageBox.Show("L'impression est declenchee maintenant!");

    // Send the unmanaged bytes to the printer.

    bSuccess = SendBytesToPrinter(szPrinterName, pUnmanagedBytes, nLength);

    // Free the unmanaged memory that you allocated earlier.

    Marshal.FreeCoTaskMem(pUnmanagedBytes);

    return bSuccess;

    }

    public static bool SendStringToPrinter( string szPrinterName, string szString )

    {

    IntPtr pBytes;

    Int32 dwCount;

    // How many characters are in the string

    dwCount = szString.Length;

    // Assume that the printer is expecting ANSI text, and then convert

    // the string to ANSI text.

    pBytes = Marshal.StringToCoTaskMemAnsi(szString);

    // Send the converted ANSI string to the printer.

    SendBytesToPrinter(szPrinterName, pBytes, dwCount);

    Marshal.FreeCoTaskMem(pBytes);

    return true;

    }

    }

    }

    ESC d : this seems to be one of the escape sequence i can send to the printer. (I have the manual with all the esc sequence available).

    Simple question : Can you provide a sample that would send this sequence to the printer

    Also is I want to send a linefeed character... How do we proceed I've been trying : \n (ascii equivalent to but that just print out to the printer)

    Once i understand how to send those I hope i can figure out how to send all the commands.

    Thanks in advance for your help.

    Best regards.

    Mat


  • Duane Douglas

    Regarding this statement

    :"You have make sure that you're sending the proper bytes to the printer. If you're just sending a byte representation of the string of commands you're not actually sending the bytes for the actual commands."

    What exactly do you mean Right now there is a process that builds the FedEx label and stores the data/commands into a text file. My application pulls that text file and I send it into the SendToPrinter method as a string(PCL5Commands).

    It calls the API method WritePrinter(lhPrinter,PCL5Commans,PCL5Commands.Length,ref pcWritten)


    In the method that calls SendToPrinter, I detect and attempt to print to their default printer, which is set as the thermal. It prints to regular laser jets, but the output is illegible. However, the client says it does not print to their thermal printer.

    Also, the thermal printer is connected directly to the printer, as opposed to network. My testing environment uses a networked laser jet.

    Any ideas



  • Poetjevel

    To send somthing directly to the printer you can try this;

    Create a Process object.
    Command: type  // this is an age-old DOS command to view a file
    Argument: "\"" + FullPathofFile.dat + "\" >  PrinterPort(ex. COM1, LPT1)" // redirect output of 'type' to the PORT


  • FQ

    But, I am open to change. How does opos work

  • jazzydex

    Hi Nava,

    I am a novice programmer devloping a POS application in VS 2003 vb.net. I have some serious trouble printing to a thermal printer that the cusomer is using which has been provided by KUBE (www.Custom.it) thorugh a Serial Port. What function in .net could i use to print directly to the printer. There is some documentation available but only HEX codes are mentioned which I cannot make any sense of. The printer is using the ESC/POS emulation

    PLS HELP !!!!!!!!!!!!!!!!!!!

    Shabbir



  • Leonid.

    Sori am a complete newbie.

  • Hugor

    No, I am trying to write to a datamax with datamax codes. I am trying to save the image in the printer memory and recall it by name.

  • OfF3nSiV3

    Are you using OPOS If so, adding the image is pretty straight forward.
  • UdiAms

    hi guys

    plz. consider me new.

    plz. tell me how to print simple two integers , their values or their address tell me how to use default printer.

    plz. send me the whole code....

    thanks



  • Faraz_Ahmed

    Alternate ways to send printer commands

    Sometimes, as in the case of the LEXMARK “RFID WRITTER” PRINTER, it is useful to have a report output directed to a file first, later you could modify this file as necessary, and then print it at a later time to the printer.

    When this is done, the PCL/POSTSCRIPT printer commands are embedded in the text of the file at the time the report is executed. This file can later be printed directly from Visual FoxPro to either a PCL or PostScript printers.

    This can be done at the time a report is created (in design mode or from the Report Wizard) or after the report is saved (programmatically, or from the Print dialog box). There are only a few steps to print to a file, and the steps are similar under all Windows Operating Systems: Windows 95, Windows NT, Windows XP, and Servers 2000 and 2003 operating systems.

    Step-by-Step Procedure

    1. Follow your Windows User's guide to install any new printers.

    2. Once a printer is installed, create a new printer of the same type, and connect it to the File: port instead of a LPT1:, COMx:, or TCP/IP port.

    3. Set this printer up to be the default printer. (As your default printer, any new reports will be printed directly to a file along with any printer-specific language.) Remember, you can create a report with the Report Wizard or the Report Designer, open the report and save it again. When you save the report to a file, this time the printer driver and printer output device information are stored in the internal structure of the report. You need this information to print it to the right printer. Remember, when a report is saved and the output device is “File:”, the “printer driver” and “printer output device settings” are saved with the report. So, each time a report is printed, the user is prompted for a file name to save and the report is printed to that file.

    4. Using a HEX editor you can proceed to modify the file, if needed. You will have to have an excellent knowledge and understanding of one or more printer languages like PJL, PCL, Postscript, or other printer languages as the file was written to.

    5. Print the file, first check that the destination printer is the same as the printer used during the report creation. Then from the Print Setup dialog box, verify that the default printer is now connected to the real printer, not the one pointing to the File: port but to the LPT1: COMx: or TCP/IP port as it should. Next, issue the print command, or click Print on the File menu.

    In the Type list, select “ASCII File”. You receive a warning that the file will be sent directly to the printer and should have all printer-specific language in the file. Enter the file name, and click OK. The file will be sent to the printer and printed exactly the as if the report had been sent directly to the printer.


  • penninha

    What I mean is that C# may be converting the string incorrectly before sending it through to the unmanaged API. If you're sending strings to the printer like "^XA^CF,0,0,0^PR12^MD30^PW800%^PON", C# doesn't recognize '^' as an escape character and wouldn't translate and send it as such. The command would then be invalid and the printer would just print it all out as text. I've had this happen many many times. It took me quite some time to get the printer I use printing correctly through printing directly to it.

    We're both using slightly different versions of WritePrinter. The one I'm using is sending bytes so I have to marshall the string of commands to an unmanaged byte array manually. I'm assuming the WritePrinter you're using is doing the marshalling itself and may not be doing it the way you want it to.

    I use commands (for the particular printer I'm using) like:

    public const string esc = "\x1b";
    public const string partial_cut = "\x1f";
    public static string variable_length_page(bool tf) {
    return (esc+"\x63"+(tf "\x1":"\x0"));
    }

    PrintDirect.SendStringToPrinter("PrinterName", variable_length_page(true));

    I also found this article, which shows sending PCL commands through C#.


  • Richard McEnery

    I routinely have to do this sort of thing (printing directly to thermal printers). I've used both methods of opening a port and talking directly to the printer or using the printer driver if it had one.

    this is the code I use to go through the driver... it's similar to what you've posted.

    public class PrintThroughDriver

    {

    // Structure and API declarions:

    [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]

    internal class DOCINFOA

    {

    [MarshalAs(UnmanagedType.LPStr)] public string pDocName;

    [MarshalAs(UnmanagedType.LPStr)] public string pOutputFile;

    [MarshalAs(UnmanagedType.LPStr)] public string pDataType;

    }

    [DllImport("winspool.Drv", EntryPoint="OpenPrinterA", SetLastError=true, CharSet=CharSet.Ansi, ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]

    internal static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter, out IntPtr hPrinter, long pd);

    [DllImport("winspool.Drv", EntryPoint="ClosePrinter", SetLastError=true, ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]

    internal static extern bool ClosePrinter(IntPtr hPrinter);

    [DllImport("winspool.Drv", EntryPoint="StartDocPrinterA", SetLastError=true, CharSet=CharSet.Ansi, ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]

    internal static extern bool StartDocPrinter( IntPtr hPrinter, Int32 level, [In, MarshalAs(UnmanagedType.LPStruct)] DOCINFOA di);

    [DllImport("winspool.Drv", EntryPoint="EndDocPrinter", SetLastError=true, ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]

    internal static extern bool EndDocPrinter(IntPtr hPrinter);

    [DllImport("winspool.Drv", EntryPoint="StartPagePrinter", SetLastError=true, ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]

    internal static extern bool StartPagePrinter(IntPtr hPrinter);

    [DllImport("winspool.Drv", EntryPoint="EndPagePrinter", SetLastError=true, ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]

    internal static extern bool EndPagePrinter(IntPtr hPrinter);

    [DllImport("winspool.Drv", EntryPoint="WritePrinter", SetLastError=true, ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]

    internal static extern bool WritePrinter(IntPtr hPrinter, IntPtr pBytes, Int32 dwCount, out Int32 dwWritten );

    [DllImport("kernel32.dll", EntryPoint="GetLastError", SetLastError=false, ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]

    internal static extern Int32 GetLastError();

    // SendBytesToPrinter()

    // When the function is given a printer name and an unmanaged array

    // of bytes, the function sends those bytes to the print queue.

    // Returns true on success, false on failure.

    private static bool SendBytesToPrinter( string szPrinterName, IntPtr pBytes, Int32 dwCount)

    {

    Int32 dwError = 0, dwWritten = 0;

    IntPtr hPrinter = new IntPtr(0);

    DOCINFOA di = new DOCINFOA();

    bool bSuccess = false; // Assume failure unless you specifically succeed.

    di.pDocName = "C# Debug Print RAW Document";

    di.pDataType = "RAW";

    // Open the printer.

    if( OpenPrinter( szPrinterName, out hPrinter, 0 ) )

    {

    // Start a document.

    if( StartDocPrinter(hPrinter, 1, di) )

    {

    // Start a page.

    if( StartPagePrinter(hPrinter) )

    {

    // Write your bytes.

    bSuccess = WritePrinter(hPrinter, pBytes, dwCount, out dwWritten);

    EndPagePrinter(hPrinter);

    }

    EndDocPrinter(hPrinter);

    }

    ClosePrinter(hPrinter);

    }

    // If you did not succeed, GetLastError may give more information

    // about why not.

    if( bSuccess == false )

    {

    dwError = GetLastError();

    }

    return bSuccess;

    }

    public static bool SendStringToPrinter( string szPrinterName, string szString )

    {

    IntPtr pBytes;

    Int32 dwCount;

    // How many characters are in the string

    dwCount = szString.Length;

    // Assume that the printer is expecting ANSI text, and then convert

    // the string to ANSI text.

    pBytes = Marshal.StringToCoTaskMemAnsi(szString);

    // Send the converted ANSI string to the printer.

    SendBytesToPrinter(szPrinterName, pBytes, dwCount);

    Marshal.FreeCoTaskMem(pBytes);

    return true;

    }

    }

    internal struct DOCINFO

    {

    [MarshalAs(UnmanagedType.LPWStr)]public string pDocName;

    [MarshalAs(UnmanagedType.LPWStr)]public string pOutputFile;

    [MarshalAs(UnmanagedType.LPWStr)]public string pDataType;

    }

    }

    You have make sure that you're sending the proper bytes to the printer. If you're just sending a byte representation of the string of commands you're not actually sending the bytes for the actual commands.


  • dpetrek

    Hi guys,

    I thought I'd throw this option into the mix. I develop point of sale software for restaurants that uses Epson TM thermal printers. For clarity's sake, you're not really printing directly to the printer if you use winspool.drv, you're just bypassing the printer selection dialog form. Whatever you're printing is still being spooled = not printing directly. We use the SDKs OPOS for .Net and Microsoft .Net for POS, which has the benefit of async printing without a spooler and is incredibly fast for thermals.

    Like I said, I just wanted to throw that out there. I realize your problem is probably more syntactical. I'd research the printer codes and make sure your text file codes will, in fact, work. With regards to the non-recognized string escape characters mentioned, you could massage those in your C# code before sending to the printer.

    - nava


  • Printing Directly to Printer