using Microsoft.Win32.SafeHandles; using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Runtime.InteropServices; using System.Text; using System.Text.RegularExpressions; namespace libEShangPB { /// /// CLR版本 :4.0.30319.42000 /// 命名空间 :TouchCashier.WorkUtils.Print /// 文件名称 :PrintDeviceHelper.cs /// 小票打印机处理类 /// 创 建 者 :liben /// 创建日期 :2021/12/16 21:26:12 /// 最后修改者 :liben /// 最后修改日期:2021/12/16 21:26:12 /// [ComVisible(false)] internal class PrintDeviceHelper { #region 打印机参数 const uint GENERIC_READ = 0x80000000; const uint GENERIC_WRITE = 0x40000000; const uint FILE_ATTRIBUTE_NORMAL = 0x80; const int OPEN_EXISTING = 3; static string prnPort = "LPT1"; [StructLayout(LayoutKind.Sequential)] private struct OVERLAPPED { int Internal; int InternalHigh; int Offset; int OffSetHigh; int hEvent; } [DllImport("kernel32.dll", CharSet = CharSet.Auto)] private static extern IntPtr CreateFile(string lpFileName, int dwDesiredAccess, int dwShareMode, int lpSecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, int hTemplateFile); [DllImport("kernel32.dll ")] private static extern bool WriteFile(int hFile, byte[] lpBuffer, int nNumberOfBytesToWrite, ref int lpNumberOfBytesWritten, ref OVERLAPPED lpOverlapped); [DllImport("kernel32.dll ")] private static extern bool DefineDosDevice(int dwFlags, string lpDeviceName, string lpTargetPath); [DllImport("kernel32.dll ")] private static extern bool CloseHandle(int hObject); [DllImport("kernel32.dll ")] private static extern bool ReadFile(int hFile, byte[] lpBuffer, int nNumberOfBytesToRead, ref int lpNumberOfBytesRead, int lpOverlapped); #endregion #region 方法 -> LPT打印票据信息 /// /// LPT打印票据信息 /// /// 待打印内容列表 /// 打印份数 /// 票尾走纸行数 /// 是否开钱箱 /// public static bool PrintList(List PrintList, int PrintCount = 1, int PaperLine = 6, bool IsOpenBox = true) { if (PrintList.Count == 0) { return false; } try { IntPtr iHandle = CreateFile(prnPort, 0x40000000, 0, 0, OPEN_EXISTING, 0, 0); using (SafeFileHandle _SafeFileHandle = new SafeFileHandle(iHandle, true)) { if (iHandle.ToInt32() == -1) { return false; } else { FileStream fs_Print = new FileStream(_SafeFileHandle, FileAccess.ReadWrite); if (IsOpenBox) { //发送开钱箱指令 char[] _open = { (char)27, (char)112, (char)0, (char)32, (char)160 }; byte[] _byte = Encoding.Default.GetBytes(_open); fs_Print.Write(_byte, 0, _byte.Length); } string str_Print = $"{(char)27}{(char)64}{(char)27}{(char)33}{(char)0}"; for (int i = 0; i < PrintCount; i++) { foreach (string str_Temp in PrintList) { str_Print += str_Temp + "\n"; } str_Print += $"{(char)27}{(char)64}{(char)27}{(char)33}{(char)0}"; for (int p = 0; p < PaperLine; p++) { str_Print += "\n"; } } byte[] byte_Print = Encoding.Default.GetBytes(str_Print); fs_Print.Write(byte_Print, 0, byte_Print.Length); fs_Print.Close(); } return true; } } catch { return false; } } #endregion #region 方法 -> LPT打印机开钱箱 /// /// LPT打印机开钱箱 /// /// public static bool LptOpenBox() { try { IntPtr iHandle = CreateFile(prnPort, 0x40000000, 0, 0, OPEN_EXISTING, 0, 0); using (SafeFileHandle _SafeFileHandle = new SafeFileHandle(iHandle, true)) { if (iHandle.ToInt32() == -1) { return false; } else { FileStream fs = new FileStream(_SafeFileHandle, FileAccess.ReadWrite); char[] _open = { (char)27, (char)112, (char)0, (char)32, (char)160 }; byte[] _byte = Encoding.Default.GetBytes(_open); fs.Write(_byte, 0, _byte.Length); fs.Close(); } return true; } } catch { return false; } } #endregion #region 方法 -> LPT打印图片 /// /// 打印图片方法 /// /// public static bool PrintQRCode(Bitmap bmpQRcode) { IntPtr iHandle = CreateFile(prnPort, 0x40000000, 0, 0, OPEN_EXISTING, 0, 0); using (SafeFileHandle _SafeFileHandle = new SafeFileHandle(iHandle, true)) { if (iHandle.ToInt32() == -1) { return false; //throw new Exception("没有连接打印机或者打印机端口不是LPT1"); } else { OVERLAPPED x = new OVERLAPPED(); int y = 0; //设置字符行间距为n点行 //byte[] data = new byte[] { 0x1B, 0x33, 0x00 }; string send = "" + (char)(27) + (char)(51) + (char)(0); byte[] data = new byte[send.Length]; for (int i = 0; i < send.Length; i++) { data[i] = (byte)send[i]; } WriteFile(iHandle.ToInt32(), data, data.Length, ref y, ref x); //Write(data); data[0] = (byte)'\x00'; data[1] = (byte)'\x00'; data[2] = (byte)'\x00'; // Clear to Zero. Color pixelColor; //ESC * m nL nH d1…dk 选择位图模式 // ESC * m nL nH byte[] escBmp = new byte[] { 0x1B, 0x2A, 0x00, 0x00, 0x00 }; escBmp[2] = (byte)'\x21'; //nL, nH escBmp[3] = (byte)(bmpQRcode.Width % 256); escBmp[4] = (byte)(bmpQRcode.Width / 256); //循环图片像素打印图片 //循环高 for (int i = 0; i < (bmpQRcode.Height / 24 + 1); i++) { //设置模式为位图模式 WriteFile(iHandle.ToInt32(), escBmp, escBmp.Length, ref y, ref x); //循环宽 for (int j = 0; j < bmpQRcode.Width; j++) { for (int k = 0; k < 24; k++) { if (((i * 24) + k) < bmpQRcode.Height) // if within the BMP size { pixelColor = bmpQRcode.GetPixel(j, (i * 24) + k); if (pixelColor.R == 0) { data[k / 8] += (byte)(128 >> (k % 8)); } } } //一次写入一个data,24个像素 WriteFile(iHandle.ToInt32(), data, data.Length, ref y, ref x); data[0] = (byte)'\x00'; data[1] = (byte)'\x00'; data[2] = (byte)'\x00'; // Clear to Zero. } //换行,打印第二行 byte[] data2 = { 0xA }; WriteFile(iHandle.ToInt32(), data2, data2.Length, ref y, ref x); } // data byte[] data3 = new byte[] { 0x1B, 0X32, 0xA, 0xA, 0xA, 0xA }; WriteFile(iHandle.ToInt32(), data3, data3.Length, ref y, ref x); CloseHandle(iHandle.ToInt32()); } return true; } } #endregion #region 方法 -> 设置打印字体大小 /// /// 设置打印字体大小 /// /// 字体放大倍数,0为默认大小 /// 字体是否加粗 /// public static string SetPrintFontSize(int multiple, bool bold = false) { if (multiple < 0) { multiple = 0; } int int_FontSize = multiple * 16; string str_FontSize = $"{(char)27}{(char)33}{(char)int_FontSize}"; if (bold) { str_FontSize += $"{(char)27}{(char)69}{(char)15}"; } else { str_FontSize += $"{(char)27}{(char)69}{(char)0}"; } return str_FontSize; } #endregion #region 方法 -> 选择/取消下划线模式 /// /// 选择/取消下划线模式 /// /// 下划线宽度,可选: /// 0:取消下划线模式 /// 1:选择下划线模式(1 点宽) /// 2:选择下划线模式(2 点宽) /// public static string SetPrintCharacter(int multiple) { if (multiple < 0) { multiple = 0; } return $"{(char)27}{(char)45}{(char)multiple}"; } #endregion #region 方法 -> 生成两侧对齐行 /// /// 生成两侧对齐行 /// /// 左侧字符字符串 /// 右侧字符串 /// 填充的字符 /// 行长度 /// public static string CreateSideLine(string leftString, string rightString, char paddingChar = ' ', int lineLength = 32) { try { int i_Len = lineLength - StringLength(leftString) - StringLength(rightString); return leftString + rightString.PadLeft(i_Len + rightString.Length, paddingChar); } catch { return leftString + rightString; } } #endregion #region 方法 -> 生成居中行 /// /// 生成居中对齐行 /// /// 居中字符串 /// 填充的字符 /// 行长度 /// public static string CreateCenterLine(string centerString, char paddingChar = ' ', int lineLength = 32) { try { int i_Len = (lineLength - StringLength(centerString)) / 2; return centerString.PadLeft(i_Len + centerString.Length, paddingChar); } catch { return centerString; } } #endregion #region 方法 -> 生成左对齐行 /// /// 生成左对齐行 /// /// 需要左对齐的文本 /// 填充的字符 /// 单行字符字节长度 /// public static string CreateLeftLine(string leftString, char paddingChar = ' ', int lineLength = 32) { try { int i_Len = lineLength - StringLength(leftString); return leftString.PadRight(i_Len + leftString.Length, paddingChar); } catch { return leftString; } } #endregion #region 方法 -> 生成右对齐行 /// /// 生成右对齐行 /// /// 需要右对齐的文本 /// 填充的字符 /// 单行字符字节长度 /// public static string CreateRightLine(string rightString, char paddingChar = ' ', int lineLength = 32) { try { int i_Len = lineLength - StringLength(rightString); return rightString.PadLeft(i_Len + rightString.Length, paddingChar); } catch { return rightString; } } #endregion #region 方法 -> 计算字符串字节长度 /// /// 计算字符串字节长度 /// /// 待计算字符串 /// private static int StringLength(string calculateString) { if (string.IsNullOrEmpty(calculateString)) { return 0; } //计算得到该字符串对应单字节字符串的长度 return Regex.Replace(calculateString, @"[^\x00-\xff]", "aa").Length; } #endregion } }