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
End If