C#针式打印机打印特殊字符显示异常问题求助
针式打印机特殊字符打印异常解决方法
问题描述
针式打印机打印度数符号(°)、直径符号(Ø)、平方上标(²)等特殊字符时,输出显示为黑色方块。调用打印的代码如下:
MyPrinter.Print((char)15 + " itemname.Text " + "\r\n");
完整的LPrinter类代码:
[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); /*=================================================*/ private IntPtr HandlePrinter; public PrinterSettings ps; public LPrinter() { HandlePrinter = IntPtr.Zero; ps = new PrinterSettings(); } public PrintDialog pd = new PrintDialog(); public string PrinterName { get { return ps.PrinterName; } set { ps.PrinterName = value; } } public bool Open(string DocName) { // see if printer is already open if (HandlePrinter != IntPtr.Zero) return false; // opens the printer bool risp = OpenPrinter(ps.PrinterName, out HandlePrinter, IntPtr.Zero); if (risp == false) return false; // starts a print job DOCINFOA MyDocInfo = new DOCINFOA(); MyDocInfo.pDocName = DocName; MyDocInfo.pOutputFile = null; MyDocInfo.pDataType = "RAW"; if (StartDocPrinter(HandlePrinter, 1, MyDocInfo)) { StartPagePrinter(HandlePrinter); //starts a page return true; } else return false; } public bool Close() { if (HandlePrinter == IntPtr.Zero) return false; if (!EndPagePrinter(HandlePrinter)) return false; if (!EndDocPrinter(HandlePrinter)) return false; if (!ClosePrinter(HandlePrinter)) return false; HandlePrinter = IntPtr.Zero; return true; } public bool Print(string outputstring) { if (HandlePrinter == IntPtr.Zero) return false; IntPtr buf = Marshal.StringToCoTaskMemAnsi(outputstring); Int32 done = 0; bool ok = WritePrinter(HandlePrinter, buf, outputstring.Length, out done); Marshal.FreeCoTaskMem(buf); if (!ok) return false; else return true; }
问题原因
当前代码使用了ANSI编码相关的API(如OpenPrinterA、DOCINFOA)和字符串转换方法Marshal.StringToCoTaskMemAnsi,ANSI编码无法支持Unicode特殊字符(如°、Ø、²),导致这些字符被转换为无效的ANSI值,打印时显示为黑色方块。
解决方法
将代码改为使用Unicode版本的打印机API,并调整字符串处理逻辑:
修改后的LPrinter类代码
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] public class DOCINFOW { [MarshalAs(UnmanagedType.LPWStr)] public string pDocName; [MarshalAs(UnmanagedType.LPWStr)] public string pOutputFile; [MarshalAs(UnmanagedType.LPWStr)] public string pDataType; } [DllImport("winspool.Drv", EntryPoint = "OpenPrinterW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] public static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPWStr)] 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 = "StartDocPrinterW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] public static extern bool StartDocPrinter(IntPtr hPrinter, Int32 level, [In, MarshalAs(UnmanagedType.LPStruct)] DOCINFOW 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); /*=================================================*/ private IntPtr HandlePrinter; public PrinterSettings ps; public LPrinter() { HandlePrinter = IntPtr.Zero; ps = new PrinterSettings(); } public PrintDialog pd = new PrintDialog(); public string PrinterName { get { return ps.PrinterName; } set { ps.PrinterName = value; } } public bool Open(string DocName) { if (HandlePrinter != IntPtr.Zero) return false; bool risp = OpenPrinter(ps.PrinterName, out HandlePrinter, IntPtr.Zero); if (!risp) return false; DOCINFOW MyDocInfo = new DOCINFOW(); MyDocInfo.pDocName = DocName; MyDocInfo.pOutputFile = null; MyDocInfo.pDataType = "RAW"; if (StartDocPrinter(HandlePrinter, 1, MyDocInfo)) { StartPagePrinter(HandlePrinter); return true; } else return false; } public bool Close() { if (HandlePrinter == IntPtr.Zero) return false; if (!EndPagePrinter(HandlePrinter)) return false; if (!EndDocPrinter(HandlePrinter)) return false; if (!ClosePrinter(HandlePrinter)) return false; HandlePrinter = IntPtr.Zero; return true; } public bool Print(string outputstring) { if (HandlePrinter == IntPtr.Zero) return false; // 使用UTF-16(Unicode)编码转换字符串为字节数组 byte[] bytes = System.Text.Encoding.Unicode.GetBytes(outputstring); IntPtr buf = Marshal.AllocCoTaskMem(bytes.Length); Marshal.Copy(bytes, 0, buf, bytes.Length); Int32 done = 0; bool ok = WritePrinter(HandlePrinter, buf, bytes.Length, out done); Marshal.FreeCoTaskMem(buf); return ok; }
关键修改点
- 将
DOCINFOA改为DOCINFOW,结构体的CharSet设为Unicode,成员的MarshalAs改为LPWStr - 将打印机API的
A后缀版本改为W后缀(如OpenPrinterA→OpenPrinterW、StartDocPrinterA→StartDocPrinterW),并设置CharSet = CharSet.Unicode - 修改
Print方法:用Encoding.Unicode.GetBytes获取字符串的Unicode字节数组,再传递给WritePrinter,替代原有的ANSI字符串转换
额外注意事项
- 确保针式打印机支持你要打印的特殊字符,部分老款针式打印机可能需要切换到对应的字符集(可通过打印机指令设置,比如ESC序列)
- 如果打印机仅支持特定代码页(如GB2312),可以将编码改为对应的代码页(如
Encoding.GetEncoding("GB2312")),但Unicode方式兼容性更优
内容的提问来源于stack exchange,提问作者Jed Zerna
相关产品推荐
相关产品推荐

