Start Service with VBscript - vbscript

I am trying to have this script take a text file running and stopped services before a reboot and start any services that did not automatically start after the machine starts back up. The script that gets the list of service names, state and startmode and creates a comma separated text file line by line works fine. Here it is for reference (taken from the interwebs, lost the link in my travels. Modified slightly.):
Const ForAppending = 2
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objLogFile = objFSO.CreateTextFile("service_list.txt", _
ForWriting, True)
strComputer = "."
Set objWMIService = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set colListOfServices = objWMIService.ExecQuery("Select * from Win32_Service")
For Each objService in colListOfServices
objLogFile.Write objService.Name & ","
objLogFile.Write objService.StartMode & ","
objLogFile.Write objService.State
objLogFile.Writeline
Next
objLogFile.Close
This next bit reads the file line by line, compares the state of all of the services with the state of the services that were recorded before the machine was shut down. If they match, do nothing, if they are different, start the service:
Const ForReading = 1
strComputer = "."
Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set objServiceName = objWMIService.get("Win32_Service.Name='" & ServiceName & "'")
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("\\some path\service_list.txt",ForReading,True,-2)
Do Until objFile.AtEndOfStream
fLine = Split(objFile.ReadLine,",")
'wscript.echo fLine(2)
if InStr(fLine(2),"Running") then
'wscript.echo "it was running!"
if objServiceName.Started then
'do nothing
else
'Set servicetostart = objWMIService.ExecQuery ("Select " & ServiceName & " from Win32_Service Where Name ='Alerter'")
'servicetostart.StartService()
'Result = objServiceName.StartService
'If 0 <> Result Then
' wscript.echo "Start " & ServiceName & " error:" & Result
'End If
objServiceName.StartService
'wscript.echo Servicename & "could not start with error: " & Result
end if
end if
'wscript.echo objServiceName
Loop
As of right now I am recieving an error whenever it actually tries to start the service. I receive a "Provider Failure code:80041004 Source:SWbemObjectEX". I have been looking through the posts about this error and attempting the fixes suggested. Also, as you can see, I have been trying variations, but I am afraid I am merely guessing.
So to my question, what is causing the "Provider Failure"? I have looked up these information for the Win32_Service Class here:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa394418%28v=vs.85%29.aspx#methods
and looked up the method here:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa393660%28v=vs.85%29.aspx
But have been unable to work out where the I am going wrong.
Thanks,
Joe
on a side note, the service I am testing, ie. making sure the service is starting, creating the text file, then stopping the service and running the "start service" code is Windows Defender. The service name is "WinDefend".
FINAL WORKING CODE:
Const ForReading = 1
strComputer = "."
Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("\\vmware-host\Shared Folders\Documents\Biffduncan\Monthly Server Maintanence\service_list.txt",ForReading,True,-2)
Do Until objFile.AtEndOfStream
fLine = Split(objFile.ReadLine,",")
Set objService = objWMIService.get("Win32_Service.Name='" & fLine(0) & "'")
if InStr(fLine(2),"Running") then
'wscript.echo "it was running!"
if objService.Started then
'do nothing
else
Result = objService.StartService()
if Result <> 0 then
wscript.echo "The service: " & objService.Name & " did not start with error: " & Result
else
wscript.echo "Service " & objService.Name & " started"
end if
end if
end if
Loop

Error code 0x80041004 means that the WMI provider encountered an error after it was already initialized. The error code doesn't say anything about the cause of the error, though, nor does it provide any details. Try running WBEMTest or WMIDiag to track down the error. Also check the eventlog for related errors/warnings. If everything else fails, try rebuilding the WMI repository.
As for your code, the first thing I'd do is strip it down to the bare minimum, to avoid potential error sources:
Set wmi = GetObject("winmgmts://./root/cimv2")
Set svc = wmi.Get("Win32_Service.Name='WinDefend'")
rc = svc.StartService
WScript.Echo rc
Also, I wouldn't recommend writing the service status to a file at some random point in time, and then try starting services according to the contents of that file. There is no guarantee that the start mode hasn't been changed since the file was created, or that the service is even installed anymore.
Whether or not a service should be started is indicated by its StartMode property, so just check those services that are set to Auto. Services set to Manual will be started by the system on demand, so there's no need to launch them just because they were running when you took the snapshot.
qry = "SELECT * FROM Win32_Service WHERE StartMode='Auto'"
For Each svc In wmi.ExecQuery(qry)
If Not svc.Started Then svc.StartService
Next

Related

.vbs to tell time with Sapi but not interrupt given some open processes

I have composed a .vbs file with near zero knowledge of this coding language (with major code from here and here). I put this together to get my computer to tell me the time every 15 minutes (combined with task scheduler). The trickiest part was to have the script check if zoom was running (as to not interrupt video calls with the speech voice). Now, I would like to take it a step further and check for a second process, Microsoft Teams, for the same reason (to not interrupt video calls). So, I have my basic script copied below. It works for telling time and checking for zoom, but I am unsure how to go about adding "Microsoft Teams" to be checked also.
Dim hour_now, minute_now, speaks, speech
hour_now = hour(time)
minute_now = minute(time)
If minute_now = 0 Then
speaks = "Il est " & hour_now & " heures"
Else
speaks = "Il est " & hour_now & " heures " & minute_now & " minutes"
End If
Set speech = CreateObject("sapi.spvoice")
Dim i
Dim strComputer
Dim FindProc
strComputer = "."
FindProc = "zoom"
Set objWMIService = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set colProcessList = objWMIService.ExecQuery _
("Select Name from Win32_Process WHERE Name LIKE '" & FindProc & "%'")
If colProcessList.count>0 then
'wscript.echo FindProc & " is running"
else
'wscript.echo FindProc & " is not running"
speech.Speak speaks
End if
Set objWMIService = Nothing
Set colProcessList = Nothing
I imagine that I'd need to either set the FindProc object to a list of two and either iterate or have it evaluate all at once. Any help is appreciated. BTW, my computer has a French voice so the telling time is written for French.
Also, if anyone has any ideas as how to set this task up with some kind of off/on button or switch, that could be useful, too.
TIA
The only issue I see with your answer code is that it will fail to say the time if any executable is running that starts with "zoom" or "teams", such as ZoomIt.exe or TeamSpirit.exe. Otherwise, it can all be done with far fewer lines of code. Here's my version:
Set oSapi = CreateObject("sapi.spvoice")
Set oWMI = GetObject("winmgmts://./root/cimv2")
Function ProcessExist(Exe)
On Error Resume Next
Set oProcesses = oWMI.ExecQuery("Select Name from Win32_Process Where Name = '" & Exe & "'")
If oProcesses.Count>0 Then ProcessExist = True Else ProcessExist = False
On Error Goto 0
End Function
If Not ProcessExist("Zoom.exe") And Not ProcessExist("Teams.exe") Then
Speech = "Il est " & hour(time) & " heures "
If minute(time)>0 Then Speech = Speech & minute(time) & " minutes"
oSapi.Speak Speech
End If
Alternate version that checks a list of Exes:
Const ExeList = "Zoom,Teams"
Set oSapi = CreateObject("sapi.spvoice")
Set oWMI = GetObject("winmgmts://./root/cimv2")
Function ProcessExist(Exe)
On Error Resume Next
Set oProcesses = oWMI.ExecQuery("Select Name from Win32_Process Where Name = '" & Exe & "'")
If oProcesses.Count>0 Then ProcessExist = True Else ProcessExist = False
On Error Goto 0
End Function
Function InProcessList()
ArrExe = Split(ExeList,",")
InProcessList = False
For Each Exe In ArrExe
If ProcessExist(Exe & ".exe") Then InProcessList = True
Next
End Function
If Not InProcessList Then
Speech = "Il est " & hour(time) & " heures "
If minute(time)>0 Then Speech = Speech & minute(time) & " minutes"
oSapi.Speak Speech
End If
I figured it out. I am not completely satisfied with the fix, but it works and that's all that really matters.
Thanks to comments on post referencing to For Each loops and with some additional sleuthing, this is what I have.
Dim hour_now
Dim minute_now
Dim speaks
Dim speech
hour_now = hour(time)
minute_now = minute(time)
If minute_now = 0 Then
speaks = "Il est " & hour_now & " heures"
Else
speaks = "Il est " & hour_now & " heures " & minute_now & " minutes"
End If
Set speech = CreateObject("sapi.spvoice")
Dim strComputer
Dim TheseProcs
Dim colProcessList
Dim mycount
strComputer = "."
Set objWMIService = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
TheseProcs = Array("zoom", "teams")
mycount = 0
For Each FindProc In TheseProcs
Set colProcessList = objWMIService.ExecQuery _
("Select Name from Win32_Process WHERE Name LIKE '" & FindProc & "%'")
If colProcessList.count>0 Then
'wscript.echo FindProc & " is running"
mycount = mycount + 1
End If
Next
If mycount=0 then
speech.Speak speaks
End if
Set objWMIService = Nothing
Set colProcessList = Nothing
I ran into a couple road blocks, though. Defining the Array() and setting up the For Each loop were straightforward enough. I had difficulty in storing the output within the loop. I tried to first define the colProcessList as an ArrayList then use the colProcessList.Add to combine the results from each iteration, but I kept getting errors thrown that it wasn't allowed for that object (I assume the incompatibility is from the .ExecQuery.). So, then, I brought the colProcessList.count code within the loop and created another variable to track it. After the loop finishes, I then evaluate this object for the speech functionality. I am unsatisfied with this part though as I'm sure there is a better way to capture the output of each iteration. Furthermore, I am not satisfied that the code will execute every iteration, rather than stopping once some condition is met (e.g., mycount>0). I welcome input on these as well as scheduling the task manually with an off/on switch.

VBScript - Multiple Issues

So I've been thrown in the deep end of the shark tank without even my arm floaters and I don't know how to swim (Translation - I don't know VBS).
So I find myself here because I keep hitting my scripts with the two sticks I have it still doesn't work. When I fix one issue another appears and when I fix that one the other returns (feel like I'm chasing my tail).
So below is the latest iteration of my code (I keep moving crap around thinking it might magically work).
'---- Set Constant for Reading
Const ForReading = 1
'----- Define at the Variables for the scripts
Set objDictionary = CreateObject("Scripting.Dictionary")
Set objFSO = CreateObject("Scripting.FileSystemObject")
'------ Path for File below is Explicit (meaning you need to enter the complete path)
Set objTextFile = objFSO.OpenTextFile("c:\users\me\documents\Small- ComputerList.txt", ForReading)
'---- Begin Loop for reading the Array
Do Until objTextFile.AtEndOfStream
strNextLine = objTextFile.Readline
arrServiceList = Split(strNextLine , ",")
' ------- strComputer = "usms-w-ksd68598" Commented out from original script
' ------- Reading from the Array
Set objWMIService = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate}!\\" & arrServiceList(0) & "\root\cimv2")
' ------- Running the Command to find all the printers
Set colPrinters = objWMIService.ExecQuery _
("Select * from Win32_Printer")
For Each objPrinter in colPrinters
objDictionary.RemoveAll
objDictionary.Add objPrinter.PortName, objPrinter.PortName
Next
' ------ Running the Command to find all the TCP/IP Printer Ports
Set colPorts = objWMIService.ExecQuery _
("Select * from Win32_TCPIPPrinterPort")
For Each objPort in colPorts
If objDictionary.Exists(objPort.Name) Then
strUsedPorts = strUsedPorts & _
objDictionary.Item(objPort.Name) & VbCrLf
Else
strFreePorts = strFreePorts & objPort.Name & vbCrLf
End If
Next
'----- Printing out the Results to the screen
For i = 1 to Ubound(arrServiceList)
& arrServiceList(i)
Next
Loop
Wscript.Echo "System Name: " & arrServiceList(0)
Wscript.Echo "The following ports are in use for: " & VbCrLf & strUsedPorts
Wscript.Echo "The following ports are not used for: " & VbCrLf & strFreePorts
If my crazy ducted taped scripts make no sense please don't be shocked. I've been stuck in a cave hitting the keyboard with two sticks and this is the result I've come up with. Not too bad for a caveman but it still doesn't work.
Any help, assistance, advice, comment, jokes, sarcasm, ranting appreciated. Any trolling will be swiftly dealt with a big mallet over the head.
Thank you,
Ed Medina
Lo and behold the gods of code have shine their light upon my path and provided me with an answer.
Anyway, Thanks to Big Chris and Mr. Roryap for their questions. I want to also thank the Academy, all of my fellow coders, my mom, coffee, the mailman, my wife, my cat and all the little people.
Here is a code that will work which will read from a file in your Temp Folder (file is named Computers.txt) and then against that file it will run and test to find all the printers ports in that computer in your Domain (Network).
The output is a simple Echo out giving it to you in the window. I just kept hitting the keyboard with my two sticks in my dark, smelly cave and out came out the code.
The only caveat is that if you have a wrong computer name or a computer turned off the script will fail at that point and won't continue (yeah, yeah, working on it).
'---- Set Constant for Reading
Const ForReading = 1
'----- Define at the Variables for the scripts
Set objDictionary = CreateObject("Scripting.Dictionary")
Set objFSO = CreateObject("Scripting.FileSystemObject")
'------ Path for File below is Explicit (meaning you need to enter the complete path)
Set objFile = objFSO.OpenTextFile("c:\Temp\Computers.txt", ForReading)
'---- Begin Loop for reading the Array
Do Until objFile.AtEndOfStream
strComputer = objFile.ReadLine
' ------- Reading from the Array
Set objWMIService = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
' ------- Running the Command to find all the printers
Set colPrinters = objWMIService.ExecQuery _
("Select * from Win32_Printer")
For Each objPrinter in colPrinters
objDictionary.RemoveAll
objDictionary.Add objPrinter.PortName, objPrinter.PortName
Next
' ------ Running the Command to find all the TCP/IP Printer Ports
Set colPorts = objWMIService.ExecQuery _
("Select * from Win32_TCPIPPrinterPort")
For Each objPort in colPorts
If objDictionary.Exists(objPort.Name) Then
strUsedPorts = strUsedPorts & _
objDictionary.Item(objPort.Name) & VbCrLf
Else
strFreePorts = strFreePorts & objPort.Name & vbCrLf
End If
Next
'---- Output to Screen
Wscript.Echo "System Name: " & strComputer
Wscript.Echo "The following ports are in use for: " & VbCrLf & strUsedPorts
Wscript.Echo "The following ports are not used for: " & VbCrLf & strFreePorts
Loop
Thank you.
BTW if anyone know how to throw an error and keep working give feel free to post. Thanks again everyone.

VBScript errors with GetObject call

The goal is to retrieve the Dell service tags of all systems in a list (pseudo-systems given below in place of real system names)
The following script was originally used, and works just fine.
On Error Resume Next
strComputer=InputBox ("Enter the computer name of the server you'd like to query for Service Tag")
Set objWMIservice = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
WScript.Echo "Error: " & Err.number
Set colitems = objWMIservice.ExecQuery("Select SerialNumber from Win32_BIOS", , 48)
For Each objitem In colitems
WScript.Echo "Dell Service Tag: " & objitem.SerialNumber
Next
It asks for the user to input the system name, and then retrieves the tag.
However, there are 200+ systems to run, and it'd be nice to avoid having to type them all in manually.
My attempt to do just that (below) is close to right, but fails with error 70 codes on systems that the first script finds just fine.
On Error Resume Next
Dim systems, splitSystems, objWMIservice, fso, output, tag, mystr
systems = "sys1,sys2,sys3,sys4"
splitSystems = Split(systems,",")
Set fso = CreateObject("Scripting.FileSystemObject")
Set output = fso.CreateTextFile("system_tags.csv", True)
output.WriteLine """System Name"",""Service Tag"""
For Each sys In splitSystems
If Ping(sys) = True Then
'Doesn't work
mystr = "winmgmts:\\" & sys & "\root\cimv2"
Set objWMIservice = GetObject(mystr)
'Also doesn't work
'Set objWMIservice = GetObject("winmgmts:\\" & sys & "\root\cimv2")
'Works just dandy
'Set objWMIservice = GetObject("winmgmts:\\sys1\root\cimv2")
If Err.number <> 0 Then
'output.WriteLine """" & sys & """,""ERROR """ & Err.number
WScript.Echo "Set objWMIservice = GetObject('" & mystr & "') failed from Err.code:" & Err.description
Else
For Each objitem In objWMIservice.ExecQuery("Select SerialNumber from Win32_BIOS",,48)
tag = objitem.SerialNumber
Next
output.WriteLine """" & sys & """,""" & tag & """"
End If
Else
output.WriteLine """" & sys & """,""OFFLINE"""
End If
Next
MsgBox "All done!"
Function Ping(strComputer)
Dim objShell, boolCode
Set objShell = CreateObject("WScript.Shell")
boolCode = objShell.Run("Ping -n 1 -w 300 " & strComputer, 0, True)
If boolCode = 0 Then
Ping = True
Else
Ping = False
End If
End Function
Can someone explain to me why the first two methods (commented "Doesn't work" and "Also doesn't work") fail, but hardcoding the system name works just fine?
EDIT: The lines in the latter script following the condition If Err.number <> 0 Then were updated to provide the error description. Output is as follows, with system names replaced with pseudonyms:
Set objWMIservice = GetObject('winmgmts:\\sys1\root\cimv2') failed from Err.code:Permission denied
Set objWMIservice = GetObject('winmgmts:\\sys3\root\cimv2') failed from Err.code:Permission denied
EDIT2: Further testing shows that it at least one issue is related to successfully finding the service tag of one system, and failing on the following system, which for some reason results in the previous tag being used

Echo whether a process is running on a range of remote computers

I'm trying to create a script that will connect to remote computers within an IP address range and then echo which of those is running the explorer.exe process.
When I run the script within a small range (10.2.1.1 - 10.2.1.10), I know that 10.2.1.4 is offline and that 10.2.1.9 and 10.2.1.10 are not Windows based computers and should therefore echo "Explorer.exe is not running" however that doesn't seem to be the case. They appear to return the same result of the previous server. For instance, 10.2.1.3 has 3 instances of Explorer.exe and echo's 3 times, I then get the same result for 10.2.1.4 which is offline.
My script is as follows:
On Error Resume Next
intStartingAddress = InputBox("Please enter a starting address: (e.g. 1)", "Starting Address")
intEndingAddress = InputBox("Please enter an ending address: (e.g. 254)", "Ending Address")
strSubnet = InputBox("Please enter a subnet excluding the last octet: (e.g. 10.2.1.)", "Subnet")
For i = intStartingAddress to intEndingAddress
strComputer = strSubnet & i
Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set colProcess = objWMIService.ExecQuery("Select * From Win32_Process Where Name = 'Explorer.exe'")
For Each objProcess in colProcess
If colProcess.Count > 0 Then
Wscript.Echo strComputer & " Explorer.exe is running."
Else
Wscript.Echo strComputer & " Explorer.exe is not running."
End If
Next
Next
Wscript.Echo "That's all folks!"
What makes you believe that non-Windows computers would respond to WMI queries in the first place? For most non-Windows computers the statement
Set objWMIService = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate}!\\" _
& strComputer & "\root\cimv2")
will simply fail, because they don't support WMI (which is short for Windows Management Instrumentation). Because of this error, the objWMIService object remains the same as it was in the previous loop cycle, so your subsequent instructions query the same host you did before. You never see the error, though, because it's masked by the global On Error Resume Next.
This can be mitigated by removing the global On Error Resume Next and changing this loop:
For i = intStartingAddress to intEndingAddress
strComputer = strSubnet & i
Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
...
Next
into something like this:
For i = intStartingAddress to intEndingAddress
strComputer = strSubnet & i
Set objWMIService = Nothing
On Error Resume Next
Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" _
& strComputer & "\root\cimv2")
On Error Goto 0
If Not objWMIService Is Nothing Then
...
Else
WScript.Echo strComputer & " cannot be accessed."
End If
Next
You can distinguish between unreachable computers and computers that don't appear to be running Windows by combining the above with a ping test:
Set wmi = GetObject("winmgmts://./root/cimv2")
qry = "SELECT * FROM Win32_PingStatus WHERE Address='" & strComputer & "'"
For Each ping In wmi.ExecQuery(qry)
reachable = (0 = ping.StatusCode)
Next
If reachable Then
If objWMIService Is Nothing Then
'computer is not running Windows
Else
'computer is running Windows
End If
Else
'computer is offline
End If
First: I would move the colProcess.Count check to occur before the colProcess loop. If there are no collections in the object you will not get an echo response.
Second: I would test for a value within the WMI query such as ProcessID and check if it is Null or if it does have a value, meaning that it is actually running.
intStartingAddress = InputBox("Please enter a starting address: (e.g. 1)", "Starting Address")
intEndingAddress = InputBox("Please enter an ending address: (e.g. 254)", "Ending Address")
strSubnet = InputBox("Please enter a subnet excluding the last octet: (e.g. 10.2.1.)", "Subnet")
For i = intStartingAddress to intEndingAddress
strComputer = strSubnet & i
Set objWMIService = Nothing
On Error Resume Next
Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
On Error Goto 0
If objWMIService Is Nothing Then
Wscript.Echo strComputer & " Explorer.exe is not running."
Else
Set colProcess = objWMIService.ExecQuery("Select * From Win32_Process Where Name = 'Explorer.exe'")
If colProcess.Count = 0 Then
Wscript.Echo strComputer & " Explorer.exe is not running."
Else
For Each objProcess in colProcess
If IsNull(objItem.ProcessID) Or Not IsNumeric(objItem.ProcessID) Then
Wscript.Echo strComputer & " Explorer.exe is not running."
Else
Wscript.Echo strComputer & " Explorer.exe is running. (Process ID: " & objItem.ProcessID & ")"
End If
Next
End If
End If
Next
Wscript.Echo "That's all folks!"
EDIT:
Modified Script to take into account the WMI Query will fail on Non-Windows Operating Systems as pointed out by Ansgar Wiechers.

Skipping computers with error

I'm having an issue with a VBScript that looks for printers on computers from a list on an Excel sheet and then finds them through WMI. It matches them through the IP address name and then writes a batch file that I can install them from. My issue is that when I have a computer that is turned off I get a 462 error which is then cleared but then the printers for the previous computer are written again. I'm quite new at this so I'm not sure if I'm just missing something really basic here.
Batch = "printerOutput.txt"
Const ForWriting = 2 'Set to 8 for appending data
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile(Batch, ForWriting)
On Error Resume Next
Dim printerDictionary 'Create Printer dictionary of names and IP addresses
Set printerDictionary = CreateObject("Scripting.Dictionary")
printerDictionary.Add "Printer","xxx.xxx.xxx.xxx"
Set objExcel_1 = CreateObject("Excel.Application")
' Statement will open the Excel Workbook needed.
Set objWorkbook = objExcel_1.Workbooks.Open _
("x\p.xls")
If Err.Number <> 0 Then
MsgBox "File not Found"
Wscript.Quit
End If
'Checks for errors
f = 1 'Sets variable that will loop through Excel column
Do
' Msgbox f,, "Begining of Do Loop"
strComputer = objExcel_1.Cells(f, 1).Value
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colPrinters = objWMIService.ExecQuery("Select * From Win32_Printer")
For Each objPrinter in colPrinters 'For ever objPrinter found in the computers WMIService
If Err.Number = 0 Then
objFile.WriteLine Err.Number
If InStr(ObjPrinter.PortName,".") = 4 then 'If the printers IP port name is written like 128.xxx.xxx.xxx
'MsgBox ObjPrinter.Name & " " & ObjPrinter.PortName,, "IfStatement"
PrtDict ObjPrinter.PortName, StrComputer
ElseIf InStr(ObjPrinter.PortName,"_") = 3 Then 'If the printers IP port name is written like IP_128.xxx.xxx.xxx
cleanIP = GetIPAddress(objPrinter.PortName) 'Clean IP
PrtDict cleanIP, StrComputer
End If
Else
objFile.WriteLine "REM " & strComputer & " - Error: " & err.number
Err.Clear
End If
Next
f = f + 1
Loop Until objExcel_1.Cells(f, 1).Value = ""
objExcel_1.ActiveWorkbook.Close
ObjExcel_1.Quit
Function PrtDict(PrtMn, CMP) 'Loops through the dictionary to find a match from the IP address found
For Each x in printerDictionary
'MsgBox PrtMn & "=" & printerDictionary.Item(x),,"InPtrDict"
If printerDictionary.Item(x) = PrtMn Then
objFile.WriteLine "psexec -u \%1 -p %2 " & CMP & " path\" & x & ".bat"
End If
Next
End Function
'100
Function GetIPAddress(Address) 'For cleaning up IP address with names like IP_128.xxx.xxx.xxx
IPtext = InStr(Address,"_")
IPaddress = len(Address) - IPtext
GetIPAddress = Right(Address,IPaddress)
End Function
What happens is this:
On Error Resume Next
This enables error-handling (or rather error suppression) for the rest of the script, since it's never disabled.
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
This command fails, because strComputer is not reachable. Because of the error the variable Err is set and objWMIService retains its previous value.
Set colPrinters = objWMIService.ExecQuery("Select * From Win32_Printer")
This command succeeds and re-reads the printer list from the previous computer, because objWMIService still refers to that computer.
For Each objPrinter in colPrinters
The script enters the loop, because colPrinters got populated with the printers from the previous computer (again), …
If Err.Number = 0 Then ... Else ... End If
… but because Err is still set, the script goes into the Else branch, where the error is reported and cleared:
objFile.WriteLine "REM " & strComputer & " - Error: " & err.number
Err.Clear
Then the script goes into the next iteration of the loop. Err is now cleared, so the rest of the printers in colPrinters is being processed normally.
Global On Error Resume Next is the root of all evil. Don't do this. EVER.
If you absolutely must use On Error Resume Next, enable error-handling locally, put some actual error handling code in place, and disable error-handling right afterwards. In your case one might implement it like this:
...
Do
strComputer = objExcel_1.Cells(f, 1).Value
On Error Resume Next
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
If Err Then
objFile.WriteLine "REM " & strComputer & " - Error: " & err.number
Set objWMIService = Nothing
End If
On Error Goto 0
If Not objWMIService Is Nothing Then
Set colPrinters = objWMIService.ExecQuery("Select * From Win32_Printer")
For Each objPrinter in colPrinters
...
Next
f = f + 1
End If
Loop Until objExcel_1.Cells(f, 1).Value = ""
...

Resources