将.NET代码发送到Zebra打印机以发送ZPL

54

有没有一种方法在.NET中向打印机发送ZPL(Zebra编程语言)?

我已经有了用Delphi实现此功能的代码,但它不是很优美,我不想尝试在.NET中重新创建它。

10个回答

56
这样,您将能够向打印机发送ZPL命令,无论它连接方式如何(如LPTUSB网络共享等)。
创建 RawPrinterHelper 类(参考微软的文章:使用Visual C# .NET发送原始数据到打印机的方法)。
using System;
using System.Drawing;
using System.Drawing.Printing;
using System.IO;
using System.Windows.Forms;
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, 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);
        // 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;
    }
}
        

调用打印方法:

private void BtnPrint_Click(object sender, System.EventArgs e)
{
    string s = "^XA^LH30,30\n^FO20,10^ADN,90,50^AD^FDHello World^FS\n^XZ";
    
    PrintDialog pd  = new PrintDialog();
    pd.PrinterSettings = new PrinterSettings();
    if(DialogResult.OK == pd.ShowDialog(this))
    {
        RawPrinterHelper.SendStringToPrinter(pd.PrinterSettings.PrinterName, s);
    }
}

我遇到的两个问题与向打印机发送带有ZPL代码的txt文件有关:

  1. The file has to end with a new line character

  2. Encoding has to be set to Encoding.Default when reading ANSI txt files with special characters

     public static bool SendTextFileToPrinter(string szFileName, string printerName)
     {
         var sb = new StringBuilder();
    
         using (var sr = new StreamReader(szFileName, Encoding.Default))
         {
             while (!sr.EndOfStream)
             {
                 sb.AppendLine(sr.ReadLine());
             }
         }
    
         return RawPrinterHelper.SendStringToPrinter(printerName, sb.ToString());
     }
    

22

看一下这个帖子:使用PrintDocument类将ZPL代码打印到ZEBRA打印机

具体而言,原帖中的回答者选择了以下函数:

[DllImport("kernel32.dll", SetLastError = true)]
static extern SafeFileHandle CreateFile(string lpFileName, FileAccess dwDesiredAccess,
uint dwShareMode, IntPtr lpSecurityAttributes, FileMode dwCreationDisposition,
uint dwFlagsAndAttributes, IntPtr hTemplateFile);

private void Print()
{
    // Command to be sent to the printer
    string command = "^XA^FO10,10,^AO,30,20^FDFDTesting^FS^FO10,30^BY3^BCN,100,Y,N,N^FDTesting^FS^XZ";

    // Create a buffer with the command
    Byte[] buffer = new byte[command.Length];
    buffer = System.Text.Encoding.ASCII.GetBytes(command);
    // Use the CreateFile external func to connect to the LPT1 port
    SafeFileHandle printer = CreateFile("LPT1:", FileAccess.ReadWrite, 0, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);
    // Aqui verifico se a impressora é válida
    if (printer.IsInvalid == true)
    {
        return;
    }

    // Open the filestream to the lpt1 port and send the command
    FileStream lpt1 = new FileStream(printer, FileAccess.ReadWrite);
    lpt1.Write(buffer, 0, buffer.Length);
    // Close the FileStream connection
    lpt1.Close();

}

2
为了支持非英文符号,command 必须以 UTF16 BE 编码。 - Alexufo
这些打印机有没有办法以某种方式使用“复印数量”功能?我找不到任何示例或文档。 - undefined

21

以下是使用TCP/IP协议的操作步骤:

// Printer IP Address and communication port
    string ipAddress = "10.3.14.42";
   int port = 9100;

// ZPL Command(s)
   string ZPLString =
    "^XA" +
    "^FO50,50" +
    "^A0N50,50" +
    "^FDHello, World!^FS" +
    "^XZ";

   try
   {
    // Open connection
    System.Net.Sockets.TcpClient client = new System.Net.Sockets.TcpClient();
    client.Connect(ipAddress, port);

    // Write ZPL String to connection
    System.IO.StreamWriter writer =
    new System.IO.StreamWriter(client.GetStream());
    writer.Write(ZPLString);
    writer.Flush();

    // Close Connection
    writer.Close();
    client.Close();
}
catch (Exception ex)
{
    // Catch Exception
}

来源: ZEBRA 网站


在调试过程中,我无法在文档中找到提到的9100端口,因此我使用“nmap 192.168.1.x -p1-10000”扫描了Zebra打印机的开放端口,但是那里也没有列出9100。问题是,即使像Nmap这样的端口扫描器似乎并未打开该端口,它仍然能正常工作。是否有人可以提供更好的官方文档链接,说明端口号? - mik13ST
这些打印机有没有办法以某种方式使用“复印数量”功能?我找不到任何相关示例或文档。 - undefined

16
最简单的解决方案是将文件复制到共享打印机中。 C#示例:
System.IO.File.Copy(inputFilePath, printerPath);

其中:

  • inputFilePath - ZPL文件的路径(不需要特殊扩展名);
  • printerPath - 共享打印机的路径,例如:\127.0.0.1\zebraGX

13

我已经使用套接字管理这个项目多年了。Zebra通常使用6101端口。我会查看代码并尽可能发布。

public void SendData(string zpl)
{
    NetworkStream ns = null;
    Socket socket = null;

    try
    {
        if (printerIP == null)
        {
            /* IP is a string property for the printer's IP address. */
            /* 6101 is the common port of all our Zebra printers. */
            printerIP = new IPEndPoint(IPAddress.Parse(IP), 6101);  
        }

        socket = new Socket(AddressFamily.InterNetwork,
            SocketType.Stream,
            ProtocolType.Tcp);
        socket.Connect(printerIP);

        ns = new NetworkStream(socket);

        byte[] toSend = Encoding.ASCII.GetBytes(zpl);
        ns.Write(toSend, 0, toSend.Length);
    }
    finally
    {
        if (ns != null)
            ns.Close();

        if (socket != null && socket.Connected)
            socket.Close();
    }
}

5
斑马移动打印机使用6101端口(QL、RW、MZ等),而较大的打印机通常使用9100端口。 - James Van Huis

8

因为这篇文章在搜索C#和ZPL时仍然排名靠前,所以我想提一下SharpZebra。它只支持EPL2,但我已经提交了一个更新,增加了对ZPL的支持,并支持通过套接字、Windows Spool服务和直接USB打印。


2

VB版本(使用端口9100-在Zebra ZM400上测试)

Sub PrintZPL(ByVal pIP As String, ByVal psZPL As String)
    Dim lAddress As Net.IPEndPoint
    Dim lSocket As System.Net.Sockets.Socket = Nothing
    Dim lNetStream As System.Net.Sockets.NetworkStream = Nothing
    Dim lBytes As Byte()

    Try
        lAddress = New Net.IPEndPoint(Net.IPAddress.Parse(pIP), 9100)
        lSocket = New Socket(AddressFamily.InterNetwork, SocketType.Stream, _                       ProtocolType.Tcp)
        lSocket.Connect(lAddress)
        lNetStream = New NetworkStream(lSocket)

        lBytes = System.Text.Encoding.ASCII.GetBytes(psZPL)
        lNetStream.Write(lBytes, 0, lBytes.Length)
    Catch ex As Exception When Not App.Debugging
        Msgbox ex.message & vbnewline & ex.tostring
    Finally
        If Not lNetStream Is Nothing Then
            lNetStream.Close()
        End If
        If Not lSocket Is Nothing Then
            lSocket.Close()
        End If
    End Try
End Sub

2
@liquide的回答非常好。最初的回答。
System.IO.File.Copy(inputFilePath, printerPath);

我從斑馬條碼打印機的ZPL程序員指南第1卷(2005年)中找到了這個答案: 輸入圖片描述。 "Original Answer"翻譯成“最初的回答”。


0

我使用这两个的组合

    Private Sub sendData(ByVal zpl As String)
    Dim ns As System.Net.Sockets.NetworkStream = Nothing
    Dim socket As System.Net.Sockets.Socket = Nothing
    Dim printerIP As Net.IPEndPoint = Nothing
    Dim toSend As Byte()

    Try
        If printerIP Is Nothing Then
            'set the IP address
            printerIP = New Net.IPEndPoint(IPAddress.Parse(IP_ADDRESS), 9100)
        End If

        'Create a TCP socket
        socket = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
        'Connect to the printer based on the IP address
        socket.Connect(printerIP)
        'create a new network stream based on the socket connection
        ns = New NetworkStream(socket)

        'convert the zpl command to a byte array
        toSend = System.Text.Encoding.ASCII.GetBytes(zpl)

        'send the zpl byte array over the networkstream to the connected printer
        ns.Write(toSend, 0, toSend.Length)

    Catch ex As Exception
        MessageBox.Show(ex.Message, "Cable Printer", MessageBoxButtons.OKCancel, MessageBoxIcon.Error)
    Finally
        'close the networkstream and then the socket
        If Not ns Is Nothing Then
            ns.Close()
        End If

        If Not socket Is Nothing Then
            socket.Close()
        End If
    End Try
End Sub


Private Function createString() As String
    Dim command As String

    command = "^XA"
    command += "^LH20,25"

    If rdoSmall.Checked = True Then
        command += "^FO1,30^A0,N,25,25^FD"
    ElseIf rdoNormal.Checked = True Then
        command += "^FO1,30^A0,N,35,35^FD"
    Else
        command += "^FO1,30^A0,N,50,50^FD"
    End If

    command += txtInput.Text
    command += "^FS"
    command += "^XZ"

    Return command

End Function

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接