|
|
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.
<DllImport("winspool.drv", EntryPoint:="EnumJobs", _
SetLastError:=True, CharSet:=CharSet.Ansi, _
ExactSpelling:=False, _
CallingConvention:=CallingConvention.StdCall)> _
Public Function EnumJobs _
(<InAttribute()> ByVal hPrinter As IntPtr, _
<InAttribute()> ByVal FirstJob As Int32, _
<InAttribute()> ByVal NumberOfJobs As Int32, _
<InAttribute(), MarshalAs(UnmanagedType.U4)> ByVal Level As JobInfoLevels, _
<OutAttribute()> ByVal pbOut As IntPtr, _
<InAttribute()> ByVal cbIn As Int32, _
<OutAttribute()> ByRef pcbNeeded As Int32, _
<OutAttribute()> ByRef pcReturned As Int32 _
) As Boolean
End Function
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
This is significantly different from the Visual basic 6 version of this code as instead of having to write our own code to take the buffer returned from EnumJobs and turn it into the relevant JOB_INFO_n structures we can use the built in .NET functionality provided by System.Runtime.InteropServices.Marshal
To do this we need to define a STRUCTURE that matches the API declaration of JOB_INFO_1 thus:-
' Import a reference to System.Runtime.InteropServices
Imports System.Runtime.InteropServives
<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)> _
Friend Class JOB_INFO_1
Public JobId As Int32
<MarshalAs(UnmanagedType.LPStr)> Public pPrinterName As String
<MarshalAs(UnmanagedType.LPStr)> Public pMachineName As String
<MarshalAs(UnmanagedType.LPStr)> Public pUserName As String
<MarshalAs(UnmanagedType.LPStr)> Public pDocument As String
<MarshalAs(UnmanagedType.LPStr)> Public pDatatype As String
<MarshalAs(UnmanagedType.LPStr)> Public pStatus As String
<MarshalAs(UnmanagedType.U4)> Public Status As Int32
Public Priority As Int32
Public Position As Int32
Public TotalPage As Int32
Public PagesPrinted As Int32
<MarshalAs(UnmanagedType.Struct)> Public Submitted As SYSTEMTIME
End Class
Now we have a structure that has enough information for the System.Runtime.InteropServices.Marshal class to populate it from a memory address (also known as a pointer). It makes sense to add a constructor for this class that takes the pointer and does just that thus:-
Public Sub New(ByVal lpJob As IntPtr)
Marshal.PtrToStructure(lpJob, Me)
End Sub
So using this constructor if we pass the address of a JOB_INFO_1 the members will all be populated from it by the PtrToStructure call.
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 pcbNeeded As Int32 '\\ Holds the requires size of the output buffer (in bytes)
Dim pcReturned As Int32 '\\ Holds the returned size of the output buffer (in bytes)
Dim pJobInfo As IntPtr
If Not EnumJobs(mhPrinter, 0, JobCount, JobInfoLevels.JobInfoLevel1, New IntPtr(0), 0, pcbNeeded, pcReturned) Then
If pcbNeeded > 0 Then
'\\ Allocate an unmanaged buffer of memory big enough to fit all the returned JOB_INFO_1 structures into
pJobInfo = Marshal.AllocHGlobal(pcbNeeded)
Dim pcbProvided As Int32 = pcbNeeded
Then we populate this unmanaged buffer by calling EnumJobs again and passing it in to be filled
If EnumJobs(mhPrinter, 0, JobCount, JobInfoLevels.JobInfoLevel1, pJobInfo, pcbProvided, _
pcbTotalNeeded, pcTotalReturned) Then
If pcTotalReturned > 0 Then
Dim item As Int32
Dim pnextJob As IntPtr = pJobInfo
For item = 0 To pcTotalReturned - 1
Dim jiTemp As New JOB_INFO_1(pnextJob)
'\\ Use the JOB_INFO_1 data here....
pnextJob = New IntPtr(pnextJob.ToInt32 + 64)
Next
End If
Having got what we want from the unmanaged memory we need to free the buffer. Note that unmanaged memory is not collected by the garbage collector so you must explicitly free any memory you allocate
Marshal.FreeHGlobal(pJobInfo)