Determining Printer Behavior with WMI and Visual Studio 2012

CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Introduction

Imagine this scenario: Your program’s duty is to print a lot of reports and charts, and relies heavily on printers. Now, imagine further that your program cannot determine whether or not a printer is working or is having some kind of paper jam. This scenario can be quite embarrassing for you or your company whenever this situation arises and you have not compensated for that.

This article will explain how to use WMI with VB.NET and C# to determine local and remote printer activity.

WMI

If you have missed my previous article entitled “Getting Hard disk Information with WMI and Visual Studio 2012” in which I have explained WMI thoroughly, you could also have a look at this article about WMI.

Design

Start a new project in either VB.NET or C# and name it On_Off_Printer.

We don’t need to add any control to our form. The reason for this is that we will create a user control, which will host all our needed objects (which we will create during run time), and the form will host the user control. So, all we need to do is to add a User Control to our project by clicking Project->Add User Control... Give it a name of MonitorPrinters

Code

MonitorPrinters

Let’s get the show on the road! I thought it best to start with our User Control, as it will expose the necessary properties for our form to use. First, let us add the necessary Namespaces:

VB.NET

Imports System
Imports System.Management 'Working With WMI
Imports System.Drawing
Imports System.Windows.Forms
Imports System.IO 'Working With Files
Imports On_Off_Printer_VB.frmPrinter

C#

using System;

using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Management; //Working With WMI
using System.IO; //Working With Files
using On_Off_Printer_C;

Remember to add a Project Reference to System.Management as well! Now, let us create the variables:

VB.NET

    'Dynamically Create Labels & Buttons For Our Control
    Private Label1 As New Label
    Private lblPrinter As New Label 'Printer Name

    Private Label2 As New Label
    Private lblPrinterStatus As New Label 'Printer Status

    Private Label3 As New Label
    Private lblPrinterError As New Label 'Error

    Private btnProperties As New Button

    Private tmrCheck As Timer

    Private intTime As System.Int32

C#

        //Dynamically Create Labels & Buttons For Our Control
        private Label Label1 = new Label();
        private Label lblPrinter = new Label(); //Printer Name

        private Label Label2 = new Label();
        private Label lblPrinterStatus = new Label(); //Printer Status

        private Label Label3 = new Label();
        private Label lblPrinterError = new Label(); //Error

        private Button btnProperties = new Button();

        private Timer tmrCheck = new Timer();

        private Int32 intTime;

Quite obviously this code creates the necessary objects. We need to go to the MonitorPrinter’s Designer Code and add the following:

VB.NET

    Private Sub InitializeComponent()
        components = New System.ComponentModel.Container()
        Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font

        '
        'MonitorPrinters
        '
        Me.SuspendLayout()

        With Label1
            .Width = 60
            .Text = "Printer"
            .Top = 5
            .Left = 10
            .TextAlign = System.Drawing.ContentAlignment.MiddleCenter
            .AutoSize = True
        End With

        Me.Controls.Add(Label1)

        With lblPrinter
            .Width = 200
            .Top = 5
            .Left = Me.Label1.Left + Me.Label1.Width + 5
            .TextAlign = System.Drawing.ContentAlignment.MiddleLeft
        End With

        Me.Controls.Add(lblPrinter)

        With Label2
            .Width = 60
            .Text = "Status"
            .Top = 5
            .Left = Me.lblPrinter.Left + Me.lblPrinter.Width + 5
            .TextAlign = System.Drawing.ContentAlignment.MiddleCenter
            .AutoSize = True
        End With

        Me.Controls.Add(Label2)

        With lblPrinterStatus
            .Text = ""
            .Width = 120
            .Top = 5
            .Left = Me.Label2.Left + Me.Label2.Width + 5
            .TextAlign = System.Drawing.ContentAlignment.MiddleCenter
        End With

        Me.Controls.Add(lblPrinterStatus)

        With Label3
            .Width = 60
            .Text = "Error"
            .Top = 5
            .Left = Me.lblPrinterStatus.Left + Me.lblPrinterStatus.Width + 5
            .TextAlign = System.Drawing.ContentAlignment.MiddleCenter
            .AutoSize = True
        End With

        Me.Controls.Add(Label3)

        With lblPrinterError
            .Text = ""
            .Width = 120
            .Top = 5
            .Left = Me.Label3.Left + Me.Label3.Width + 5
            .TextAlign = System.Drawing.ContentAlignment.MiddleCenter
        End With

        Me.Controls.Add(lblPrinterError)

        With btnProperties
            .Text = "Print Properties"
            .Top = 5
            .Left = lblPrinterError.Left + lblPrinterError.Width + 10
        End With

        Me.Controls.Add(btnProperties)

        AddHandler Me.btnProperties.Click, AddressOf btnPropertiesClick

        Me.ClientSize = New System.Drawing.Size(btnProperties.Left + btnProperties.Width + 10, btnProperties.Top + btnProperties.Height + 5)

        tmrCheck = New System.Windows.Forms.Timer
        tmrCheck.Interval = 5000

        AddHandler tmrCheck.Tick, AddressOf tmrCheckTick

        Me.ResumeLayout()

    End Sub

C#

        private void InitializeComponent()
        {
            components = new System.ComponentModel.Container();
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;

            Label1.Width = 60;
            Label1.Text = "Printer";
            Label1.Top = 5;
            Label1.Left = 10;
            Label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
            Label1.AutoSize = true;

            this.Controls.Add(Label1);

            lblPrinter.Width = 200;
            lblPrinter.Top = 5;
            lblPrinter.Left = this.Label1.Left + this.Label1.Width + 5;
            lblPrinter.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;

        this.Controls.Add(lblPrinter);

             Label2.Width = 60;
            Label2.Text = "Status";
            Label2.Top = 5;
            Label2.Left = this.lblPrinter.Left + this.lblPrinter.Width + 5;
            Label2.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
            Label2.AutoSize = true;

        this.Controls.Add(Label2);

            lblPrinterStatus.Text = "";
            lblPrinterStatus.Width = 120;
            lblPrinterStatus.Top = 5;
            lblPrinterStatus.Left = this.Label2.Left + this.Label2.Width + 5;
            lblPrinterStatus.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;

        this.Controls.Add(lblPrinterStatus);

        Label3.Width = 60;
            Label3.Text = "Error";
            Label3.Top = 5;
            Label3.Left = this.lblPrinterStatus.Left + this.lblPrinterStatus.Width + 5;
            Label3.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
            Label3.AutoSize = true;

        this.Controls.Add(Label3);

            lblPrinterError.Text = "";
            lblPrinterError.Width = 120;
            lblPrinterError.Top = 5;
            lblPrinterError.Left = this.Label3.Left + this.Label3.Width + 5;
            lblPrinterError.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;

        this.Controls.Add(lblPrinterError);

        btnProperties.Text = "Print Properties";
            btnProperties.Top = 5;
            btnProperties.Left = lblPrinterError.Left + lblPrinterError.Width + 10;

        this.Controls.Add(btnProperties);
            btnProperties.Click += new System.EventHandler( this.btnPropertiesClick );

        this.ClientSize = new System.Drawing.Size(btnProperties.Left + btnProperties.Width + 10, btnProperties.Top + btnProperties.Height + 5);

        tmrCheck = new System.Windows.Forms.Timer(this.components);

        tmrCheck.Interval = 5000;
        tmrCheck.Enabled = true;


        tmrCheck.Tick += new System.EventHandler(this.tmrCheckTick);

        this.components.Add(tmrCheck);
        this.ResumeLayout();

        }

With the above code, we simply give each of our objects some properties in order to work with them, and in order for them to display correctly. Remember, we are creating these objects on the fly. This was just simpler to do. I guess it is a force of habit. You could obviously have put all these objects on your design window – the choice is ultimately yours.

Let us now initialize our User Control class:

VB.NET

    Public Sub New()
        MyBase.New()
    End Sub

    Public Sub New(ByVal strPrinterName As System.String)

        MyBase.New()
        Me.InitializeComponent()

        PrinterNameProp = strPrinterName 'Printer name

    End Sub

    Public Sub New(ByVal strPrinterName As System.String, ByVal intCheckInTime As Integer)

        MyBase.New()

        Me.InitializeComponent()

        CheckTimeProp = intCheckInTime 'Interval
        PrinterNameProp = strPrinterName 'Printer name

    End Sub

C#

        public MonitorPrinters() : base()
        {
            this.InitializeComponent();
        }

       public MonitorPrinters(String strPrinterName) : base()
        {

           this.InitializeComponent();

           PrinterNameProp = strPrinterName; //Printer Name

        }

        public MonitorPrinters(String strPrinterName, int intCheckInTime) : base()
        {

            // Must be called for initialization
	        this.InitializeComponent();

	        CheckTimeProp = intCheckInTime; //Interval

            PrinterNameProp = strPrinterName; //Printer Name

        }

Now we can call this class three different ways:

  1. Normally, i.e. Without any outside input via parameters.
  2. We could supply a printer name for which to obtain the settings.
  3. We could supply the printer name, as well as a interval time. This means that we could check every interval whether or not there is a change in the printer’s behavior.

Now we need to add the Class properties that will be exposed for use with our form:

VB.NET

    'Printer Name Property
    Public Property PrinterNameProp As String

        Get

            Return lblPrinter.Text

        End Get

        Set(ByVal value As String)

            lblPrinter.Text = value

            tmrCheck.Start()
            GetPrinterStatus()

        End Set

    End Property

    'Printer Status Property
    Public ReadOnly Property PrinterStatusProp As String

        Get

            Return lblPrinterStatus.Text

        End Get

    End Property

    'Error Property
    Public ReadOnly Property PrinterErrorProp As String

        Get

            Return lblPrinterError.Text

        End Get

    End Property

    'Interval Property
    Public Property CheckTimeProp As Int32

        Get

            Return tmrCheck.Interval

        End Get

        Set(ByVal value As Int32)

            tmrCheck.Interval = value
        End Set

    End Property

C#

        //Printer Name Property
    public String PrinterNameProp
    {

	get

    {
        return lblPrinter.Text;
    }

	set
    {

		lblPrinter.Text = value;

        tmrCheck.Start();
		GetPrinterStatus();

	    }

    }
        //Printer Status Property
    public String PrinterStatusProp
    {
	   get

        {
            return lblPrinterStatus.Text;
        }

    }
        //Error Property
    public String PrinterErrorProp

    {

        get
        {
            return lblPrinterError.Text;

        }

    }
        //Interval Property
    public Int32 CheckTimeProp
    {

        get
        {
            return tmrCheck.Interval;
        }

	set
    {
        tmrCheck.Interval = value;
    }

    }

Right, the easy stuff is now out of the way; we can now start with the more complicated code – the code where we make use of WMI. The first procedure we will tackle is the GetPrinterStatus procedure. It looks like:

VB.NET

    Public Sub GetPrinterStatus()

        Dim moObj As ManagementObject 'Our Object to work with WMI
        Dim mosQuery As ManagementObjectSearcher 'Retrieves a collection of management objects based on a specified query
        Dim mocColl As ManagementObjectCollection 'Represents different collections of management objects retrieved through WMI

        mosQuery = New ManagementObjectSearcher("Select * from Win32_Printer") 'Execute Query to Retreive Printers

        mocColl = mosQuery.Get 'Get All Info

        'Search Each Printer's Properties
        For Each moObj In mocColl

            If moObj.Properties("Name").Value = lblPrinter.Text Then 'If We Have A Match

                lblPrinterStatus.Text = moObj.Properties("Status").Value 'Get Printer Status
                lblPrinterError.Text = [Enum].GetName(GetType(DetectedErrorState), moObj.Properties("DetectedErrorState").Value) 'Get Error State

            End If

        Next

        'Dispose of All WMI Objects
        mosQuery.Dispose()
        mocColl.Dispose()
        moObj.Dispose()

    End Sub

C#

    public void GetPrinterStatus()

    {

        ManagementObjectSearcher mosQuery = default(ManagementObjectSearcher); //Retrieves a collection of management objects based on a specified query
        ManagementObjectCollection mocColl = default(ManagementObjectCollection); //Represents different collections of management objects retrieved through WMI

        mosQuery = new ManagementObjectSearcher("Select * from Win32_Printer"); //Execute Query to Retreive Printers

        mocColl = mosQuery.Get(); //Get All Info

        //Search Each Printer's Properties
        foreach (ManagementObject _mo in mocColl) //Our Object to work with WMI
        {
            PropertyDataCollection moProperties = _mo.Properties;

            if (moProperties["Name"].Value.ToString() == lblPrinter.Text) //If We Have A Match
            {

                lblPrinterStatus.Text = moProperties["Status"].Value.ToString(); //Get Printer Status

			    lblPrinterError.Text = Enum.GetName(typeof(On_Off_Printer_C.frmPrinter.DetectedErrorState), moProperties["DetectedErrorState"].Value); //Get Error State

		    }

	    }

        //Dispose of All WMI Objects
	    mosQuery.Dispose();
	    mocColl.Dispose();

    }

Hello! You still there? OK, good, glad to see that you haven’t run away after this piece of code! Here, we created all the WMI objects. We made use of the mosQuery object to search for all the printers installed on the computer (local as well as remote), then we looped through the mocColl object to obtain each printer’s properties. These properties include the printer’s name and status, as well as any error reported.

This information is actually just the tip of the iceberg. With our next procedure we will dive deeper into each printer’s settings. These settings include the number of pages to print at a time, what print driver is actually used etc. Let us create the ShowPrinterProperties procedure now:

VB.NET

    Public Sub ShowPrinterProperties(ByRef moObj As Management.ManagementObject)

        Dim pdcProperties As PropertyDataCollection = moObj.Properties

        Dim pdProperty As PropertyData

        Dim strPath As String = Application.StartupPath & "Printer" & lblPrinter.Text.Replace("", " ") & ".txt"

        Dim swPropertyWriter As StreamWriter = File.CreateText(strPath)

        swPropertyWriter.WriteLine(moObj("Name"))

        'Write Each Printer's Properties to A Seperate File
        For Each pdProperty In pdcProperties

            If pdProperty.Value Is Nothing Then

                swPropertyWriter.WriteLine("")

            Else

                Select Case pdProperty.Name

                    Case "Availablility"

                        swPropertyWriter.WriteLine("Availabilty Properties : ")
                        swPropertyWriter.WriteLine([Enum].GetName(GetType(PrinterAvailability), pdProperty.Value))

                    Case "PrinterStatus"

                        swPropertyWriter.WriteLine("Printer Status Properties : ")
                        swPropertyWriter.WriteLine([Enum].GetName(GetType(PrinterStatus2), pdProperty.Value))

                    Case "ExtendedPrinterStatus"

                        swPropertyWriter.WriteLine("Extended Printer Status Properties :")
                        swPropertyWriter.WriteLine([Enum].GetName(GetType(ExtendedPrinterStatus), pdProperty.Value))

                    Case "PrinterStatusInfo"

                        swPropertyWriter.WriteLine("Printer Status Info Properties : ")
                        swPropertyWriter.WriteLine([Enum].GetName(GetType(PrinterStatus1), pdProperty.Value))

                    Case "PrinterState"

                        swPropertyWriter.WriteLine("Printer State Properties :")
                        swPropertyWriter.WriteLine([Enum].GetName(GetType(PrinterState), pdProperty.Value))

                    Case "DetectedErrorState"

                        swPropertyWriter.WriteLine("Detected Error State Properties :")
                        swPropertyWriter.WriteLine([Enum].GetName(GetType(DetectedErrorState), pdProperty.Value))

                    Case "ExtendedDetectedErrorState"

                        swPropertyWriter.WriteLine("Extended Detected Error State Properties :")
                        swPropertyWriter.WriteLine([Enum].GetName(GetType(ExtendedDetectedErrorState), pdProperty.Value))

                    Case Else

                        swPropertyWriter.WriteLine("Other Properties :")
                        swPropertyWriter.WriteLine(Convert.ToString(pdProperty.Value))

                End Select

            End If

        Next

        swPropertyWriter.Close()

    End Sub

C#

    public void ShowPrinterProperties(ref ManagementObject moObj)
    {

        PropertyDataCollection pdcProperties = moObj.Properties;

        string strPath = Application.StartupPath + "\Printer" + lblPrinter.Text.Replace("\", " ") + ".txt";

        StreamWriter swPropertyWriter = File.CreateText(strPath);

        swPropertyWriter.WriteLine(moObj["Name"]);

        //Write Each Printer's Properties to A Seperate File
        foreach (PropertyData moProperty in pdcProperties)
        {

            if (moProperty.Value == null)

            {

                swPropertyWriter.WriteLine("");

            }

            else

            {

                switch (moProperty.Name)

                {
                    case "Availablility":

                        swPropertyWriter.WriteLine("Availabilty Properties : ");
                        swPropertyWriter.WriteLine(Enum.GetName(typeof(On_Off_Printer_C.frmPrinter.PrinterAvailability), moProperty.Value));
                        break;

                    case "PrinterStatus":

                        swPropertyWriter.WriteLine("Printer Status Properties : ");
                        swPropertyWriter.WriteLine(Enum.GetName(typeof(On_Off_Printer_C.frmPrinter.PrinterStatus2), moProperty.Value));
                        break;

                    case "ExtendedPrinterStatus":

                        swPropertyWriter.WriteLine("Extended Printer Status Properties :");
                        swPropertyWriter.WriteLine(Enum.GetName(typeof(On_Off_Printer_C.frmPrinter.ExtendedPrinterStatus), moProperty.Value));
                        break;

                    case "PrinterStatusInfo":

                        swPropertyWriter.WriteLine("Printer Status Info Properties :");
                        swPropertyWriter.WriteLine(Enum.GetName(typeof(On_Off_Printer_C.frmPrinter.PrinterStatus1), moProperty.Value));
                        break;

                    case "PrinterState":

                        swPropertyWriter.WriteLine("Printer State Properties :");
                        swPropertyWriter.WriteLine(Enum.GetName(typeof(On_Off_Printer_C.frmPrinter.PrinterState), moProperty.Value));
                        break;

                    case "DetectedErrorState":

                        swPropertyWriter.WriteLine("Detected Error State Properties :");
                        swPropertyWriter.WriteLine(Enum.GetName(typeof(On_Off_Printer_C.frmPrinter.DetectedErrorState), moProperty.Value));
                        break;

                    case "ExtendedDetectedErrorState":

                        swPropertyWriter.WriteLine("Extended Detected Error State Properties :");
                        swPropertyWriter.WriteLine(Enum.GetName(typeof(On_Off_Printer_C.frmPrinter.ExtendedDetectedErrorState), moProperty.Value));
                        break;

                    default:

                        swPropertyWriter.WriteLine("Other Properties :");
                        swPropertyWriter.WriteLine(Convert.ToString(moProperty.Value));
                        break;

                    }
            }

        }
    }

As mentioned, we delve a bit deeper into each printer’s settings. We actually have a look through seven enumerations (which we will create in our Form ) and determine each enumeration’s value or setting. We then print each printer’s settings out onto a separate file for each associated printer. For more information on the Enums we are referring to here, have a look here.

All that is needed inside this class is the calls to the above procedures we created. Let us add the last events for MonitorPrinter now:

VB.NET

    Private Sub tmrCheckTick(ByVal sender As System.Object, ByVal e As System.EventArgs)

        GetPrinterStatus()

    End Sub

    Private Sub btnPropertiesClick(ByVal sender As System.Object, ByVal e As System.EventArgs)

        Dim moObj As ManagementObject
        Dim mosQuery As ManagementObjectSearcher
        Dim mocColl As ManagementObjectCollection

        mosQuery = New ManagementObjectSearcher("Select * from Win32_Printer")

        mocColl = mosQuery.Get

        'Get All Printer Properties
        For Each moObj In mocColl

            If moObj.Properties("Name").Value = lblPrinter.Text Then

                ShowPrinterProperties(moObj)

            End If

        Next

        mosQuery.Dispose()
        mocColl.Dispose()
        moObj.Dispose()

    End Sub

C#

   private void tmrCheckTick(System.Object sender, System.EventArgs e)
    {

        GetPrinterStatus();

    }

    private void btnPropertiesClick(System.Object sender, System.EventArgs e)
    {
	    ManagementObject moObj = default(ManagementObject);
	    ManagementObjectSearcher mosQuery = default(ManagementObjectSearcher);
	    ManagementObjectCollection mocColl = default(ManagementObjectCollection);

	    mosQuery = new System.Management.ManagementObjectSearcher("Select * from Win32_Printer");

        mocColl = mosQuery.Get();

        //Get All Printer Properties
	    foreach ( ManagementObject _mo in mocColl)
        {

            if (_mo["Name"].ToString() == lblPrinter.Text)

            {

                moObj = _mo;
			    ShowPrinterProperties(ref moObj);

		    }

	    }

	    mosQuery.Dispose();
	    mocColl.Dispose();


        }
    }

We call the GetPrinterStatus sub every five seconds via our Timer. In the Button’s event we simply ensure that we are dealing with the correct printer each time, and then call the sub for printing our extended printer settings. You may be wondering now where do I create the Enums? What else is left?

Well, let us move our focus to our form.

frmPrinters

The User Control is now set up nicely. It will list all the printers on the system along with each printer’s status and extended properties. Obviously, we need a host for our User Control, and this host is our form. Because we deal with an unknown amount of printers, we will need to make an array of user controls for the amount of printers we have. We also need to include our Win32_Printer class enums here, or else we will not be able to obtain our extended properties as shown above. Add the following Namespace to your form’s code:

VB.NET

Imports System.Management 'Working with WMI. Reference System.Management as well

C#

using System.Management; //Working with WMI. Reference System.Management as well

Add the necessary enumerations:

VB.NET

    Public Enum PrinterAvailability As Integer 'PrinterAvailability WMI Settings
        Other = 1
        Unknown = 2
        Running = 3
        Warning = 4
        InTest = 5
        NotApplicable = 6
        PowerOff = 7
        OffLine = 8
        OffDuty = 9
        Degraded = 10
        NotInstalled = 11
        InstallError = 12
        PowerSaveUnknown = 13
        PowerSaveLowPower = 14
        PowerSaveStandBy = 15
        PowerCycle = 16
        PowerSaveWarning = 17
    End Enum

    Public Enum DetectedErrorState As Integer 'Detected ErrorState WMI Settings
        Unknown = 1
        Other = 2
        NoError = 3
        LowPaper = 4
        NoPaper = 5
        LowToner = 6
        NoToner = 7
        DoorOpen = 8
        Jammed = 9
        Offline = 10
        ServiceRequested = 11
        OutputBinFull = 12
    End Enum

    Public Enum ExtendedDetectedErrorState As Integer 'Extended ErrorState WMI Settings
        Unknown = 0
        Other = 1
        NoError = 2
        LowPaper = 3
        NoPaper = 4
        LowToner = 5
        NoToner = 6
        DoorOpen = 7
        Jam = 8
        ServiceRequested = 9
        OutputBinFull = 10
        PaperProblem = 11
        CannotPrintPage = 12
        UserInterventionRequired = 13
        OutOfMemory = 14
        ServerUnknown = 15
    End Enum

    Public Enum PrinterState As Integer 'Printer State WMI Settings
        Unknown = 0
        Paused = 1
        PrintError = 2
        PendingDeletion = 3
        PaperJam = 4
        PaperOut = 5
        ManualFeed = 6
        PaperProblem = 7
        Offline = 8
        IoActive = 9
        Busy = 10
        Printing = 11
        OutputBinFull = 12
        NotAvailable = 13
        Waiting = 14
        Processing = 15
        Initialization = 16
        WarmingUp = 17
        TonerLow = 18
        NoToner = 19
        PagePunt = 20
        UserInterventionRequired = 21
        OutOfMemory = 22
        DoorOpen = 23
        ServerUnknown = 24
        PowerSave = 25
    End Enum

    Public Enum PrinterStatus1 As Integer 'Printer Status WMI Settings
        Other = 1
        Unknown = 2
        Enabled = 3
        Disabled = 4
        NotApplicable = 5
    End Enum

    Public Enum PrinterStatus2 As Integer
        Other = 1
        Unknown = 2
        Idle = 3
        Printing = 4
        WarmUp = 5
        StoppedPrinting = 6
        OffLine = 7
    End Enum

    Public Enum ExtendedPrinterStatus As Integer 'Printer Status WMI Settings
        Other = 1
        Unknown = 2
        Idle = 3
        Printing = 4
        WarmUp = 5
        StoppedPrinting = 6
        Offline = 7
        Paused = 8
        GeneralError = 9
        Busy = 10
        NotAvailable = 11
        Waiting = 12
        Processing = 13
        Initialization = 14
        PowerSave = 15
        PendingDeletion = 16
        IoActive = 17
        ManualFeed = 18
    End Enum

C#

        public enum PrinterAvailability : int //PrinterAvailability WMI Settings
        {
            Other = 1,
            Unknown = 2,
            Running = 3,
            Warning = 4,
            InTest = 5,
            NotApplicable = 6,
            PowerOff = 7,
            OffLine = 8,
            OffDuty = 9,
            Degraded = 10,
            NotInstalled = 11,
            InstallError = 12,
            PowerSaveUnknown = 13,
            PowerSaveLowPower = 14,
            PowerSaveStandBy = 15,
            PowerCycle = 16,
            PowerSaveWarning = 17
        }

        public enum DetectedErrorState : int //Detected ErrorState WMI Settings
        {
            Unknown = 1,
            Other = 2,
            NoError = 3,
            LowPaper = 4,
            NoPaper = 5,
            LowToner = 6,
            NoToner = 7,
            DoorOpen = 8,
            Jammed = 9,
            Offline = 10,
            ServiceRequested = 11,
            OutputBinFull = 12
        }

        public enum ExtendedDetectedErrorState : int //Extended ErrorState WMI Settings
        {
            Unknown = 0,
            Other = 1,
            NoError = 2,
            LowPaper = 3,
            NoPaper = 4,
            LowToner = 5,
            NoToner = 6,
            DoorOpen = 7,
            Jam = 8,
            ServiceRequested = 9,
            OutputBinFull = 10,
            PaperProblem = 11,
            CannotPrintPage = 12,
            UserInterventionRequired = 13,
            OutOfMemory = 14,
            ServerUnknown = 15
        }

        public enum PrinterState : int //Printer State WMI Settings
        {
            Unknown = 0,
            Paused = 1,
            PrintError = 2,
            PendingDeletion = 3,
            PaperJam = 4,
            PaperOut = 5,
            ManualFeed = 6,
            PaperProblem = 7,
            Offline = 8,
            IoActive = 9,
            Busy = 10,
            Printing = 11,
            OutputBinFull = 12,
            NotAvailable = 13,
            Waiting = 14,
            Processing = 15,
            Initialization = 16,
            WarmingUp = 17,
            TonerLow = 18,
            NoToner = 19,
            PagePunt = 20,
            UserInterventionRequired = 21,
            OutOfMemory = 22,
            DoorOpen = 23,
            ServerUnknown = 24,
            PowerSave = 25
        }

        public enum PrinterStatus1 : int //Printer Status WMI Settings
        {
            Other = 1,
            Unknown = 2,
            Enabled = 3,
            Disabled = 4,
            NotApplicable = 5
        }

        public enum PrinterStatus2 : int
        {
            Other = 1,
            Unknown = 2,
            Idle = 3,
            Printing = 4,
            WarmUp = 5,
            StoppedPrinting = 6,
            OffLine = 7
        }

        public enum ExtendedPrinterStatus : int //Printer Status WMI Settings
        {
            Other = 1,
            Unknown = 2,
            Idle = 3,
            Printing = 4,
            WarmUp = 5,
            StoppedPrinting = 6,
            Offline = 7,
            Paused = 8,
            GeneralError = 9,
            Busy = 10,
            NotAvailable = 11,
            Waiting = 12,
            Processing = 13,
            Initialization = 14,
            PowerSave = 15,
            PendingDeletion = 16,
            IoActive = 17,
            ManualFeed = 18
        }

For more information on these enums, please refer to the Win32_Printer Class link above.

Let us now create the User Control array:

VB.NET

    Private ucPrinters() As MonitorPrinters 'UserControl To Show Printer Values

C#

        private MonitorPrinters[] ucPrinters; //UserControl To Show Printer Values

Finally, let us add the Form_Load event to get the party started:

VB.NET

    Private Sub Form_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        Dim mocColl As Management.ManagementObjectCollection 'Represents different collections of management objects retrieved through WMI
        Dim mosQuery As Management.ManagementObjectSearcher 'Retrieves a collection of management objects based on a specified query
        Dim moObj As Management.ManagementObject 'Our Object to work with WMI
        Dim msScope As Management.ManagementScope 'Represents a scope (namespace) for management operations.

        Dim ucPrinters() As MonitorPrinters 'Create UserControl Array

        Dim iYPos As Integer = 15 'Vertical Pos of Controls
        Dim iXPos As Integer = 15 'Horizontal Pos of Controls

        Dim iInterval As Integer = 5000 'Printer Checking Interval

        msScope = New ManagementScope("rootcimv2") 'We Want To Search Our Local Machine

        msScope.Connect() 'Connect to Local Machine

        mosQuery = New Management.ManagementObjectSearcher("Select * from Win32_Printer") 'Execute Query to Retreive Printers

        mocColl = mosQuery.Get 'Get All Info


        For Each moObj In mocColl 'Loop Through WMI Collection

            If ucPrinters Is Nothing Then 'If No Printers

                ReDim ucPrinters(0)

                ucPrinters(0) = New MonitorPrinters(moObj.Properties("Name").Value, iInterval)

            Else 'If Printers

                ReDim Preserve ucPrinters(ucPrinters.Length) 'Resize to Amount of Printers Installed

                ucPrinters(ucPrinters.Length - 1) = New MonitorPrinters(moObj.Properties("Name").Value, iInterval) 'Send Printer Name to UserControl

            End If

            With ucPrinters(ucPrinters.Length - 1) 'Set Location of Each UserControl

                .Top = iYPos
                .Left = iXPos

            End With

            Me.Controls.Add(ucPrinters(ucPrinters.Length - 1)) 'Add UserControl to Form

            iYPos = iYPos + ucPrinters(ucPrinters.Length - 1).Height + 5

        Next

        'Resize Form
        Me.ClientSize = New System.Drawing.Size(ucPrinters(0).Width + 10, ucPrinters(ucPrinters.Length - 1).Top + ucPrinters(ucPrinters.Length - 1).Height + 5)

    End Sub

C#

        private void frmPrinter_Load(object sender, EventArgs e)
        {

            ManagementObjectCollection mocColl = default(ManagementObjectCollection); //Represents different collections of management objects retrieved through WMI
            ManagementObjectSearcher mosQuery = default(ManagementObjectSearcher); //Retrieves a collection of management objects based on a specified query
            ManagementScope msScope = default(ManagementScope); //Represents a scope (namespace) for management operations.

            MonitorPrinters[] ucPrinters = null; //Create UserControl Array

            int iYPos = 15; //Vertical Pos of Controls
            int iHorPos = 15; //Horizontal Pos of Controls

            int iInterval = 5000; //Printer Checking Interval

            msScope = new ManagementScope("\root\cimv2"); //We Want To Search Our Local Machine

            msScope.Connect(); //Connect to Local Machine

            mosQuery = new ManagementObjectSearcher("Select * from Win32_Printer"); //Execute Query to Retreive Printers

            mocColl = mosQuery.Get(); //Get All Info

           foreach (ManagementObject moObj in  mocColl) //Loop Through WMI Collection
            {

               PropertyDataCollection properties = moObj.Properties; //Store Properties

                if (ucPrinters == null || ucPrinters.Length == 0) //If No Printer Exists
                {

                    Array.Resize(ref ucPrinters, 1);

		            ucPrinters[0] = new MonitorPrinters(properties["Name"].Value.ToString(), iInterval);

                }
                else //If Printers
                {

		            Array.Resize(ref ucPrinters, ucPrinters.Length + 1); //resize to Amount of Printers

		            ucPrinters[ucPrinters.Length - 1] = new MonitorPrinters(properties["Name"].Value.ToString(), iInterval); //Send Names to UserControl

                }

               //Set Location of Each UserCOntrol
	        ucPrinters[ucPrinters.Length - 1].Top = iYPos;
            ucPrinters[ucPrinters.Length - 1].Left = iHorPos;


	        this.Controls.Add(ucPrinters[ucPrinters.Length - 1]); //Add UserControl to Form

	        iYPos = iYPos + ucPrinters[ucPrinters.Length - 1].Height + 5;

        }

           //Resize Form
        this.ClientSize = new Size(ucPrinters[0].Width + 10, ucPrinters[ucPrinters.Length - 1].Top + ucPrinters[ucPrinters.Length - 1].Height + 5);

            }
    }

What happens above is we query our WMI objects again and create the MonitorPrinters control for each associated printer. The rest of the code concentrates on spacing the controls correctly.

If you were to run your project now, you would see a screen listing of all your installed printers. Once you click the Print button, you will be able to determine which printer is possibly at fault. I am including the projects in zipped format below, just in case…

Conclusion

Phew! That was some work indeed! Thank you for reading my article. I hope it gives you much joy and helps you out with a lot of printer problems in the future. Until next time, Cheers!

Hannes DuPreez
Hannes DuPreez
Ockert J. du Preez is a passionate coder and always willing to learn. He has written hundreds of developer articles over the years detailing his programming quests and adventures. He has written the following books: Visual Studio 2019 In-Depth (BpB Publications) JavaScript for Gurus (BpB Publications) He was the Technical Editor for Professional C++, 5th Edition (Wiley) He was a Microsoft Most Valuable Professional for .NET (2008–2017).

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read