Visual C# 入门
本文共分为两个部分:
第一部分:介绍如何与Zebar进行连接,把ZPL指令或者模板文件发送到斑马打印机进行打印。
第二部分:介绍如何接收Zebar进行打印之后如何得到斑马打印机的反馈信息,防止打印出错的情况下继续进行工业的操作。需要用上位机得到打印出错的信息,然后反馈给PLC。
一、C# 将指令发送到Zebar打印机的方式
Zebra Technologies (GitHub Demo)
1、利用winspool打印机库函数(LPT,USB网络共享)
(1)RawPrinterHelper类能够向打印机发送ZPL指令,无论何种连接包括(LPT,USB网络共享),指定打印机名称进行打印:RawPrinterHelper类
( 从上的Microsoft article文章如何使用可视化 C#. NET 将原始数据发送到打印机):
调用方法:
private void button2_Click(object sender, System.EventArgs e)
{
string s ="^XA^LH30,30n^FO20,10^ADN,90,50^AD^FDHello World^FSn^XZ";
//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);
}
}
(2)若直接发送已经保存好的TXT模板文件,两个问题:
1、文件必须以换行符结束
2、在读取带有特殊字符的ANSI.txt文件时,编码必须设置为Encoding.Default
public static bool SendTextFileToPrinter(string szFileName, string printerName)
{
var sb = new StringBuilder();
using (var sr = new StreamReader(szFileName, Encoding.Default))//Set the correct encoding
{
while (!sr.EndOfStream)
{
sb.AppendLine(sr.ReadLine());//This will automatically fix the last line
}
}
return RawPrinterHelper.SendStringToPrinter(printerName, sb.ToString());
}
2、利用web seat类利用kernel32.dll,发送ZPL代码到Zebar打印机(LPT1)
打印ZPL代码到Zebar打印机使用的web seat类参考链接:Print ZPL codes to ZEBRA printer using PrintDocument class
[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();
}
3、使用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
}
4、使用斑马的6106端口(IP 和 Port)
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、利用BarTender的COM组件进行打印
下载BarTender,在VS里面需要添加引用
private static BarTender.Application btApp = new BarTender.Application();
private static BarTender.Format btFormat = new BarTender.Format();
//第一个参数设置为模板的路径,在debug下
btFormat = btApp.Formats.Open(AppDomain.CurrentDomain.BaseDirectory + "\\Template.btw", false, "");//@"C:\Users\Administrator\Desktop\lable1.btw",
//设置同序列打印的份数
btFormat.PrintSetup.IdenticalCopiesOfLabel = 1;
//向bartender模板传递变量
//btFormat.SetNamedSubStringValue("模板名","值");
//btformat.SetNamedSubStringValue("txtGoodCode1", "品号"); //这个是数据源名称
//btformat.SetNamedSubStringValue("txtGoodName1", "品名");//设置“数据源名称”
//btformat.SetNamedSubStringValue("txtOrderPo1", "订单");
//第二个False设置打印时是否跳出打印属性
int Count = btFormat.PrintOut(true, false);//Print不弹出打印对话框
//退出时是否跳保存标签,关闭摸板文件,并且关闭文件流
btFormat.Close(BarTender.BtSaveOptions.btSaveChanges);
//打印完毕
//btApp.Quit(BarTender.BtSaveOptions.btDoNotSaveChanges);
6、将打印文件复制到共享打印机
System.IO.File.Copy(inputFilePath, printerPath);
- inputFilePath - ZPL文件( 不需要特殊扩展)的路径;
- printerPath - 共享( ) 打印机的路径,例如: ! 127.0.0.1 zebraGX
二、 接收Zebar反馈的打印状态信息
软件开发人员编写代码以打印到或管理Zebra打印机,请查阅技术文档、开发手册、SDK使用手册.针对于:Best Practices for Printing
针对于{
- 软件开发人员编写代码以打印到或管理Zebra打印机。
- 测试工程师寻求最佳方法来测试涉及Zebra打印机的应用程序。
- 用户体验工程师寻求有关在打印时提供更好的UX的建议。
- 希望通过Zebra验证其软件的Zebra合作伙伴。
}
包含斑马打印机的各种使用教程(工具类、样例代码、文献资料及话题讨论):斑马打印机官方首页
ZPL手册包含了一个完整的SGD命令列表: ZPL指令英文手册
Zebar打印的网络连接指南驱动及Demo: 开发人员工具
多平台SDK支持: SDK官方下载链接
如果可以使用串行端口,并且根据打印机模型,则可以利用'斑马状态'部分不必解析: 多平台SDK支持:SDK官方下载链接
1、利用SGD命令获取
在工具箱中,发送“!U1 getvar“ apl”“,它将命令发送到打印机,然后将输出写入控制台。通过将命令转换为字节并将其传输,成功地将命令发送给打印机。从打印机读取状态信息。
利用SGD getvar命令可以返回响应。 以下命令将返回打印机状态( ZPL手册页 704 )的当前状态:
! U1 getvar"device.host_status"
[Notice SGD commands must end with a carriage return/line feed ]
注意:在ZPL字符串的末尾添加新行字符(0x0A)。如果打印机以换行符终止,则它们只能识别SGD命令(!U1命令)!
在数据末尾添加新行(0x0A)。
2、利用SDK获取打印机信息
Zebra提供了一个SDK来帮助与其打印机进行通信。Zebra SDK是最好的工具,因为它已经内置了许多状态检查功能。
您会在Windows SDK中找到诸如Connection对象上的“ sendAndWaitForResponse”之类的命令,
可以参考的SDK连接打印机Demo,包括Java和C#:SDK LinkOS-PC Demo
官方利用SDK获取打印机状态的C#demo:ZSDK_DevDemos_.NET
划重点:若想获取打印机的反馈状态信息,利用下载好的斑马SDK编写程序,详细使用教程及API参考下面:
API文档<包括如何在VS中使用SDK>:https://techdocs.zebra.com/link-os/2-15/pc_net/
以下笔记均来自于API文档;
下面开始介绍如何通过SDK获取Zebarda的状态信息
(1)将SDK添加到VS项目
从计算机将C#SDK添加到Visual Studio项目的步骤。注意这种办法在下载完SDK安装时需要提前安装好Java虚拟机。
-
从[Install Dir] / [Platform] / [build version] / lib目录中,将所有文件拖到项目的根目录(其中[Install Dir]是在安装过程中选择的目录,[Platform]是所需的平台,[构建版本]对应于所需的构建)。
-
打开您的Visual Studio项目。
-
单击项目>添加引用...。
-
选择浏览...。
-
导航到项目的根目录,然后从步骤1中选择添加到其中的文件。
-
选择lib文件夹中的所有文件。
-
单击添加以完成将C#SDK添加到您的项目中。
-
确保“ .dlls”在C#本机库路径中(通常,它们在项目的引用文件夹中)。
从NuGet将C#SDK添加到Visual Studio项目的步骤
-
打开您的Visual Studio项目。
-
点击项目>管理的NuGet包...。
-
选择浏览选项卡。
-
将您的Package源设置为nuget.org。
-
搜索Zebra.Printer.SDK。
-
从列表中选择Zebra.Printer.SDK。
-
点击安装。
(2)将示例代码导入Visual Studio
-
导航到Link-OS Multiplatform SDK安装中的[Platform] / [build version] / demos / visualStudio(其中[Platform]是所需的平台,[build版本]对应于所需的版本)。
-
右键单击DeveloperDemo_Windows.zip,然后选择全部提取...以解压缩示例代码。
-
从Visual Studio菜单栏中,选择“ 文件”>“打开”>“项目/解决方案...”。
-
导航到在步骤2中解压缩的DeveloperDemo_Windows文件夹。
-
选择DeveloperDemo_Windows.csproj。
-
点击打开。
(3)TCP/IP连接打印机的TcpConnection类
using System;
using System.Text;
using Zebra.Sdk.Comm;
using Zebra.Sdk.Printer;
public class TcpConnectionExample {
public static void Main(string[] args) {
new TcpConnectionExample().SendZplOverTcp("1.2.3.4");
new TcpConnectionExample().SendCpclOverTcp("1.2.3.4");
new TcpConnectionExample().PrintConfigLabelUsingDnsName("PrinterName");
}
private void SendZplOverTcp(string theIpAddress) {
// Instantiate connection for ZPL TCP port at given address
Connection thePrinterConn = new TcpConnection(theIpAddress, TcpConnection.DEFAULT_ZPL_TCP_PORT);
try {
// Open the connection - physical connection is established here.
thePrinterConn.Open();
// This example prints "This is a ZPL test." near the top of the label.
string zplData = "^XA^FO20,20^A0N,25,25^FDThis is a ZPL test.^FS^XZ";
// Send the data to printer as a byte array.
thePrinterConn.Write(Encoding.UTF8.GetBytes(zplData));
} catch (ConnectionException e) {
// Handle communications error here.
Console.WriteLine(e.ToString());
} finally {
// Close the connection to release resources.
thePrinterConn.Close();
}
}
private void SendCpclOverTcp(string theIpAddress) {
// Instantiate connection for CPCL TCP port at given address
Connection thePrinterConn = new TcpConnection(theIpAddress, TcpConnection.DEFAULT_CPCL_TCP_PORT);
try {
// Open the connection - physical connection is established here.
thePrinterConn.Open();
// This example prints "This is a CPCL test." near the top of the label.
string cpclData = "! 0 200 200 210 1\r\n"
+ "TEXT 4 0 30 40 This is a CPCL test.\r\n"
+ "FORM\r\n"
+ "PRINT\r\n";
// Send the data to printer as a byte array.
thePrinterConn.Write(Encoding.UTF8.GetBytes(cpclData));
} catch (ConnectionException e) {
// Handle communications error here.
Console.WriteLine(e.ToString());
} finally {
// Close the connection to release resources.
thePrinterConn.Close();
}
}
private void PrintConfigLabelUsingDnsName(string dnsName) {
Connection connection = new TcpConnection(dnsName, 9100);
try {
connection.Open();
ZebraPrinter p = ZebraPrinterFactory.GetInstance(connection);
p.PrintConfigurationLabel();
} catch (ConnectionException e) {
Console.WriteLine(e.ToString());
} catch (ZebraPrinterLanguageUnknownException e) {
Console.WriteLine(e.ToString());
} finally {
// Close the connection to release resources.
connection.Close();
}
}
}
(4)打印机的PrinterStatus Class:A class used to obtain the status of a Zebra printer.
Syntax
public abstract class PrinterStatus
Namespace: Zebra.Sdk.Printer
Constructors
PrinterStatus | Constructs a PrinterStatus instance that can be used to determine the status of a printer. |
Methods
Equals | Determines whether the specified object is equal to the current object. (Inherited from Object.) | |
GetHashCode | Serves as the default hash function. (Inherited from Object.) | |
GetType | Gets the Type of the current instance. (Inherited from Object.) | |
ToString | Returns a string that represents the current object. (Inherited from Object.) |
Fields
Name | Description | |
---|---|---|
isHeadCold | true if the head is cold. For CPCL printers this is always false | |
isHeadOpen | true if the head is open. | |
isHeadTooHot | true if the head is too hot. For CPCL printers this is always false | |
isPaperOut | true if the paper is out. | |
isPartialFormatInProgress | true if there is a partial format in progress. For CPCL printers this is always false. | |
isPaused | true if the printer is paused. For CPCL printers this is always false | |
isReadyToPrint | true if the printer reports back that it is ready to print | |
isReceiveBufferFull | true if the receive buffer is full. For CPCL printers this is always false | |
isRibbonOut | true if the ribbon is out. | |
labelLengthInDots | The length of the label in dots. For CPCL printers this is always 0. | |
labelsRemainingInBatch | The number of labels remaining in the batch. For CPCL printers this is always 0. | |
numberOfFormatsInReceiveBuffer | The number of formats currently in the receive buffer of the printer. For CPCL printers this is always 0. | |
printMode | The print mode. For CPCL printers this is always UNKNOWN |
C#获取Zebar打印机状态示例:
using System;
using Zebra.Sdk.Comm;
using Zebra.Sdk.Printer;
public class PrinterStatusExample {
public static void Main(string[] args) {
Connection connection = new TcpConnection("192.168.1.100", TcpConnection.DEFAULT_ZPL_TCP_PORT);
try {
connection.Open();
ZebraPrinter printer = ZebraPrinterFactory.GetInstance(connection);
PrinterStatus printerStatus = printer.GetCurrentStatus();
if (printerStatus.isReadyToPrint) {
Console.WriteLine("Ready To Print");
} else if (printerStatus.isPaused) {
Console.WriteLine("Cannot Print because the printer is paused.");
} else if (printerStatus.isHeadOpen) {
Console.WriteLine("Cannot Print because the printer head is open.");
} else if (printerStatus.isPaperOut) {
Console.WriteLine("Cannot Print because the paper is out.");
} else {
Console.WriteLine("Cannot Print.");
}
} catch (ConnectionException e) {
Console.WriteLine(e.ToString());
} catch (ZebraPrinterLanguageUnknownException e) {
Console.WriteLine(e.ToString());
} finally {
connection.Close();
}
}
}
(5)打印可变字段,FormatUtilLinkOs接口
using System;
using System.Collections.Generic;
using System.IO;
using Zebra.Sdk.Comm;
using Zebra.Sdk.Graphics;
using Zebra.Sdk.Printer;
public class FormatUtilLinkOsExample {
/// Print a stored format with the given variables. This ZPL will store a format on the Link-OS™ printer, for use with example2.
///
/// ^XA
/// ^DFE:FORMAT2.ZPL
/// ^FS
/// ^FT26,243^A0N,56,55^FH\^FN12"First Name"^FS
/// ^FT26,296^A0N,56,55^FH\^FN11"Last Name"^FS
/// ^FT258,73^A0N,39,38^FH\^FDVisitor^FS
/// ^FO100,100^XG^FN13,1,1^FS
/// ^FO5,17^GB601,379,8^FS
/// ^XZ
public static void Main(string[] args) {
string pathToImageFileOnHost = "c:\\example.png";
Connection connection = new TcpConnection("1.2.3.4", TcpConnection.DEFAULT_ZPL_TCP_PORT);
try {
connection.Open();
Dictionary<int, string> vars = new Dictionary<int, string> {
{ 12, "John" },
{ 11, "Smith" }
};
ZebraImageI image = ZebraImageFactory.GetImage(pathToImageFileOnHost);
Dictionary<int, ZebraImageI> imgVars = new Dictionary<int, ZebraImageI> {
{ 13, image }
};
ZebraPrinter genericPrinter = ZebraPrinterFactory.GetInstance(connection);
ZebraPrinterLinkOs linkOsPrinter = ZebraPrinterFactory.CreateLinkOsPrinter(genericPrinter);
if (linkOsPrinter != null) {
linkOsPrinter.PrintStoredFormatWithVarGraphics("E:FORMAT2.ZPL", imgVars, vars);
}
} catch (ConnectionException e) {
Console.WriteLine(e.ToString());
} catch (ZebraPrinterLanguageUnknownException e) {
Console.WriteLine(e.ToString());
} catch (IOException e) {
Console.WriteLine(e.ToString());
} finally {
connection.Close();
}
}
}
(6)打印图形
using System;
using System.IO;
using Zebra.Sdk.Comm;
using Zebra.Sdk.Printer;
public class GraphicsUtilExample {
public static void Main(string[] args) {
Connection connection = new TcpConnection("192.168.1.32", TcpConnection.DEFAULT_ZPL_TCP_PORT);
try {
connection.Open();
ZebraPrinter printer = ZebraPrinterFactory.GetInstance(connection);
int x = 0;
int y = 0;
printer.PrintImage("/sdcard/sample.jpg", x, y);
} catch (ConnectionException e) {
Console.WriteLine(e.ToString());
} catch (ZebraPrinterLanguageUnknownException e) {
Console.WriteLine(e.ToString());
} catch (IOException e) {
Console.WriteLine(e.ToString());
} finally {
connection.Close();
}
}
}
(7),打印机实用类PrinterUtil类,简化打印操作
using System;
using Zebra.Sdk.Printer;
public class PrinterUtilExample {
private static string CONNECTION_STRING = "172.30.16.135";
public static void Main(string[] args) {
// Create a few files on the printer
SendContentsExample();
// List the files just created
ListFilesExample();
// Silently delete some of the files
DeleteFilesExample();
// List the remaining files
ListFilesExample();
// Verbosely delete the rest of the files
DeleteFilesVerboseExample();
// List the remaining files
ListFilesExample();
}
private static void SendContentsExample() {
PrinterUtil.SendContents(CONNECTION_STRING, "^XA^DFR:ZSDK_TEST1.ZPL^FO100,100^A0N,30,30^FDZSDK_Test1.zpl^FS^XZ");
PrinterUtil.SendContents(CONNECTION_STRING, "^XA^DFR:ZSDK_TEST2.ZPL^FO100,100^A0N,30,30^FDZSDK_Test2.zpl^FS^XZ");
PrinterUtil.SendContents(CONNECTION_STRING, "^XA^DFR:ZSDK_TEST3.ZPL^FO100,100^A0N,30,30^FDZSDK_Test3.zpl^FS^XZ");
PrinterUtil.SendContents(CONNECTION_STRING, "^XA^DFR:ZSDK_TEST4.ZPL^FO100,100^A0N,30,30^FDZSDK_Test4.zpl^FS^XZ");
PrinterUtil.SendContents(CONNECTION_STRING, "^XA^DFR:ZSDK_TEST11.ZPL^FO100,100^A0N,30,30^FDZSDK_Test11.zpl^FS^XZ");
PrinterUtil.SendContents(CONNECTION_STRING, "^XA^DFR:ZSDK_TEST12.ZPL^FO100,100^A0N,30,30^FDZSDK_Test12.zpl^FS^XZ");
PrinterUtil.SendContents(CONNECTION_STRING, "^XA^DFR:ZSDK_TEST22.ZPL^FO100,100^A0N,30,30^FDZSDK_Test22.zpl^FS^XZ");
PrinterUtil.SendContents(CONNECTION_STRING, "^XA^DFR:ZSDK_TEST33.ZPL^FO100,100^A0N,30,30^FDZSDK_Test33.zpl^FS^XZ");
PrinterUtil.SendContents(CONNECTION_STRING, "^XA^DFR:ZSDK_TEST44.ZPL^FO100,100^A0N,30,30^FDZSDK_Test44.zpl^FS^XZ");
}
private static void ListFilesExample() {
string[] filesOnE = PrinterUtil.ListFiles(CONNECTION_STRING, "R:ZSDK_TEST*.*");
Console.WriteLine("--- List of Files on R: ---");
foreach (string thisFile in filesOnE) {
Console.WriteLine($" - {thisFile}");
}
Console.WriteLine("--- End of List ---");
}
private static void DeleteFilesExample() {
Console.WriteLine("Deleting R:ZSDK_TEST1*.ZPL...");
PrinterUtil.DeleteFile(CONNECTION_STRING, "R:ZSDK_TEST1*.ZPL");
}
private static void DeleteFilesVerboseExample() {
Console.WriteLine("Verbosely Deleting R:ZSDK_TEST*.ZPL...");
string[] filesDeleted = PrinterUtil.DeleteFileReportDeleted(CONNECTION_STRING, "R:ZSDK_TEST*.ZPL");
foreach (string thisDeletedFile in filesDeleted) {
Console.WriteLine($" - {thisDeletedFile} deleted");
}
}
}
SomeQuestion:
请多多查阅官方技术文档:应用须知(各种疑难问题)
For Example:
打印原始ZPL指令的解决办法:
实际打印ZPL命令的原因是因为正在使用ZebraDesigner驱动程序。更正此
问题,请使用“通用/纯文本”打印机重新安装打印机。
通用打印机驱动程序不适用于任何特定型号的打印机,但它允许您输出自己的打印机代码。
通常,您不可以从应用程序中提取信息,但是可以使用通用文本驱动程序,因为
通用文本驱动程序的输出是原始文本文件。
安装通用打印机驱动程序的步骤
1.点击开始->“设置”->“打印机和传真”
2.选择“添加打印机”
3.添加打印机向导会询问如何连接此打印机,“选择本地打印机”
4.出现制造商和打印机列表,向下滚动以选择“ GENERIC”
5.在打印机列表中选择名为“仅通用/文本”的打印机,然后单击“下一步”
6.将出现一个菜单,可让您将该打印机选择为“默认”打印机。选择“否”,然后单击“下一步”
7.添加打印机向导将提示您打印测试页,选择“否”,然后单击“完成”