Partilhar via


Getting Monitor Hardware Information

Ok, so I forgot how to post to this blog for a couple of years.  I finally had a reason to track it down.

First, I’d like to acknowledge an unknown scripter who created this script.  I tried to track down the original  author of the VBScript I started with.  This particular script is posted on a variety of forums with a number of edits along the way—presumably added by multiple people, and I was unable to locate the original source, but to this unknown person, thank you.

Now, I took this quite complex script which collects EDID information from the registry.  I converted it to PowerShell.  Like most VBScript to PowerShell conversions, the new script is greatly less complicated and more readable.

This script utilizes PowerShell Remoting (https://technet.microsoft.com/en-us/library/dd347744.aspx) if a computer list is supplied on the pipeline, so you would need to make sure that you have configured PowerShell for remoting.  If you don’t supply a computer list, the script block will be executed on the local computer.

There is an optional LogPath parameter.  You would run it as follows:

  • .\Get-MonitorInfo.ps1
  • "computer1","computer2","computer3" | .\Get-MonitorInfo.ps1 -LogPath
    .\monitorlist.csv

 

Param ([string] $LogPath)

$ComputerList = @()

# Computer names should be piped into the script (comma separated)

$input | ForEach-Object {$ComputerList += $_}

 

Function Main {

      If ($ComputerList){

            # If a list of computers was specified on the pipeline, execute

            # the scriptblock remotely against all in a single job

            $Job = Invoke-Command -ScriptBlock $ScriptBlock -AsJob `

                  -JobName EDIDInfo -ComputerName $ComputerList

            # Wait for the job to complete

            Wait-Job -Job $Job

            # Receive data from the job

            $Data = Receive-Job $Job

            # Once we have received the data, we can remove the job

            Remove-Job -Job $Job

      } Else {

            # If a computer list was not specified, we don't need a job.

            # We can get the data directly by running the scriptblock

            Write-Warning ("A computer list was not specified. The script " `

                  + "will run against the local computer.")

            $Data = (& $ScriptBlock)

      }

      If ($LogPath){

            # If a log path was specified (using the -LogPath parameter),

            # export the data to it.

            $Data | Export-Csv $LogPath -NoTypeInformation

      }

      # Display the collected data in the default host (usually the console)

      $Data | Format-Table PSComputerName,VendorID,ModelName,DeviceID `

            ,SerialNumber,ManufactureDate,EDIDVersion

}

 

 

$ScriptBlock = {

      # This script block will always run locally on the target

      # $EDIDByteArrays will hold all of the collected EDID values from the

      # registry.

      $EDIDByteArrays = @()

 

      # $KeysWithControl will contain the path to the key for each display

      # device whose "Control" subkey has an ActiveService value--meaning

      # that the device is currently being used.

      $KeysWithControl = (

            dir HKLM:\system\currentcontrolset\enum\display\*\*\control\ `

            | ForEach-Object{

                  $_.PsParentPath

            }

      )

     

      $KeysWithControl | ForEach-Object{

            # Enumerate the "Device Parameters" subkey for each key

            # identified above

            dir "$($_)\device parameters*" | ForEach-Object {

                  # The "EDID" value of the "Device Parameters" key contains

                  # the EDID data (byte array)

                  $EDIDByteArray = $_.GetValue("EDID")

                  # Add the current value to $EDIDByteArrays

                  # Since the value we're adding is a byte array and don't want

                  # to append it to the current array, we need to add a new

                  # element first and then assign the whole value to the new element

                  $EDIDByteArrays += ""

                  $EDIDByteArrays[($EDIDByteArrays.GetUpperBound(0))] = $EDIDByteArray

            }

      }

     

      # Now we will parse each EDID value. For information about the data structure,

      # see https://en.wikipedia.org/wiki/Extended\_display\_identification\_data

     

      # $Descriptors will contain the four prescribed descriptors

      $Descriptors = @("","","","")

      # A serial number descriptor starts with: 00 00 00 ff

      [Object[]] $SerialNumberHeader = ([byte]0x00,[byte]0x00,[byte]0x00,[byte]0xFF)

      # A model name descriptor starts with: 00 00 00 fc

      [Object[]] $ModelNameHeader = ([byte]0x00,[byte]0x00,[byte]0x00,[byte]0xFC)

     

      $EDIDByteArrays | Foreach-Object {

      $EDIDValue = $_

            # If this is a valid EDID value...

      If($EDIDValue){

                  # Populate the four descriptors with each subset of bytes

                  $Descriptors[0] = $EDIDValue[54..71]

                  $Descriptors[1] = $EDIDValue[72..89]

                  $Descriptors[2] = $EDIDValue[90..107]

                  $Descriptors[3] = $EDIDValue[108..125]

      # Initialize Values to "Not Found"

                  $SerialNumber = "Not Found"

                  $ModelName = "Not Found"

                  $ManufacturerID = "Not Found"

                  $DeviceID = "Not Found"

                  $ManufactureDate = "Not Found"

                  $EDIDVersion = "Not Found"

                 

                  $Descriptors | ForEach-Object {

                        # Get the header of the current descriptor to determin if it

                        # is a model name or serial number. These are two of several

                        # types of optional descriptors, and they may be in any order

                        # If the header is a match, the value will be at offset 5

                        $DescriptorHeader = $_[0..3]

                        If(($DescriptorHeader -join " ") -eq ($SerialNumberHeader -join " ")){

                              $SerialNumber =

                                    [System.Text.Encoding]::ASCII.GetString($_[5..17]).Trim()

                        }

                        If(($DescriptorHeader -join " ") -eq ($ModelNameHeader -join " ")){

                              $ModelName =

                                    [System.Text.Encoding]::ASCII.GetString($_[5..17]).Trim()

                        }

                  }

                 

                  # The manufacture date is stored as follows: Week is byte 16.

                  # Year (as offset from 1990) is byte 17.

                  $ManufactureWeek = $EDIDValue[16]

      $ManufactureYear = 1990 + $EDIDValue[17]

                  # Format "MM/yyyy"

      $ManufactureDate = (get-date "1/1/$($ManufactureYear)").AddDays(

                        7 * $ManufactureWeek).ToString("MM/yyyy")

     

      # EDID version is stored in bytes 18 & 19

      $EDIDMajorVersion = $EDIDValue[18].ToString()

      $EDIDRevision = $EDIDValue[19].ToString()

      $EDIDVersion = "$($EDIDMajorVersion).$($EDIDRevision)"

     

                  # The manufacturerID is a three character id stored in a two

                  # bytes (8 & 9)

                  $ManufacturerIDEncoded = $EDIDValue[8..9]

                  # The easiest way to work with it is to convert it to a

                  # concatenated binary string

                  $ManufacturerIDBinaryString = ($ManufacturerIDEncoded | ForEach-Object{

                        [System.Convert]::ToString($_,2).PadLeft(8,"0")

                  }) -join ""

                  # Parse out the three five-byte character codes (1=A) and add

                  # 64 to get the real character code

                  $ManufacturerFirstLetterCode =

                  [system.convert]::ToInt32($ManufacturerIDBinaryString.substring(1,5),2) + 64

                  $ManufacturerSecondLetterCode =

                  [system.convert]::ToInt32($ManufacturerIDBinaryString.substring(6,5),2) + 64

                  $ManufacturerThirdLetterCode =

                  [system.convert]::ToInt32($ManufacturerIDBinaryString.substring(11,5),2) + 64

                  # Concatenate the three character codes into a byte array

                  [byte[]] $ManufacturerIDByteArray =

                        (

                              $ManufacturerFirstLetterCode,

                              $ManufacturerSecondLetterCode,

                              $ManufacturerThirdLetterCode

                        )

                  # Convert the byte array to a string

                  $ManufacturerID =

                        [system.text.encoding]::Default.GetString($ManufacturerIDByteArray)

 

                  # The device id is stored in bytes 10 and 11 (16-bit little endian)

                  $DeviceID = ("{0:X}" -f $EDIDValue[11]).PadLeft(2,"0") `

                        + ("{0:X}" -f $EDIDValue[10]).PadLeft(2,"0")

                 

                  # Put all of the values into a new custom object on the pipeline

            $Properties = @{

                              VendorID=$ManufacturerID;

                              DeviceID=$DeviceID;

                              ManufactureDate=$ManufactureDate;

                              SerialNumber=$SerialNumber;

                              ModelName=$ModelName;

                              EDIDVersion=$EDIDVersion

                  }

                  New-Object psobject -Property $Properties

      }

      }

# end script block

}

 

Main

Comments

  • Anonymous
    March 23, 2014
    Thanks for sharing this. It's really helped me out converting a ConfigMgr custom inventory script from VBScript to PowerShell.
  • Anonymous
    April 29, 2014
    Seems like on Windows 8 / 8.1 the "Control" Key is no longer maintained in the Registry
  • Anonymous
    May 15, 2014
    Any news for Windows 8 / 8.1 about the "Control" key missing ?

    What can we do to retreive the serial number ???