发送ZPL指令到斑马打印机,并监控打印成功或者失败的状态信息

Visual C# 入门

本文共分为两个部分:

第一部分:介绍如何与Zebar进行连接,把ZPL指令或者模板文件发送到斑马打印机进行打印。

第二部分:介绍如何接收Zebar进行打印之后如何得到斑马打印机的反馈信息,防止打印出错的情况下继续进行工业的操作。需要用上位机得到打印出错的信息,然后反馈给PLC。

 

一、C#  将指令发送到Zebar打印机的方式

发送ZPL到打印机的参考链接

Zebra Technologies (GitHub Demo)

C#控制打印的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 

针对于{

  1. 软件开发人员编写代码以打印到或管理Zebra打印机。
  2. 测试工程师寻求最佳方法来测试涉及Zebra打印机的应用程序。
  3. 用户体验工程师寻求有关在打印时提供更好的UX的建议。
  4. 希望通过Zebra验证其软件的Zebra合作伙伴。

参考链接:https://github.com/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虚拟机。

  1. [Install Dir] / [Platform] / [build version] / lib目录中,将所有文件拖到项目的根目录(其中[Install Dir]是在安装过程中选择的目录,[Platform]是所需的平台,[构建版本]对应于所需的构建)。

  2. 打开您的Visual Studio项目。

  3. 单击项目>添加引用...

  4. 选择浏览...

  5. 导航到项目的根目录,然后从步骤1中选择添加到其中的文件。

  6. 选择lib文件夹中的所有文件。

  7. 单击添加以完成将C#SDK添加到您的项目中。

  8. 确保“ .dlls”在C#本机库路径中(通常,它们在项目的引用文件夹中)。

从NuGet将C#SDK添加到Visual Studio项目的步骤

  1. 打开您的Visual Studio项目。

  2. 点击项目>管理的NuGet包...

  3. 选择浏览选项卡。

  4. 将您的Package源设置为nuget.org

  5. 搜索Zebra.Printer.SDK

  6. 从列表中选择Zebra.Printer.SDK

  7. 点击安装

(2)将示例代码导入Visual Studio

  1. 导航到Link-OS Multiplatform SDK安装中的[Platform] / [build version] / demos / visualStudio(其中[Platform]是所需的平台,[build版本]对应于所需的版本)。

  2. 右键单击DeveloperDemo_Windows.zip,然后选择全部提取...以解压缩示例代码。

  3. 从Visual Studio菜单栏中,选择“ 文件”>“打开”>“项目/解决方案...”

  4. 导航到在步骤2中解压缩的DeveloperDemo_Windows文件夹。

  5. 选择DeveloperDemo_Windows.csproj

  6. 点击打开

(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

Public methodPrinterStatus

Constructs a PrinterStatus instance that can be used to determine the status of a printer.

Methods

Public methodEquals

Determines whether the specified object is equal to the current object.

(Inherited from Object.)
Public methodGetHashCode

Serves as the default hash function.

(Inherited from Object.)
Public methodGetType

Gets the Type of the current instance.

(Inherited from Object.)
Public methodToString

Returns a string that represents the current object.

(Inherited from Object.)

 Fields

 NameDescription
Public fieldisHeadCold

true if the head is cold. For CPCL printers this is always false

Public fieldisHeadOpen

true if the head is open.

Public fieldisHeadTooHot

true if the head is too hot. For CPCL printers this is always false

Public fieldisPaperOut

true if the paper is out.

Public fieldisPartialFormatInProgress

true if there is a partial format in progress. For CPCL printers this is always false.

Public fieldisPaused

true if the printer is paused. For CPCL printers this is always false

Public fieldisReadyToPrint

true if the printer reports back that it is ready to print

Public fieldisReceiveBufferFull

true if the receive buffer is full. For CPCL printers this is always false

Public fieldisRibbonOut

true if the ribbon is out.

Public fieldlabelLengthInDots

The length of the label in dots. For CPCL printers this is always 0.

Public fieldlabelsRemainingInBatch

The number of labels remaining in the batch. For CPCL printers this is always 0.

Public fieldnumberOfFormatsInReceiveBuffer

The number of formats currently in the receive buffer of the printer. For CPCL printers this is always 0.

Public fieldprintMode

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.添加打印机向导将提示您打印测试页,选择“否”,然后单击“完成”

  • 5
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值