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的类贴出来。