急:关于调用WIn32API函数打印中文 呈现乱码的问题?

时间:2022-01-23 18:12:08
 // 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 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 szString)
        {
            PrintDocument pdc = new PrintDocument();
            string szPrinterName = pdc.PrinterSettings.PrinterName;
            
            IntPtr pBytes;
            Int32 dwCount;
            // How many characters are in the string?
            dwCount = szString.Length;
            // 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;
        }

哪位大神帮忙看看,怎么才能打印中文。

8 个解决方案

#1


 dwCount = szString.Length; 
改成asciiing.defuatl.getBytes(szString).length

new FileStream(szFileName, FileMode.Open);  传的用文件用ANSI编码,试试

#2


引用 1 楼 WM_JAWIN 的回复:
 dwCount = szString.Length; 
改成asciiing.defuatl.getBytes(szString).length

new FileStream(szFileName, FileMode.Open);  传的用文件用ANSI编码,试试


还是不行 

#3


SendStringToPrinter, 不知楼主是不是处在石器时代,除非楼主的打印机是文本打印机,并带所谓中文字库的那种!
发送字节过去的时候,估计得用GB2312编码吧:
byte[] bs = Encoding.GetEncoding("GB2312").GetBytes("string");

#4


你这个是RawPringterHelper?
我用过,好像就是不能打印中文.

后来我用的是LTPControls
里面调用的kernel32.dll
是通过LPT端口访问打印机的.

#5


引用 4 楼 Z65443344 的回复:
你这个是RawPringterHelper?
我用过,好像就是不能打印中文.

后来我用的是LTPControls
里面调用的kernel32.dll
是通过LPT端口访问打印机的.

我使用的是USB端口 无法用LPT端口访问

#6


没有特殊要求的话,也可以用vs自带的document.printer
不过在Epson LQ-380K上使用的时候会出现字高小于30就不打印,大于30小于50就只能打出来一半
在Epson LQ-680K上没有问题.
激光打印机也都没有问题.

#7


引用 6 楼 Z65443344 的回复:
没有特殊要求的话,也可以用vs自带的document.printer
不过在Epson LQ-380K上使用的时候会出现字高小于30就不打印,大于30小于50就只能打出来一半
在Epson LQ-680K上没有问题.
激光打印机也都没有问题.

这个是调用的GDI+画个图片,打印的其实是图片,所以也不存在中文英文的问题
而且可以预览.

#8


 /// <summary>
        /// 打印标签带有中文字符的ZPL指令
        /// </summary>
        /// <param name="printerName"></param>
        /// <param name="szString"></param>
        /// <returns></returns>
        public static bool SendStringToPrinter(string printerName,string szString)
        {
            byte[] bytes = Encoding.GetEncoding("GB2312").GetBytes(szString);       //转换格式注意编码名称,有时候是"utf-8"才能打印中文
            IntPtr ptr = Marshal.AllocHGlobal(bytes.Length + 2);
            try
            {
                Marshal.Copy(bytes, 0, ptr, bytes.Length);
                SendBytesToPrinter(printerName, ptr, bytes.Length);
            }
            catch
            {
            }
            finally
            {
                Marshal.FreeCoTaskMem(ptr);
            }
            return true;
        }

#1


 dwCount = szString.Length; 
改成asciiing.defuatl.getBytes(szString).length

new FileStream(szFileName, FileMode.Open);  传的用文件用ANSI编码,试试

#2


引用 1 楼 WM_JAWIN 的回复:
 dwCount = szString.Length; 
改成asciiing.defuatl.getBytes(szString).length

new FileStream(szFileName, FileMode.Open);  传的用文件用ANSI编码,试试


还是不行 

#3


SendStringToPrinter, 不知楼主是不是处在石器时代,除非楼主的打印机是文本打印机,并带所谓中文字库的那种!
发送字节过去的时候,估计得用GB2312编码吧:
byte[] bs = Encoding.GetEncoding("GB2312").GetBytes("string");

#4


你这个是RawPringterHelper?
我用过,好像就是不能打印中文.

后来我用的是LTPControls
里面调用的kernel32.dll
是通过LPT端口访问打印机的.

#5


引用 4 楼 Z65443344 的回复:
你这个是RawPringterHelper?
我用过,好像就是不能打印中文.

后来我用的是LTPControls
里面调用的kernel32.dll
是通过LPT端口访问打印机的.

我使用的是USB端口 无法用LPT端口访问

#6


没有特殊要求的话,也可以用vs自带的document.printer
不过在Epson LQ-380K上使用的时候会出现字高小于30就不打印,大于30小于50就只能打出来一半
在Epson LQ-680K上没有问题.
激光打印机也都没有问题.

#7


引用 6 楼 Z65443344 的回复:
没有特殊要求的话,也可以用vs自带的document.printer
不过在Epson LQ-380K上使用的时候会出现字高小于30就不打印,大于30小于50就只能打出来一半
在Epson LQ-680K上没有问题.
激光打印机也都没有问题.

这个是调用的GDI+画个图片,打印的其实是图片,所以也不存在中文英文的问题
而且可以预览.

#8


 /// <summary>
        /// 打印标签带有中文字符的ZPL指令
        /// </summary>
        /// <param name="printerName"></param>
        /// <param name="szString"></param>
        /// <returns></returns>
        public static bool SendStringToPrinter(string printerName,string szString)
        {
            byte[] bytes = Encoding.GetEncoding("GB2312").GetBytes(szString);       //转换格式注意编码名称,有时候是"utf-8"才能打印中文
            IntPtr ptr = Marshal.AllocHGlobal(bytes.Length + 2);
            try
            {
                Marshal.Copy(bytes, 0, ptr, bytes.Length);
                SendBytesToPrinter(printerName, ptr, bytes.Length);
            }
            catch
            {
            }
            finally
            {
                Marshal.FreeCoTaskMem(ptr);
            }
            return true;
        }