Search code examples
.netvb.netwinformsprintingservicecontroller

Selecting specific printer by name VB


I've made a program that will run on startup asking the users to select their default printer. The code shows all installed printers in a ComboBox and the submit Button will set the Printer selected in the ComboBox as the default printer for that user.

Is there a way I can only show Printers names that contain specific text?
As e.g.:

ABC
printer1ABC
network1 abc
printer2 def
network2def

So it would only show printer1ABC and network1 abc? Here's the code I have so far (one ComboBox and one Button)

Imports System.Drawing.Printing

Public Class Form1
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        Dim strInstalledPrinters As String
        Dim prntDoc As New PrintDocument

        'check if there is installed printer
        If PrinterSettings.InstalledPrinters.Count = 0 Then
            MsgBox("No printer installed")
            Exit Sub
        End If

        'display installed printer into combobox list item
        For Each strInstalledPrinters In PrinterSettings.InstalledPrinters
            ComboBox1.Items.Add(strInstalledPrinters)
        Next strInstalledPrinters

        'Display current default printer on combobox texts
        ComboBox1.Text = prntDoc.PrinterSettings.PrinterName

        Button1.Text = "Set Default Printer"
    End Sub
End Class

Function to set a printer as default:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    Shell(String.Format("rundll32 printui.dll,PrintUIEntry /y /n ""{0}""", ComboBox1.Text))

    MsgBox("You have changed your default printer")
    Me.Close()
End Sub

Thanks for any help


Solution

  • PrinterSettings.InstalledPrinters is collection of string that enumerates the available Printer names. You can filter this collection in different ways to select only the Printers that you want to show.

    Here, using LINQ's Where() method, the collection is filtered selectin strings that contain part of the name you're looking for:

    dim myPrinters = PrinterSettings.InstalledPrinters.OfType(Of String).
                     Where(Function(p) p.Contains("1ABC")).ToList()
    

    You can add OrElse conditions to include other filters. E.g., :

    .Where(Function(p) p.Contains("1ABC") OrElse p.StartsWith("Network1"))
    

    Before enumerating the Printers, you should verify whether the Spooler Service is actually available. You can use the ServiceController class to check the Status property of the spooler service, to see if it's ServiceControllerStatus.Running and act on it. If the Spooler is not running, you get an exception if you try to enumerate the available printers in any way.
    ▶ Add a Project Reference to the System.ServiceProcess assembly.

    You can also get other exceptions when you try to perform this enumeration, depending on different conditions, so better catch these exceptions and make different choices.

    An example:
    As a suggestion, give your Controls meaningful names, in case these are the actual names you're using

    Imports System.Drawing.Printing
    Imports System.Linq
    Imports System.ServiceProcess
    
    Private Const RPC_ERROR As Integer = 1722
    
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    
        Button1.Text = "Set Default Printer"
        Button1.Enabled = False
    
        Dim spoolerStatus = New ServiceController("spooler").Status
    
        If spoolerStatus = ServiceControllerStatus.Running Then
            Try
                Dim myPrinters = PrinterSettings.InstalledPrinters.OfType(Of String).
                    Where(Function(p) p.Contains("1ABC")).ToList()
                If myPrinters.Count > 0 Then
                    ComboBox1.DataSource = myPrinters
                    Button1.Enabled = True
                Else
                    ComboBox1.Items.Add("<Printer not installed>")
                End If
            Catch wEx As Win32Exception
                If wEx.NativeErrorCode = RPC_ERROR Then
                    ' RPC Server unavailable
                    ComboBox1.Items.Add($"<{wEx.Message}>")
                Else
                    ' Log Message
                    MessageBox.Show(wEx.Message)
                    ComboBox1.Items.Add("<Error>")
                End If
            Catch ex As Exception
                ' Log Message
                MessageBox.Show(ex.Message)
                ComboBox1.Items.Add("<Error>")
            End Try
        Else
            ComboBox1.Items.Add("<Spooler Service not available>")
        End If
        ComboBox1.SelectedIndex = 0
    End Sub
    

    To change the default printer, I suggest to use the SetdefaultPrinter Win32 function or the corresponding WMI's Win32_Printer class SetDefaultPrinter method.

    ▶ When you click your Button to set the new default Printer, use the GetItemText() method to get the ComboBox1.SelectedItem content, don't rely on the Text property of that Control.

    Imports System.Runtime.InteropServices
    
    <DllImport("winspool.drv", CharSet:=CharSet.Auto, SetLastError:=True)>
    Friend Shared Function SetDefaultPrinter(printerName As String) As Boolean
    End Function
    
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim printeNname = ComboBox1.GetItemText(ComboBox1.SelectedItem)
        If SetDefaultPrinter(printeNname) Then
             MessageBox.Show($"Your default printer is now {printeNname}")
        Else
             MessageBox.Show("Couldn't change the default printer")
        End If
        Me.Close()
    End Sub