Hi everybody,

those of you out there working with the Quest KACE SMA and DELL Clients with the DELLCommand|Monitor software toolkit installed may know that you can see which external screens are connected to your client machines:

9k=

Today I want to show you a script that can provide the same information to you if you do not run Dell machines or you don’t have Dell Command|Monitor installed.
In other words: a universal (Windows-based) solution to get your screen-inventory into your KACE SMA! :)

The solution requires at least two and a third optional step:

1. a VBScript that is run periodically as a KACE SMA script
2. a custom inventory rule (CIR) to read the script results into your inventory
3. (optional) setup a report that gives you an overview of all your screens



1. The Script

The script reads the external screens information from the WMI interface of your Windows installation. Please note that in the present design it just handles EXTERNAL screens. INTERNAL screens of notebooks may be added as well (see the comments inside the VBScript to change that). 
The script run results are saved to the file "c:\windows\MonitorInfo.txt". If you don’t like that path feel free to adjust it, you can edit this at the bottom of the script.

So first you need to copy and paste the following script code into a text editor (I prefer Notepad++) of your choice and save it, for example as “DisplayInformation.vbs”:


Function BytesToString(ByVal Bytes)
  Dim Result, N
  Result = ""
  If IsNull(Bytes) Then
	BytesToString = ""
	Exit Function
  End If
  For N = 0 To UBound(Bytes)
    If CInt(Bytes(N)) <> 0 Then
      Result = Result & Chr(Bytes(N))
    Else
      Exit For
    End If
  Next
  BytesToString = Result
End Function

Function GetConnectionType(ByVal intType)
	GetConnectionType = ""
	Select Case intType
		Case -2 GetConnectionType = "uninitialised"
		Case -1 GetConnectionType = "other"
		Case 0 GetConnectionType = "VGA"
		Case 1 GetConnectionType = "S-Video"
		Case 2 GetConnectionType = "Composite Video"
		Case 3 GetConnectionType = "Component Video"
		Case 4 GetConnectionType = "DVI"
		Case 5 GetConnectionType = "HDMI"
		Case 6 GetConnectionType = "LVDS/MIPI"
		Case 8 GetConnectionType = "D-Jpn"
		Case 9 GetConnectionType = "SDI"
		Case 10 GetConnectionType = "Display Port (external)"
		Case 11 GetConnectionType = "Display Port (internal)"
		Case 12 GetConnectionType = "UDI (external)"
		Case 13 GetConnectionType = "UDI (internal)"
		Case 14 GetConnectionType = "SDTV Dongle"
		Case 15 GetConnectionType = "Miracast"
	End Select
End Function

Dim WMI, Monitors, Output, Monitor, BasicDisplayParams, BDP, ListedSupportedSourceModes, LSSM, MonitorConnectionParams, MCP
Dim MaxHorImgSize, MaxVertImgSize

Dim filesys, filetxt
Set filesys = CreateObject("Scripting.FileSystemObject") 

Output = ""

Set WMI = GetObject("winmgmts:{impersonationlevel=impersonate}!root/wmi")

Set MonitorConnectionParams = WMI.InstancesOf("WmiMonitorConnectionParams")
For Each MCP in MonitorConnectionParams

	'Comment the following line and line 100 (End If) to output internal screens as well
	If MCP.VideoOutputTechnology < 2147483648 And MCP.VideoOutputTechnology > -2147483648 Then

		Set Monitors = WMI.InstancesOf("WmiMonitorID")
			For Each Monitor In Monitors
			
				If Monitor.InstanceName = MCP.InstanceName Then
				
					MaxHorImgSize = 0.0
					MaxVertImgSize = 0.0
					
					If Len(Output) <> 0 Then Output = Output & vbNewLine & vbNewLine
					
					Output = Output & "Description: " & BytesToString(Monitor.UserFriendlyName)
					Output = Output & vbNewLine & "Manufacturer Code: " & BytesToString(Monitor.ManufacturerName) 
					'Output = Output & vbNewLine & "ProductCode ID: " & BytesToString(Monitor.ProductCodeID)
					Output = Output & vbNewLine & "Serialnumber: " & BytesToString(Monitor.SerialNumberID)
					'Output = Output & vbNewLine & "Active: " & CStr(Monitor.Active)
					'Output = Output & vbNewLine & "InstanceName: " & Monitor.InstanceName
					Output = Output & vbNewLine & "Manufactured: " & CStr(Monitor.YearOfManufacture) & " Week " & CStr(Monitor.WeekOfManufacture)
					
					Set BasicDisplayParams = WMI.InstancesOf("WmiMonitorBasicDisplayParams")
					For Each BDP In BasicDisplayParams
						If BDP.InstanceName = Monitor.InstanceName Then
							MaxHorImgSize = BDP.MaxHorizontalImageSize
							MaxVertImgSize = BDP.MaxVerticalImageSize
							If MaxHorImgSize <> 0 And MaxVertImgSize <> 0 Then
								Output = Output & vbNewLine & "Screen Size (cm): " & CStr(MaxHorImgSize) & "x" & CStr(MaxVertImgSize)
								Output = Output & vbNewLine & "Diagonale (inch): " & CStr( Round((Sqr((MaxHorImgSize^2) + (MaxVertImgSize^2))/2.54),1) )
								Output = Output & vbNewLine & "Diagonale (cm): " & CStr( Round((Sqr((MaxHorImgSize^2) + (MaxVertImgSize^2))),1) )
							End If
						End If
					Next
					
					Set ListedSupportedSourceModes = WMI.InstancesOf("WmiMonitorListedSupportedSourceModes")
					For Each LSSM in ListedSupportedSourceModes
						If LSSM.InstanceName = Monitor.InstanceName Then
							Output = Output & vbNewLine & "Best Resolution: " & CStr( LSSM.MonitorSourceModes(LSSM.PreferredMonitorSourceModeIndex).HorizontalActivePixels ) & "x" & CStr( LSSM.MonitorSourceModes(LSSM.PreferredMonitorSourceModeIndex).VerticalActivePixels )
						End If
					Next
					Output = Output & vbNewLine & "Connection Type: " & GetConnectionType(MCP.VideoOutputTechnology)
					
				End If
			Next
	
	'Comment the following line and line 55 (If MCP.VideoOutputTechnology...) to output internal screens as well
	End If
	
Next


WScript.Echo "Detected Screen(s):"
WScript.Echo "-------------------"
WScript.Echo Output

If Len(Output) <> 0 Then
	'recreate output-file
	If filesys.FileExists("c:\windows\MonitorInfo.txt") Then 
		filesys.DeleteFile "c:\windows\MonitorInfo.txt"
	End If 
	filesys.CreateTextFile("c:\windows\MonitorInfo.txt"), True
	'Write
	Set filetxt = filesys.OpenTextFile("c:\windows\MonitorInfo.txt", 8, True)
	filetxt.Write(Output)
	filetxt.Close
End If


Now go to the Scripting section of your KACE SMA and create a new script. Be sure to do the following:

  • Provide a nifty name for the script
  • Script type is “Online KScript”
  • DON’T FORGET TO TICK “ENABLE” AT YOUR SCRIPT AFTER TESTING! (don’t know how often I forgot that...)
  • Select a bunch of machines, labels or even all devices for deployment
  • Select at least one Windows OS as target “Operating Systems”
  • Run as “Local System”
  • Choose an appropriate schedule (I took every 24 hours)
  • Enable “Allow run without a logged-in user”
  • Preferably enable “Run on next connection if offline”
  • Upload your previously created script file (“DisplayInformation.vbs”) as dependency

Inside the script you just need one single task with one single step in the “On Success”-section:

  • Step type is “Launch a program...”
  • “Directory” is:
    $(KACE_SYS_DIR)
  • “File” is:
    cscript.exe
  • Enable “Wait for completion”
  • Disable “Visible”
  • Parameters: (include the quotation marks!)
    //nologo "$(KACE_DEPENDENCY_DIR)\DisplayInformation.vbs"


…so it should look like this in the end:

2Q==

That’s all for the script. Now save everything and test run it on a couple of machines. You should see an appropriate output in the script run’s log file and, of course, in the output file of the script on the local machine ("c:\windows\MonitorInfo.txt" if you did not change it).


2. The Custom Inventory Rule (CIR)

To attach the generated info to the machines inventory you need to create a custom inventory rule that allows the KACE SMA to upload the content to its database.
Here is how:

Go to your “Inventory” and inside the “Software” section, hit the “Choose Action” button and then click “New”.
Z

Now fill the “Name” field. You can enter anything meaningful you like, e.g. “External Screens”.
In “Supported Operating Systems” you have to select at least the Windows OS you chose at the KScript before!
I suggest you pick all Windows Client OS in both cases. Don’t worry, neither the script nor the custom inventory rule will eat up performance or memory on your systems.

Most important: fill the text box “Custom Inventory Rule:” with this one:

ShellCommandTextReturn(cmd /c type C:\Windows\MonitorInfo.txt)


All other fields are not necessary to fill for making this custom inventory rule work. Just save it now and you're done.

After setting up this custom inventory rule you should see output like this in a machines inventory under “Software” in the “Custom Inventory Fields” section:
Z

NOTE THAT THE KSCRIPT MUST HAVE RUN SUCCESSFULLY FIRST ON THE MACHINE(S) AND THE MACHINE(S) NEED TO DO AN INVENTORY BEFORE YOU’LL SEE DATA HERE! (Of course, you can force inventory if you want to)


3. Optional: the Report

If you want a pretty report about all the screens in your network, here is how:

First we need to grab the ID of the custom inventory rule we created in step 2. Therefore go back to your software inventory, locate the custom inventory software you just created and check the tooltip info if you hover the link:

2Q==


You can also right click the link and copy the links’ address to the clipboard, paste it into a text editor and check it out there.

What we need is the value behind the equal-sign.
In my case the URL is https://kacesmaserverurl/adminui/software.php?ID=8793 and so the ID we want is 8793. IT WILL BE A DIFFERENT VALUE IN YOUR SETUP ALMOST FOR SURE. Note it somewhere, we’ll need it in a moment.

Let’s create the report now. Go to the “Reporting” section of the SMA, hit “Choose Action” and then click “New (SQL)”.

Z


Enter a useful name in the “Title” field and assign a fitting category.

I suggest to disable “Show Line Numbers” unless explicitly needed.

I love the report output with “Break on Columns:” set to “Screen square” (without quotation marks. You may change this at any time if you do not like it.

Most important, here is the SQL command required:

Select
  x.Computername,
  x.Computerbenutzer As `Computer user`,
  x.Computermodell As `Computer model`,
  x.Computerhersteller As `Computer manufacturer`,
  x.Computerbauart As `Computer type`,
  x.ComputerSN As `Computer serialnumber`,
  x.ComputerOS As `Computer OS`,
  x.Monitormodell As `Screen model`,
  x.Monitorhersteller As `Screen manufacturer`,
  x.MonitorSN As `Screen serialnumber`,
  x.MonitorSquare As `Screen square`,
  x.MonitorRes As `Screen resolution`,
  x.MonitorConn As `Screen connection type`,
  x.MoniAgeYears As `Screen age (years)`,
  x.MoniAgeDays As `Screen age (days)`
From
  (Select
    mach.NAME As Computername,
    mach.USER_FULLNAME As Computerbenutzer,
    mach.CS_MODEL As Computermodell,
    mach.CS_MANUFACTURER As Computerhersteller,
    mach.CHASSIS_TYPE As Computerbauart,
    mach.BIOS_SERIAL_NUMBER As ComputerSN,
    mach.OS_NAME As ComputerOS,
    SubString_Index(SubString(mci.STR_FIELD_VALUE From Locate('Description: ',
    mci.STR_FIELD_VALUE) + 13), '<br/>', 1) As Monitormodell,
    SubString_Index(SubString(mci.STR_FIELD_VALUE From
    Locate('Manufacturer Code: ', mci.STR_FIELD_VALUE) + 19), '<br/>',
    1) As Monitorhersteller,
    SubString_Index(SubString(mci.STR_FIELD_VALUE From Locate('Serialnumber: ',
    mci.STR_FIELD_VALUE) + 14), '<br/>', 1) As MonitorSN,
    SubString_Index(SubString(mci.STR_FIELD_VALUE From
    Locate('Diagonale (inch): ', mci.STR_FIELD_VALUE) + 18), '<br/>',
    1) As MonitorSquare,
    SubString_Index(SubString(mci.STR_FIELD_VALUE From
    Locate('Best Resolution: ', mci.STR_FIELD_VALUE) + 17), '<br/>',
    1) As MonitorRes,
    SubString_Index(SubString(mci.STR_FIELD_VALUE From
    Locate('Connection Type: ', mci.STR_FIELD_VALUE) + 17), '<br/>',
    1) As MonitorConn,
    SubString_Index(SubString(mci.STR_FIELD_VALUE From Locate('Manufactured: ',
    mci.STR_FIELD_VALUE) + 14), '<br/>', 1) As MoniAgeInfo,
    SubString_Index(SubString(mci.STR_FIELD_VALUE From Locate('Manufactured: ',
    mci.STR_FIELD_VALUE) + 14), ' Week ', 1) As MoniAgeYear,
    LPad(SubString_Index(SubString(mci.STR_FIELD_VALUE From Locate(' Week ',
    mci.STR_FIELD_VALUE) + 6), '<br/>', 1), 2, '0') As MoniAgeWeek,
    Str_To_Date(Concat(SubString_Index(SubString(mci.STR_FIELD_VALUE From
    Locate('Manufactured: ', mci.STR_FIELD_VALUE) + 14), ' Week ', 1),
    LPad(SubString_Index(SubString(mci.STR_FIELD_VALUE From Locate(' Week ',
    mci.STR_FIELD_VALUE) + 6), '<br/>', 1), 2, '0'), ' Monday'), '%x%v %W') As
    MoniAgeRefDate,
    DateDiff(Now(),
    Str_To_Date(Concat(SubString_Index(SubString(mci.STR_FIELD_VALUE From
    Locate('Manufactured: ', mci.STR_FIELD_VALUE) + 14), ' Week ', 1),
    LPad(SubString_Index(SubString(mci.STR_FIELD_VALUE From Locate(' Week ',
    mci.STR_FIELD_VALUE) + 6), '<br/>', 1), 2, '0'), ' Monday'), '%x%v %W')) As
    MoniAgeDays,
    Round((DateDiff(Now(),
    Str_To_Date(Concat(SubString_Index(SubString(mci.STR_FIELD_VALUE From
    Locate('Manufactured: ', mci.STR_FIELD_VALUE) + 14), ' Week ', 1),
    LPad(SubString_Index(SubString(mci.STR_FIELD_VALUE From Locate(' Week ',
    mci.STR_FIELD_VALUE) + 6), '<br/>', 1), 2, '0'), ' Monday'), '%x%v %W')) /
    365), 1) As MoniAgeYears
  From
    MACHINE mach Inner Join
    MACHINE_CUSTOM_INVENTORY mci
      On mach.ID = mci.ID
  Where
    mci.SOFTWARE_ID = 8793 And
    mci.STR_FIELD_VALUE Like '%Description: %') As x
Order By
  `Screen square` Desc,
  x.MoniAgeRefDate Desc

Before saving and testing your report it is REQUIRED you change the software ID in the line “mci.SOFTWARE_ID = 8793 And” to the value you noted down before or the report won’t show anything!

If everything went right and your machines ran the script and at least one inventory cycle (already mentioned in step 2) you should now be able to run the report and get a list table of all your screens!

 

That’s it for today, if you got any questions drop a comment below!