VBScript Error - Object Required ObjTSO - vbscript

'========================================================================
'## Global Object and Variable Settings
'========================================================================
Dim WshShell: Set WshShell = CreateObject("WScript.Shell")
Dim objFSO: Set objFSO = CreateObject("Scripting.FileSystemObject")
Dim strSystemDrive: strSystemDrive = WshShell.ExpandEnvironmentStrings("%SystemDrive%")
'========================================================================
'## Main Code
'========================================================================
'On Error Resume Next
'---------------------------------------------------------------------------------------------------------------------------
'## Script Variables
'---------------------------------------------------------------------------------------------------------------------------
Dim strFolder: strFolder = strSystemDrive & "\Endpoint Reboot Info"
Dim strFile: strFile = strSystemDrive & "\Endpoint Reboot Info\Endpoint_Reboot_Logfile.txt"
strcurrentDateTime = Now()
Dim string0: string0 = "Endpoint restart analysis initialized"
Dim string1: string1 = "Calculating the endpoint uptime .. If greater than 14 days, machine will be restarted"
Dim string2: string2 = "Warning: The current OS Uptime exceeds 14 days! This system will be rebooted!"
Dim string3: string3 = " As the current OS Uptime is less than 14 days, this system will NOT be rebooted currently"
Dim string4: string4 = "This system will restart now!"
Dim string5: string5 = "User has clicked cancel, hence PC was NOT restarted"
Dim string6: string6 = "User has clicked OK. Restarting PC now.."
'---------------------------------------------------------------------------------------------------------------------------
'## Code for creating the folder and file necessary for logging and initializing the log file
'---------------------------------------------------------------------------------------------------------------------------
Function CreateLogFile(filename)
Dim f: set f = objFSO.OpenTextFile(strFile, 8, True)
f.WriteLine strcurrentDateTime & " " & string0
f.WriteLine strcurrentDateTime & " " & string1
Set CreateLogFile = f
End Function
'---------------------------------------------------------------------------------------------------------------------------
'## Code for checking endpoint OS uptime and force restarting post message display to end user
'---------------------------------------------------------------------------------------------------------------------------
Dim objTSO: Set objTSO = CreateLogFile(strFile)
strComputer = "."
Const FOR_APPENDING = 8
SET objWMIDateTime = CREATEOBJECT("WbemScripting.SWbemDateTime")
SET objWMI = GETOBJECT("winmgmts:\\" & strComputer & "\root\cimv2")
SET colOS = objWMI.InstancesOf("Win32_OperatingSystem")
FOR EACH objOS in colOS
objWMIDateTime.Value = objOS.LastBootUpTime
objTSO.WriteLine "Last Boot Up Time: " & objWMIDateTime.GetVarDate & vbcrlf & _
"System Up Time: " & TimeSpan(objWMIDateTime.GetVarDate,NOW) & _
" (hh:mm:ss)"
NEXT
FUNCTION TimeSpan(dt1, dt2)
' Function to display the difference between
' 2 dates in hh:mm:ss format
IF (ISDATE(dt1) AND ISDATE(dt2)) = FALSE THEN
TimeSpan = "00:00:00"
EXIT FUNCTION
END IF
seconds = ABS(DATEDIFF("S", dt1, dt2))
minutes = seconds \ 60
hours = minutes \ 60
minutes = minutes MOD 60
seconds = seconds MOD 60
IF LEN(hours) = 1 THEN hours = "0" & hours
TimeSpan = hours & ":" & _
RIGHT("00" & minutes, 2) & ":" & _
RIGHT("00" & seconds, 2)
If (hours > 336) Then
f.WriteLine strcurrentDateTime & " " & string2
Dim retval: retval = InputBox("Warning!: The current OS Uptime exceeds 14 days! This system will be rebooted! Please save ALL of your work and ONLY then click OK")
If IsEmpty(retval) Then
msgbox ("User has terminated the action by clicking cancel")
objTSO.WriteLine string5
Else
objTSO.WriteLine string6
WshShell.Run "shutdown.exe -R -T 0"
End If
Else
WScript.Sleep 10000
strcurrentDateTime = Now()
objTSO.WriteLine strcurrentDateTime & string3
WScript.Quit
End If
f.Close()
END FUNCTION
'---------------------------------------------------------------------------------------------------------------------------
'## End of code/VB Script
'---------------------------------------------------------------------------------------------------------------------------
I have now put up the full script as suggested. Please help with below queries:
This Line is not getting written into the log file
objTSO.WriteLine "Last Boot Up Time: " & objWMIDateTime.GetVarDate & vbcrlf & _
"System Up Time: " & TimeSpan(objWMIDateTime.GetVarDate,NOW) & _
" (hh:mm:ss)"
Also please check the logic of the code by changing the (hours>336) condition. This script will be executed locally on the machines, so instead of the previous reboot function, i have now modified it to WshShell.Run "shutdown.exe -R -T 0"
Please guide! Thanks!!

Your function CreateLogFile creates/opens the file, but then closes it right away:
Function CreateLogFile()
If objFSO.FileExists(strFile) Then
Set objTSO = objFSO.OpenTextFile(strFile, FOR_APPENDING)
objTSO.WriteLine strcurrentDateTime & " " & string0
objTSO.WriteLine strcurrentDateTime & " " & string1
objTSO.Close()
Else
objFSO.CreateTextFile(strFile)
Set objTSO = objFSO.OpenTextFile(strFile, FOR_APPENDING)
objTSO.WriteLine strcurrentDateTime & " " & string0
objTSO.WriteLine strcurrentDateTime & " " & string1
objTSO.Close()
End If
End Function
meaning that when the function returns the file handle is already closed, causing all subsequent attempts to write to the file (without opening it again) to fail.
What you actually want to do is change your function to something like this:
Function CreateLogFile(filename)
Dim f : Set f = objFSO.OpenTextFile(filename, 8, True)
f.WriteLine Now & " " & string0
f.WriteLine Now & " " & string1
Set CreateLogFile = f
End Function
Dim objTSO : Set objTSO = CreateLogFile(strFile)
...
objTSO.Close 'at the end of the script
Setting the 3rd parameter of the OpenTextFile method to True causes it to create the file in case it's missing.

Related

Script to check number of days since last reboot on Windows 10

Below VB script is written to get the number of days since last reboot on Windows 10 Devices. The aim is to run the script as a scheduled task and if the number of days is less than 13 then it'll exit 0 with no action. If the number of days is higher than 13 then exit 1. The script works fine on many devices. But on some devices it's showing negative value for the number of days. Any suggestions to overcome the issue.
PC with Issue
TIA
ON ERROR RESUME NEXT
'Set Variables
Const ForReading = 1, ForWriting = 2, ForAppending = 8
Set objfso = CreateObject("Scripting.FileSystemObject")
Set wshell = CreateObject("WScript.Shell")
strComputer = "."
str_folder = "C:\Temp\LogFolder"
str_logfile = str_folder & "\Logfile.log"
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colOperatingSystems = objWMIService.ExecQuery _
("Select * from Win32_OperatingSystem")
For Each objOS in colOperatingSystems
dtmBootup = objOS.LastBootUpTime
dtmLastBootupTime = WMIDateStringToDate(dtmBootup)
dtmSystemUptime = DateDiff("n", dtmLastBootUpTime, Now)
numUptDays = (dtmSystemUptime \ 60 ) \ 24
Next
Function WMIDateStringToDate(dtmBootup)
WMIDateStringToDate = CDate(Mid(dtmBootup, 5, 2) & "/" & _
Mid(dtmBootup, 7, 2) & "/" & Left(dtmBootup, 4) _
& " " & Mid (dtmBootup, 9, 2) & ":" & _
Mid(dtmBootup, 11, 2) & ":" & Mid(dtmBootup,13, 2))
End Function
If numUptDays > 13 Then
'Create Folder
If not objfso.FolderExists(str_folder) Then
objfso.CreateFolder str_folder
End If
'Create Log File
If not objfso.FileExists(str_logfile) Then
Set objFile = objFSO.CreateTextFile(str_logfile)
objFile.Close
End If
'Update Log File - Rebooting
str_text = "Restart Required"
UpdateLog(Now & " -- " & str_text)
'quit and set exit code
wscript.quit(1)
Else
'Create Folder
If not objfso.FolderExists(str_folder) Then
objfso.CreateFolder str_folder
End If
'Create Log File
If not objfso.FileExists(str_logfile) Then
Set objFile = objFSO.CreateTextFile(str_logfile)
objFile.Close
End If
'Update Log File - Reboot not Required
str_text = " days since last reboot. No reboot required."
UpdateLog(Now & " -- " & numUptDays & str_text)
'quit and set exit code
wscript.quit(0)
End If
'Function to Update LogFile
Function UpdateLog(str_text)
Set objFile = objfso.OpenTextFile(str_logfile, ForAppending, TristateFalse)
objFile.Write str_text & vbcrlf
objFile.Close
End Function
The script is not working on some devices due to a date format difference. The script uses mm/dd/yyyy but inherits its locale setting from the local machine. If the local machine uses dd/mm/yyyy format, then the script will return an incorrect result.
To ensure that the script runs the same, regardless of the machine's locale, the script must have an explicitly set locale. This is best set somewhere near the top of the script. In this case, the line that needs to be added is:
Setlocale("en-us")

VBScript *unintentionally* pauses on background

For a brief description of the code I have, I want to keep in track a certain app named 'CostX.exe' if it closes and opens and I log it on a file called CostXLog.txt
I keep it on an infinite loop. The problem is, i dont know why but it works for a brief period when Im not doing anything productive, but when I do, the script just pauses or should I say it doesnt work anymore though it still stays in the background.
This should be its desired output.
Opened at: 4/7/2021 9:00:52 AM
Closed at: 4/7/2021 9:07:56 AM
CostX was opened for 7 minutes and 4 seconds.
But when I say I do anything productive, the output stays at
Opened at: 4/7/2021 9:00:52 AM
This is my code
Option Explicit
Const ForReading = 1, ForAppending = 8
Dim i, processName, strComputer
Dim fso, inName, outName, shell, curDir, objWMIService, colProcessList
Set fso = CreateObject("Scripting.FileSystemObject")
Set shell = CreateObject("WScript.Shell")
curDir = shell.CurrentDirectory
outName = curDir & "\CostXLog.txt"
Dim counter1, counter2, initialOpen
Dim minDiff, secDiff
Dim timedate1, timedate2
timedate1 = Date() & " " & Time()
initialOpen = 0
counter1 = 0
counter2 = 1
'msgbox curDir
strComputer = "."
processName = "CostX.exe"
Dim newFile, outputFile
newFile = Not fso.FileExists( outName )
while(true)
Set objWMIService = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set colProcessList = objWMIService.ExecQuery _
("Select Name from Win32_Process WHERE Name LIKE '" & processName & "%'")
Set outputFile = fso.OpenTextFile( outName, ForAppending, True )
if initialOpen = 0 Then
outputFile.WriteLine "Ignore time after this line"
initialOpen = 1
else
If colProcessList.count>0 then
If counter1 = 0 Then
outputFile.WriteLine "Opened at: " & Date() & " " & Time()
timedate1 = Date() & " " & Time()
counter1 = 1
counter2 = 1
End If
else
If counter2 = 1 Then
outputFile.WriteLine "Closed at: " & Date() & " " & Time()
timedate2 = Date() & " " & Time()
minDiff = DateDiff("s", timedate1, timedate2)/60
secDiff = DateDiff("s", timedate1, timedate2)
outputFile.WriteLine "CostX was opened for " & Fix(minDiff) & " minutes and " & secDiff - (Fix(secDiff/60) * 60) & " seconds."
counter2 = 0
counter1 = 0
End If
End if
End If
outputFile.Close
WScript.Sleep 50
wend
Set objWMIService = Nothing
Set colProcessList = Nothing
PS: I am not a programmer, I have assembled that from many sources in the internet, I do understand a bit, but some of the parts in there, I really dont have any idea, I just know that they work as I want them to. So i guess someone could provide me an idea as to how to keep it running no matter what I do in my computer. Is there anything wrong with what I did in the code?
I have also done a batch script with the same intended purpose, the issue is the same, it was also made with a loop, and it pauses when I am busy working.

VBScript RegEx : Replace Content

I want to update the Unbound_DNS configuration file from a raw source but I can not get the desired result.
I would like to format each entry (each line):
address=/abc.com/0.0.0.0
To
local-zone: "abc.com" redirect
local-data: "abc.com 86400 IN A 0.0.0.0"
Here is what I did (thanks to hackoofr):
Option Explicit
Dim URL,Save2File,ws
If Not WScript.Arguments.Named.Exists("elevate") Then
CreateObject("Shell.Application").ShellExecute WScript.FullName _
, """" & WScript.ScriptFullName & """ /elevate", "", "runas", 1
WScript.Quit
End If
URL = "https://raw.githubusercontent.com/notracking/hosts-blocklists/master/domains.txt"
Set ws = CreateObject("wscript.Shell")
Save2File = ws.ExpandEnvironmentStrings("%Windir%\Temp\test")
Call Download(URL,Save2File)
'**********************************************************************************************
Sub Download(URL,Save2File)
Dim File,Line,BS,ws,RegExp
On Error Resume Next
Set File = CreateObject("MSXML2.XMLHTTP")
File.Open "GET",URL, False
File.Send
If err.number <> 0 then
Line = Line & vbcrlf & "Error Getting File"
Line = Line & vbcrlf & "Error " & err.number & "(0x" & hex(err.number) & ") " & vbcrlf &_
err.description
Line = Line & vbcrlf & "Source " & err.source
MsgBox Line,vbCritical,"Error getting file"
Err.clear
wscript.quit
End If
If File.Status = 200 Then
'**********************************************************************************************
' Replace content for use with the file service.conf of soft Unbound_DNS
'
' address=/abc.com/0.0.0.0 to local-zone: "abc.com" redirect
' local-data: "abc.com 3600 IN A 0.0.0.0"
'**********************************************************************************************
Set RegExp = CreateObject("VBScript.RegExp")
RegExp.IgnoreCase = True
RegExp.Global = True
RegExp.Pattern = "address=/(.*)/([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})"
File.ResponseBody = RegExp.Replace(File.ResponseBody, "local-zone: \""$1\"" redirect $1" & ret & ">local-data: \""$1 3600 IN A $2\""")
Set RegExp = Nothing
'**********************************************************************************************
' Write content
'**********************************************************************************************
Set BS = CreateObject("ADODB.Stream")
Set ws = CreateObject("wscript.Shell")
BS.type = 1
BS.open
BS.Write File.ResponseBody
BS.SaveToFile Save2File, 2
'**********************************************************************************************
' Clean cache DNS
'**********************************************************************************************
wshShell.run("cmd /c psexec \\ -s ipconfig /flushdns >> & hostName,TRUE")
ElseIf File.Status = 404 Then
MsgBox "UpdateHostname.vbs : File Not Found : " & File.Status,vbCritical,"UpdateHostname.vbs : Error File Not Found"
Else
MsgBox "UpdateHostname.vbs : Unknown Error : " & File.Status,vbCritical,"UpdateHostname.vbs : Error getting file"
End If
End Sub
'**********************************************************************************************
Thank you in advance for your help.
Edit 1:
The content does not change. File.ResponseBody returns the content correctly, but no modification by the regexp!
Replace the following code:
Set RegExp = CreateObject("VBScript.RegExp")
RegExp.IgnoreCase = True
RegExp.Global = True
RegExp.Pattern = "address=/(.*)/([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})"
File.ResponseBody = RegExp.Replace(File.ResponseBody, "local-zone: \""$1\"" redirect $1" & ret & ">local-data: \""$1 3600 IN A $2\""")
Set RegExp = Nothing
with this:
Dim objReg, strTest, objMatches, objMatch
Set objReg = New RegExp
strTest = File.ResponseBody 'address=/abc.com/0.0.0.0
objReg.Global = True
objReg.Pattern = "address=/(.*?)/(\d{1,3}(?:\.\d{1,3}){3})" 'abc.com gets stored in Group 1 and the IP address gets stored in Group 2
Set objMatches = objReg.Execute(strTest)
For Each objMatch In objMatches
strTest = "local zone: """ & objMatch.Submatches.Item(0) & """ redirect" & vbCrLf &_
"local data: """ & objMatch.Submatches.Item(0) & " 86400 in A " & objMatch.Submatches.Item(1)&""""
Next
File.ResponseBody = strTest
set objReg = Nothing
Click for Regex Demo(in the demo, / is escaped by \)
Regex Explanation:
address=/(.*?)/(\d{1,3}(?:\.\d{1,3}){3})
address=/ - matches address=/ literally
(.*?) - matches 0+ occurrences of any character(except a newline), as few as possible. The parenthesis are used to capture this match as group 1
/ - matches / literally
(\d{1,3}(?:\.\d{1,3}){3}) - matches a string of the pattern 12.222.212.33 and captures it in Group 2
Update:
Here is my final solution. From what I could understand from your code, you first get the response body from the server, modify and store the updated response in a file called test in the temp folder. Below is the code I have written to do the same thing. I have tested it on my system and the final output which gets stored in the C:\Windows\Temp\test.txt file looks correct as shown in the attached screenshot. Now, this may not be exactly what you want but you can get an idea from this. Store this code in a new vbs file and run it directly as it is.
Note: Since the response text from the server is very long, It takes a bit long to get executed. If you just want to see if it is working or not, uncomment the code inside the for loop. You will be able to see that you are getting the desired result for the first few URLs
Regex Demo
Option Explicit
Dim File, objReg, strTest, objMatches, objMatch, saveToFile, fso, outFile, strReplace, objShell, i
Set objShell = CreateObject("wscript.shell")
saveToFile = objShell.ExpandEnvironmentStrings("%windir%\Temp\test.txt")
Set File = CreateObject("MSXML2.XMLHTTP")
File.Open "GET","https://raw.githubusercontent.com/notracking/hosts-blocklists/master/domains.txt", False
File.send
If File.Status = 200 Then
Set objReg = New RegExp
strTest = File.responseText 'address=/abc.com/0.0.0.0
objReg.Global = True
objReg.Pattern = "address=/(.*?)/(\d{1,3}(?:\.\d{1,3}){3})" 'abc.com gets stored in Group 1 and the IP address gets stored in Group 2
Set objMatches = objReg.Execute(strTest)
For Each objMatch In objMatches
strReplace = "local zone: """ & objMatch.Submatches.Item(0) & """ redirect" & vbCrLf &_
"local data: """ & objMatch.Submatches.Item(0) & " 86400 in A " & objMatch.Submatches.Item(1)&"""" & vbCrLf
strTest = Replace(strTest,objMatch.Value,strReplace)
'Uncomment the following code to see the result for the 1st 5 URLs, if the whole thing is taking too long to get executed
'i=i+1
'If(i>5) Then
' Exit for
'End If
Next
set objReg = Nothing
'**********************************************************************************************
' Write content
'**********************************************************************************************
Set fso = CreateObject("scripting.filesystemobject")
Set outFile = fso.OpenTextFile(saveToFile,2,True)
outFile.Write strTest
outFile.Close
End If
Output:
Here is the update of the code that works very well based on the response of #Gurman and the comment of #Ansgar Wiechers. Thank you for your help
Option Explicit
Dim File, objReg, strTest, RegExp, objMatches, objMatch, saveToFile, fso, outFile, strReplace, objShell, i
Set objShell = CreateObject("wscript.shell")
saveToFile = objShell.ExpandEnvironmentStrings("%windir%\Temp\test.txt")
Set File = CreateObject("MSXML2.XMLHTTP")
File.Open "GET","https://raw.githubusercontent.com/notracking/hosts-blocklists/master/domains.txt", False
File.send
If File.Status = 200 Then
'**********************************************************************************************
' Replace content for use with the file service.conf of soft Unbound_DNS
'
' address=/abc.com/0.0.0.0 to local-zone: "abc.com" redirect
' local-data: "abc.com 86400 IN A 0.0.0.0"
'**********************************************************************************************
strTest = File.responseText
Set RegExp = CreateObject("VBScript.RegExp")
RegExp.IgnoreCase = True
RegExp.Global = True
RegExp.Pattern = "address=/(.*?)/(\d{1,3}(?:\.\d{1,3}){3})"
strReplace = "local-zone: ""$1"" redirect" & vbCrLf & "local-data: ""$1 86400 IN A $2"""
strTest = RegExp.Replace(strTest, strReplace)
Set RegExp = Nothing
'**********************************************************************************************
' Write content
'**********************************************************************************************
Set fso = CreateObject("scripting.filesystemobject")
Set outFile = fso.OpenTextFile(saveToFile,2,True)
outFile.Write strTest
outFile.Close
End If

Ping function makes the whole excel table slow/unresponsive

I have a function that pings computers from an excel list and gets the ping value of them.
While the script was running, the excel was completely unresponsive. I could fix this with DoEvents, this made it a bit more responsive.
However, the problem starts when the function gets to an offline computer. While it waits for the response of the offline PC, Excel freezes again and the script does not jump to the next PC until it gets the "timeout" from the actual one.
As the default ping timeout value is 4000ms, if I have 100 computers in my list, and 50 of them are turned off, that means I have to wait an extra 3,3 minutes for the script to finish, and also blocks the entire Excel, making it unusable for the duration.
My question is, if is there any way to make this faster or more responsive or smarter?
The actual code:
Function:
Function sPing(sHost) As String
Dim oPing As Object, oRetStatus As Object
Set oPing = GetObject("winmgmts:{impersonationLevel=impersonate}").ExecQuery _
("select * from Win32_PingStatus where address = '" & sHost & "'")
DoEvents
For Each oRetStatus In oPing
DoEvents
If IsNull(oRetStatus.StatusCode) Or oRetStatus.StatusCode <> 0 Then
sPing = "timeout" 'oRetStatus.StatusCode <- error code
Else
sPing = sPing & vbTab & oRetStatus.ResponseTime
End If
Next
End Function
Main:
Sub pingall_Click()
Dim c As Range
Dim p As String
Dim actives As String
actives = ActiveSheet.Name
StopCode = False
Application.EnableCancelKey = xlErrorHandler
On Error GoTo ErrH:
DoEvents
For Each c In Sheets(actives).UsedRange.Cells
If StopCode = True Then
Exit For
End If
DoEvents
If Left(c, 7) = "172.21." Then
p = sPing(c)
[...]
End If
Next c
End Sub
As already noted in the comments, to prevent this from blocking after each call, you need to invoke your pings asynchronously from your function. The way I would approach this would be to delegate your sPing(sHost) function to a VBScript that you create on the fly in a temp folder. The script would look something like this, and it takes the IP address as a command line argument and outputs the result to a file:
Dim args, ping, status
Set ping = GetObject("winmgmts:{impersonationLevel=impersonate}").ExecQuery _
("select * from Win32_PingStatus where address = '" & Wscript.Arguments(0) & "'")
Dim result
For Each status In ping
If IsNull(status.StatusCode) Or status.StatusCode <> 0 Then
result = "timeout"
Else
result = result & vbTab & status.ResponseTime
End If
Next
Dim fso, file
Set fso = CreateObject("Scripting.FileSystemObject")
Set file = fso.CreateTextFile(Wscript.Arguments(0), True)
file.Write result
file.Close
You can create a Sub to write this to a path something like this:
Private Sub WriteScript(path As String)
Dim handle As Integer
handle = FreeFile
Open path & ScriptName For Output As #handle
Print #handle, _
"Dim args, ping, status" & vbCrLf & _
"Set ping = GetObject(""winmgmts:{impersonationLevel=impersonate}"").ExecQuery _" & vbCrLf & _
" (""select * from Win32_PingStatus where address = '"" & Wscript.Arguments(0) & ""'"")" & vbCrLf & _
"Dim result" & vbCrLf & _
"For Each status In ping" & vbCrLf & _
" If IsNull(status.StatusCode) Or status.StatusCode <> 0 Then" & vbCrLf & _
" result = ""timeout""" & vbCrLf & _
" Else" & vbCrLf & _
" result = result & vbTab & status.ResponseTime" & vbCrLf & _
" End If" & vbCrLf & _
"Next" & vbCrLf & _
"Dim fso, file" & vbCrLf & _
"Set fso = CreateObject(""Scripting.FileSystemObject"")" & vbCrLf & _
"Set file = fso.CreateTextFile(Wscript.Arguments(0), True)" & vbCrLf & _
"file.Write result" & vbCrLf & _
"file.Close"
Close #handle
End Sub
After that, it's pretty straightforward - create a new directory in the user's temp directory, plop the script in there, and then use the Shell command to run each ping in its own process. Wait for the length of your timeout, then read the results from the files:
Private Const TempDir = "\PingResults\"
Private Const ScriptName As String = "ping.vbs"
'Important - set this to the time in seconds of your ping timeout.
Private Const Timeout = 4
Sub pingall_Click()
Dim sheet As Worksheet
Set sheet = ActiveSheet
Dim path As String
'Create a temp folder to use.
path = Environ("Temp") & TempDir
MkDir path
'Write your script to the temp folder.
WriteScript path
Dim results As Dictionary
Set results = New Dictionary
Dim index As Long
Dim ip As Variant
Dim command As String
For index = 1 To sheet.UsedRange.Rows.Count
ip = sheet.Cells(index, 1)
If Len(ip) >= 7 Then
If Left$(ip, 1) = "172.21." Then
'Cache the row it was in.
results.Add ip, index
'Shell the script.
command = "wscript " & path & "ping.vbs " & ip
Shell command, vbNormalFocus
End If
End If
Next index
Dim completed As Double
completed = Timer + Timeout
'Wait for the timeout.
Do While Timer < completed
DoEvents
Loop
Dim handle As String, ping As String, result As String
'Loop through the resulting files and update the sheet.
For Each ip In results.Keys
result = Dir$(path & ip)
If Len(result) <> 0 Then
handle = FreeFile
Open path & ip For Input As #handle
ping = Input$(LOF(handle), handle)
Close #handle
Kill path & ip
Else
ping = "timeout"
End If
sheet.Cells(results(ip), 2) = ping
Next ip
'Clean up.
Kill path & "*"
RmDir path
End Sub
Note that this has exactly zero error handling for the file operations, and doesn't respond to your StopCode flag. It should give the basic gist of it though. Also note that if you need to allow the user to cancel it, you won't be able to remove the temp directory because it will still be in use. If that is the case, only create it if it isn't already there and don't remove it when you're done.
You might be able to implement something like this, but I haven't tried it with multiple servers
if your network is fast you can reduce the timeout to 500 ms or less:
.
Public Function serverOk(ByVal dbSrvrNameStr As String) As Boolean
Const PINGS As Byte = 1
Const PING_TIME_OUT As Byte = 500
Const PING_LOCATION As String = "C:\Windows\System32\"
Dim commandResult As Long, serverIsActive As Boolean
commandResult = 1
serverIsActive = False
If Len(dbSrvrNameStr) > 0 Then
Err.Clear
With CreateObject("WScript.Shell")
commandResult = .Run("%comspec% /c " & PING_LOCATION & "ping.exe -n " & PINGS & " -w " & PING_TIME_OUT & " " & dbSrvrNameStr & " | find ""TTL="" > nul 2>&1", 0, True)
commandResult = .Run("%comspec% " & PING_LOCATION & "/c ping.exe -n " & PINGS & " -w " & PING_TIME_OUT & " " & dbSrvrNameStr, 0, True)
serverIsActive = (commandResult = 0)
End With
If serverIsActive And Err.Number = 0 Then
'"DB Server - valid, Ping response: " & commandResult
Else
'"Cannot connect to DB Server, Error: " & Err.Description & ", Ping response: " & commandResult
End If
Err.Clear
End If
serverOk = serverIsActive
End Function
.
Link to "Run Method (Windows Script Host)" from Microsoft:
https://msdn.microsoft.com/en-us/library/d5fk67ky(VS.85).aspx
The 3rd parameter of this command can be overlooked: "bWaitOnReturn" - allows you to execute it asynchronously from VBA

Running command on remote machine using WMI

I am trying to run following VB Script to run command on remote machine. I want this script to wait until command is executed completely.
Here is my code:
Function RemoteExecute(strServer, strUser, strPassword, strCommand,pro)
Dim objLocator , objWMIService
wbemImpersonationLevelImpersonate = 3
wbemAuthenticationLevelPktPrivacy = 6
RemoteExecute = -1
Set objLocator = CreateObject("WbemScripting.SWbemLocator")
On Error Resume Next
Set objWMIService = objLocator.ConnectServer(strServer,"root\cimv2", strUser,strPassword)
objWMIService.Security_.ImpersonationLevel = wbemImpersonationLevelImpersonate
objWMIService.Security_.AuthenticationLevel = wbemAuthenticationLevelPktPrivacy
If Err.Number <> 0 Then
WScript.Echo "Failed to connect to " &strServer, "Error # " & CStr(Err.Number) & " " & Err.Description & vbcrlf & _
"Please check if " & strServer & " is pingable from this client & credentials are correct"
Err.Clear
On Error GoTo 0
RemoteExecute = -1
Set objWMIService = nothing
Set objLocator = nothing
Exit function
end if
' Configure the process to show a window
Set objStartup = objWMIService.Get("Win32_ProcessStartup")
Set objConfig = objStartup.SpawnInstance_
objConfig.ShowWindow = SW_NORMAL
Set Process = objWMIService.Get("Win32_Process")
'Process.Create Syntax: '
' uint32 Create(
'[in] string CommandLine,
'[in] string CurrentDirectory,
'[in] Win32_ProcessStartup ProcessStartupInformation,
'[out] uint32 ProcessId
');
'Return code Description
'0 Successful Completion
'2 Access Denied
'3 Insufficient Privilege
'8 Unknown failure
'9 Path Not Found
'21 Invalid Parameter
intReturn = Process.Create(strCommand,NULL, objConfig, intProcessID)
If intReturn <> 0 Then
Wscript.Echo "Process could not be created." & _
vbNewLine & "Command line: " & strCommand & _
vbNewLine & "Return value: " & intReturn
Wscript.Quit
Else
Wscript.Echo "Process created." & _
vbNewLine & "Command line: " & strCommand & _
vbNewLine & "Process ID: " & intProcessID
RemoteExecute = intProcessID
End If
' Set objWMIService = GetObject("winmgmts:\\" & strServer & "\root\cimv2")
Set colMonitoredProcesses = objWMIService.ExecNotificationQuery_("SELECT *" +" FROM __InstanceDeletionEvent " +"WITHIN 5 WHERE TargetInstance ISA 'Win32_Process' " )
Do
Set objProcess = colMonitoredProcesses.NextEvent
Wscript.Echo objProcess.TargetInstance.Name
Wscript.Echo objProcess.TargetInstance.ExecutablePath
Wscript.Echo "1"
Wscript.Echo "proc:" & objProcess.TargetInstance.ProcessID
Wscript.Echo "int:" & intProcessID
If objProcess.TargetInstance.ProcessID = intProcessID Then
Wscript.Echo "I will end the monitoring of the process "
Wscript.Echo pro & objProcess.TargetInstance.Name
Exit Do
end If
Loop
Set objWMIService = nothing
Set objLocator = nothing
End Function
strServer = WScript.Arguments.Item(0)
strUser = WScript.Arguments.Item(1)
strPassword = WScript.Arguments.Item(2)
strCommand = WScript.Arguments.Item(3)
pro = WScript.Arguments.Item(4)
Call RemoteExecute(strServer, strUser, strPassword, strCommand,pro)
But problem I am facing is that, script run the process in background but not waiting for it.
Another point that I do not understand is, when try to echo following:
Wscript.Echo objProcess.TargetInstance.Name
Wscript.Echo objProcess.TargetInstance.ExecutablePath
Wscript.Echo "proc:" & objProcess.TargetInstance.ProcessID
it does no echo anything, whereas following are properly echoed with a nice pop up
Wscript.Echo "1"
Wscript.Echo "int:" & intProcessID
Can someone please resolve my problem, may be this problem is naive for someone, sorry I am naive here.

Resources