VBSCRIPT - SWbemObjectSet error 8004100C - vbscript

I wrote a script to detect when I have a second monitor connected, and to switch Rainmeter layouts accordingly. However, occasionally when I put my computer to sleep, then wake it up, I get the following error:
---------------------------
Windows Script Host
---------------------------
Script: C:\Users\Tim\Documents\Shortcuts\Create\scripting\commandSniffer\detectMonitor.vbs
Line: 12
Char: 2
Error: Not supported
Code: 8004100C
Source: SWbemObjectSet
---------------------------
OK
---------------------------
All I really want to do is keep my script from crashing when I sleep my computer. If there's not an easy fix for this, how can I catch the error in the script and ignore it? Full source code below:
strComputer = "Localhost"
singleMon = "myLayout"
doubleMon = "myLayout(2monitor)"
rainmeterPath = """C:\Program Files\Rainmeter\Rainmeter.exe"" !LoadLayout "
previousState = 1
set wshshell = createobject("wscript.shell")
do
Set objWMI = GetObject("winmgmts:\\" & strComputer & "\root\wmi")
Set colItems = objWMI.ExecQuery ("SELECT * FROM WMIMonitorID")
'Wscript.Echo strComputer & " has " & colItems.count & " monitors configured."
if not isnull(colItems) and previousState <> colItems.count then
if colItems.count = 2 then
wshshell.run rainmeterPath & doubleMon,0
else
wshshell.run rainmeterPath & singleMon,0
end if
previousState = colItems.count
else
wscript.sleep 9000
end if
wscript.sleep 1000
loop

On Error Resume Next
transfers error handling from vbscript to you. You now need to test for errors after every call that might cause one.
If err.number <> 0 then
'fix error or ignore
err.clear
'If decide to crash
'err.raise(err.number, blah, blah, blah)
'wscript.Quit
End If
Error handling is a chain. From lowest function call up to the app. Windows looks for error handlers, if it can't find one it crashes. Err.raise allows you to propagate errors up the chain.

Related

Why no Description on error 80070002?

From some Visual Basic Script I accidentally tried to launch a program that did not exist:
Dim WshShell
Set WshShell = CreateObject("Wscript.Shell")
On Error Resume Next
WshShell.Run "incorrect"
WScript.Echo "Error:" & vbTab & Err.Description & vbCrLf & _
"Code:" & vbTab & Hex(Err.Number)
That gives:
Error:
Code: 80070002
So no description.
When I disable error checking (comment out the On Error Resume Next), I do get a description:
---------------------------
Windows Script Host
---------------------------
Script: D:\Folder\MyScript.vbs
Line: 3
Char: 1
Error: The system cannot find the file specified.
Code: 80070002
Source: (null)
---------------------------
OK
---------------------------
Is this difference in behavior a bug? Or am I missing something? I cannot find this documented anywhere.
I've done some testing myself and it appears to only happen with WshShell.Run().
For example try the following code which will work the same way but note the output.
Dim WshShell
Set WshShell = CreateObject("WScript.Shell")
On Error Resume Next
'This line will fail because there is no Type of 88.
WshShell.LogEvent 88, "incorrect"
WScript.Echo "Error:" & vbTab & Err.Description & vbCrLf & _
"Code:" & vbTab & Hex(Err.Number)
Output (with On Error Resume Next):
Error: Invalid procedure call or argument
Code: 5
Output (without On Error Resume Next):
Error: Invalid procedure call or argument
Code: 5
My guess is that WshShell.Run() is only reading from StdOut and not StdErr when On Error Resume Next is used, this is unique to WshShell.Run() because it is attempting to create a new process.
You could take this further by perhaps testing using WshShell.Exec() which gives access to both output streams.
Dim WshShell
Set WshShell = CreateObject("WScript.Shell")
On Error Resume Next
Set WshExec = WshShell.Exec("incorrect")
WScript.Echo "Error:" & vbTab & Err.Description & vbCrLf & _
"Code:" & vbTab & Hex(Err.Number)
Weirdly checked both WshExec.StdOut and WshExec.StdErr and neither contain the error output but the method behaves as expected when On Error Resume Next is used.
So not by any means conclusive.
But as the accompanying article states;
Just remember, scripting without mysteries would be insipid and boring
Useful Links
Doctor Scripto's Script Shop: To Err Is VBScript – Part 1

Scripting MMC with vbscript

I would like to add a snap in via vbscript and I have been having a problem getting the snap in to add to the console. It will be run in a Windows 7 environment. If someone could have a look see and direct me in the right direction I would be most grateful. Thanks.
<code>
'Elevated privileges start
'Start of UAC workaround code
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If WScript.Arguments.length =0 Then
Set objShell = CreateObject("Shell.Application")
objShell.ShellExecute "wscript.exe", Chr(34) & _
WScript.ScriptFullName & Chr(34) & " uac", "", "runas", 1
Else
consoleName = "C:\Burnett.msc"
Set fso = CreateObject("Scripting.FileSystemObject")
If fso.FileExists(consoleName) Then
Wscript.Echo "console already exists"
Else
On Error Resume Next
Set objMMC = CreateObject("MMC20.Application")
If err.Number <> 0 Then
Wscript.Echo "an error occurred. unable to create mmc console"
Wscript.Quit(0)
End If
objMMC.Show
Set objDoc = objMMC.Document
objDoc.snapins.add("Local Computer\Non-Administrators")
if err then
'Trap the error just after the statement where an error/exception can occur and handle it elegantly
msgbox("Snap-in Not found")
err.clear
end if
objDoc.ActiveView.StatusBarText = "Pane 1|Pane 2|Pane 3"
objMMC.UserControl = 1
objDoc.Name = consoleName
objDoc.Save()
End If
Set fso = Nothing
End If
</code>
"Local Computer\Non-Administrators" is just a system-supplied description for the particular configuration of a snap-in. In this case, the actual snap-in name is "Group Policy Object Editor". Thus to eliminate the error in the code change
objDoc.snapins.add("Local Computer\Non-Administrators")
to
objDoc.snapins.add("Group Policy Object Editor")
Unfortunately, this will only get you as far as MMC putting up a "Select Group Policy Object" dialog. You will then have to manually select the configuration you need using that dialog. As far as I can tell there is no way to supply Snapins.Add with the parameters to select the local non-admin users.
The code below will fully automate the process of setting up the snap-in. However, its reliance on SendKeys makes it extremely brittle. It worked on my system, but there's a good chance you'll need to modify the sequence of key strokes and/or the timing delays to make it work on your system. And once you get it working, there's no guarantee it will continue to do so as local conditions are mutable and can greatly effect the timing.
option explicit
if WScript.Arguments.Named.Exists("elevated") = false then
'Launch the script again with UAC permissions
CreateObject("Shell.Application").ShellExecute "wscript.exe", """" & WScript.ScriptFullName & """ /elevated", "", "runas", 1
WScript.Quit
end if
Dim mmc : set mmc = WScript.CreateObject("MMC20.Application")
mmc.Show
mmc.UserControl = 1 'to keep MMC open
Dim oShell : set oShell = WScript.CreateObject("Wscript.Shell")
oShell.AppActivate "Console1"
WScript.Sleep 200
oShell.SendKeys "%f"
WScript.Sleep 200
oShell.SendKeys "m"
WScript.Sleep 400
oShell.SendKeys "group{TAB}{ENTER}"
WScript.Sleep 1000
oShell.SendKeys "{TAB}{ENTER}"
WScript.Sleep 1000
oShell.SendKeys "{TAB}{TAB}{TAB}{RIGHT}{TAB}Non{ENTER}"
WScript.Sleep 1000
oShell.SendKeys "{TAB}{TAB}{ENTER}"
WScript.Sleep 1000
oShell.SendKeys "{TAB}{TAB}{TAB}{TAB}{ENTER}"

VBS To Event Log

I have a script that I am currently using to check when that network goes up or down. Its writing to a pinglog.txt .
For the life of me I can not figure out how to get it to write to the event log when the network goes down. Where it says:
Call logme(Time & " - " & machine & " is not responding to ping, CALL FOR
HELP!!!!",strLogFile)
Thats what I need to write to the Event Log "Machine is not repsonding to ping, CALL FOR HELP!!!!
'Ping multiple computers and log when one doesn't respond.
'################### Configuration #######################
'Enter the IPs or machine names on the line below separated by a semicolon
strMachines = "4.2.2.2;8.8.8.8;8.8.4.4"
'Make sure that this log file exists, if not, the script will fail.
strLogFile = "c:\logs\pinglog.txt"
'################### End Configuration ###################
'The default application for .vbs is wscript. If you double-click on the script,
'this little routine will capture it, and run it in a command shell with cscript.
If Right(WScript.FullName,Len(WScript.FullName) - Len(WScript.Path)) <> "\cscript.exe" Then
Set objWMIService = GetObject("winmgmts: {impersonationLevel=impersonate}!\\.\root\cimv2")
Set objStartup = objWMIService.Get("Win32_ProcessStartup")
Set objConfig = objStartup.SpawnInstance_
Set objProcess = GetObject("winmgmts:root\cimv2:Win32_Process")
objProcess.Create WScript.Path + "\cscript.exe """ + WScript.ScriptFullName + """", Null, objConfig, intProcessID
WScript.Quit
End If
Const ForAppending = 8
Const ForReading = 1
Const ForWriting = 2
Set objFSO = CreateObject("Scripting.FileSystemObject")
If objFSO.FileExists(strLogFile) Then
Set objFolder = objFSO.GetFile(strLogFile)
Else
Wscript.Echo "Log file does not exist. Please create " & strLogFile
WScript.Quit
End If
aMachines = Split(strMachines, ";")
Do While True
For Each machine In aMachines
Set objPing = GetObject("winmgmts:{impersonationLevel=impersonate}")._
ExecQuery("select * from Win32_PingStatus where address = '"_
& machine & "'")
For Each objStatus In objPing
If IsNull(objStatus.StatusCode) Or objStatus.StatusCode<>0 Then
Call logme(Time & " - " & machine & " is not responding to ping, CALL FOR
HELP!!!!",strLogFile)
Else
WScript.Echo(Time & " + " & machine & " is responding to ping, we are good")
End If
Next
Next
WScript.Sleep 5000
Loop
Sub logme(message,logfile)
Set objTextFile = objFSO.OpenTextFile(logfile, ForAppending, True)
objtextfile.WriteLine(message)
WScript.Echo(message)
objTextFile.Close
End Sub
Sorry about the spacing in the code. Thanks for the help
Use the WshShell object:
object.LogEvent(intType, strMessage [,strTarget])
object WshShell object.
intType Integer value representing the event type.
strMessage String value containing the log entry text.
strTarget Optional. String value indicating the name of the computer
system where the event log is stored (the default is the local
computer system). Applies to Windows NT/2000 only.
Like so:
Option Explicit
Dim shl
Set shl = CreateObject("WScript.Shell")
Call shl.LogEvent(1,"Some Error Message")
Set shl = Nothing
WScript.Quit
The first argument to LogEvent is an event type:
0 SUCCESS
1 ERROR
2 WARNING
4 INFORMATION
8 AUDIT_SUCCESS
16 AUDIT_FAILURE
EDIT: more detail
Replace your entire 'logme' sub-routine with this
Sub logme(t,m)
Dim shl
Set shl = CreateObject("WScript.Shell")
Call shl.LogEvent(t,m)
Set shl = Nothing
End Sub
Then change this line:
Call logme(Time & " - " & machine & " is not responding to ping, CALL FOR HELP!!!!",strLogFile)
To:
Call logme(1, machine & " is not responding to ping, CALL FOR HELP!!!!")

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 = ""
...

How to output Command prompt to a log file using VBScript

I'm not a programmer so I don't want to overly irritate the fine folk in this forum. My issue is that I would like to use VBScript to Telnet into a Linux device, issue a DF command and output all response to a log file which I can parse later. I originally found a method to successfully Telnet but I have have been experimenting without success regarding the text file output requirement. The following code certainly does not work but I am wondering if I am even close to the correct method?
Dim WshShell, oExec
Set WshShell = CreateObject("WScript.Shell")
Set oExec = WshShell.Exec("cmd /c dir")
WshShell.run"cmd" '*** open command window ***
WScript.Sleep 250
WshShell.SendKeys("{Enter}")
WshShell.SendKeys"telnet 10.13.2.2"
WshShell.SendKeys("{Enter}")
WScript.Sleep 2000
WshShell.SendKeys"root"
WshShell.SendKeys("{Enter}")
WScript.Sleep 1500
WshShell.SendKeys"password"
WshShell.SendKeys("{Enter}")
WScript.Sleep 1500
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objLogFile = objFSO.OpenTextFile("C:\VBSmemSize.txt", 2, True)
WshShell.SendKeys"df /mnt/cf"
WshShell.SendKeys("{Enter}")
Do
strFromProc = oExec.Stdout.Readline()
WScript.Echo strFromProc
Loop While Not objLogFile.StdOut.atEndOfStream
You can capture output from external commands but not at the same time interact with them like you do with sendkeys. Here an example of what works
Function ExecPing(strTarget)
Set objShell = CreateObject("WScript.Shell")
Set objExec = objShell.Exec("ping -n 2 -w 1000 " & strTarget)
strPingResults = LCase(objExec.StdOut.ReadAll)
If InStr(strPingResults, "antwoord van") Then '"reply from" in E
WScript.Echo VbCrLf & strTarget & " responded to ping."
ExecPing = True
Else
WScript.Echo VbCrLf & strTarget & " did not respond to ping."
ExecPing = False
End If
End Function
ExecPing pcname

Resources