Microsoft KB Archive/821769

From BetaArchive Wiki

Article ID: 821769

Article Last Modified on 5/16/2007



APPLIES TO

  • Microsoft Visual Basic .NET 2003 Standard Edition
  • Microsoft Visual Basic .NET 2002 Standard Edition




SUMMARY

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.


INTRODUCTION

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.

back to the top

Technical description

The 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:

    1. Click Start, point to Settings, and then click Printers and Faxes.
    2. 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.

back to the top

Step-by-step sample

  1. Start Microsoft Visual Studio .NET.
  2. On the File menu, point to New, and then click Project. The New Project dialog box appears.
  3. Under Project Types, click Visual Basic Projects.
  4. Under Templates, click Windows Application.
  5. In the Name box, type PrinterStatus, and then click OK. By default, a form that is named Form1 is created.
  6. On the Project menu, click Add Module. The Add New Item - PrinterStatus dialog box appears.
  7. Under Templates, click Module, and then click Open. By default, a file that is named Module1.vb is created.
  8. In the Module1.vb file, replace the existing code with the following sample code.

    Option Explicit On 
    
    Imports System.Drawing.Printing.PrinterSettings
    Imports System.Runtime.InteropServices
    
    Module Module1
        Public Class WINAPI
    
            Declare 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 Boolean
    
            Declare Auto Function lstrcpy Lib "Kernel32.Lib" Alias "lstrcpyA" _
            (<OutAttribute(), MarshalAs(UnmanagedType.LPStr)> ByVal lpString1 As String, _
            <MarshalAs(UnmanagedType.LPStr)> ByVal lpString2 As String) As Long
    
            Declare Auto Function ClosePrinter Lib "winspool.drv" Alias "ClosePrinter" (ByVal hPrinter As IntPtr) As Long
    
            Public 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 Long
    
            Declare Function OpenPrinter Lib "winspool.drv" Alias "OpenPrinterA" (ByVal pPrinterName As String, _
            ByRef phPrinter As IntPtr, ByVal pDefault As PRINTER_DEFAULTS) As Long
        End Class
    
        'Constants for the PRINTER_DEFAULTS structure
        Public Const PRINTER_ACCESS_USE = &H8
        Public Const PRINTER_ACCESS_ADMINISTER = &H4
    
        'Constants for the DEVMODE structure
        Public Const CCHDEVICENAME = 32
        Public Const CCHFORMNAME = 32
        Public API As New WINAPI
        <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)> Structure SYSTEMTIME
            Public wYear As Short
            Public wMonth As Short
            Public wDayOfWeek As Short
            Public wDay As Short
            Public wHour As Short
            Public wMinute As Short
            Public wSecond As Short
            Public wMilliseconds As Short
        End Structure
    
        <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)> Structure JOB_INFO_2
            Public PrinterJobId As Integer
            Public pPrinterName As Integer
            Public PrinterName As Integer
            Public PrinterUserName As Integer
            Public PrinterDocument As Integer
            Public PrinterNotifyName As Integer
            Public PrinterDatatype As Integer
            Public PrintProcessor As Integer
            Public PrinterParameters As Integer
            Public PrinterDriverName As Integer
            Public PrinterDevMode As Integer
            Public PrinterStatus As Integer
            Public PrinterSecurityDescriptor As Integer
            Public pStatus As Integer
            Public PrinterPriority As Integer
            Public Position As Integer
            Public StartTime As Integer
            Public UntilTime As Integer
            Public TotalPages As Integer
            Public Size As Integer
            Public Submitted As SYSTEMTIME
            Public time As Integer
            Public PagesPrinted As Integer
        End Structure
    
        <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)> Structure PRINTER_INFO_2
            Public pServerName As Integer
            Public pPrinterName As Integer
            Public pShareName As Integer
            Public pPortName As Integer
            Public pDriverName As Integer
            Public pComment As Integer
            Public pLocation As Integer
            Public pDevMode As Integer
            Public pSepFile As Integer
            Public pPrintProcessor As Integer
            Public pDatatype As Integer
            Public pParameters As Integer
            Public pSecurityDescriptor As Integer
            Public Attributes As Integer
            Public Priority As Integer
            Public DefaultPriority As Integer
            Public StartTime As Integer
            Public UntilTime As Integer
            Public Status As Integer
            Public cJobs As Integer
            Public AveragePPM As Integer
        End Structure
    
        Public Function Pointer_to_String(ByVal Add As Long) As String
            Dim Temp_var As String
            Temp_var = New String(CChar(""), 512)
            Dim x As Long
            x = API.lstrcpy(Temp_var, Add)
            If (InStr(1, Temp_var, Chr(0)) = 0) Then
                Pointer_to_String = ""
            Else
                Pointer_to_String = Left(Temp_var, InStr(1, Temp_var, Chr(0)) - 1)
            End If
        End Function
    
        Public 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 structure
            Dim Data_to_Size As Long = Marshal.SizeOf(type_to_change)
            If Data_to_Size > datas.Length Then
                Return Nothing
            End If
    
            Dim buffer As IntPtr = Marshal.AllocHGlobal(Data_to_Size)
            Dim startindex As Long
            Dim i As Integer
            For i = 0 To NumJub - 1
                If i = 0 Then
                    startindex = 0
                Else
                    startindex = startindex + Data_to_Size
                End If
            Next
            '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_obj
        End Function
    
        <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)> _
        Structure PRINTER_DEFAULTS
            Public pDatatype As String
            Public pDevMode As Long
            Public DesiredAccess As Long
        End Structure
    
        'Define the printer status constants.
        Public Const ERROR_INSUFFICIENT_BUFFER = 122
        Public Const PRINTER_STATUS_BUSY = &H200
        Public Const PRINTER_STATUS_DOOR_OPEN = &H400000
        Public Const PRINTER_STATUS_ERROR = &H2
        Public Const PRINTER_STATUS_INITIALIZING = &H8000
        Public Const PRINTER_STATUS_IO_ACTIVE = &H100
        Public Const PRINTER_STATUS_MANUAL_FEED = &H20
        Public Const PRINTER_STATUS_NO_TONER = &H40000
        Public Const PRINTER_STATUS_NOT_AVAILABLE = &H1000
        Public Const PRINTER_STATUS_OFFLINE = &H80
        Public Const PRINTER_STATUS_OUT_OF_MEMORY = &H200000
        Public Const PRINTER_STATUS_OUTPUT_BIN_FULL = &H800
        Public Const PRINTER_STATUS_PAGE_PUNT = &H80000
        Public Const PRINTER_STATUS_PAPER_JAM = &H8
        Public Const PRINTER_STATUS_PAPER_OUT = &H10
        Public Const PRINTER_STATUS_PAPER_PROBLEM = &H40
        Public Const PRINTER_STATUS_PAUSED = &H1
        Public Const PRINTER_STATUS_PENDING_DELETION = &H4
        Public Const PRINTER_STATUS_PRINTING = &H400
        Public Const PRINTER_STATUS_PROCESSING = &H4000
        Public Const PRINTER_STATUS_TONER_LOW = &H20000
        Public Const PRINTER_STATUS_USER_INTERVENTION = &H100000
        Public Const PRINTER_STATUS_WAITING = &H2000
        Public Const PRINTER_STATUS_WARMING_UP = &H10000
        'Define the job status constants.
        Public Const JOB_STATUS_PAUSED = &H1
        Public Const JOB_STATUS_ERROR = &H2
        Public Const JOB_STATUS_DELETING = &H4
        Public Const JOB_STATUS_SPOOLING = &H8
        Public Const JOB_STATUS_PRINTING = &H10
        Public Const JOB_STATUS_OFFLINE = &H20
        Public Const JOB_STATUS_PAPEROUT = &H40
        Public Const JOB_STATUS_PRINTED = &H80
        Public Const JOB_STATUS_DELETED = &H100
        Public Const JOB_STATUS_BLOCKED_DEVQ = &H200
        Public Const JOB_STATUS_USER_INTERVENTION = &H400
        Public Const JOB_STATUS_RESTART = &H800
    
        Public Function GetString(ByVal PtrStr As Long) As String
            Dim StrBuff As String
            StrBuff = New String(CChar(""), 256)
            'Determine if a zero address is used.
            If PtrStr = 0 Then
                GetString = " "
                Exit Function
            End 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 Function
    
        Public Function StripNulls(ByVal OriginalStr As String) As String
    
            'Remove any trailing nulls from the input string.
            If (InStr(OriginalStr, Chr(0)) > 0) Then
                OriginalStr = Left(OriginalStr, InStr(OriginalStr, Chr(0)) - 1)
            End If
            'Return the modified string.
            StripNulls = OriginalStr
        End Function
    
        Public Function CheckPrinterStatus(ByVal PI2Status As Long) As String
            Dim tempStr As String
            If PI2Status = 0 Then   ' Return the "Ready" status.
                CheckPrinterStatus = "Printer Status = Ready" & vbCrLf
            Else
                tempStr = ""
                'Determine the printer state.
                If (PI2Status And PRINTER_STATUS_BUSY) Then
                    tempStr = tempStr & "Busy  "
                End If
                If (PI2Status And PRINTER_STATUS_DOOR_OPEN) Then
                    tempStr = tempStr & "Printer Door Open  "
                End If
                If (PI2Status And PRINTER_STATUS_ERROR) Then
                    tempStr = tempStr & "Printer Error  "
                End If
                If (PI2Status And PRINTER_STATUS_INITIALIZING) Then
                    tempStr = tempStr & "Initializing  "
                End If
                If (PI2Status And PRINTER_STATUS_IO_ACTIVE) Then
                    tempStr = tempStr & "I/O Active  "
                End If
                If (PI2Status And PRINTER_STATUS_MANUAL_FEED) Then
                    tempStr = tempStr & "Manual Feed  "
                End If
                If (PI2Status And PRINTER_STATUS_NO_TONER) Then
                    tempStr = tempStr & "No Toner  "
                End If
                If (PI2Status And PRINTER_STATUS_NOT_AVAILABLE) Then
                    tempStr = tempStr & "Not Available  "
                End If
                If (PI2Status And PRINTER_STATUS_OFFLINE) Then
                    tempStr = tempStr & "Off Line  "
                End If
                If (PI2Status And PRINTER_STATUS_OUT_OF_MEMORY) Then
                    tempStr = tempStr & "Out of Memory  "
                End If
                If (PI2Status And PRINTER_STATUS_OUTPUT_BIN_FULL) Then
                    tempStr = tempStr & "Output Bin Full  "
                End If
                If (PI2Status And PRINTER_STATUS_PAGE_PUNT) Then
                    tempStr = tempStr & "Page Punt  "
                End If
                If (PI2Status And PRINTER_STATUS_PAPER_JAM) Then
                    tempStr = tempStr & "Paper Jam  "
                End If
                If (PI2Status And PRINTER_STATUS_PAPER_OUT) Then
                    tempStr = tempStr & "Paper Out  "
                End If
                If (PI2Status And PRINTER_STATUS_OUTPUT_BIN_FULL) Then
                    tempStr = tempStr & "Output Bin Full  "
                End If
                If (PI2Status And PRINTER_STATUS_PAPER_PROBLEM) Then
                    tempStr = tempStr & "Page Problem  "
                End If
                If (PI2Status And PRINTER_STATUS_PAUSED) Then
                    tempStr = tempStr & "Paused  "
                End If
                If (PI2Status And PRINTER_STATUS_PENDING_DELETION) Then
                    tempStr = tempStr & "Pending Deletion  "
                End If
                If (PI2Status And PRINTER_STATUS_PRINTING) Then
                    tempStr = tempStr & "Printing  "
                End If
                If (PI2Status And PRINTER_STATUS_PROCESSING) Then
                    tempStr = tempStr & "Processing  "
                End If
                If (PI2Status And PRINTER_STATUS_TONER_LOW) Then
                    tempStr = tempStr & "Toner Low  "
                End If
                If (PI2Status And PRINTER_STATUS_USER_INTERVENTION) Then
                    tempStr = tempStr & "User Intervention  "
                End If
                If (PI2Status And PRINTER_STATUS_WAITING) Then
                    tempStr = tempStr & "Waiting  "
                End If
                If (PI2Status And PRINTER_STATUS_WARMING_UP) Then
                    tempStr = tempStr & "Warming Up  "
                End If
                If Len(tempStr) = 0 Then
                    tempStr = "Unknown Status of " & PI2Status
                End If
                'Return the status.
                CheckPrinterStatus = "Printer Status = " & tempStr & vbCrLf
            End If
        End Function
    End Module
  9. In Solution Explorer, right-click the Form1.vb file, and then click View Code.
  10. In the Form1.vb file, replace the existing code with the following sample code.

    Imports System.Diagnostics.Debug
    Imports System.Drawing.Printing
    Imports System.Runtime.InteropServices
    
    Public Class Form1
        Inherits 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() call
        End Sub
    
        'Form overrides dispose to clean up the component list.
        Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
            If disposing Then
                If Not (components Is Nothing) Then
                    components.Dispose()
                End If
            End If
            MyBase.Dispose(disposing)
        End Sub
    
        'Required by the Windows Form Designer
        Private 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.TextBox
        Friend WithEvents TextBox2 As System.Windows.Forms.TextBox
        Friend WithEvents Command1 As System.Windows.Forms.Button
        Friend WithEvents Command2 As System.Windows.Forms.Button
        Friend WithEvents Command3 As System.Windows.Forms.Button
        Friend WithEvents Timer1 As System.Windows.Forms.Timer
        Friend WithEvents TextBox3 As System.Windows.Forms.TextBox
        <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
            Me.components = New System.ComponentModel.Container
            Me.Command1 = New System.Windows.Forms.Button
            Me.Command2 = New System.Windows.Forms.Button
            Me.Command3 = New System.Windows.Forms.Button
            Me.TextBox1 = New System.Windows.Forms.TextBox
            Me.TextBox2 = New System.Windows.Forms.TextBox
            Me.Timer1 = New System.Windows.Forms.Timer(Me.components)
            Me.TextBox3 = New System.Windows.Forms.TextBox
            Me.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 = 0
            Me.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 = 1
            Me.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 = 2
            Me.Command3.Text = "Button3"
            '
            'TextBox1
            '
            Me.TextBox1.Location = New System.Drawing.Point(8, 24)
            Me.TextBox1.Multiline = True
            Me.TextBox1.Name = "TextBox1"
            Me.TextBox1.Size = New System.Drawing.Size(320, 80)
            Me.TextBox1.TabIndex = 3
            Me.TextBox1.Text = "TextBox1"
            '
            'TextBox2
            '
            Me.TextBox2.Location = New System.Drawing.Point(8, 120)
            Me.TextBox2.Multiline = True
            Me.TextBox2.Name = "TextBox2"
            Me.TextBox2.Size = New System.Drawing.Size(320, 80)
            Me.TextBox2.TabIndex = 4
            Me.TextBox2.Text = "TextBox2"
            '
            'Timer1
            '
            Me.Timer1.Enabled = True
            '
            'TextBox3
            '
            Me.TextBox3.Location = New System.Drawing.Point(8, 216)
            Me.TextBox3.Multiline = True
            Me.TextBox3.Name = "TextBox3"
            Me.TextBox3.ScrollBars = System.Windows.Forms.ScrollBars.Vertical
            Me.TextBox3.Size = New System.Drawing.Size(320, 80)
            Me.TextBox3.TabIndex = 5
            Me.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 Region
    
        Public Shared Sub main()
            Dim PrntInfo As New Form1
            PrntInfo.ShowDialog()
        End Sub
    
        Private 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 = True
            Timer1.Start()
    
            'Enable and disable the start and stop buttons.
            Command1.Enabled = False
            Command2.Enabled = True
            Command3.Enabled = True
        End Sub
    
        Private 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 = True
            Command2.Enabled = False
            Command3.Enabled = True
            API = Nothing
        End Sub
    
        Private 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 Sub
    
        Private 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 = False
            Command3.Enabled = False
    
            'Set the timer interval to 500 milliseconds to examine the printer status.
            Timer1.Enabled = False
            Timer1.Interval = 500
        End Sub
    
        Private Sub Timer1_Tick(ByVal sender As System.Object, _
            ByVal e As System.EventArgs) Handles Timer1.Tick
            Dim PrinterStatus As String
            Dim JobStatus As String
            Dim 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 = PrinterStatus
            TextBox3.Text = JobStatus
        End Sub
    
        Public Function CheckPrinter(ByRef PrinterStr As String, _
            ByRef JobStr As String) As String
            Dim hPrinter As IntPtr
            Dim ByteBuf As Long
            Dim BytesNeeded As Int32
            Dim PI2 As New PRINTER_INFO_2
            Dim intCount As Long
            Dim JI2(intCount) As JOB_INFO_2
            Dim PrinterInfo() As Byte
            Dim JobInfo() As Byte
            Dim result As Long
            Dim LastError As Long
            Dim PrinterName As String
            Dim tempStr As String
            Dim NumJI2 As Int32
            Dim pDefaults As PRINTER_DEFAULTS
    
            'Set a default return value if no errors occur.
            CheckPrinter = "Printer info retrieved!"
    
            Dim PD As New PrintDocument
            PrinterName = 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 Then
    
                CheckPrinter = "Cannot open printer " & PrinterName & _
                   ", Error: " & Marshal.GetLastWin32Error()
                Exit Function
            End 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 Then
                CheckPrinter = " > GetPrinter Failed on initial call! <"
                API.ClosePrinter(hPrinter)
                Exit Function
            End If
            ReDim 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 = " _
                   & LastError
                API.ClosePrinter(hPrinter)
                Exit Function
            End 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) & vbCrLf
            PrinterStr = PrinterStr & "Printer Driver Name = " & _
              GetString(PI2.pDriverName) & vbCrLf
            PrinterStr = 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 = " _
                   & LastError
                API.ClosePrinter(hPrinter)
                Exit Function
            End If
    
            'If no current jobs exist, display the message.
            If BytesNeeded = 0 Then
                JobStr = "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 = " _
                       & LastError
                    API.ClosePrinter(hPrinter)
                    Exit Function
                End If
                ReDim JI2(NumJI2)
    
                'Copy the contents of print job info byte array into a
                'JOB_INFO_2 structure.
                Try
                    For intCount = 0 To NumJI2 - 1 ' Loop through jobs and obtain the job information.
                        Dim test As Object
                        JI2(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 & vbCrLf
    
                        tempStr = ""
                        'Check for a ready state.
                        If JI2(intCount).pStatus = 0& Then   ' If pStatus is Null, check Status.
                            If JI2(intCount).pStatus = 0 Then
                                tempStr = tempStr & "Ready!  " & vbCrLf
                            Else  'Check for the various print job states.
                                If (JI2(intCount).pStatus And JOB_STATUS_SPOOLING) Then
                                    tempStr = tempStr & "Spooling  "
                                End If
    
                                If (JI2(intCount).pStatus And JOB_STATUS_OFFLINE) Then
                                    tempStr = tempStr & "Off line  "
                                End If
    
                                If (JI2(intCount).pStatus And JOB_STATUS_PAUSED) Then
                                    tempStr = tempStr & "Paused  "
                                End If
    
                                If (JI2(intCount).pStatus And JOB_STATUS_ERROR) Then
                                    tempStr = tempStr & "Error  "
                                End If
    
                                If (JI2(intCount).pStatus And JOB_STATUS_PAPEROUT) Then
                                    tempStr = tempStr & "Paper Out  "
                                End If
    
                                If (JI2(intCount).pStatus And JOB_STATUS_PRINTING) Then
                                    tempStr = tempStr & "Printing  "
                                End If
    
                                If (JI2(intCount).pStatus And JOB_STATUS_USER_INTERVENTION) Then
                                    tempStr = tempStr & "User Intervention Needed  "
                                End If
    
                                If Len(tempStr) = 0 Then
                                    tempStr = "Unknown Status of " & JI2(intCount).PrinterStatus
                                End If
                            End If
                        Else
    
                            tempStr = Pointer_to_String(JI2(intCount).pStatus)
                        End If
    
                        'Report the job status.
                        JobStr = JobStr & tempStr & vbCrLf
                    Next intCount
                Catch ex As Exception
                    MessageBox.Show(ex.Message)
                End Try
            End If
            'Close the printer handle.
            API.ClosePrinter(hPrinter)
        End Function
    End Class
  11. On the Build menu, click Build Solution.
  12. 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.
  13. 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.
  14. 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.
  15. On the Debug menu in Visual Studio .NET, click Start to run the application.
  16. In the Form1 form, click Start to obtain the printer information and the list of jobs in the queue.

back to the top

Troubleshooting

Only 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.

back to the top

REFERENCES

For more information about the PrinterSettings class, visit the following Microsoft Developer Network (MSDN) Web site:

For more information, click the following article number to view the article in the Microsoft Knowledge Base:

202480 How to determine printer status and print job status from Visual Basic


back to the top

Keywords: kbvs2005doesnotapply kbvs2005swept kbprinters kbmarshal kbinterop kbhowtomaster KB821769