1
votes

This is my *.prn file:

I8,A,001 Q0001,0
q831
rN
S5
D10
ZT
JF
O
R20,0
f100
N
B775,188,2,1,2,6,160,B,"SM00020000"
X0,199,1,0,200
P1

SM00020000 being the barcode.

string s = "I8,A,001\n\n\nQ0001,0\nq831\nrN\nS5\nD10\nZT\nJF\nO\nR20,0\nf100\nN\nB775,188,2,1,2,6,160,B,\"SM00020000\",199,1,0,200\nP1\n";

PrintDialog pd = new new PrintDialog();
pd.PrinterSettings = new System.Drawing.Printing.
pd.PrinterSettings.PrinterName = "ZDesigner GT800 (EPL)";
RawPrinterHelper.SendStringToPrinter(pd.PrinterSettings.PrinterName, s);

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;
}

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;
}

This code is not helping me print my label. The document goes to the print queue but nothing happens after that. Although the printer is correctly configured and I have successfully printed with Zebra Designer.

Also I'd like the above code such that it prints 3 labels in one row, since I have the media which has 3 stickers in one row. How can that be achieved?

My printer model is ZDesigner GT800 (EPL).

4
You don't need StringToCoTaskMemAnsi, use System.Text.Encoding.Ascii.GetBytes. You also don't need to start pages, you are writing raw print data to the printer, only use StartDocPrinter and WritePrinter.GSerg
"\n" can be ambiguous depending on your environment. EPL printers expect a LINEFEED to terminate the line, not a CR. Try swapping with "\x0A" (or the C# equivilent)charlesbridge

4 Answers

3
votes

Here's a Microsoft class for sending bytes to printer. It's ready to use out of the box:

    using System;
using System.IO;
using System.Runtime.InteropServices;

public class RawPrinterHelper
{
    // 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, int 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, int dwCount, out int 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, int dwCount)
    {
        int 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);
        // 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;
        int dwCount;

        // How many characters are in the string?
        // Fix from Nicholas Piasecki:
        // dwCount = szString.Length;
        dwCount = (szString.Length + 1) * Marshal.SystemMaxDBCSCharSize;

        // 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;
    }
}

After that Format your ZPL request and send it to the Print Class:

     StringBuilder ZplBuilder = new StringBuilder();

    // Exemple ZPL String

                        ZplBuilder.Append("^XA");   //Start ZPL
                        ZplBuilder.Append("^FO320,42^APN,48,48^FD").Append(DateTime.Now.Date.ToString("dd/MM/yyyy")).Append("^FS");
ZplBuilder.Append("^FO0,304^GB720,168,1^FS");
ZplBuilder.Append("^FO0,306^GD720,166,1,B,L^FS");
ZplBuilder.Append("^FO0,306^GD720,166,1,B,R^FS");
ZplBuilder.Append("^XZ"); 
    // End ZPL

string ZplString = ZplBuilder.ToString();

MemoryStream lmemStream = new MemoryStream();

StreamWriter lstreamWriter = new StreamWriter(lmemStream);
lstreamWriter.Write(ZplString);
lstreamWriter.Flush();
lmemStream.Position = 0;

byte[] byteArray = lmemStream.ToArray();

IntPtr cpUnmanagedBytes = new IntPtr(0);
int cnLength = byteArray.Length;
cpUnmanagedBytes = Marshal.AllocCoTaskMem(cnLength);
Marshal.Copy(byteArray, 0, cpUnmanagedBytes, cnLength);

RawPrinterHelper.SendBytesToPrinter("Intermec PC43d (203 dpi)", cpUnmanagedBytes, cnLength);
Marshal.FreeCoTaskMem(cpUnmanagedBytes);

It works fine on an intermec Printec with ZPL Protocel.

I hope this will help.

1
votes

This is what we do.

namespace SafeHandles
{
    public class PrinterSafeHandle : global::Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid
    {
        [DllImport("winspool.drv", CharSet=CharSet.Auto, SetLastError=true)]
        private static extern bool OpenPrinter(string pPrinterName, out IntPtr phPrinter, IntPtr pDefault);

        [DllImport("winspool.drv", CharSet=CharSet.Auto, SetLastError=true, ExactSpelling=true)]
        private static extern bool ClosePrinter(IntPtr hPrinter);

        public PrinterSafeHandle(string PrinterName) : base(true)
        {
            if (!OpenPrinter(PrinterName, out this.handle, IntPtr.Zero))
            {
                throw new System.ComponentModel.Win32Exception();
            }
        }

        protected override bool ReleaseHandle()
        {
            return ClosePrinter(this.handle);
        }
    }
}
[DllImport("winspool.drv", CharSet=CharSet.Auto, SetLastError=true)]
private static extern bool WritePrinter (SafeHandles.PrinterSafeHandle hPrinter, [In, MarshalAs(UnmanagedType.LPArray)] byte[] pBuf, int cdBuf, out int pcWritten);

[DllImport("winspool.drv", CharSet=CharSet.Auto, SetLastError=true)]
private static extern bool WritePrinter (SafeHandles.PrinterSafeHandle hPrinter, IntPtr pBuf, int cdBuf, out int pcWritten);

[DllImport("winspool.drv", CharSet=CharSet.Auto, SetLastError=true)]
private static extern int StartDocPrinter(SafeHandles.PrinterSafeHandle hPrinter, int Level, [In] ref DOC_INFO_1 pDocInfo);

[DllImport("winspool.drv", CharSet=CharSet.Auto, SetLastError=true)]
private static extern bool EndDocPrinter (SafeHandles.PrinterSafeHandle hPrinter);

[DllImport("winspool.drv", CharSet=CharSet.Auto, SetLastError=true)]
private static extern bool SetJob (SafeHandles.PrinterSafeHandle hPrinter, int JobID, int Level, IntPtr pJob, int Command);

private const int JOB_CONTROL_PAUSE = 1;
private const int JOB_CONTROL_DELETE = 5;

[StructLayout(LayoutKind.Sequential)]
private struct DOC_INFO_1
{
    [MarshalAs(UnmanagedType.LPTStr)] public string pDocName;
    [MarshalAs(UnmanagedType.LPTStr)] public string pOutputFile;
    [MarshalAs(UnmanagedType.LPTStr)] public string pDatatype;
}


public static int SendPrinterCommand(string PrinterName, string DocumentName, string Command, bool Suspended = false)
{
    return SendPrinterCommand(null, PrinterName, DocumentName, Command, Suspended);
}

public static int SendPrinterCommand(string PrinterName, string DocumentName, string Command, System.Text.Encoding Encoding, bool Suspended = false)
{
    return SendPrinterCommand(null, PrinterName, DocumentName, Command, Encoding, Suspended);
}

public static int SendPrinterCommand(string PrinterName, string DocumentName, byte[] Command, bool Suspended = false)
{
    return SendPrinterCommand(null, PrinterName, DocumentName, Command, Suspended);
}

public static int SendPrinterCommand(string ServerName, string PrinterName, string DocumentName, string Command, bool Suspended = false) 
{
    return SendPrinterCommand(ServerName, PrinterName, DocumentName, Command, System.Text.Encoding.ASCII, Suspended);
}

public static int SendPrinterCommand(string ServerName, string PrinterName, string DocumentName, string Command, System.Text.Encoding Encoding, bool Suspended = false)
{
    return SendPrinterCommand(ServerName, PrinterName, DocumentName, Encoding.GetBytes(Command), Suspended);
}

public static int SendPrinterCommand(string ServerName, string PrinterName, string DocumentName, byte[] Command, bool Suspended = false)
{
    string FullPrinterPath = string.IsNullOrEmpty(ServerName) ? PrinterName : System.IO.Path.Combine(ServerName, PrinterName);

    using (var h = new SafeHandles.PrinterSafeHandle(FullPrinterPath))
    {
        var di1 = new DOC_INFO_1()
        {
            pDocName = DocumentName,
            pOutputFile = null,
            pDatatype = "RAW"
        };

        int job_id = StartDocPrinter(h, 1, ref di1);
        if (job_id == 0)
        {
            throw new System.ComponentModel.Win32Exception();
        }

        if (Suspended)
        {
            if (!SetJob(h, job_id, 0, IntPtr.Zero, JOB_CONTROL_PAUSE))
            {
                throw new System.ComponentModel.Win32Exception();
            }
        }

        try
        {
            int total_bytes_written = 0;

            if (!WritePrinter(h, Command, Command.Length, out total_bytes_written))
            {
                throw new System.ComponentModel.Win32Exception();
            }

            if (total_bytes_written < Command.Length)
            {
                var gch = GCHandle.Alloc(Command, GCHandleType.Pinned);

                try
                {
                    do
                    {
                        int next_index = total_bytes_written;
                        int next_requred_len = Command.Length - next_index;
                        int bytes_written_this_time = 0;

                        if (!WritePrinter(h, Marshal.UnsafeAddrOfPinnedArrayElement(Command, next_index), next_requred_len, out bytes_written_this_time))
                        {
                            throw new System.ComponentModel.Win32Exception();
                        }

                        total_bytes_written += bytes_written_this_time;
                    } while (total_bytes_written < Command.Length);
                }
                finally
                {
                    gch.Free();
                }
            }
        }
        catch
        {
            SetJob(h, job_id, 0, IntPtr.Zero, JOB_CONTROL_DELETE);
            throw;
        }
        finally
        {
            EndDocPrinter(h);
        }

        return job_id;
    }

}
string s = "I8,A,001\n\n\nQ0001,0\nq831\nrN\nS5\nD10\nZT\nJF\nO\nR20,0\nf100\nN\nB775,188,2,1,2,6,160,B,\"SM00020000\",199,1,0,200\nP1\n";

SendPrinterCommand("ZDesigner GT800 (EPL)", "Foo", s);
0
votes

I would suggest not reinventing the wheel. Take a look at third-party libraries that already do this. For example, ThermalLabel SDK. It's a paid library, but it does well. You might even find some free/open-source ones out there as well.

0
votes

If any of these does not work, you can write your zpl commands into a file using c# and directly copy it to the printer, if it is shared in network.

    public void Print(int numCopies = 1)
    {

        int randomInt = (new Random()).Next(1000); //this random value is intented to get the collision change of prn file names reduced 
        string SERVERPATH = Path.Combine(System.Web.HttpContext.Current.Server.MapPath("~/PrintOperations/"));

        if (!Directory.Exists(SERVERPATH))
        {
            Directory.CreateDirectory(SERVERPATH);

        }
        String prnFilePath = SERVERPATH + MethodName + randomInt + ".prn";
        String fullPrinterPath = @"\\"+ServerName+@"\"+ printerName;
        var createdFile = System.IO.File.Create(prnFilePath);
        createdFile.Close();
        System.IO.File.WriteAllText(prnFilePath, parsedZpl);
        System.Diagnostics.Process process = new System.Diagnostics.Process();
        System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
        startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
        startInfo.FileName = "cmd.exe";
        startInfo.Arguments = string.Format("/C Copy \"{0}\" \"{1}\"", prnFilePath ,fullPrinterPath);
        process.StartInfo = startInfo;
        for (int i =0; i< numCopies; i++)
        {
            process.Start();
            process.WaitForExit();

        }

        System.IO.File.Delete(prnFilePath);
}