Reading the jobs in the print queue from Visual Basic
What is the print queue?
Once you know how to get additional information about the printer you will notice that one of the bits of information returned is the Pending Jobs count. This tells you how many jobs are queued up on that printer waiting to print out.
Of course just having a count of the number of pending jobs is only partly useful. It would be more useful to get a list of the jobs pending and some information about them, such as the document name, user name, pages printed so far etc..
The EnumPrintJobs API call provides this functionality.
'\\ Declaration to enumerate the active jobs on this printer
Private Declare Function EnumJobs Lib "winspool.drv" Alias "EnumJobsA" _
(ByVal hPrinter As Long, _
ByVal FirstJob As Long, _
ByVal NoJobs As Long, _
ByVal Level As Long, _
pJob As Long, _
ByVal cdBuff As Long, _
pcbNeeded As Long, _
pcBytesReturned As Long) As Long
This takes the handle to a printer in hPrinter that is open (see how to get additional information about the printer) and fills the buffer passed in with an array of JOB_INFO_n structures.
The Level parameter passed in tells the API which kind of structure you want returned. Currently there are three defined structures:
'\\ JOB_INFO_1 - Standard print job info
Private Type JOB_INFO_1
JobId As Long
lpPrinterName As String
lpMachinename As String
lpUserName As String
lpDocumentName As String
lpDataType As String
lpStatus As String
Status As PrintJobStatuses
Priority As Long
Position As Long
TotalPages As Long
PagesPrinted As Long
Submitted As SYSTEMTIME
End Type
'\\ JOB_INFO_2 - Extended print job info
Private Type JOB_INFO_2
JobId As Long
lpPrinterName As String
lpMachinename As String
lpUserName As String
lpDocumentName As String
lpNotifyName As String
lpDataType As String
lpPrintProcessor As String
lpParameters As String
lpDriverName As String
lpDevmode As Long '\\Pointer to DevMode
lpStatus As String
lpSecurityDescriptor As Long '\\Pointer to SECURITY_DESCRIPTOR
Status As PrintJobStatuses
Priority As Long
Position As Long
StartTime As Long
UntilTime As Long
TotalPages As Long
JobSize As Long
Submitted As SYSTEMTIME
time As Long
PagesPrinted As Long
End Type
'\\ JOB_INFO_3 -Only available from NT4 and above
Private Type JOB_INFO_3
JobId As Long
NextJobId As Long
Reserved As Long '\\must be set to zero
End Type
Getting all the current jobs
In order to allocate the buffer needed for these JOB_INFO_n we need to know how many of them we expect to be returned. To get this information we call EnumPrintJobs with insufficient buffer and it will return a value in pcbSizeRequired to inform us how much buffer is needed:
Dim lRet As Long
Dim pcbSizeRequired As Long, pcbBytesReturned As Long
Dim buffer() As Long
ReDim Preserve buffer(0) As Long '\\ Allocate a 1 byte buffer
lRet = EnumJobs(mhPrinter, 0, 255, 1, buffer(0), UBound(buffer), pcbSizeRequired, pcbBytesReturned)
At this stage if pcbSizeRequired is non-zero then there are jobs in the queue for which we can get information. To do so we resize the buffer to be big enough and call EnumPrintJobs again.
If pcbSizeRequired > 0 Then
'\\ Need to resize our array to cope with this data
ReDim Preserve buffer(0 To (pcbSizeRequired / 4) + 3) As Long
lRet = EnumJobs(mhPrinter, 0, 255, 1, buffer(0), UBound(buffer) * 4, pcbSizeRequired, pcbBytesReturned)
The field pcbBytesReturned will tell us how many PRINTER_INFO_1 records are returned so we can make a list of all their JobId properties thus:
Dim pJobId As Long
Dim JobIds() As Long
For pJobId = 0 To (pcbBytesReturned - 1)
Redim Preserve JobIds(pJobId) As Long
JobIds(pJobId) = buffer(pJobId * 16)
Next pJobId
Getting the JOB_INFO for a given job id
It is not desirable to list all the print jobs every time if you are only interested in the details of one particular job. For this purpose the GetJob API call is used.
Private Declare Function GetJob Lib "winspool.drv" Alias "GetJobA" _
(ByVal hPrinter As Long, _
ByVal JobId As Long, _
ByVal Level As Long, _
buffer As Long, _
ByVal pbSize As Long, _
pbSizeNeeded As Long) As Long
This takes the JobId as returned by the call to EnumPrintJobs and returns a PRINT_JOB_n structure filled with the details of that queued print job.
Dim lRet As Long
Dim SizeNeeded As Long
Dim buffer() As Long
'\\ Get the required buffer size...
ReDim Preserve buffer(0 To 1) As Long
lRet = GetJob(mhPrinter, mJobId, 1, buffer(0), UBound(buffer), SizeNeeded)
If SizeNeeded > 0 Then
ReDim Preserve buffer(0 To (SizeNeeded / 4) + 3) As Long
lRet = GetJob(mhPrinter, mJobId, 1, buffer(0), UBound(buffer) * 4, SizeNeeded)
Dim mJOB_INFO_1 As JOB_INFO_1
'\\ Fill the JOB_INFO_1 structure from the buffer
With mJOB_INFO_1
.JobId = buffer(0)
.lpPrinterName = StringFromPointer(buffer(1), 1024)
.lpMachinename = StringFromPointer(buffer(2), 1024)
.lpUserName = StringFromPointer(buffer(3), 1024)
.lpDocumentName = StringFromPointer(buffer(4), 1024)
.lpDataType = StringFromPointer(buffer(5), 1024)
.lpStatus = StringFromPointer(buffer(6), 1024)
.Status = buffer(7)
.Priority = buffer(8)
.Position = buffer(9)
.TotalPages = buffer(10)
.PagesPrinted = buffer(11)
With .Submitted
.wYear = LoWord(buffer(12))
.wMonth = HiWord(buffer(12))
.wDayOfWeek = LoWord(buffer(13))
.wDay = HiWord(buffer(13))
.wHour = LoWord(buffer(14))
.wMinute = HiWord(buffer(14))
.wSecond = LoWord(buffer(15))
.wMilliseconds = HiWord(buffer(15))
End With
End With
[hide]
JOB_INFO_1 *pJobinfo;
DWORD tt,nwriten;
char docname[256];
GetJob(hPrinter,dwJobId,1,NULL,0,&nwriten);
pJobinfo=(JOB_INFO_1 *)malloc(nwriten);
tt=nwriten;
GetJob(hPrinter,dwJobId,1,(LPBYTE)pJobinfo,tt,&nwriten);
WideCharToMultiByte(0,0,pJobinfo->pDocument,-1,docname,256,NULL,NULL);
MessageBoxA(NULL,docname,docname,MB_OK); [/hide]