Monitor a workstation for package arrival (local)

When you are waiting for a package to arrive and process at a workstation, this script will notify you when the Referenced CacheElements count changes.

With no parameters, it just displays the current information, plus an extensive summary of the cache details.

Parameter: WAIT will wait until one change and then exit

Parameter: WATCH will keep displaying as changes occur without exiting.

Features:

  • It will relaunch itself if it finds it’s running as a WSCRIPT program instead of the desired CSCRIPT program.
  • It shows the numbers using a big display font, for easy-to-see changes
  • Works on remote machines
[VBScript code]
' Script to monitor arrival of a package into the cache, etc
' Author: Roger C
' Strategy:	Read the list of REFERENCED classic packages from the cache
'			Also read the list of files, so we cna detect if files come or go
'			repeatedly check the cache, and trigger when the item count changes

Option Explicit

forceCScriptExecution ' avoid running as a wscript, which shows popup message boxes

'======================================================================
'---[ Define variables
'======================================================================
Dim service
Dim oUIResManager
Dim oCache
Dim oCacheElement
Dim oCacheElements, oPrograms
Dim strWMIClass, oWMI
Dim strQuery
Dim NextAvailableID
Dim ColItems
Dim CurrentElementCount, CurrentReferenceCount, CurrentProgramsCount
Dim objItem
Dim arrCurrentFilenames(0), arrPreviousFilenames() 
dim OldElementCount, OldReferenceCount

Dim i
Dim Version : Version = "2.0.0.0"

Dim arg()
Dim HostName: Hostname = ""
Dim Verbose : Verbose=False
Dim WATCH: WATCH = False
Dim WAIT: WAIT = False
Dim Verb : Verb = "Details for "
DIM  RelaunchedAsCscript : RelaunchedAsCscript = False

'======================================================================
'---[ Initial processing of arguments
'======================================================================
reDim arg(wscript.arguments.count)
for i = 0 to wscript.arguments.count - 1
	arg(i) = ucase(wscript.arguments(i))
	if instr(arg(i),"HOST:") then
		HostName = Mid(arg(i),instr(arg(i),"HOST:") + 5)
	end if
	if instr(arg(i),"HOSTNAME:") then
		HostName = Mid(arg(i),instr(arg(i),"HOSTNAME:") + 9)
	end if
	if arg(i)="VERBOSE" or arg(i)="-VERBOSE" or arg(i)="/VERBOSE" then
		Verbose=True
	end if
	if arg(i)="WATCH" or arg(i)="-WATCH" or arg(i)="/WATCH" then
		WATCH=True
	end if
	if arg(i)="WAIT" or arg(i)="-WAIT" or arg(i)="/WAIT" then
		WAIT=True
	end if
	if arg(i)="RELAUNCHED" then
		RelaunchedAsCscript = true
	end if
	if arg(i)="/?" or arg(i)="-?" or arg(i)="/HELP" or arg(i)="-HELP" or arg(i)="HELP" then 
		wscript.echo left(wscript.ScriptName,len(wscript.ScriptName)-4) & "[v." & Version & "]"
		wscript.echo "Displays information about and monitors changes to the the SCCM cache" & vbcrlf
		wscript.echo "Parameters:"
		wscript.echo "   HOST : Name/IP of the remote host to monitor"
		wscript.echo "   WAIT : Wait for the referenced cache items to change once"
		wscript.echo "  WATCH : Wait forever for the referenced cache items to change"
		wscript.echo "VERBOSE : Display addional information about the cache" & vbcrlf
		CleanupAndExit()
	end if
next

if HostName = "" then 
	HostName = "localhost"
end if

if WATCH then Verb ="Watching for change to "
if WAIT  then Verb ="Waiting for change to "
if Verb = "Details for " then Verbose = True
wscript.echo Verb &  Hostname

'======================================================================
'---[ Confirm WMI is operational
'======================================================================
on error resume next
set service = GetObject ("winmgmts:\\" & Hostname )
on error goto 0
if not IsObject(service) then
	LogWriteln("Can't connect to WMI interface on " & Hostname	)
	CleanupAndExit()
end if
		
'======================================================================
'---[ Connect to SCCM class
'======================================================================
on error resume next
set oUIResManager = createobject("UIResource.UIResourceMgr")
on error goto 0
if NOT isobject(oUIResManager) then
    wscript.echo "Can't create WMI connection to SCCM Resource Manager"
	CheckForCCMEXECProcess()		
    CleanupAndExit()
end if

'======================================================================
'---[ read the basic Cache info
'======================================================================
on error resume next
set oCache=oUIResManager.GetCacheInfo()
on error goto 0
if oCache is nothing then
	LogWriteln("Error accessing oUIResManager.GetCacheInfo()")
    wscript.echo "Couldn't get available cache info"
    set oUIResManager=nothing
	CheckForCCMEXECProcess()		
    CleanupAndExit()
end if
if Verbose then 
	wscript.echo "TotalSize: " & oCache.TotalSize /1024 & " GB"
	wscript.echo "FreeSize: " & oCache.FreeSize /1024 & " GB"
	wscript.echo "Location: " & oCache.Location
	wscript.echo "MaxCacheDuration: " & oCache.MaxCacheDuration /60/24 & " days"
	wscript.echo "ReservedSize: " & oCache.ReservedSize & " GB"
	wscript.echo "TombstoneDuration: " & oCache.TombstoneDuration /60 & " Hours"
end if

'======================================================================
'---[ read the next available Cache ID (next folder name) using WMI
'======================================================================
strWMIClass = "winmgmts:\\" & Hostname & "\root\ccm\SoftMgmtAgent"
on error resume next
Set oWMI = GetObject(strWMIClass)
on error goto 0
if not IsObject(oWMI) then	' didn't read it
	CheckForCCMEXECProcess()		
	CleanupAndExit()
end if
on error goto 0

strQuery = "SELECT * FROM CacheConfig"

on error resume next
Set colItems = oWMI.ExecQuery(strQuery,,48)
on error goto 0

NextAvailableID = "none"
if isobject(colItems) then
	on error resume next
	For Each objItem In colItems
		NextAvailableID = objItem.NextAvailableId ' read the only record, to get the nextavailableID
	Next
	on error goto 0

	if Err.number  0 then
		LogWriteln("CacheConfig WMI query did not return a collection: [" & strWMIClass & " : " & strQuery & "]. Aborting.")
		CleanupAndExit()
	end if
	on error goto 0
else
	LogWriteln("Error: no results from query against local WMI class: [" & strWMIClass & " : " & strQuery & "]. Aborting.")
	CleanupAndExit()
end if
if Verbose then LogWriteln("NextAvailableID: " & NextAvailableID)

'======================================================================
'---[ read the Scripts
'======================================================================
on error goto 0
set oCache=oUIResManager.GetAvailableScripts()
if oCache is nothing then
     set oUIResManager=nothing
     wscript.echo "Couldn't get available scripts info"
end if
if Verbose then wscript.echo "Scripts: " & oCache.Count

'======================================================================
'---[ read the Program elements
'======================================================================
if Verbose then 
	set oPrograms = createobject("UIResource.Programs") ' connect to Class: PROGRAMS
	if oPrograms is nothing then
		wscript.echo "Couldn't get programs info"
	else
		CurrentProgramsCount = oPrograms.Count 
		wscript.echo "Programs: " & CurrentProgramsCount
		for i = 1 to CurrentProgramsCount
			wscript.echo oPrograms.item(i)
		next
	end if 	
end if
'======================================================================
'---[ read the application items
'======================================================================
' ** these are applications (not programs), which are not used in this organization

if Verbose then 
	on error goto 0
	set oUIResManager = createobject("UIResource.UIResourceMgr")
	set oCache=oUIResManager.GetAvailableApplications()  ' class is Programs
	if oCache is nothing then
		set oUIResManager=nothing
		wscript.echo "Unable to read applications"
	end if
	wscript.echo "Applications: " & oCache.Count
	if oCache.Count > 0 then 
		for i = 0  to oCache.Count + 1
			wscript.echo "Applications: " & oCache.item(i).Name
		next
	end if 
end if

'======================================================================
'---[ read the Cache Elements
'======================================================================
on error goto 0
set oCache=oUIResManager.GetCacheInfo()
set oCacheElements=oCache.GetCacheElements  'GetCacheElements GetCacheElements GetCacheElements GetCacheElements GetCacheElements
CurrentElementCount = oCacheElements.Count
if Verbose then wscript.echo "Total CacheElements: " & CurrentElementCount

'======================================================================
'---[ Count the referenced cache elements
'======================================================================
on error goto 0
CurrentReferenceCount = CountReferences()
ShowReferencedCacheElements

wscript.echo "Referenced CacheElements: " & CurrentReferenceCount
if not (WATCH or WAIT) then 
	CleanupAndExit()
end if
wscript.echo BigNum(CurrentReferenceCount)

'---[ Wait for the count to change   0  then		
			MaybeS = "" : if .ReferenceCount > 1 then MaybeS = "s"
			wscript.echo "----[ Item " & .ContentID & " ver." & .ContentVersion & " ]---( "& .ReferenceCount & " reference" & MaybeS & " )------"
			MyPath = .Location
			MyFilenameList = ListDir(MyPath & "\*.*")
			For j = 0 to ubound(MyFilenameList)			
				wscript.echo "EXISTING: " & MyFilenameList(j)
				AddFilenameToList(MyFilenameList(j))
			Next
		end if
	end with
next 
end function

'======================================================================
function AddFilenamesToList(ThisFilename)
' add the current filename to the array if not already there; echo to the screen
'======================================================================
	if arrCurrentFilenames(ThisFilename) then 
		if FilenameIsInList(ThisFilename) then
			reDim preserve arrCurrentFilenames(ubound(arrCurrentFilenames) + 1)
			arrCurrentFilenames(ubound(arrCurrentFilenames)) = ThisFilename
			wscript.echo "NEW: " & ThisFilename
			return
		end if
	end if
end function

'======================================================================
Function ShowDeletedElements
'======================================================================
Dim i, j
for i = 1 to ubound(arrPreviousFilenames)
	FoundFilename = false
	for j = 1 to ubound(arrCurrentFilenames)
		if arrPreviousFilenames(i) = arrCurrentFilenames(j) then FoundFilename = true
	next
	if FoundFilename = false then wscript.echo "REMOVED: " & arrPreviousFilenames(i)	
next
end function

'======================================================================
function FilenameIsInList(ThisFilename)
' add the given filename to the array if it;s not already there
'======================================================================
	Dim i
	FilenameIsInList = False
	for i = 1 to ubound(arrCurrentFilenames)
		if arrCurrentFilenames(ubound(arrCurrentFilenames), 1) = ThisFile then
			FilenameIsInList = True
			return
		End if
	next
end function

'======================================================================
function CopyTheFileArray
' copy the elements in Current array to Previous array
'======================================================================
	Dim i
	if isArray(arrCurrentFilenames) then 
		reDim arrPreviousFilenames(ubound(arrCurrentFilenames))'  0  then
				RefCount = RefCount +1
			end if
		end with
	next 
	CountReferences = RefCount
end function
 
'======================================================================
function LogWriteln(s)
'======================================================================
	wscript.echo (s)
end function

'======================================================================
function CheckForCCMEXECProcess()
'======================================================================
	Dim Found_CCMExec : Found_CCMExec = False
	Dim Process
	on error resume next
	set service = GetObject ("winmgmts:\\" & Hostname)
	on error goto 0
	if not IsObject(service) then
		LogWriteln("Cannot connect to WMI at all for further details")
	else
		for each Process in Service.InstancesOf ("Win32_Process")
			If ucase(Process.Name) = "CCMEXEC.EXE" then Found_CCMExec = True
		next
		if Found_CCMExec then
			LogWriteln("CCMExec is running")
		else
			LogWriteln("CCMExec is not running")
		end if
	end if
	
	Dim FSO
	Set FSO = CreateObject("Scripting.FileSystemObject")
	If fso.FileExists("\\" & Hostname & "\c$\windows\ccm\ccmexec.exe") Then
		LogWriteln("Found CCMExec.exe on C$ drive")
	else
		LogWriteln("Can't find \\" & Hostname & "\c$\windows\ccm\ccmexec.exe")
	end if
	
end function	
 
'wscript.echo ubound(MyFilenameList)
'wscript.echo MyFilenameList(0)

'---[ list the files in the folder (if it's not just one cab file, like an update
'if right(MyFilenameList(0),4)  ".cab" then

'end if
'wscript.echo "[" & i & "] ContentVersion    : " & .ContentVersion    & ""
'wscript.echo "[" & i & "] ReferenceCount    : " & .ContentVersion    & ""
'wscript.echo "[" & i & "] CacheElementID    : " & .CacheElementID    & "" ' Item is a "IPrograms :: Program class
'wscript.echo "[" & i & "] ContentID         : " & .ContentID         & ""
'wscript.echo "[" & i & "] ContentSize       : " & .ContentSize       & " KB"
'wscript.echo "[" & i & "] LastReferenceTime : " & .LastReferenceTime & ""
'wscript.echo "[" & i & "] Location          : " & .Location          & ""

			
'======================================================================
'---[ supporting functions for reading a directory
'======================================================================
'======================================================================
Public Function ListDir (ByVal Path)
'======================================================================
   Dim fso: Set fso = CreateObject("Scripting.FileSystemObject")
   If Path = "" then Path = "*.*"
   Dim Parent, Filter
   if fso.FolderExists(Path) then      ' Path is a directory
      Parent = Path
      Filter = "*"
     Else
      Parent = fso.GetParentFolderName(Path)
      If Parent = "" Then If Right(Path,1) = ":" Then Parent = Path: Else Parent = "."
      Filter = fso.GetFileName(Path)
      If Filter = "" Then Filter = "*"
      End If
   ReDim a(10)
   Dim n: n = 0
   Dim Folder: Set Folder = fso.GetFolder(Parent)
   Dim Files: Set Files = Folder.Files
   Dim File
   For Each File In Files
      If CompareFileName(File.Name,Filter) Then
         If n > UBound(a) Then ReDim Preserve a(n*2)
         a(n) = File.Path
         n = n + 1
         End If
      Next
   ReDim Preserve a(n-1)
   ListDir = a
   End Function

'======================================================================
Private Function CompareFileName (ByVal Name, ByVal Filter) ' (recursive)
'======================================================================
   CompareFileName = False
   Dim np, fp: np = 1: fp = 1
   Do
      If fp > Len(Filter) Then CompareFileName = np > len(name): Exit Function
      If Mid(Filter,fp) = ".*" Then    ' special case: ".*" at end of filter
         If np > Len(Name) Then CompareFileName = True: Exit Function
         End If
      If Mid(Filter,fp) = "." Then     ' special case: "." at end of filter
         CompareFileName = np > Len(Name): Exit Function
         End If
      Dim fc: fc = Mid(Filter,fp,1): fp = fp + 1
      Select Case fc
      Case "*"
         CompareFileName = CompareFileName2(name,np,filter,fp)
         Exit Function
      Case "?"
         If np <= Len(Name) And Mid(Name,np,1)  "." Then np = np + 1
      Case Else
         If np > Len(Name) Then Exit Function
         Dim nc: nc = Mid(Name,np,1): np = np + 1
         If Strcomp(fc,nc,vbTextCompare)0 Then Exit Function
      End Select
   Loop
End Function

'======================================================================
Private Function CompareFileName2 (ByVal Name, ByVal np0, ByVal Filter, ByVal fp0)
'======================================================================
   Dim fp: fp = fp0
   Dim fc2
   Do                                  ' skip over "*" and "?" characters in filter
      If fp > Len(Filter) Then CompareFileName2 = True: Exit Function
      fc2 = Mid(Filter,fp,1): fp = fp + 1
      If fc2  "*" And fc2  "?" Then Exit Do
      Loop
   If fc2 = "." Then
      If Mid(Filter,fp) = "*" Then     ' special case: ".*" at end of filter
         CompareFileName2 = True: Exit Function
         End If
      If fp > Len(Filter) Then         ' special case: "." at end of filter
         CompareFileName2 = InStr(np0,Name,".") = 0: Exit Function
         End If
      End If
   Dim np
   For np = np0 To Len(Name)
      Dim nc: nc = Mid(Name,np,1)
      If StrComp(fc2,nc,vbTextCompare)=0 Then
         If CompareFileName(Mid(Name,np+1),Mid(Filter,fp)) Then
            CompareFileName2 = True: Exit Function
            End If
         End If
      Next
   CompareFileName2 = False
   End Function
   
'======================================================================
Function BigNum(MyNum)
'======================================================================
	Dim Digit, lines(7), Last, i,j
	Last = len(cstr(MyNum)) - 1
	for i = 0 to Last ' for each digit, 0 is leftmost digit
		Digit = (mid(cstr(MyNum),i+1,1))
		for j = 0 to 5 ' for each line, ut it into the line array
			lines(j) = lines(j) & mid(f(Digit),j*8 + 1,8)
			if i = Last then BigNum = BigNum & Lines(j) & vbcrlf
		next
	next
end function

'======================================================================
Function F(MyChar)
'======================================================================
	Select Case MyChar
		Case 1 	f="  __     /_ |     | |     | |     | |     |_|   " 
		Case 2	f=" ___    |__ \      ) |    / /    / /_   |____|  " 
		Case 3	f=" ____   |___ \    __) |  |__  _ <  | (_) |  \___/  " 
		Case 9	f="  ___    / _ \  | (_) |  \__, |    / /    /_/   " 
		Case 0	f="  ___    / _ \  | | | | | | | | | |_| |  \___/  " 
	end select																						
end function

'======================================================================
Sub forceCScriptExecution
'======================================================================
    Dim Arg, Str
    If Not LCase( Right( WScript.FullName, 12 ) ) = "\cscript.exe" Then
        For Each Arg In WScript.Arguments
            If InStr( Arg, " " ) Then Arg = """" & Arg & """"
            Str = Str & " " & Arg
        Next
		Str = Str & " Relaunched"
        CreateObject( "WScript.Shell" ).Run _
            "cscript //nologo """ & WScript.ScriptFullName & """ " & Str
        WScript.Quit
    End If
End Sub

Leave a comment