Powershell – Collecting installed Printer drivers

Printing on Citrix is a very intresting topic :). There are many things that you should be awair of to make it working flawlessly.
Her I’m going to show a short script that can help you in identifying inconsistent Printer Drivers accross your XenApp environment.

In a nutshell, we have to make sure that the driver version installed on a Print server or on a Client machine [depending on the type of printing redirection you use] is the same as on a XenApp server.

The manual approach requires as to open a Print Managment console on each of the machines and then do a comparison of the Driver Names and Versions:
Print1

If yo have many Servers to check, then this task can be quite challenging.
So lets use Powershell to help as:

cls

$Servers = "SERVER1","SERVER2"
$Servers += "SERVER3"

foreach ($Server in $Servers) {
    ''
    $Server

    # Querying WMI
    $PrintDrivers = get-wmiobject -class "Win32_PrinterDriver" -namespace "root\CIMV2" -computername $Server 

    # Collect details for every detected printer
    foreach ($PrinterDriver in $PrintDrivers) { 

        write-host "Name: " $PrinterDriver.Name 

        $Drive = $PrinterDriver.DriverPath.Substring(0,1)

        # Extracting Driver version info:
        $DriverVersion = New-Object PSObject -Property @{Version = (Get-ItemProperty ($PrinterDriver.DriverPath.Replace("$Drive`:","\\$Server\$Drive`$"))).VersionInfo.ProductVersion}

        # Show Driver file version:
        write-host " Version of the Driver: "$DriverVersion.Version

        # For logging
        $PrinterDriver.Name + ',' + $DriverVersion.Version

        # Extracting Driver path:
        write-host " Driver Path: " $PrinterDriver.DriverPath 

        # Few more details:
        write-host "   Data File: " $PrinterDriver.DataFile
        write-host "   Configuration File: " $PrinterDriver.ConfigFile
        write-host
    }
}

As a result we gonna get a list of installed drivers including a version and some other useful details. Here is an example of an output:

Name:  HP Color LaserJet 2800 Series PS,3,Windows NT x86
 Version of the Driver:  6.1.7600.16385
HP Color LaserJet 2800 Series PS,3,Windows NT x86,6.1.7600.16385
 Driver Path:  C:\Windows\system32\spool\DRIVERS\W32X86\3\PSCRIPT5.DLL
   Data File:  C:\Windows\system32\spool\DRIVERS\W32X86\3\HPMCPD25.PPD
   Configuration File:  C:\Windows\system32\spool\DRIVERS\W32X86\3\PS5UI.DLL

“Win32_PrinterDriver” class is used to collect tha data – http://msdn.microsoft.com/en-us/library/aa394366%28v=vs.85%29.aspx

Further, generated reports can be aggregated in a nice Excel file 🙂
Print2

7 thoughts on “Powershell – Collecting installed Printer drivers

  1. Unfortunately this won’t work with some type of drivers.
    We are using Xerox and the DriverPath for the Xerox references the HP universal driver so we are not getting the correct information.
    Here is what I did to compensate for this :

    foreach ($Driver in (Get-WmiObject Win32_PrinterDriver -ComputerName $StrComputer))
        {   
    	$driverdetails=$Driver.Name.split(",")
    	#$driverdetails
    	$Name=$driverdetails[0]
    	
    	$Drive=''
    
    	if ($Name.contains("Xerox"))  
                 {$Drive=$Driver.DependentFiles[3]}
    	#$Drive = $Driver.DriverPath.Substring(0,1)
    	else 
                 {$Drive=$Driver.DriverPath}
    	
    	$Drive= $Drive -Replace ":" , "`$"
    	$Drive = "\\$StrComputer\$Drive"
    	#write-host $Drive
    
    	if ($Drive -ne "\\$StrComputer\") {
    	$global:PrintDriver += New-Object PSObject -Property @{
                Device_name=$StrComputer
                Name = $driverdetails[0]
    	        DriverVersion=$driverdetails[2]
                Version = (Get-ItemProperty ($Drive)).VersionInfo.ProductVersion
    		}
    	}
    	else {
    	$global:PrintDriver += New-Object PSObject -Property @{
                Device_name=$StrComputer
                Name = "n/a"
    	    DriverVersion="n/a"
                Version = "n/a"
    	    }
    	}
        }
    

    Basically if it’s a Xerox Printer I am getting the information from the “Dependent Files”. The 3rd Item ($driverdetails[2]) is always a DLL for the xerox printers so I am pulling the version from that file.

    If you have any other suggestion you are more than welcome as this drived me crazy for a while..

  2. Thank you Carlos for your comment. Yes, the idea is based on finding the file version. Therefore if the DriverPath is pointing to a wrong file then it is not very helpful. On the other hand it’s rather strange that a Xerox printer is using HP Universal Printer Driver.

    Thought it can be useful to show what DriverPath and DependentFiles properties are:

    __GENUS                 : 2
    __CLASS                 : Win32_PrinterDriver
    __SUPERCLASS            : CIM_Service
    __DYNASTY               : CIM_ManagedSystemElement
    __RELPATH               : Win32_PrinterDriver.Name="HP Color LaserJet 2800 Series PS,3,Windows NT x86"
    __PROPERTY_COUNT        : 22
    __DERIVATION            : {CIM_Service, CIM_LogicalElement, CIM_ManagedSystemElement}
    __SERVER                : Server1
    __NAMESPACE             : root\CIMV2
    __PATH                  : \\Server1\root\CIMV2:Win32_PrinterDriver.Name="HP Color LaserJet 2800 Series PS,3,Windows NT x86"
    Caption                 : 
    ConfigFile              : C:\Windows\system32\spool\DRIVERS\W32X86\3\PS5UI.DLL
    CreationClassName       : Win32_PrinterDriver
    DataFile                : C:\Windows\system32\spool\DRIVERS\W32X86\3\HPMCPD25.PPD
    DefaultDataType         : 
    DependentFiles          : {C:\Windows\system32\spool\DRIVERS\W32X86\3\HPZEVWN7.DLL, C:\Windows\system32\spool\DRIVERS\W32X86\3\HPZSTWN7.DLL, C:\Windows\system32\spool\DRIVERS\W32X86\3\HPZUIWN7.
                              DLL, C:\Windows\system32\spool\DRIVERS\W32X86\3\HPZLSWN7.DLL...}
    Description             : 
    DriverPath              : C:\Windows\system32\spool\DRIVERS\W32X86\3\PSCRIPT5.DLL
    FilePath                : 
    HelpFile                : C:\Windows\system32\spool\DRIVERS\W32X86\3\PSCRIPT.HLP
    InfName                 : 
    InstallDate             : 
    MonitorName             : 
    Name                    : HP Color LaserJet 2800 Series PS,3,Windows NT x86
    OEMUrl                  : http://go.microsoft.com/fwlink/?LinkID=37&prd=10798&sbp=Printers
    Started                 : 
    StartMode               : 
    Status                  : 
    SupportedPlatform       : Windows NT x86
    SystemCreationClassName : Win32_ComputerSystem
    SystemName              : 
    Version                 : 3
    
  3. I agree.. this is quite odd indeed.
    First time I pulled the information for my print servers into a spreadsheet and started reviewing the version I was reaaaaally dumbfounded when I look at the version as it wasn’t making any sense…

    FYI, here is the output for one of these Xerox drivers :

    __GENUS                 : 2
    __CLASS                 : Win32_PrinterDriver
    __SUPERCLASS            : CIM_Service
    __DYNASTY               : CIM_ManagedSystemElement
    __RELPATH               : Win32_PrinterDriver.Name="Xerox Global Print Driver PCL6,3,Windows NT x86"
    __PROPERTY_COUNT        : 22
    __DERIVATION            : {CIM_Service, CIM_LogicalElement, CIM_ManagedSystemElement}
    __SERVER                : someserver
    __NAMESPACE             : root\cimv2
    __PATH                  : \\LXPRINT01\root\cimv2:Win32_PrinterDriver.Name="Xerox Global Print Driver PCL6,3,Windows NT
                              x86"
    Caption                 :
    ConfigFile              : C:\WINNT\system32\spool\DRIVERS\W32X86\3\UNIDRVUI.DLL
    CreationClassName       : Win32_PrinterDriver
    DataFile                : C:\WINNT\system32\spool\DRIVERS\W32X86\3\xUNIVXMS.gpd
    DefaultDataType         :
    DependentFiles          : {C:\WINNT\system32\spool\DRIVERS\W32X86\3\locale.gpd, C:\WINNT\system32\spool\DRIVERS\W32X86\
                              3\p6disp.gpd, C:\WINNT\system32\spool\DRIVERS\W32X86\3\p6font.gpd, C:\WINNT\system32\spool\DR
                              IVERS\W32X86\3\pcl5eres.dll...}
    Description             :
    DriverPath              : C:\WINNT\system32\spool\DRIVERS\W32X86\3\UNIDRV.DLL
    FilePath                :
    HelpFile                : C:\WINNT\system32\spool\DRIVERS\W32X86\3\UNIDRV.HLP
    InfName                 :
    InstallDate             :
    MonitorName             :
    Name                    : Xerox Global Print Driver PCL6,3,Windows NT x86
    OEMUrl                  : http://www.xerox.com
    Started                 :
    StartMode               :
    Status                  :
    SupportedPlatform       : Windows NT x86
    SystemCreationClassName : Win32_ComputerSystem
    SystemName              :
    Version                 : 3 
    

    You may say, yes this is a universal print driver for Xerox… but here is another one with a dedicated driver :

    __GENUS                 : 2
    __CLASS                 : Win32_PrinterDriver
    __SUPERCLASS            : CIM_Service
    __DYNASTY               : CIM_ManagedSystemElement
    __RELPATH               : Win32_PrinterDriver.Name="Xerox ColorQube 9303 PCL6new,3,Windows NT x86"
    __PROPERTY_COUNT        : 22
    __DERIVATION            : {CIM_Service, CIM_LogicalElement, CIM_ManagedSystemElement}
    __SERVER                : someotherserver
    __NAMESPACE             : root\cimv2
    __PATH                  : \\LXPRINT01\root\cimv2:Win32_PrinterDriver.Name="Xerox ColorQube 9303 PCL6new,3,Windows NT x8
                              6"
    Caption                 :
    ConfigFile              : C:\WINNT\system32\spool\DRIVERS\W32X86\3\UNIDRVUI.DLL
    CreationClassName       : Win32_PrinterDriver
    DataFile                : C:\WINNT\system32\spool\DRIVERS\W32X86\3\xDTESXN6.gpd
    DefaultDataType         :
    DependentFiles          : {C:\WINNT\system32\spool\DRIVERS\W32X86\3\locale.gpd, C:\WINNT\system32\spool\DRIVERS\W32X86\
                              3\p6disp.gpd, C:\WINNT\system32\spool\DRIVERS\W32X86\3\p6font.gpd, C:\WINNT\system32\spool\DR
                              IVERS\W32X86\3\pcl5eres.dll...}
    Description             :
    DriverPath              : C:\WINNT\system32\spool\DRIVERS\W32X86\3\UNIDRV.DLL
    FilePath                :
    HelpFile                : C:\WINNT\system32\spool\DRIVERS\W32X86\3\UNIDRV.HLP
    InfName                 :
    InstallDate             :
    MonitorName             :
    Name                    : Xerox ColorQube 9303 PCL6new,3,Windows NT x86
    OEMUrl                  : http://www.xerox.com
    Started                 :
    StartMode               :
    Status                  :
    SupportedPlatform       : Windows NT x86
    SystemCreationClassName : Win32_ComputerSystem
    SystemName              :
    Version                 : 3
    
    

    I am really interested if you have a method in finding the “real” version independently of the driver used… windows does it properly so there must be somewhere else to look … registry ?

    thx

    • Take a look here:
      HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Print\Environments\Windows x64
      HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class

      My understanding is that related keys are created/populated at the moment of device installations.

  4. looking at the registry I get better information but it is a bit harder to retrieve it :

    PS D:\scripts\powershell\ServerInventory> $xerox=Get-RegValue -ComputerName server1 -key $regkey
    PS D:\scripts\powershell\ServerInventory> $xerox
    
    ComputerName Hive            Key                  Value                     Data                 Type
    ------------ ----            ---                  -----                     ----                 ----
    server1    LocalMachine    SYSTEM\CurrentCon... Configuration File        UNIDRVUI.DLL         String
    server1    LocalMachine    SYSTEM\CurrentCon... Data File                 xUNIVXMS.gpd         String
    server1    LocalMachine    SYSTEM\CurrentCon... Driver                    UNIDRV.DLL           String
    server1    LocalMachine    SYSTEM\CurrentCon... Help File                 UNIDRV.HLP           String
    server1    LocalMachine    SYSTEM\CurrentCon... Monitor                                        String
    server1    LocalMachine    SYSTEM\CurrentCon... Datatype                                       String
    server1    LocalMachine    SYSTEM\CurrentCon... Dependent Files           {locale.gpd, p6di... MultiString
    server1    LocalMachine    SYSTEM\CurrentCon... Previous Names            {}                   MultiString
    server1    LocalMachine    SYSTEM\CurrentCon... Version                   3                    DWord
    server1    LocalMachine    SYSTEM\CurrentCon... TempDir                   0                    DWord
    server1    LocalMachine    SYSTEM\CurrentCon... Attributes                2                    DWord
    server1    LocalMachine    SYSTEM\CurrentCon... Manufacturer              Xerox                String
    server1    LocalMachine    SYSTEM\CurrentCon... OEM URL                   http://www.xerox.com String
    server1    LocalMachine    SYSTEM\CurrentCon... HardwareID                wsdprint\xeroxpha... String
    server1    LocalMachine    SYSTEM\CurrentCon... Provider                  Xerox                String
    server1    LocalMachine    SYSTEM\CurrentCon... DriverDate                {0, 128, 222, 60...} Binary
    server1    LocalMachine    SYSTEM\CurrentCon... DriverVersion             {0, 0, 0, 0...}      Binary
    

    The “DriverVersion” is has follow : 0 0 0 0 64 6 183 20

    which doesn’t coincide at all with the “real” driver version which is 5.303.16.0N 2013.02.08.

    I am looking on where that info is located… it eludes me for now .. the version I get from my script above is 6.0.5479.0 which doesn’t coincide at all with the current driver version … the hunt continues ..

  5. And the search is over …
    The solution was indeed in the registry.
    Problem was that the value was stored as Hexa, by pair and in reverse order …
    ie for my xerox it was stored as 00 00 00 00 40 06 b7 14
    so translated :
    14b7 : 5303
    0640 : 1600
    and here is my driver version : 5303.1600

    so here is my “final script” hoping it will help someone.
    I set it as a function.
    also I am using a nice module PSRemoteRegistry that allows to remotely check registry keys… very neat.

     
    if ( (Get-module -Name PSRemoteRegistry -ErrorAction SilentlyContinue) -eq $null )
    {
        Write-Progress -Activity "Loading psregistry module" -status "Please wait ..." -id 1
        Import-Module .\PSRemoteRegistry
    }
    #set log file
    $logfile="runlog.txt"
    " " | out-file "$logfile"
    function GETPRINTERDRIVER {
    foreach ($Computer in $StrComputer) {
            $Computer | out-file -append $logfile
            Write-Progress -Activity "Checking $Computer " -status "Please wait ..." -id 1
            $regkeyroot="SYSTEM\CurrentControlSet\Control\Print\Environments\"
            $regkeyWinversion=("Windows x64","Windows NT x86")
            foreach ($reg in $regkeyWinversion) {
                    $reg | out-file -append $logfile
                    $regkey=$regkeyroot+"\$reg\Drivers\Version-3"
                    $keyDriverList=Get-Regkey -ComputerName $Computer -key $regkey
                    Write-Progress -Activity "Loading $reg hive" -status "Please wait ..." -id 2
                    $dcount=$keyDriverList.count + 1
                    $d1=0
                    foreach ($hive in $keyDriverList) {
                            $d1++
                            $key=$hive.key
                            Write-Progress -Activity "Loading $d1 / $dcount drivers"  -status "loading $key" -id 2
                            $key | out-file -append $logfile
                            $drivers=Get-Regvalue -ComputerName $Computer -key $key
                            $version=""
                            foreach ($d in $drivers) {
                                    $d | out-file -append $logfile
                                    $temp=$hive.key.split('\')
                                    $n=$temp.count - 1
                                    $driverName=$temp[$n]
                                    Write-Progress -Activity "Checking $reg hive " -status "$driverName" -id 2
                                    #$driverName  | out-file -append "testdriver.txt"
                                    #we loop each properties to get driver version .. this is needed as some drivers have more properties than others
                                    "$driverName - $d.value" | out-file -append $logfile
                                    if ($d.value -eq "DriverVersion") {
                                            $driver=$d
                                            $driver.data.count | out-file -append $logfile
                                            if ($driver.data.count -gt 1){
                                                    #converting data value to dec and pulling it all together 8 value by pair and reversed.
                                                    $v1="0x"+"{0:X0}" -f $driver.data[7] + "{0:X0}" -f $driver.data[6]
                                                    $v1=[system.convert]::toint32($v1,16)
                                                    $v2="0x"+"{0:X0}" -f $driver.data[5] + "{0:X0}" -f $driver.data[4]
                                                    $v2=[system.convert]::toint32($v2,16)
                                                   
                                                    $v3="0x"+"{0:X0}" -f $driver.data[3] + "{0:X0}" -f $driver.data[2]
                                                    $v3=[system.convert]::toint32($v3,16)
                                                    $v4="0x"+"{0:X0}" -f $driver.data[1] + "{0:X0}" -f $driver.data[0]
                                                    $v4=[system.convert]::toint32($v4,16)
                                                    $version="$v1.$v2.$v3.$v4"
                                            }
                                            else
                                            {
                                                    $version = $driver.data
                                                    $version | out-file -append $logfile
                                            }
                                            #$version | out-file -append "testdriver.txt"
                                    }
                                   
                                   
                            }
                            if ($version -eq ""){
                                            $version = "not available"
                                            $version | out-file -append $logfile
                                    }
                            $PrintDriver += New-Object PSObject -Property @{
                                    Device_name=$Computer
                                    Name = $driverName
                                    DriverVersion=$version
                                    Version = $reg
                                    }
                            }
                    }
            }
    return $PrintDriver
    }
    $StrComputer="plprint01"
    $global:PrintDriver=@()
    
    $global:PrintDriver = GETPRINTERDRIVER
    $global:PrintDriver
    

Leave a Comment here