2。1。2监测当前打印机状态
Windows标准的打印机监测程序。通过该程序,我们可以了解当前打印机的状态,包括打印机任务队列,
各项任务状态、所有者、进度和开始时间,并且可以及时暂停、清除打印任务。
监控打印机就有必要对Windows后台打印机制有所了解。后台程序可以减轻应用程序的打印负担。Windows在启动时就加载后台
打印程序。因此,当应用程序开始打印时,它已经是活动的了。当程序打印一个文件时,GDI模块创建打印的数据文件。后台
打印程序的任务是将这些文件发往打印机。GDI模块发出一个消息来通知它开始一个新的打印作业。然后打印机开始读文件,
并将文件传送到打印机。为了传送打印文件,后台程序为打印机所连接的并口和串口提供各种通信函数。在后台程序向打
印机发送文件操作结束的命令后,它就删除包含输出数据的临时文件。
后台打印程序的组合。
----------------------------------------
打印请求后台程序 | 将数据流传递给打印机
本地打印提供者 | 为本地打印机创建后台文件
网络打印提供者 | 为网络打印机创建后台文件。
打印处理程序 | 将后台的设备无关数据转换为特定打印机的格式
端口监视程序 | 控制连接打印机的端口
语言监视程序 | 控制双向通信的打印机,配置并检测打印机的状态
使用后台打印程序,真正的文件打印操作是后台打印程序的任务,而不是应用程序的任务。
我们可以暂停打印作业、改变作业的优先级或者取消打印作业。这种管理方式使得应用程序
可能比下面的这种情况“打印”得更快。即作业以实时方式打印,且必须等到打印完一页后
才能处理下一页。
但是如果我们拥有更好的打印机软硬件或者在网络打印机等某些特殊的情况下,可以去掉Windows后台打印程序。
去掉Windows后以中打印程序可以加快打印速度。因为打印输出不必保存在硬盘上,而可以直接输出到打印机,并被
外部硬件或软件后台程序所接受。
实现类似Windows打印监测试程序的功能,需要调用Win32假脱机(SPOOL)枚举API函数。"[使用Win32假脱机枚举函数需要调用
两次所需的函数。这些API函数通常要填充一组结构。但是,这些结构通常包含指向字符串和其他类型数据的指针。
这些外来的数据必须存储在返回的内存中,使得字符串和其他数据存储在结构中。所以简单的在堆栈中声明一组结构变量
不足以为API函数返回的信息设置足够的内存。]"
因此,正确的函数调用是:首先调用API函数确定需要的内存空间,在随后的调用中传入一个指针,该指针指向了一块动态
分配的大小合适内存空间。需要进行该类处理的枚举函数包括:
EnumForms(),EnumJobs(),EnumMonitors(),EnumPorts(),EnumPrinterDrivers(),EnumPrinters()和EnumPrinterProcessors()
下面这段程序,使用EnumJobs()API函数,枚举出所选打印机当前的打印任务。关于其他打印监控函数的使用大家可以参考
微软平台SDK中关于打印函数和打印假脱机枚举函数的文档。
注意:独占设备是指在一个程序(作业、用户)的整个运行其间独占的设备,直到该程序(作业、用户)完成。系统的独占设备是
有限的(譬如一台计算机只能连接一台打印机),往往不能满足多进程的要求,会引起大量进程由于等待某些独占设备而阻塞,
成为系统的“瓶颈”。另一方面,申请到独占设备的进程在其整个运行期间虽然占有设备,利用率却常常很低,设备还是经常
处于空闲状态。为了解决这种矛盾,最常用的方法就是用共享设备来模拟独占设备,从而提高系统地效率和设备利用率.这种
技术称为虚拟设备技术.实现这一技术的软、硬件系统被称为假脱机(Simultaneous Peripaheral Operation On Line SPOOL)
系统。打印机是典型的独占设备。引入SPOOL技术后,用户的打印请求传递给SPOOL系统,而不是真正的把打印机分配给用户。
SPOOL系统的输出进程是先磁盘上申请一个空闲区域,把需要打印的数据传输到里面,再把用户的打印请求挂到打印机队列上。
如果打印机空闲,就会从打印机队列中取出一个打印请求,再从磁盘上的指定区域取出数据,执行打印操作。由于磁盘是共享
的,SPOOL系统可以随时响应打印请求并把数据缓存起来,这样独占设备改造成了共享设备,从而提高了设备的利用率和系统
利用率。
/*
OpenPrinter
Declare Function OpenPrinter Lib "winspool.drv" Alias "OpenPrinterA"(ByVal pPrinterName As String,phPrinter As Long,
pDefault As PRINTER_DEFAULTS)As Long
说明:
打开指定的打印机,并获取打印机的句柄。
返回值:
Long,非零表示成功,零表示失败。会设置GetLastError
参数表:
pPrinterName String,要打开的打印机的名字
phPrinter Long,用于装载打印机的句柄。
pDefault PRINTER_DEFAULT,这个结构保存要载入的打印机信息。
*/
/*
Declare Function EnumJobs Lib "winspool.drv"Alias "EnumJobsA"(ByVal hPrinter As Long,
ByVal First Job As Long,ByVal NoJobs As Long,ByVal Level As Long,pJob As Byte,ByVal cdBuf As Long
,pcbNeeded As Long,pcReturned As Long)As Long
说明:
枚举打印队列中的作业
返回值:
Long,非零表示成功,零表示失败。 可以通过GetLastError获取错误信息。
hPrinter: Long,一个已打开的打印机对象的句柄(用OpenPrinter获得)
FirstJob: Long,作业列表中要枚举的第一个作业的索引(注意编号从0开始)
NoJobs Long,要枚举的作业数量。
Level Long,1或2
pJob Byte,包含JOB_INFO_1或JOB_INFO_2结构的缓冲区
cbBuf Long,pJob缓冲区中的字符数量。
pcbNeeded Long,指向一个LONG型变量的指针,该变量用于保存请求的缓冲区长度
或者实际读入的字节数量。
pcReturned Long,载入缓冲区的结构数量(用于那些能返回多个结构的函数)。
*/
/*
GetLastError
Declare Function GetLastError lib "kernel32"Alias "GetLastError"As Long
说明:
针对之前调用的api函数,用这个函数取得扩展错误信息
返回值:Long,由api函数决定.请参考api32.txt文件,其中列出了一系列错误常数;都以ERROR_前
缀起头.常用的错误代码见下表:
ERROR_INVALID_HANDLE:无效的句柄作为一个参数传递。
ERROR_CALL_NOT_IMPLEMENTED 在win95下调用专为win nt设计的win32 api函数
ERROR_INVALID_PARAMETER 函数中有个参数不正确。
*/
/*
EnumJobs
Declare Function EnumJobs Lib "winspool.drv" alias "EnumJobsA"(ByVal hPrinter As Long,ByVal FirstJob As Long,ByVal NoJobs As Long,By Level As Long
pJob As Byte,ByVal cdBuf As Long,pcbNeeded As Long,pcReturned As Long) As Long
说明:
枚举打印队列中的作业
返回值:
Long,非零表示成功,零表示失败。可以通过GetLastError获取错误信息
参数表:
hPrinter Long,一个已打开的打印机对象的句柄(用OpenPrinter获得)
FirstJob Long,作业列表中要枚举的第一个作业的索引(注意编号从0开始)
NoJobs Long,要枚举的作业数量。
Level Long,1或2
pJob Byte,包含JOB_INFO_1或JOB_INFO_2结构的缓冲区。
cbBuf Long,pJob缓冲区中的字符数量。
pcbNeeded Long,指向一个Long型变量的指针,该变量用于保存请求的缓冲区长度,或者实际读入的字节数。
pcbReturned Long,载入缓冲区的结构数量。
*/
BOOL CPrintListDlg::PrintJobList(LPCTSTR szPrintName)
{
HANDLE hPrinter;
DWORD dwNeeded,dwReturned,i;
JOB_INFO_1 *pJobInfo;
if(!OpenPrinter(szPrinterName,&hPrinter,NULL))
return FALSE;
if(EnumJobs(hPrinter,0,0xFFFFFFFF,NULL,1,NULL,0,&dwNeeded,&dwReturned))
{
if(GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
ClosePrinter(hPrinter);
return FALSE;
}
}
if((pJobInfo = (JOB_INFO_1*)malloc(dwNeeded)) == NULL)
{
ClosePrinter(hPrinter);
return FALSE;
}
if(!EnumJobs(hPrinter,0,0xFFFFFFFF,1,(LPBYTE)pJobInfo,dwNeeded,&dwNeeded,&dwReturned))
{
ClosePrinter(hPrinter);
free(pJobInfo);
return FALSE;
}
ClosePrinter(hPrinter);
for(UINT i = 0; i < dwReturned; i++)
{
m_listBox2.AddString(pJobInfo[i].pDocument);
}
free(pJobInfo);
return TRUE;
}
void CPrintListDlg::OnSelchangePrinters()
{
int nIndex = m_listBox.GetCurSel();
CString strPrinterName;
if(nIndex != CB_ERR)
{
m_listBox.GetText(nIndex,strPrinterName);
if(strPrinterName.IsEmpty())
{
PrintJobList((LPCTSTRstrPrinterName));
}
}
}
/*
这个例子启发我们,应用打印枚举API函数可以对打印机进行有效的监控。
*/