C# 实现虚拟打印机 HP Color LaserJet 4500 1
无聊了研究了下PCL和HPGL两种语言。如果要实现虚拟打印机只使用.NET来做,驱动是最大的问题。其实我们可以使用已经写好的打印机驱动来实现。只是让驱动最终生成的打印语言输出到我们想要的位置。并且我们对打印语言进行模拟显示就可以。经过几天的研究发现HP Color LaserJet 4500 打印出的图形为PCL和HPGL的混合体。那就写个控制HP Color LaserJet 4500的程序进行控制并且把打印出的打印语言输出到我们想要的位置。
首先,先给系统添加打印机 HP Color LaserJet 4500
其他的设置不用管。用程序来控制把,免得配置复杂以后我自己都忘了怎么配置的。
思路 先给打印机更换端口 使用写注册表 把临时目录放做为打印端口 然后设置打印后保留文档 通过API EnumJobs 获取打印任务 重新执行打印后获取临时文件 ,这里临时文件就是我们需要的PCL/HPGL文件( ImagePRN 这个类 我暂时不贴出来) 因为包含两种打印语言所以这个东西的类我还的找个时间再整理下,到目前为止就10来个类了贴出来太累了。等整理后、成一个类后我会帖出来。
效果图
不说了 先看代码把
测试使用代码
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.Text;
using System.Windows.Forms;using Zgke.Code;namespace WindowsApplication1
{public partial class Form1 : Form{public Form1(){InitializeComponent();}/// <summary>/// 最终保存PCL文件的路径/// </summary>private string p_FileDir = @"D:/temp/PCL/";/// <summary>/// 打印控制器 /// </summary>private PrintControlSet m_SetControl;/// <summary>/// PCL文件里的文档/// </summary>private IList<Image> m_PrintImageList;private void Form1_Load(object sender, EventArgs e){string[] _Filelist = System.IO.Directory.GetFiles(p_FileDir, "*.PCL");for (int i = 0; i != _Filelist.Length; i++){listBox1.Items.Add(_Filelist[i]);}m_SetControl = new PrintControlSet("Zgke PrintOK", @"C:/1.PRN");m_SetControl.PrintJobLoad += new PrintControlSet.PrintJobFile(_SetControl_PrintJobLoad);m_SetControl.SaveFileOver += new PrintControlSet.SaveOver(_SetControl_SaveFileOver);m_SetControl.StarMonitor(); }/// <summary>/// 文件复制完成后触发/// </summary>/// <param name="p_FileName"></param>void _SetControl_SaveFileOver(string p_FileName){this.Invoke((MethodInvoker)delegate { listBox1.Items.Add(p_FileName); }); } /// <summary>/// 需要处理时触发 这里可以根据需要写到数据库里。 我只用了日期 并没有使用档名 这个名字可能文件不能创建/// </summary> /// <param name="p_Document"></param>/// <param name="p_PrintDateTime"></param>/// <param name="p_MachineName"></param>/// <param name="p_UserName"></param>/// <param name="p_PageCount"></param>/// <param name="p_PrintOK"></param>/// <param name="p_SaveFileName"></param>void _SetControl_PrintJobLoad(string p_Document, DateTime p_PrintDateTime, string p_MachineName, string p_UserName, int p_PageCount, out bool p_PrintOK, out string p_SaveFileName){p_PrintOK = true;p_SaveFileName = p_FileDir +p_PrintDateTime.ToString("yyyyMMddHHmmssffff")+".PCL"; }private void comboBox1_SelectedIndexChanged(object sender, EventArgs e){pictureBox1.Image = m_PrintImageList[comboBox1.SelectedIndex];}private void listBox1_DoubleClick(object sender, EventArgs e){ if (listBox1.SelectedItem == null) return;comboBox1.Items.Clear();ImagePRN _HPGL = new ImagePRN(listBox1.SelectedItem.ToString());m_PrintImageList = _HPGL.PrintBitmap;for (int i = 0; i != m_PrintImageList.Count; i++){comboBox1.Items.Add(i.ToString());}if (m_PrintImageList.Count != 0) comboBox1.SelectedIndex = 0; }}
}
下面是打印控制的类
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing.Printing;
using System.Threading;
using System.Runtime.InteropServices;namespace Zgke.Code
{/// <summary>/// zgke@sina.com/// qq:116149/// 虚拟打印机控制器/// </summary>public class PrintControlSet{// <summary>/// 保存文件/// </summary>/// <param name="p_Document">文档名称</param> /// <param name="p_PrintDateTime">打印日期</param>/// <param name="p_MachineName">计算机名</param>/// <param name="p_UserName">用户名</param>/// <param name="p_PageCount">用户名</param>/// <param name="m_PrintOK">是否处理完成 如果为false 那下次监控还会触发这个</param>/// <param name="p_SaveFileName">保存PRN文件到指定的位置</param>public delegate void PrintJobFile(string p_Document,DateTime p_PrintDateTime,string p_MachineName,string p_UserName,int p_PageCount, out bool m_PrintOK,out string p_SaveFileName);/// <summary>/// 获取打印事件/// </summary>public event PrintJobFile PrintJobLoad;/// <summary>/// 保存完成/// </summary>/// <param name="p_FileName">复制完成后的文件位置</param> public delegate void SaveOver(string p_FileName);/// <summary>/// 保存完成/// </summary>public event SaveOver SaveFileOver;/// <summary>/// 监视线程/// </summary>private System.Timers.Timer m_Timer;/// <summary>/// 设置时间间隔/// </summary>public double Interval { get { return m_Timer.Interval; } set { m_Timer.Interval = value; } } /// <summary>/// 打印器名称 /// </summary>private string m_PrintName = "";/// <summary>/// 打印临时目录/// </summary>private string m_TempFile = "";/// <summary>/// 打印设置器 先去安装 HP Color LaserJet 4500 打印机 /// </summary>/// <param name="p_PrintName"></param>public PrintControlSet(string p_PrintName,string p_TempFile){if (p_PrintName.Length == 0) throw new Exception("必须指定打印机名称!");m_PrintName = p_PrintName;m_TempFile = p_TempFile;m_Timer = new System.Timers.Timer();m_Timer.Interval = 1000;m_Timer.Elapsed += new System.Timers.ElapsedEventHandler(m_Timer_Elapsed);Microsoft.Win32.RegistryKey _Regisity = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE/Microsoft/Windows NT/CurrentVersion/Print/Printers");string[] _PrintName =_Regisity.GetSubKeyNames();for (int i = 0; i != _PrintName.Length; i++){if (_PrintName[i] == m_PrintName){AddPort();SetPrintAttrib();RestSpooler();return;}}throw new Exception("无法找到对应的打印机!"); }/// <summary>/// 监控打印缓冲区/// </summary>/// <param name="sender"></param>/// <param name="e"></param>void m_Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e){m_Timer.Stop();IntPtr _PrintIntPtr = IntPtr.Zero;IntPtr _PrintDefault = IntPtr.Zero;bool A = Win32API.OpenPrinter("Zgke PrintOK", out _PrintIntPtr, _PrintDefault);uint _dwNeeded;uint _dwReturned;for (int i = 0; i != 65535; i++){IntPtr _JobIntPtr = Marshal.AllocHGlobal(1000);Win32API.EnumJobs(_PrintIntPtr, (uint)i, 1, 1, _JobIntPtr, 1000, out _dwNeeded, out _dwReturned);if (_dwReturned == 0){Marshal.FreeHGlobal(_JobIntPtr);break;}JOB_INFO_1 _JobInfo = (JOB_INFO_1)Marshal.PtrToStructure(_JobIntPtr, typeof(JOB_INFO_1));if (_JobInfo.Status != 128){Marshal.FreeHGlobal(_JobIntPtr);continue;}DateTime _PrintTime = new DateTime(_JobInfo.Submitted.wYear, _JobInfo.Submitted.wMonth, _JobInfo.Submitted.wDay, _JobInfo.Submitted.wHour, _JobInfo.Submitted.wMinute, _JobInfo.Submitted.wSecond, _JobInfo.Submitted.wMilliseconds);if (PrintJobLoad != null){bool _PrintOK = false;string _SaveFile = "";PrintJobLoad(_JobInfo.pDocument, _PrintTime, _JobInfo.pMachineName, _JobInfo.pUserName, _JobInfo.TotalPages, out _PrintOK, out _SaveFile);if (_PrintOK){int _State = Win32API.SetJob(_PrintIntPtr, _JobInfo.JobId, 0, IntPtr.Zero, JOB_CONTROL.JOB_CONTROL_RESTART);WaitForPrintOver(_PrintIntPtr, i);if (_SaveFile.Length != 0)System.IO.File.Copy(m_TempFile, _SaveFile, true); _State = Win32API.SetJob(_PrintIntPtr, _JobInfo.JobId, 0, IntPtr.Zero, JOB_CONTROL.JOB_CONTROL_CANCEL);if (SaveFileOver != null) SaveFileOver(_SaveFile);}Marshal.FreeHGlobal(_JobIntPtr);} }Win32API.ClosePrinter(_PrintIntPtr);m_Timer.Start();}/// <summary>/// 等待打印完成/// </summary>/// <param name="p_JobId">任务ID</param>public void WaitForPrintOver(IntPtr p_PrintIntPtr, int p_JobId){while (true){uint _dwNeeded;uint _dwReturned;IntPtr _JobIntPtr = Marshal.AllocHGlobal(1000);Win32API.EnumJobs(p_PrintIntPtr, (uint)p_JobId, 1, 1, _JobIntPtr, 1000, out _dwNeeded, out _dwReturned);if (_dwReturned != 0){JOB_INFO_1 _JobInfo = (JOB_INFO_1)Marshal.PtrToStructure(_JobIntPtr, typeof(JOB_INFO_1)); if (_JobInfo.Status == 128) return;}System.Windows.Forms.Application.DoEvents();Marshal.FreeHGlobal(_JobIntPtr);}} /// <summary>/// 添加一个端口/// </summary>private void AddPort(){Microsoft.Win32.RegistryKey _Regisity = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE/Microsoft/Windows NT/CurrentVersion/Ports",true);string[] _ValueName = _Regisity.GetValueNames();for (int i = 0; i != _ValueName.Length; i++){if (_ValueName[i] == m_TempFile) return;}_Regisity.SetValue(m_TempFile, "", Microsoft.Win32.RegistryValueKind.String);}/// <summary>/// 设置打印机属性/// </summary>private void SetPrintAttrib(){Microsoft.Win32.RegistryKey _Regisity = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE/Microsoft/Windows NT/CurrentVersion/Print/Printers/"+m_PrintName,true);int _Attrib = (int)_Regisity.GetValue("Attributes");_Attrib |= 0x100;_Regisity.SetValue("Attributes", _Attrib);_Regisity.SetValue("Port", m_TempFile);}/// <summary>/// 重新启动服务/// </summary>private void RestSpooler(){System.ServiceProcess.ServiceController[] _Spooler = System.ServiceProcess.ServiceController.GetServices();for (int i = 0; i != _Spooler.Length; i++){ if (_Spooler[i].ServiceName == "Spooler"){if (_Spooler[i].Status != System.ServiceProcess.ServiceControllerStatus.Stopped){_Spooler[i].Stop();_Spooler[i].WaitForStatus(System.ServiceProcess.ServiceControllerStatus.Stopped);}_Spooler[i].Start();_Spooler[i].WaitForStatus(System.ServiceProcess.ServiceControllerStatus.Running);}}}/// <summary>/// 开始监控/// </summary>public void StarMonitor(){m_Timer.Start();}/// <summary>/// 停止监控/// </summary>public void StopMonitor(){m_Timer.Stop();}}public class Win32API{[DllImport("winspool.drv", EntryPoint = "EnumJobsA")]public static extern bool EnumJobs(IntPtr hPrinter, uint FirstJob, uint NoJobs, uint Level, IntPtr pJob, uint cdBuf, out uint pcbNeeded, out uint pcReturned);[DllImport("winspool.drv", EntryPoint = "SetJob")]public static extern int SetJob(IntPtr hPrinter, int JobId, int Level, IntPtr pJob, JOB_CONTROL Command);[DllImport("winspool.drv", CharSet = CharSet.Auto)]public static extern bool OpenPrinter(string pPrinterName, out IntPtr phPrinter, IntPtr pDefault);[DllImport("winspool.drv", CharSet = CharSet.Auto)]public static extern bool ClosePrinter(IntPtr ptrPrinter); }public enum JOB_CONTROL{JOB_CONTROL_PAUSE = 1,JOB_CONTROL_RESUME = 2,JOB_CONTROL_CANCEL = 3,JOB_CONTROL_RESTART = 4}[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]public struct JOB_INFO_1{public int JobId;[MarshalAs(UnmanagedType.LPStr)]public string pPrinterName;[MarshalAs(UnmanagedType.LPStr)]public string pMachineName;[MarshalAs(UnmanagedType.LPStr)]public string pUserName;[MarshalAs(UnmanagedType.LPStr)]public string pDocument;[MarshalAs(UnmanagedType.LPStr)]public string pDatatype;[MarshalAs(UnmanagedType.LPStr)]public string pStatus;public int Status;public int Priority;public int Position;public int TotalPages;public int PagesPrinted;public SYSTEMTIME Submitted;}[StructLayout(LayoutKind.Sequential)]public struct SYSTEMTIME{public short wYear;public short wMonth;public short wDayOfWeek;public short wDay;public short wHour;public short wMinute;public short wSecond;public short wMilliseconds;}
}
下一篇,我会把PCL/HPGL的类贴出来。