You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#实现GPT-4672-PM-C32热敏打印机无DLL跨平台打印功能求助

.NET 对接GPT-4672-PM-C32热敏打印机问题

我正在对接GPT-4672-PM-C32热敏打印机,需要通过.NET实现该设备的打印功能。厂商提供的源码依赖若干DLL文件,由于应用需要兼容Windows 10以外的系统,因此需要避免使用这些DLL。尝试了如下代码,但运行后仅会生成打印任务,不会下发到打印机:

using System;
using System.Drawing;
using System.Drawing.Printing;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
public class RawPrinterHelper
{
    public static void Main()
    {
        byte[] Array = ConvertToByteArray("Druck mich bitte bitte Ich hab keine Lust nicht gedruckt zu werden!\n", Encoding.ASCII);
        string stuff = Formatio(Array);
        byte[] Binary = ConvertToByteArray(stuff, Encoding.ASCII);
        IntPtr unmanagedPointer = Marshal.AllocHGlobal(Binary.Length);
        Marshal.Copy(Binary, 0, unmanagedPointer, Binary.Length);
        // Call unmanaged code
        Int32 Counter = Binary.Length;
        /*
        if(SendBytesToPrinter("MyPrinter",unmanagedPointer, Counter) != false)
        {
            Console.WriteLine("HOORAY!");
        }
        else
        {
            Console.WriteLine("BOOOOH");
        }
        Marshal.FreeHGlobal(unmanagedPointer);
        */
    
    }
    // 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 byte[] ConvertToByteArray(string str, Encoding encoding)
    {
        return encoding.GetBytes(str);
    }
    //Formats a byte[] into a binary string (010010010010100101010)
    public static string Formatio(byte[] data)
    {
        //storage for the resulting string
        string result = string.Empty;
        //iterate through the byte[]
        foreach (byte value in data)
        {
            //storage for the individual byte
            string binarybyte = Convert.ToString(value, 2);
            //if the binarybyte is not 8 characters long, its not a proper result
            while (binarybyte.Length < 8)
            {
                //prepend the value with a 0
                binarybyte = "0" + binarybyte;
            }
            //append the binarybyte to the result
            result += binarybyte;
        }
        //return the result
        return result;
    }
    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?
        // 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;
    }
}

该打印机使用专属ESC指令集通信,需要向设备发送对应指令。已知厂商提供的演示代码仅需将字符串和ESC指令作为非托管字节数据发送即可完成打印,目前没有找到可行的通信方案。


解决方案

问题根因

你当前的代码存在两个核心问题:

  • Main方法中对原始打印字节做了二进制字符串转码,相当于把原始打印指令转换成了由0和1组成的ASCII字符串,打印机无法识别该格式的数据
  • 当前的RawPrinterHelper实现依赖Windows专属的winspool.drv系统API,不支持跨系统运行

修复Windows环境下的打印逻辑

首先删除Main方法中错误的Format二进制转码逻辑,直接发送原始ESC指令+文本的字节数组即可:

// 替换原有Main方法内容
public static void Main()
{
    // 构造打印字节流,指令需替换为你打印机专属的ESC指令
    List<byte> printBytes = new List<byte>();
    // 示例:初始化打印机指令
    printBytes.AddRange(new byte[] { 0x1B, 0x40 });
    // 加入打印文本,编码需和打印机配置一致,通常为GB2312或UTF-8
    printBytes.AddRange(Encoding.GetEncoding("GB2312").GetBytes("测试打印内容\n"));
    // 示例:走纸切纸指令
    printBytes.AddRange(new byte[] { 0x1D, 0x56, 0x01 });
    
    // 直接发送原始字节,无需额外转码
    IntPtr unmanagedPointer = Marshal.AllocHGlobal(printBytes.Count);
    Marshal.Copy(printBytes.ToArray(), 0, unmanagedPointer, printBytes.Count);
    if(SendBytesToPrinter("你的打印机系统名称", unmanagedPointer, printBytes.Count))
    {
        Console.WriteLine("打印成功");
    }
    else
    {
        Console.WriteLine($"打印失败,错误码:{Marshal.GetLastWin32Error()}");
    }
    Marshal.FreeHGlobal(unmanagedPointer);
}

跨平台实现方案

如果需要兼容Linux、macOS等非Windows系统,不要使用winspool.drv相关API,根据打印机连接方式选择对应方案:

  • USB连接:使用LibUsbDotNet库直接读写USB设备端点,发送原始ESC指令字节
  • 网口连接:直接通过Socket连接打印机9100端口,发送原始字节即可,无需依赖任何系统API和厂商DLL
  • 蓝牙连接:使用.NET跨平台蓝牙库发送原始字节即可

网口打印机最简实现示例:

public static void PrintOverNetwork(string printerIp, int port = 9100)
{
    List<byte> printBytes = new List<byte>();
    // 构造打印指令和内容
    printBytes.AddRange(new byte[] { 0x1B, 0x40 });
    printBytes.AddRange(Encoding.GetEncoding("GB2312").GetBytes("网口打印测试\n"));
    printBytes.AddRange(new byte[] { 0x1D, 0x56, 0x01 });
    
    using TcpClient client = new TcpClient(printerIp, port);
    using NetworkStream stream = client.GetStream();
    stream.Write(printBytes.ToArray(), 0, printBytes.Count);
}

注意事项

  • 发送给打印机的字节必须严格遵循设备专属ESC指令集,不要对原始字节做任何额外转码
  • 文本编码必须和打印机设置的编码一致,否则会出现乱码

内容的提问来源于stack exchange,提问作者Manuel P

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 23:57:02