Symptoms
The EnumJobs function is available in the Winspool.drv print spool interface. You can use the EnumJobs function to retrieve the status of the jobs that are queued in a local print queue. The EnumJobs function enumerates the number of jobs, the job identification, the job status, and other parameters based on your requirements.
When you try to print any document, the print job is queued in the local print queue until the printer performs that job. The port monitor communicates with the printer to obtain information about a print job and then communicates the information to the local print queue. Therefore, you can monitor the local print queue to retrieve the status of your print job.
The local print queue accepts any number of jobs, even if the printer hardware is in an error state. Therefore, the Ready state of the print queue does not determine whether the job will print.Many statuses are available to report. However, many of them are not supported. The printer hardware and the port monitor determine the status that appears.
Resolution
This step-by-step article describes how to use Microsoft Windows API functions in Microsoft Visual Basic .NET to determine the printer status or the print job status programmatically. Although an application does not typically have to examine the status of a printer before the printer prints, it may be useful to determine the status of a printer or a print job programmatically.
Technical descriptionThe following information will help you use the sample application to obtain information about your printer status: The term printer refers to a hardware device, a queue, a driver, or a port. Here, the term printer status refers to the status of a local print queue.The sample code in the “Step-by-step sample” section returns the status that the operating system reports. This is the same status that the spooler reports. You can verify this status by monitoring the local print queue. The application continuously monitors the printer status.
To view the local print queue on a computer that is running the Microsoft Windows XP operating system, follow these steps: Click Start, point to Settings, and then click Printers and Faxes.In the Printers and Faxes window, double-click the icon for the printer whose queue you want to view. You cannot communicate directly with the physical printer. We recommend that you do not do this because the operating system controls access to the hardware. The “Step-by-step sample” section examines the local print queue that obtains the information from the port monitor.The port monitor communicates with the physical device. The sample code in the “Step-by-step sample” section reports the printer status and the job statuses.The queue is considered to be in a Ready state because it can accept jobs, even if the hardware is in an error state. For example, if the last job that printed used the last sheet of paper, the operating system cannot determine that the printer is out of paper until the system tries to print again.Although many statuses can be reported, many statuses are not supported in practice. The printer hardware and the port monitor determine the status to report. For example, if the printer is out of paper and is offline, the status may be reported as Printing because that is what the job is trying to do. Therefore, regardless of whether a local print queue displays the Ready status, the print job may not be completed successfully.The sample code in the “Step-by-step sample” section examines only the local print queue. This information may be sufficient for most applications. However, when you connect to remote printers, the process to obtain sufficient information may become complex. You may have a chain of print queues, and the port for the local print queue may be another queue. You may also use printer pooling. In printer pooling, multiple printers work from a common super queue. When the architecture becomes more complex, the code to retrieve a meaningful status also becomes more complex, and the usefulness of the status is reduced.
Step-by-step sampleStart Microsoft Visual Studio .NET. On the File menu, point to New, and then click Project. The New Project dialog box appears.Under Project Types, click Visual Basic Projects. Under Templates, click Windows Application.In the Name box, type PrinterStatus, and then click OK. By default, a form that is named Form1 is created.On the Project menu, click Add Module. The Add New Item – PrinterStatus dialog box appears. Under Templates, click Module, and then click Open. By default, a file that is named Module1.vb is created.In the Module1.vb file, replace the existing code with the following sample code.
Option Explicit On Imports System.Drawing.Printing.PrinterSettingsImports System.Runtime.InteropServicesModule Module1Public Class WINAPIDeclare Auto Function GetPrinter Lib “winspool.drv” (ByVal hPrinter As _IntPtr, ByVal Level As Integer, ByRef pPrinter As Byte, ByVal cbBuf _As Integer, ByRef pcbNeeded As Integer) As BooleanDeclare Auto Function lstrcpy Lib “Kernel32.Lib” Alias “lstrcpyA” _(<OutAttribute(), MarshalAs(UnmanagedType.LPStr)> ByVal lpString1 As String, _<MarshalAs(UnmanagedType.LPStr)> ByVal lpString2 As String) As LongDeclare Auto Function ClosePrinter Lib “winspool.drv” Alias “ClosePrinter” (ByVal hPrinter As IntPtr) As LongPublic Declare Function EnumJobs Lib “winspool.drv” Alias “EnumJobsA” _(ByVal hPrinter As IntPtr, _ByVal FirstJob As Int32, _ByVal NoJobs As Int32, _ByVal Level As Int32, _ByVal pJob As Byte(), _ByVal cdBuf As Int32, _ByRef pcbNeeded As Int32, _ByRef pcReturned As Int32) _As LongDeclare Function OpenPrinter Lib “winspool.drv” Alias “OpenPrinterA” (ByVal pPrinterName As String, _ByRef phPrinter As IntPtr, ByVal pDefault As PRINTER_DEFAULTS) As LongEnd Class’Constants for the PRINTER_DEFAULTS structurePublic Const PRINTER_ACCESS_USE = &H8Public Const PRINTER_ACCESS_ADMINISTER = &H4′Constants for the DEVMODE structurePublic Const CCHDEVICENAME = 32Public Const CCHFORMNAME = 32Public API As New WINAPI<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)> Structure SYSTEMTIMEPublic wYear As ShortPublic wMonth As ShortPublic wDayOfWeek As ShortPublic wDay As ShortPublic wHour As ShortPublic wMinute As ShortPublic wSecond As ShortPublic wMilliseconds As ShortEnd Structure<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)> Structure JOB_INFO_2Public PrinterJobId As IntegerPublic pPrinterName As IntegerPublic PrinterName As IntegerPublic PrinterUserName As IntegerPublic PrinterDocument As IntegerPublic PrinterNotifyName As IntegerPublic PrinterDatatype As IntegerPublic PrintProcessor As IntegerPublic PrinterParameters As IntegerPublic PrinterDriverName As IntegerPublic PrinterDevMode As IntegerPublic PrinterStatus As IntegerPublic PrinterSecurityDescriptor As IntegerPublic pStatus As IntegerPublic PrinterPriority As IntegerPublic Position As IntegerPublic StartTime As IntegerPublic UntilTime As IntegerPublic TotalPages As IntegerPublic Size As IntegerPublic Submitted As SYSTEMTIMEPublic time As IntegerPublic PagesPrinted As IntegerEnd Structure<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)> Structure PRINTER_INFO_2Public pServerName As IntegerPublic pPrinterName As IntegerPublic pShareName As IntegerPublic pPortName As IntegerPublic pDriverName As IntegerPublic pComment As IntegerPublic pLocation As IntegerPublic pDevMode As IntegerPublic pSepFile As IntegerPublic pPrintProcessor As IntegerPublic pDatatype As IntegerPublic pParameters As IntegerPublic pSecurityDescriptor As IntegerPublic Attributes As IntegerPublic Priority As IntegerPublic DefaultPriority As IntegerPublic StartTime As IntegerPublic UntilTime As IntegerPublic Status As IntegerPublic cJobs As IntegerPublic AveragePPM As IntegerEnd StructurePublic Function Pointer_to_String(ByVal Add As Long) As StringDim Temp_var As StringTemp_var = New String(CChar(“”), 512)Dim x As Longx = API.lstrcpy(Temp_var, Add)If (InStr(1, Temp_var, Chr(0)) = 0) ThenPointer_to_String = “”ElsePointer_to_String = Left(Temp_var, InStr(1, Temp_var, Chr(0)) – 1)End IfEnd FunctionPublic Function DatatoDeserial(ByVal datas() As Byte, ByVal type_to_change As Type, _ByVal NumJub As Long) As Object’Returns the size of the JOB_INFO_2 structureDim Data_to_Size As Long = Marshal.SizeOf(type_to_change)If Data_to_Size > datas.Length ThenReturn NothingEnd IfDim buffer As IntPtr = Marshal.AllocHGlobal(Data_to_Size)Dim startindex As LongDim i As IntegerFor i = 0 To NumJub – 1If i = 0 Thenstartindex = 0Elsestartindex = startindex + Data_to_SizeEnd IfNext’Copy data from the datas array to the unmanaged memory pointer.Marshal.Copy(datas, startindex, buffer, Data_to_Size)’Marshal data from the buffer pointer to a managed object.Dim result_obj As Object = Marshal.PtrToStructure(buffer, type_to_change)’Free the memory that is allocated from the unmanaged memory.Marshal.FreeHGlobal(buffer)Return result_objEnd Function<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)> _Structure PRINTER_DEFAULTSPublic pDatatype As StringPublic pDevMode As LongPublic DesiredAccess As LongEnd Structure’Define the printer status constants.Public Const ERROR_INSUFFICIENT_BUFFER = 122Public Const PRINTER_STATUS_BUSY = &H200Public Const PRINTER_STATUS_DOOR_OPEN = &H400000Public Const PRINTER_STATUS_ERROR = &H2Public Const PRINTER_STATUS_INITIALIZING = &H8000Public Const PRINTER_STATUS_IO_ACTIVE = &H100Public Const PRINTER_STATUS_MANUAL_FEED = &H20Public Const PRINTER_STATUS_NO_TONER = &H40000Public Const PRINTER_STATUS_NOT_AVAILABLE = &H1000Public Const PRINTER_STATUS_OFFLINE = &H80Public Const PRINTER_STATUS_OUT_OF_MEMORY = &H200000Public Const PRINTER_STATUS_OUTPUT_BIN_FULL = &H800Public Const PRINTER_STATUS_PAGE_PUNT = &H80000Public Const PRINTER_STATUS_PAPER_JAM = &H8Public Const PRINTER_STATUS_PAPER_OUT = &H10Public Const PRINTER_STATUS_PAPER_PROBLEM = &H40Public Const PRINTER_STATUS_PAUSED = &H1Public Const PRINTER_STATUS_PENDING_DELETION = &H4Public Const PRINTER_STATUS_PRINTING = &H400Public Const PRINTER_STATUS_PROCESSING = &H4000Public Const PRINTER_STATUS_TONER_LOW = &H20000Public Const PRINTER_STATUS_USER_INTERVENTION = &H100000Public Const PRINTER_STATUS_WAITING = &H2000Public Const PRINTER_STATUS_WARMING_UP = &H10000′Define the job status constants.Public Const JOB_STATUS_PAUSED = &H1Public Const JOB_STATUS_ERROR = &H2Public Const JOB_STATUS_DELETING = &H4Public Const JOB_STATUS_SPOOLING = &H8Public Const JOB_STATUS_PRINTING = &H10Public Const JOB_STATUS_OFFLINE = &H20Public Const JOB_STATUS_PAPEROUT = &H40Public Const JOB_STATUS_PRINTED = &H80Public Const JOB_STATUS_DELETED = &H100Public Const JOB_STATUS_BLOCKED_DEVQ = &H200Public Const JOB_STATUS_USER_INTERVENTION = &H400Public Const JOB_STATUS_RESTART = &H800Public Function GetString(ByVal PtrStr As Long) As StringDim StrBuff As StringStrBuff = New String(CChar(“”), 256)’Determine if a zero address is used.If PtrStr = 0 ThenGetString = ” “Exit FunctionEnd If’Copy data from PtrStr to the buffer.Dim PtrInt As IntPtr = New IntPtr(PtrStr)StrBuff = Marshal.PtrToStringAuto(PtrInt)’Remove any trailing nulls from the string.GetString = StripNulls(StrBuff)End FunctionPublic Function StripNulls(ByVal OriginalStr As String) As String’Remove any trailing nulls from the input string.If (InStr(OriginalStr, Chr(0)) > 0) ThenOriginalStr = Left(OriginalStr, InStr(OriginalStr, Chr(0)) – 1)End If’Return the modified string.StripNulls = OriginalStrEnd FunctionPublic Function CheckPrinterStatus(ByVal PI2Status As Long) As StringDim tempStr As StringIf PI2Status = 0 Then’ Return the “Ready” status.CheckPrinterStatus = “Printer Status = Ready” & vbCrLfElsetempStr = “”‘Determine the printer state.If (PI2Status And PRINTER_STATUS_BUSY) ThentempStr = tempStr & “Busy”End IfIf (PI2Status And PRINTER_STATUS_DOOR_OPEN) ThentempStr = tempStr & “Printer Door Open”End IfIf (PI2Status And PRINTER_STATUS_ERROR) ThentempStr = tempStr & “Printer Error”End IfIf (PI2Status And PRINTER_STATUS_INITIALIZING) ThentempStr = tempStr & “Initializing”End IfIf (PI2Status And PRINTER_STATUS_IO_ACTIVE) ThentempStr = tempStr & “I/O Active”End IfIf (PI2Status And PRINTER_STATUS_MANUAL_FEED) ThentempStr = tempStr & “Manual Feed”End IfIf (PI2Status And PRINTER_STATUS_NO_TONER) ThentempStr = tempStr & “No Toner”End IfIf (PI2Status And PRINTER_STATUS_NOT_AVAILABLE) ThentempStr = tempStr & “Not Available”End IfIf (PI2Status And PRINTER_STATUS_OFFLINE) ThentempStr = tempStr & “Off Line”End IfIf (PI2Status And PRINTER_STATUS_OUT_OF_MEMORY) ThentempStr = tempStr & “Out of Memory”End IfIf (PI2Status And PRINTER_STATUS_OUTPUT_BIN_FULL) ThentempStr = tempStr & “Output Bin Full”End IfIf (PI2Status And PRINTER_STATUS_PAGE_PUNT) ThentempStr = tempStr & “Page Punt”End IfIf (PI2Status And PRINTER_STATUS_PAPER_JAM) ThentempStr = tempStr & “Paper Jam”End IfIf (PI2Status And PRINTER_STATUS_PAPER_OUT) ThentempStr = tempStr & “Paper Out”End IfIf (PI2Status And PRINTER_STATUS_OUTPUT_BIN_FULL) ThentempStr = tempStr & “Output Bin Full”End IfIf (PI2Status And PRINTER_STATUS_PAPER_PROBLEM) ThentempStr = tempStr & “Page Problem”End IfIf (PI2Status And PRINTER_STATUS_PAUSED) ThentempStr = tempStr & “Paused”End IfIf (PI2Status And PRINTER_STATUS_PENDING_DELETION) ThentempStr = tempStr & “Pending Deletion”End IfIf (PI2Status And PRINTER_STATUS_PRINTING) ThentempStr = tempStr & “Printing”End IfIf (PI2Status And PRINTER_STATUS_PROCESSING) ThentempStr = tempStr & “Processing”End IfIf (PI2Status And PRINTER_STATUS_TONER_LOW) ThentempStr = tempStr & “Toner Low”End IfIf (PI2Status And PRINTER_STATUS_USER_INTERVENTION) ThentempStr = tempStr & “User Intervention”End IfIf (PI2Status And PRINTER_STATUS_WAITING) ThentempStr = tempStr & “Waiting”End IfIf (PI2Status And PRINTER_STATUS_WARMING_UP) ThentempStr = tempStr & “Warming Up”End IfIf Len(tempStr) = 0 ThentempStr = “Unknown Status of ” & PI2StatusEnd If’Return the status.CheckPrinterStatus = “Printer Status = ” & tempStr & vbCrLfEnd IfEnd FunctionEnd ModuleIn Solution Explorer, right-click the Form1.vb file, and then click View Code.In the Form1.vb file, replace the existing code with the following sample code.
Imports System.Diagnostics.DebugImports System.Drawing.PrintingImports System.Runtime.InteropServicesPublic Class Form1Inherits System.Windows.Forms.Form#Region ” Windows Form Designer generated code “Public Sub New()MyBase.New()’This call is required by the Windows Form Designer.InitializeComponent()’Add any initialization after the InitializeComponent() callEnd Sub’Form overrides dispose to clean up the component list.Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)If disposing ThenIf Not (components Is Nothing) Thencomponents.Dispose()End IfEnd IfMyBase.Dispose(disposing)End Sub’Required by the Windows Form DesignerPrivate components As System.ComponentModel.IContainer’NOTE: The following procedure is required by the Windows Form Designer’It can be modified using the Windows Form Designer.’Do not modify it using the code editor.Friend WithEvents TextBox1 As System.Windows.Forms.TextBoxFriend WithEvents TextBox2 As System.Windows.Forms.TextBoxFriend WithEvents Command1 As System.Windows.Forms.ButtonFriend WithEvents Command2 As System.Windows.Forms.ButtonFriend WithEvents Command3 As System.Windows.Forms.ButtonFriend WithEvents Timer1 As System.Windows.Forms.TimerFriend WithEvents TextBox3 As System.Windows.Forms.TextBox<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()Me.components = New System.ComponentModel.ContainerMe.Command1 = New System.Windows.Forms.ButtonMe.Command2 = New System.Windows.Forms.ButtonMe.Command3 = New System.Windows.Forms.ButtonMe.TextBox1 = New System.Windows.Forms.TextBoxMe.TextBox2 = New System.Windows.Forms.TextBoxMe.Timer1 = New System.Windows.Forms.Timer(Me.components)Me.TextBox3 = New System.Windows.Forms.TextBoxMe.SuspendLayout()”Command1′Me.Command1.Location = New System.Drawing.Point(352, 24)Me.Command1.Name = “Command1″Me.Command1.Size = New System.Drawing.Size(136, 23)Me.Command1.TabIndex = 0Me.Command1.Text = “Button1″”Command2′Me.Command2.Location = New System.Drawing.Point(360, 112)Me.Command2.Name = “Command2″Me.Command2.Size = New System.Drawing.Size(128, 23)Me.Command2.TabIndex = 1Me.Command2.Text = “Button2″”Command3′Me.Command3.Location = New System.Drawing.Point(360, 224)Me.Command3.Name = “Command3″Me.Command3.Size = New System.Drawing.Size(128, 23)Me.Command3.TabIndex = 2Me.Command3.Text = “Button3″”TextBox1′Me.TextBox1.Location = New System.Drawing.Point(8, 24)Me.TextBox1.Multiline = TrueMe.TextBox1.Name = “TextBox1″Me.TextBox1.Size = New System.Drawing.Size(320, 80)Me.TextBox1.TabIndex = 3Me.TextBox1.Text = “TextBox1″”TextBox2′Me.TextBox2.Location = New System.Drawing.Point(8, 120)Me.TextBox2.Multiline = TrueMe.TextBox2.Name = “TextBox2″Me.TextBox2.Size = New System.Drawing.Size(320, 80)Me.TextBox2.TabIndex = 4Me.TextBox2.Text = “TextBox2″”Timer1′Me.Timer1.Enabled = True”TextBox3′Me.TextBox3.Location = New System.Drawing.Point(8, 216)Me.TextBox3.Multiline = TrueMe.TextBox3.Name = “TextBox3″Me.TextBox3.ScrollBars = System.Windows.Forms.ScrollBars.VerticalMe.TextBox3.Size = New System.Drawing.Size(320, 80)Me.TextBox3.TabIndex = 5Me.TextBox3.Text = “TextBox3″”Form1′Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)Me.ClientSize = New System.Drawing.Size(504, 317)Me.Controls.Add(Me.TextBox3)Me.Controls.Add(Me.TextBox2)Me.Controls.Add(Me.TextBox1)Me.Controls.Add(Me.Command3)Me.Controls.Add(Me.Command2)Me.Controls.Add(Me.Command1)Me.Name = “Form1″Me.Text = “Form1″Me.ResumeLayout(False)End Sub#End RegionPublic Shared Sub main()Dim PrntInfo As New Form1PrntInfo.ShowDialog()End SubPrivate Sub Command1_Click(ByVal sender As System.Object, _ByVal e As System.EventArgs) Handles Command1.Click’Enable the timer to start printer status checks.Timer1.Enabled = TrueTimer1.Start()’Enable and disable the start and stop buttons.Command1.Enabled = FalseCommand2.Enabled = TrueCommand3.Enabled = TrueEnd SubPrivate Sub Command2_Click(ByVal sender As System.Object, _ByVal e As System.EventArgs) Handles Command2.Click’Disable the timer to stop additional printer checks.Timer1.Enabled = False’Enable and disable the start and stop buttons.Command1.Enabled = TrueCommand2.Enabled = FalseCommand3.Enabled = TrueAPI = NothingEnd SubPrivate Sub Command3_Click(ByVal sender As System.Object, _ByVal e As System.EventArgs) Handles Command3.Click’Clear the text boxes to display the printer status.TextBox1.Text = “”TextBox2.Text = “”TextBox3.Text = “”End SubPrivate Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) _Handles MyBase.Load’Initialize captions for the buttons.Command1.Text = “Start”Command2.Text = “Stop”Command3.Text = “Clear”‘Clear the text boxes to display the printer status.TextBox1.Text = “”TextBox2.Text = “”TextBox3.Text = “”Command1.Enabled = True’Disable the stop and clear buttons.Command2.Enabled = FalseCommand3.Enabled = False’Set the timer interval to 500 milliseconds to examine the printer status.Timer1.Enabled = FalseTimer1.Interval = 500End SubPrivate Sub Timer1_Tick(ByVal sender As System.Object, _ByVal e As System.EventArgs) Handles Timer1.TickDim PrinterStatus As StringDim JobStatus As StringDim ErrorInfo As String’Clear the text boxes to display the status.TextBox1.Text = “”TextBox2.Text = “”TextBox3.Text = “”‘Call the CheckPrinter function.TextBox1.Text = CheckPrinter(PrinterStatus, JobStatus)TextBox2.Text = PrinterStatusTextBox3.Text = JobStatusEnd SubPublic Function CheckPrinter(ByRef PrinterStr As String, _ByRef JobStr As String) As StringDim hPrinter As IntPtrDim ByteBuf As LongDim BytesNeeded As Int32Dim PI2 As New PRINTER_INFO_2Dim intCount As LongDim JI2(intCount) As JOB_INFO_2Dim PrinterInfo() As ByteDim JobInfo() As ByteDim result As LongDim LastError As LongDim PrinterName As StringDim tempStr As StringDim NumJI2 As Int32Dim pDefaults As PRINTER_DEFAULTS’Set a default return value if no errors occur.CheckPrinter = “Printer info retrieved!”Dim PD As New PrintDocumentPrinterName = PD.PrinterSettings.PrinterName’Set the access security setting that you want.pDefaults.DesiredAccess = PRINTER_ACCESS_USE’Call the API to obtain a handle to the printer.’If an error occurs, display the error.result = API.OpenPrinter(PrinterName, hPrinter, pDefaults)If result = 0 ThenCheckPrinter = “Cannot open printer ” & PrinterName & _”, Error: ” & Marshal.GetLastWin32Error()Exit FunctionEnd If’Initialize the BytesNeeded variable.BytesNeeded = 0′Clear the error object.Err.Clear()’Determine the buffer size that is required to obtain the printer information.result = API.GetPrinter(hPrinter, 2, 0&, 0, BytesNeeded)’Display the error message that you receive when you call the GetPrinter function,’and then close the printer handle.If Marshal.GetLastWin32Error() <> ERROR_INSUFFICIENT_BUFFER ThenCheckPrinter = ” > GetPrinter Failed on initial call! <”API.ClosePrinter(hPrinter)Exit FunctionEnd IfReDim PrinterInfo(BytesNeeded)ByteBuf = BytesNeeded’Call the GetPrinter function to obtain the status.result = API.GetPrinter(hPrinter, 2, PrinterInfo(0), ByteBuf, _BytesNeeded)’Check for any errors.If result = 0 Then’Get the error.LastError = Marshal.GetLastWin32Error()’Display the error message, and then close the printer handle.CheckPrinter = “Could not get Printer Status!Error = ” _& LastErrorAPI.ClosePrinter(hPrinter)Exit FunctionEnd If’Copy the contents of the printer status byte array into a’PRINTER_INFO_2 structure.PI2 = CType(DatatoDeserial(PrinterInfo, GetType(PRINTER_INFO_2), 1), PRINTER_INFO_2)PrinterStr = CheckPrinterStatus(PI2.Status)’Add the printer name, the driver, and the port to the text box.PrinterStr = PrinterStr & “Printer Name = ” & _GetString(PI2.pPrinterName) & vbCrLfPrinterStr = PrinterStr & “Printer Driver Name = ” & _GetString(PI2.pDriverName) & vbCrLfPrinterStr = PrinterStr & “Printer Port Name = ” & _GetString(PI2.pPortName) & vbCrLf’Call the API to obtain the buffer size that is required.result = API.EnumJobs(hPrinter, 0, &HFFFFFFFF, 2, JobInfo, 0, BytesNeeded, NumJI2)If result = 0 Then’Display the error, and then close the printer handle.LastError = Marshal.GetLastWin32Error()CheckPrinter = ” > EnumJobs Failed on initial call! <Error = ” _& LastErrorAPI.ClosePrinter(hPrinter)Exit FunctionEnd If’If no current jobs exist, display the message.If BytesNeeded = 0 ThenJobStr = “No Print Jobs!”Else’Resize the byte array to hold information about the print jobs.ReDim JobInfo(BytesNeeded – 1)’Call the API to obtain the print job information.result = API.EnumJobs(hPrinter, 0, &HFFFFFFFF, 2, JobInfo, _BytesNeeded, BytesNeeded, NumJI2)’Check for errors.If result = 0 Then’Display the error, and then close the printer handle.LastError = Marshal.GetLastWin32Error()CheckPrinter = ” > EnumJobs Failed on second call! <Error = ” _& LastErrorAPI.ClosePrinter(hPrinter)Exit FunctionEnd IfReDim JI2(NumJI2)’Copy the contents of print job info byte array into a’JOB_INFO_2 structure.TryFor intCount = 0 To NumJI2 – 1 ‘ Loop through jobs and obtain the job information.Dim test As ObjectJI2(intCount) = CType(DatatoDeserial(JobInfo, _GetType(JOB_INFO_2), intCount + 1), JOB_INFO_2)JobStr = JobStr & “Job ID = ” & JI2(intCount).PrinterJobId & _vbCrLf & “Total Pages = ” & JI2(intCount).TotalPages & vbCrLftempStr = “”‘Check for a ready state.If JI2(intCount).pStatus = 0& Then’ If pStatus is Null, check Status.If JI2(intCount).pStatus = 0 ThentempStr = tempStr & “Ready!” & vbCrLfElse’Check for the various print job states.If (JI2(intCount).pStatus And JOB_STATUS_SPOOLING) ThentempStr = tempStr & “Spooling”End IfIf (JI2(intCount).pStatus And JOB_STATUS_OFFLINE) ThentempStr = tempStr & “Off line”End IfIf (JI2(intCount).pStatus And JOB_STATUS_PAUSED) ThentempStr = tempStr & “Paused”End IfIf (JI2(intCount).pStatus And JOB_STATUS_ERROR) ThentempStr = tempStr & “Error”End IfIf (JI2(intCount).pStatus And JOB_STATUS_PAPEROUT) ThentempStr = tempStr & “Paper Out”End IfIf (JI2(intCount).pStatus And JOB_STATUS_PRINTING) ThentempStr = tempStr & “Printing”End IfIf (JI2(intCount).pStatus And JOB_STATUS_USER_INTERVENTION) ThentempStr = tempStr & “User Intervention Needed”End IfIf Len(tempStr) = 0 ThentempStr = “Unknown Status of ” & JI2(intCount).PrinterStatusEnd IfEnd IfElsetempStr = Pointer_to_String(JI2(intCount).pStatus)End If’Report the job status.JobStr = JobStr & tempStr & vbCrLfNext intCountCatch ex As ExceptionMessageBox.Show(ex.Message)End TryEnd If’Close the printer handle.API.ClosePrinter(hPrinter)End FunctionEnd ClassOn the Build menu, click Build Solution.Click Start, and then click Printers and Faxes.
Note On a computer that is running Microsoft Windows 2000, click Start, point to Settings, and then click Printers.In the Printers and Faxes window, double-click the icon for the printer whose queue you want to view.
Note In the Printers window on a computer that is running Windows 2000, double-click the icon for the printer whose queue you want to view.On the Printer menu in the PrinterName dialog box, click Pause Printing.
Note You may not be able to pause the print queue on a network printer.On the Debug menu in Visual Studio .NET, click Start to run the application.In the Form1 form, click Start to obtain the printer information and the list of jobs in the queue.
TroubleshootingOnly a specific device driver can obtain accurate printer status information. This sample code obtains the same status that the Windows spooler reports.
The exact status that is reported may vary for different printers and for different drivers.