请问如何监控打印机的打印队列?

XiaoDi_Liu 2004-01-10 11:09:08
请问如何监控打印机的打印队列,可以获得正在打印文档的文档名,状态,所有者,页数,大小等.谢谢!
...全文
1655 5 打赏 收藏 转发到动态 举报
写回复
用AI写文章
5 条回复
切换为时间正序
请发表友善的回复…
发表回复
kundy 2004-04-03
  • 打赏
  • 举报
回复
关注 @_@
lhb1980 2004-01-10
  • 打赏
  • 举报
回复
一、API介绍:

API是Application Programming Interface的缩写,也就是应用程序调用系统功能的接口。Windows API主要包括系统定义的函数、消息及与之相关的数据类型。我们使用各种开发工具归根结底都是和API打交道。

二、与打印状态相关的API:

Api函数名 函数说明 W3.x W9x NT
EnumJobs 获取打印作业信息 否 是 是
OpenPrinter 获取指定打印机的句柄 否 是 是


在 Windows NT 版本以及 Windows 9x 中,增加了一批全新的、专门用来处理打印机及打印机队列状态的 API 函数。在这里,将通过 API 函数 OpenPrinter 与 EnumJobs,来确定打印机打印作业状态所需的信息。

OpenPrinter,用来获取给定打印机的句柄,通过该句柄可以实现对相应打印机的操作。

函数 EnumJobs,用来列出所指定打印机上正在打印的作业信息,这里给定打印机的引用就是通过使用上述OpenPrinter函数反馈回来的打印机句柄。


EnumJobs函数介绍:

EnumJobs (ByVal hPrinter As Long, ByVal FirstJob As Long, ByVal NoJobs As Long, ByVal Level As Long, pJob As Long, ByVal cbBuf As Long, pcbNeeded As Long, pcReturned As Long)

API函数EnumJobs用于列举给定打印机当前所有正在打印的任务状态信息,该函数可以列举给定打印机简要的或者详细的(Level决定)队列任务状态信息。在引用该函数时,通常先把cbBuf变量设置为0,以获得打印机队列任务的字节数,该字节数存储在pcbNeeded里以决定pJob变量大小,然后再次引用该函数,获得打印机队列任务的详细信息。

参数:

hPrinter

打印机句柄。

FirstJob

统计打印机队列任务数的起始点,0表示从第一个任务开始统计列举所有的打印任务。

NoJobs

需要列举的最大打印任务数。一般设为127。

Level

标示存储在pJob里的信息格式。如果该值为1,数据以JOB_INFO_1的格式存储;如果该值为2,数据以JOB_INFO_2的格式存储。

pJob

数组变量,保存打印任务的所有信息;cbBuf设为0时,该值也为0。Level变量的值决定pJob的数据格式(JOB_INFO_1 OR JOB_INFO_2)

cbBuf

该值通常为0。

pcbNeeded

存储打印机里打印任务的字节数。

pcReturned

用来存放打印任务数量的变量。

通过上表可以看出,这两个API函数只用使用在Windows 9x 以及 Windows NT环境中。

三、开发工具:

C++ Builder作为快速开发Windows平台下应用程序的工具,已经为越来越多的开发者采用。但是,如果要开发出专业的Windows应用软件,还需要使用大量的Windows API函数,以下是笔者应用C++ Builder以及Windows API函数来实现对打印机状态的时时监控。

选择C++ Builder的理由:

一:由于Windows API 都是用C或C++编写的,C++ Builder更易于底层应用。

二:C++ Builder对于Windows API的引用更加简单,这一点是Visual Basic所欠缺的。


四、程序实例:

1、启动C++ Builder程序,新创建一个工程;

2、进入工程,在当前窗体分别添加一个CheckListBox控件、一个Timer控件;

3、在CheckListBox控件的Item属性添加打印机名称列表(假设在网络中HostPrinter主机上有三台打印机Printer1、Printer2、Printer3),则Item项目里内容格式为:\\HostPrinter\Printer1、\\HostPrinter\\Printer2、\\HostPrinter\\Printer3 ;

4、Timer的Interval属性设置为60000(一分钟),在Timer的OnTimer事件加入如下代码:


{

HANDLE hPrinter; //打印机句柄

JOB_INFO_2 jobs[30]; //保留打印作业详细信息

DWORD size=sizeof(jobs);

JOB_INFO_2 *jobs1=NULL; //动态数组用于对大于size的任务进行操作

DWORD pcbNeeded=0; //所有打印字节数

int actNeed=0;

DWORD pcReturned=0; //打印任务数

char buf[19]; //指向打印机或机器名

int ret1; //获得打印机句柄返回值

int ret; //获得打印机任务返回值

TDateTime startTime;

Word Hour=0;

Word Hour1=0;

Word Min=0;

Word Min1=0;

Word Sec=0;

Word MSec=0;

int StartTime=0; //任务开始时间

int NowTime=0; //当前时间

int TotalTime=0; //任务停留时间

String str;

int length;

int PrintCount;

int InitCount=15; //设定的打印队列数量--------报警底线

int InitTime=10; //设定的打印队列事件--------报警底线(分钟)


PrintCount =CheckListBox1->Count ;

for (int i=0; i < PrintCount; i++)

{


//..............取得列表里的打印机

str=CheckListBox1->Items->Strings[i] ;

length=str.Length() ;


//................字符串到字符的转换,

StrPLCopy(buf,str,length);


//................获得打印机句柄

ret1= OpenPrinter(buf, &hPrinter, NULL);


//................获得打印任务数

ret= EnumJobs(hPrinter, 0, 127, 2,0, 0, &pcbNeeded, &pcReturned); //必要的一步,先取出打印机里任务的字节数,然后决定jobs1变量大小。//--------------------------①


//.................重定义Jobs1, 根据pcbNeeded决定使用哪个变量

if (pcbNeeded>size)

{

actNeed=pcbNeeded;

jobs1=new JOB_INFO_2[actNeed];

ret= EnumJobs(hPrinter, 0, 127, 2,(LPBYTE)jobs1, actNeed, &pcbNeeded, &pcReturned);

startTime=SystemTimeToDateTime(jobs1[0].Submitted); //------------------②

delete jobs1;

}

else

{

ret= EnumJobs(hPrinter, 0, 127, 2,(LPBYTE)jobs, size, &pcbNeeded, &pcReturned);//-----------------------------③

if (pcReturned>0)

startTime=SystemTimeToDateTime(jobs[0].Submitted);

}

//.................pcbNeeded>size结束


//................如果队列里有打印任务,比对数量与时间

if (pcReturned>0)

{


//.................时间转换,计算一天内的时间差

{

//............时间比较运算处理;

//............时间转换StartTime转换成分钟


}

DecodeTime(Now(),Hour1, Min1, Sec,MSec);

NowTime=Hour1*60+Min1;

TotalTime=NowTime-StartTime;

}


//依据条件判断是否报警

if ((pcReturned>InitCount) |(TotalTime >InitTime))

{


Beep();


}


pcReturned=0;

pcbNeeded=0;


}


//..........................pcReturned>0结束


}//....................................................for结束



5、编译并运行上述程序:

①程序中,此处的获得打印机任务的目的主要是获得打印机作业的字节数------ pcbNeeded,该变量里字节的大小,将决定进一步获得打印机里作业的状态时所用的变量是Jobs还是Jobs1。

②当pcbNeeded>size,给Jobs1分配空间,然后获得打印机当前作业的状态。

③当pcbNeeded<size,用Jobs作为变量,获得当前打印机作业状态。


用C++ Builder编程监控打印机队列状态:
只需要转换成vc++即可

注:②、③参数引用与①不同。

五、应用分析:

可以说IT技术在制造业得到了广泛的应用,而打印控制与监控则是制造业中众多应用中的较为典型的应用。通常,在生产制造过程中,都有很多打印机用于打印生产排序单、装配单、铭牌、标签等等。因此打印机的时时状态(缺纸、墨尽、卡纸)在这里就显得尤为重要了,如果不能及时的监控到打印机的状态势必会影响生产的正常持续运行。所以为了保证生产正常运行拥有一套用于监控打印机时时状态的程序是很有必要的。
sxslyy 2004-01-10
  • 打赏
  • 举报
回复
更完整的例子可以参考微软的Knowledge Base的文章:“Q158828 OWTO: How To Call Win32 Spooler Enumeration APIs Properly”和微软的Knowledge Base的文章:“Q228769 OWTO: Retrieve Print Job Information”。

http://support.microsoft.com/support/kb/articles/Q158/8/28.asp
http://support.microsoft.com/support/kb/articles/Q228/7/69.asp
sxslyy 2004-01-10
  • 打赏
  • 举报
回复
确定打印队列的状态

问题
  有时,程序员需要显示存在于系统中的各种打印机中打印作业的状态,但似乎不能找到一种方法来查询当前哪些打印机是可用的以及在这些打印机上正在运行哪些作业。
  那么,如何利用 Windows API 函数来查询可用的打印机及其所处的工作状态呢?

方法
  在 Windows NT 版本以及 Windows 9x 中,增加了一批全新的、专门用来处理打印机及打印机队列状态的 API 函数。在本节中,将讨论 API 函数 EnumPrinters 与 EnumJobs,这两个函数可提供给程序员确定打印作业状态所需的信息。
  在 Windows 9x 中添加的函数 EnumPrinters,用来列出所有正在有效运行的打印机的名称及其标识符,此函数可用于列出用户能够访问的无论是在本地工作的还是在网络上运行的所有打印机,在本节的例子中,我们只考虑本地打印机的情况。
  新的 Windows 9x API 中增加的另一个函数 EnumJobs,用来列出所指定打印机上正在打印的作业,为了能够使用这个函数,必须有打印机的句柄,这就需要使用另外一个 API 函数 OpenPrinter。
  需要注意的是,这几个 API 函数只能在 Windows 9x 以及 Windows NT 中使用。

步骤
  按照下列方法生成一个例子程序。选择菜单 Dialog,并从此菜单中选择菜单项 Printer Jobs,此时显示一个所有可用打印机的对话框,在第一个列表框中选择其中一个打印机后,即在第二个列表框中显示该打印机的作业列表。

  实现例子程序的步骤如下:
  1.在 Visual C++ 中利用 AppWizard 创建新的工程 Ld104。
  2.进入 AppStudio,创建新的对话框。把按钮 OK 与 Cancel 移动到对话框的底部。在对话框中添加两个列表框。
  3.进入 ClassWizard,为刚创建的对话模板生成新的对话框类,把此新类命名为 CPrintQDlg。
  4.在 ClassWizard 中,从下拉列表中选择类 CPrintQDlg,从对象列表中选择对象 CPrintQDlg,从消息列表中选择消息 WM_INITDIALOG,点击按钮 Add Function 添加新的函数 OnInitDialog。把下面的代码输入到类 CPrintQDlg 的方法 OnInitDialog 中。


BOOL CPrintQDlg::OnInitDialog()
{
CDialog::OnInitDialog();

int size=4096;
unsigned long sizeNeeded=0;
unsigned long numPrinters;
PPRINTER_INFO_1 pPrinters;
pPrinters=(PPRINTER_INFO_1)LocalAlloc((LMEM_FIXED/LMEM_ZEROINIT),size);
int ret=EnumPrinters(PRINTER_ENUM_LOCAL,NULL,1,
(LPBYTE)pPrinters,size,&sizeNeeded,&numPrinters);
CListBox * list=(CListBox *)GetDlgItem(IDC_LIST1);
for(int i=0;i<(int)numPrinters;++i)
list->AddString(pPrinters[i].pName);
LocalFree(pPrinters);

return TRUE; // return TRUE unless you set the focus to a control
}

  5.在 ClassWizard 中,从下拉列表中选择类 CPrintQDlg,从对象列表中选择对象 IDC_LIST1,从消息列表中选择消息 LBN_SELCHANGE,点击按钮 Add Function 添加新的函数 OnSelectPrinter。把下面的代码输入到类 CPrintQDlg 的方法 OnSelectPrinter 中。

void CPrintQDlg::OnSelectPrinter()
{
HANDLE handle;
JOB_INFO_1 jobs[10];

CListBox * list1=(CListBox *)GetDlgItem(IDC_LIST1);
char buf[256];
list1->GetText(list1->GetCurSel(),buf);

if(!OpenPrinter(buf,&handle,NULL))
{
MessageBox("Could not open printer","Error",
MB_OK/MB_ICONINFORMATION);
return;
}

DWORD size=sizeof(jobs);
DWORD numBytes=0;
DWORD numEntries=0;
int ret=EnumJobs(handle,0,10,1,(LPBYTE)jobs,
size,&numBytes,&numEntries);

CListBox * list2=(CListBox *)GetDlgItem(IDC_LIST2);
for(int i=0;i<(int)numEntries;++i)
{
char buf[80];
sprintf(buf,"Job %ld",jobs[i].JobId);
if(jobs[i].pStatus!=NULL)
strcat(buf,jobs[i].pStatus);
else
{
switch(jobs[i].Status)
{
case JOB_STATUS_PAUSED:
strcat(buf," Paused");
break;
case JOB_STATUS_PRINTED:
strcat(buf," Printed");
break;
case JOB_STATUS_PRINTING:
strcat(buf," Printing");
break;
case JOB_STATUS_SPOOLING:
strcat(buf," Spooling");
break;
}
}
list2->AddString(buf);
}
}

  6.把下面一行代码添加到文件 PrintQDlg.cpp 的顶端。
  #include "winspool.h"
  7.进入 AppStudio 并在菜单 IDR_MAINFRAME 中添加新菜单 Dialog。在此菜单中添加标题为 Printer Jobs 的菜单项,标识符命名为 ID_PRINTER_JOBS。
  8.进入 ClassWizard,从下拉组合框中选择对象 CMainFrame,从对象列表中选择对象 ID_PRINTER_JOBS,从消息列表中选择消息 COMMAND,点击按钮 Add Function 添加新的函数 OnPrinterJobs,把下面的代码输入到此方法中。

void CMainFrame::OnPrinterJobs()
{
CPrintQDlg dlg;

dlg.DoModal();
}

  9.把下面一行代码添加到文件 MainFrame.cpp 的顶端。
  #include "PrintQDlg.h"
  10.编译并运行此例子程序。


zhangcrony 2004-01-10
  • 打赏
  • 举报
回复
//樓上的代碼是C++ Builder的啊﹗
//我提供一段VC++的代碼﹐樓主試試﹗
BOOL GetJobs(HANDLE hPrinter, /* Handle to the printer. */

JOB_INFO_2 **ppJobInfo, /* Pointer to be filled. */
int *pcJobs, /* Count of jobs filled. */
DWORD *pStatus) /* Print Queue status. */

{

DWORD cByteNeeded,
nReturned,
cByteUsed;
JOB_INFO_2 *pJobStorage = NULL;
PRINTER_INFO_2 *pPrinterInfo = NULL;

/* Get the buffer size needed. */
if (!GetPrinter(hPrinter, 2, NULL, 0, &cByteNeeded))
{
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
return FALSE;
}

pPrinterInfo = (PRINTER_INFO_2 *)malloc(cByteNeeded);
if (!(pPrinterInfo))
/* Failure to allocate memory. */
return FALSE;

/* Get the printer information. */
if (!GetPrinter(hPrinter,
2,
(LPSTR)pPrinterInfo,
cByteNeeded,
&cByteUsed))
{
/* Failure to access the printer. */
free(pPrinterInfo);
pPrinterInfo = NULL;
return FALSE;
}

/* Get job storage space. */
if (!EnumJobs(hPrinter,
0,
pPrinterInfo->cJobs,
2,
NULL,
0,
(LPDWORD)&cByteNeeded,
(LPDWORD)&nReturned))
{
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
free(pPrinterInfo);
pPrinterInfo = NULL;
return FALSE;
}
}

pJobStorage = (JOB_INFO_2 *)malloc(cByteNeeded);
if (!pJobStorage)
{
/* Failure to allocate Job storage space. */
free(pPrinterInfo);
pPrinterInfo = NULL;
return FALSE;
}

ZeroMemory(pJobStorage, cByteNeeded);

/* Get the list of jobs. */
if (!EnumJobs(hPrinter,
0,
pPrinterInfo->cJobs,
2,
(LPBYTE)pJobStorage,
cByteNeeded,
(LPDWORD)&cByteUsed,
(LPDWORD)&nReturned))
{
free(pPrinterInfo);
free(pJobStorage);
pJobStorage = NULL;
pPrinterInfo = NULL;
return FALSE;
}

/*
* Return the information.
*/
*pcJobs = nReturned;
*pStatus = pPrinterInfo->Status;
*ppJobInfo = pJobStorage;
free(pPrinterInfo);

return TRUE;

}

BOOL IsPrinterError(HANDLE hPrinter)
{

JOB_INFO_2 *pJobs;
int cJobs,
i;
DWORD dwPrinterStatus;

/*
* Get the state information for the Printer Queue and
* the jobs in the Printer Queue.
*/
if (!GetJobs(hPrinter, &pJobs, &cJobs, &dwPrinterStatus))
return FALSE;

/*
* If the Printer reports an error, believe it.
*/
if (dwPrinterStatus &
(PRINTER_STATUS_ERROR |
PRINTER_STATUS_PAPER_JAM |
PRINTER_STATUS_PAPER_OUT |
PRINTER_STATUS_PAPER_PROBLEM |
PRINTER_STATUS_OUTPUT_BIN_FULL |
PRINTER_STATUS_NOT_AVAILABLE |
PRINTER_STATUS_NO_TONER |
PRINTER_STATUS_OUT_OF_MEMORY |
PRINTER_STATUS_OFFLINE |
PRINTER_STATUS_DOOR_OPEN))
{
return TRUE;
}

/*
* Find the Job in the Queue that is printing.
*/
for (i=0; i < cJobs; i++)
{
if (pJobs[i].Status & JOB_STATUS_PRINTING)
{
/*
* If the job is in an error state,
* report an error for the printer.
* Code could be inserted here to
* attempt an interpretation of the
* pStatus member as well.
*/
if (pJobs[i].Status &
(JOB_STATUS_ERROR |
JOB_STATUS_OFFLINE |
JOB_STATUS_PAPEROUT |
JOB_STATUS_BLOCKED_DEVQ))
{
return TRUE;
}
}
}

/*
* No error condition.
*/
return FALSE;

}
打开链接下载源码: https://pan.quark.cn/s/589796089f72 C++课程设计任务列表,涵盖以下内容:1、识别并显示10至99范围内,各位数乘积超过各位数和的数值,例如数字12不满足条件,因为1乘以2小于1加2,故不输出;而数字27满足条件,因为2乘以7大于2加7,因此需要输出该数。2、开发一个功能,用于从用户输入的任意数量实数中找出最大值与最小值:首先要求用户输入一个正整数n,代表数值的个数,随后用户可输入任意n个实数,程序需找出这n个数中的最大值和最小值并将它们展示出来。3、实现两个已排序数组的合并操作:设有数组A和B,且假设这两个数组的元素均已按照降序排列。编写程序将A和B合并成一个新的数组C,并确保C中的元素同样保持降序排列。int A[10]={123, 86,80, 49,33,15,7,0,-1,-3};int B[10]={100,64,51,50,27,19,15,12,5,2};4、设计一个函数,用于计算特定分数序列前n项的总和,该序列为1/2,1/3,1/4,1/5,1/6,1/7,...。要求在主程序中提示用户输入一个整数n,并验证输入的合法性(n需大于1方为有效),若输入合法,则调用求和函数并显示计算结果。5、编写一个程序,计算两个用户输入日期之间的天数差:用户需以year1,month1,day1和year2,month2,day2的格式输入两个日期,程序随后计算这两个日期之间的天数间隔,并将结果输出到屏幕上。要求编制具有如下原型的函数difs2Date:long GetDayDifference(int y1,int m1,int d1,int y2,int m2,int d2);并在主函数中调用此函数,将计...
代码下载地址: https://pan.quark.cn/s/ee8627e4e6d7 ABAP调试器是一种功能强大的工具,可用于在执行期间对ABAP代码进行检验。除了常规的核心功能(例如逐行运行代码以及检验变量、字段符号和引用的值)之外,它还提供了一些辅助性的特性,能够简化并压缩调试会话的时长。并非所有使用者都熟悉这些辅助特性。SAP ABAP调试器是处理和优化ABAP代码开发与维护工作的核心资源,它配备了多样的功能来协助开发人员在运行状态下进行检验和排除故障。此资源着重阐述了ABAP调试器的一些高级特性,涵盖了深入分析调用堆栈、系统级调试、更新会话调试以及提升调试效率的方法。 1. **深入分析调用堆栈**:除了常规的应用程序调试,开发人员有时需要对调用堆栈的内部层级进行深入调试,特别是在错误出现在异步执行的更新处理或系统级程序时。通过启用**系统级调试**,可以访问通常不公开的系统代码,但这也会导致调用堆栈的显著增加,因此需要审慎操作。 2. **系统级调试**:对于不含业务逻辑的系统级程序,开发人员通常无需进行调试。然而,在特定情形下,例如进行错误追踪时,可能需要进入系统代码。借助调试器的“系统调试启用/禁用”选项,可以赋予对系统程序的调试权限。 3. **更新会话调试**:在处理异步更新任务,例如持久化业务数据时,错误可能发生在更新任务内部。激活**更新会话调试**,在更新任务完成后,调试器将自动启动,展示执行路径。比如,在变更成本中心后,通过输入调试指令 "/h" 启动调试,保存后能够看到更新过程中的错误。 4. **分析调用堆栈**:在进行深入调试时,调用堆栈是至关重要的。通过分析调用堆栈,能够定位到引发问题的具体位置,如在VB_V2_NORMAL...
源码链接: https://pan.quark.cn/s/a4b39357ea24 小程序雷达 AI 驱动的小程序生态选型与风险评估工具,把微信小程序开发资源转化为可筛选、可评估、可对比的技术雷达。 线上地址 主站: Vercel: 适合谁 正在做微信小程序技术选型的产品、研发和架构团队。 需要判断 Taro、uni-app、原生小程序、组件库、云开发和 SDK 风险的团队。 需要把历史 awesome 列表转成可筛选、可对比、可验证技术雷达的维护者。 可以做什么 Radar:按推荐状态、风险等级、资源类型、分类和适用场景浏览小程序生态资源。 Quick Search:快速搜索资源并跳转常用页面。 Compare:对比 Taro、uni-app、原生小程序等核心方案。 Advisor:输入选型问题,获得推荐结论、适用/不适用条件、迁移成本、下一步和证据来源。 Doctor:粘贴小程序项目配置,识别框架依赖、过时方案和迁移风险。 Weekly:查看小程序生态周报和近期风险信号。 数据概览 当前数据集中包含 236 个小程序生态资源。 完整资源可在 Radar 页面和导出能力中查看。 核心样例 Taro ★30.6k+ - 使用 React 的方式开发小程序的框架,同时支持生成多端应用 uni-app ★36.1k+ - 使用 Vue 语法开发小程序、H5、App的统一框架 MPX ★2.1k+ - 增强型小程序框架,深度性能优化,支持跨小程序平台开发,完全兼容原生小程序组件 WePY ★21.7k+ - 支持组件化的小程序开发框架 vant-weapp ★12.3k+ - 高颜值、好用、易扩展的微信小程序 UI 库 tdesign-miniprogram ★1.3...

16,550

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • Creator Browser
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

试试用AI创作助手写篇文章吧