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

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;
}

关键修改点

  1. 将DOCINFOA改为DOCINFOW,结构体的CharSet设为Unicode,成员的MarshalAs改为LPWStr
  2. 将打印机API的A后缀版本改为W后缀(如OpenPrinterA→OpenPrinterW、StartDocPrinterA→StartDocPrinterW),并设置CharSet = CharSet.Unicode
  3. 修改Print方法:用Encoding.Unicode.GetBytes获取字符串的Unicode字节数组,再传递给WritePrinter,替代原有的ANSI字符串转换

额外注意事项

  • 确保针式打印机支持你要打印的特殊字符,部分老款针式打印机可能需要切换到对应的字符集(可通过打印机指令设置,比如ESC序列)
  • 如果打印机仅支持特定代码页(如GB2312),可以将编码改为对应的代码页(如Encoding.GetEncoding("GB2312")),但Unicode方式兼容性更优

内容的提问来源于stack exchange,提问作者Jed Zerna

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 22:24:21