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:
- Normally, i.e. Without any outside input via parameters.
- We could supply a printer name for which to obtain the settings.
- 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!