How to send a raw ZPL to zebra printer using C# via USB
Asked Answered
H

2

7

I'm a beginner C# programmer. I have a project that requires me to send the raw command to Zebra printer LP 2844 via USB and make it work. I did a lot of research and tried to figure out a way to do that. I'm using the code from http://support.microsoft.com/kb/322091, but it didn't work. Based on my test, it seems that I have sent commands to the printer, but it didn't respond and print. I have no idea about this. Can someone help me out?

I'm using button to send the command directly

private void button2_Click(object sender, EventArgs e)
{
    string s = "A50,50,0,2,1,1,N,\"9129302\"";

    // Allow the user to select a printer.
    PrintDialog pd = new PrintDialog();
    pd.PrinterSettings = new PrinterSettings();
    if (DialogResult.OK == pd.ShowDialog(this))
    {
        // Send a printer-specific to the printer.
        RawPrinterHelper.SendStringToPrinter(pd.PrinterSettings.PrinterName, s);
        MessageBox.Show("Data sent to printer.");
    }
}
Homology answered 4/9, 2014 at 15:29 Comment(1)
That does not look like ZPL, it looks like EPL. ZPL commands normally start with ^ then a letter.Scissor
S
18

EDIT: To address your update, The problem you are having is you are using SendStringToPrinter which sends a ANSI string (a null terminated) string to the printer which is not what the printer is expecting. According to the official EPL2 programming guide page 23 (Which is what you are really doing, not ZPL according to your example).

Each command line must be terminated with a Line Feed (LF) character (Dec. 10). Most PC based systems send CR/LF when the Enter key is pressed. The Carriage Return (CR) character is ignored by the printer and cannot be used in place of LF.

So you must either modify SendStringToPrinter to send a \n at the end of the string instead of a \0 or you must build the ASCII byte array yourself and use RawPrinterHelper.SendBytesToPrinter yourself (like I did in my original answer below).

So to fix your simple posted example we change out your function call, we also must tell the printer to actually print by sending a P1\n

private void button2_Click(object sender, EventArgs e)
{
    string s = "A50,50,0,2,1,1,N,\"9129302\"\n";
    s += "P1\n";

    // Allow the user to select a printer.
    PrintDialog pd = new PrintDialog();
    pd.PrinterSettings = new PrinterSettings();
    if (DialogResult.OK == pd.ShowDialog(this))
    {
        var bytes = Encoding.ASCII.GetBytes(s);
        // Send a printer-specific to the printer.
        RawPrinterHelper.SendBytesToPrinter(pd.PrinterSettings.PrinterName, bytes, bytes.Length);
        MessageBox.Show("Data sent to printer.");
    }
}

//elsewhere
public static class RawPrinterHelper
{
    //(Snip) The rest of the code you already have from http://support.microsoft.com/kb/322091

    [DllImport("winspool.Drv", EntryPoint="WritePrinter", SetLastError=true, ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]
    public static extern bool WritePrinter(IntPtr hPrinter, byte[] pBytes, Int32 dwCount, out Int32 dwWritten );


    private static bool SendBytesToPrinter(string szPrinterName, byte[] bytes, Int32 dwCount)
    {
        Int32 dwError = 0, dwWritten = 0;
        IntPtr hPrinter = new IntPtr(0);
        DOCINFOA di = new DOCINFOA();
        bool bSuccess = false;

        di.pDocName = "Zebra Label";
        di.pDataType = "RAW";


        if (OpenPrinter(szPrinterName.Normalize(), out hPrinter, IntPtr.Zero))
        {
            if (StartDocPrinter(hPrinter, 1, di))
            {
                if (StartPagePrinter(hPrinter))
                {
                    bSuccess = WritePrinter(hPrinter, bytes, dwCount, out dwWritten);
                    EndPagePrinter(hPrinter);
                }
                EndDocPrinter(hPrinter);
            }
            ClosePrinter(hPrinter);
        }
        if (bSuccess == false)
        {
            dwError = Marshal.GetLastWin32Error();
            throw new Win32Exception(dwError);
        }
        return bSuccess;
    }
}

I did this with Zebra's older EPL2 language, but it should be very similar to what you need to do with ZPL. Perhaps it will get you started in the right direction.

public class Label
{
    #region Print logic. Taken from http://support.microsoft.com/kb/322091

    //Snip stuff unchanged from the KB example.   

    [DllImport("winspool.Drv", EntryPoint="WritePrinter", SetLastError=true, ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]
    public static extern bool WritePrinter(IntPtr hPrinter, byte[] pBytes, Int32 dwCount, out Int32 dwWritten );     

    private static bool SendBytesToPrinter(string szPrinterName, byte[] Bytes, Int32 dwCount)
    {
        Int32 dwError = 0, dwWritten = 0;
        IntPtr hPrinter = new IntPtr(0);
        DOCINFOA di = new DOCINFOA();
        bool bSuccess = false;

        di.pDocName = "Zebra Label";
        di.pDataType = "RAW";


        if (OpenPrinter(szPrinterName.Normalize(), out hPrinter, IntPtr.Zero))
        {
            if (StartDocPrinter(hPrinter, 1, di))
            {
                if (StartPagePrinter(hPrinter))
                {
                    bSuccess = WritePrinter(hPrinter, Bytes, dwCount, out dwWritten);
                    EndPagePrinter(hPrinter);
                }
                EndDocPrinter(hPrinter);
            }
            ClosePrinter(hPrinter);
        }
        if (bSuccess == false)
        {
            dwError = Marshal.GetLastWin32Error();
            throw new Win32Exception(dwError);
        }
        return bSuccess;
    }
    #endregion

    public byte[] CreateCompleteCommand(bool headerAndFooter)
    {
        List<byte> byteCollection = new List<byte>();

        //Static header content describing the label.
        if (headerAndFooter)
        {
            byteCollection.AddRange(Encoding.ASCII.GetBytes("\nN\n"));
            byteCollection.AddRange(Encoding.ASCII.GetBytes(String.Format("S{0}\n", this.Speed)));
            byteCollection.AddRange(Encoding.ASCII.GetBytes(String.Format("D{0}\n", this.Density)));
            byteCollection.AddRange(Encoding.ASCII.GetBytes(String.Format("q{0}\n", this.LabelHeight)));
            if (this.AdvancedLabelSizing)
            {
                byteCollection.AddRange(Encoding.ASCII.GetBytes(String.Format("Q{0},{1}\n", this.LableLength, this.GapLength)));
            }
        }

        //The content of the label.
        foreach (var command in this.Commands)
        {
            byteCollection.AddRange(command.GenerateByteCommand());
        }

        //The footer content of the label.
        if(headerAndFooter)
            byteCollection.AddRange(Encoding.ASCII.GetBytes(String.Format("P{0}\n", this.Pages)));

        return byteCollection.ToArray();
    }

    public bool PrintLabel(string printer)
    {
        byte[] command = this.CreateCompleteCommand(true);
        return SendBytesToPrinter(printer, command, command.Length); 
    }

    public List<Epl2CommandBase> Commands { get; private set; }

    //Snip rest of the code.
}

public abstract partial class Epl2CommandBase
{
    protected Epl2CommandBase() { }

    public virtual byte[] GenerateByteCommand()
    {
        return Encoding.ASCII.GetBytes(CommandString + '\n');
    }
    public abstract string CommandString { get; set; }
}


public class Text : Epl2CommandBase 
{
    public override string CommandString
    {
        get
        {
            string printText = TextValue;
            if (Font == Fonts.Pts24)
                printText = TextValue.ToUpperInvariant();
            printText = printText.Replace("\\", "\\\\"); // Replace \ with \\
            printText = printText.Replace("\"", "\\\""); // replace " with \"
            return String.Format("A{0},{1},{2},{3},{4},{5},{6},\"{7}\"", X, Y, (byte)TextRotation, (byte)Font, HorziontalMultiplier, VertricalMultiplier, Reverse, printText);
        }
        set
        {
            GenerateCommandFromText(value);
        }
    }

    private void GenerateCommandFromText(string command)
    {
        if (!command.StartsWith(GetFactoryKey()))
            throw new ArgumentException("Command must begin with " + GetFactoryKey());
        string[] commands = command.Substring(1).Split(',');
        this.X = int.Parse(commands[0]);
        this.Y = int.Parse(commands[1]);
        this.TextRotation = (Rotation)byte.Parse(commands[2]);
        this.Font = (Fonts)byte.Parse(commands[3]);
        this.HorziontalMultiplier = int.Parse(commands[4]);
        this.VertricalMultiplier = int.Parse(commands[5]);
        this.ReverseImageColor = commands[6].Trim().ToUpper() == "R";
        string message = String.Join(",", commands, 7, commands.Length - 7);
        this.TextValue = message.Substring(1, message.Length - 2); // Remove the " at the beginning and end of the string.
    }

    //Snip
}
Scissor answered 4/9, 2014 at 16:29 Comment(2)
Thank u!! it's very specific! I appreciated!Homology
This method is working great when I print from my local machine but when we deploy our app to a Remote desktop It is not printing anything. Any idea what might be going on? This is a WPF application mounted as a VDI in RDP. One thing I noticed was the printer name changed slightly in the Remote desktop compared to the name in my local. It added "(redirected 3)" at the end of the name of the printer. I am not sure if this is the issue.Civilian
B
2

In case people are trying to get that snippet from the answer above:

        [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);

        [DllImport("winspool.Drv", EntryPoint = "WritePrinter", SetLastError = true, ExactSpelling = true,
            CallingConvention = CallingConvention.StdCall)]
        public static extern bool WritePrinter(IntPtr hPrinter, byte[] pBytes, Int32 dwCount, out Int32 dwWritten);
Boschbok answered 16/4, 2021 at 12:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.