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
相关产品推荐
相关产品推荐

