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
Related
Kind of a novice with VbScript, and trying to implement error handling. My method is to pass the error object to a HandleErr sub, but the error apparently gets cleared by the "On Error Resume Next" statement withing the sub. Using Windows 7.
On Error Resume Next
Dim x
x = 1/0
msgbox "Original Error: " & err.Number & " - " & err.Description
if err.number <> 0 then HandleErr err
Sub HandleErr(objErr)
on error resume next '### Without this On Error statement, the script runs fine.
msgbox "Error in HandleErr: " & objErr.Number & " - " & objErr.Description '### objErr.Number becomes zero.
WScript.Quit objErr.Number
End Sub
I imagine there is a simple answer for this. Any help would be greatly appreciated.
You want to stop the skipping errors with On Error Resume Next once you reach HandleErr(). Also use Err.Clear() to reset Err object.
On Error Resume Next
Dim x
x = 1/0
MsgBox "Original Error: " & Err.Number & " - " & Err.Description
if Err.Number <> 0 then HandleErr Err
'Stop skipping lines when errors occur.
On Error Goto 0
Sub HandleErr(objErr)
MsgBox "Error in HandleErr: " & objErr.Number & " - " & objErr.Description '### objErr.Number becomes zero.
'Clear current error now you have trapped it.
Err.Clear
WScript.Quit objErr.Number
End Sub
Personally though I wouldn't pass Err into your function because Err is a global built-in object so you can still check the values without passing it in.
On Error Resume Next
Dim x
x = 1/0
MsgBox "Original Error: " & Err.Number & " - " & Err.Description
Call HandleErr()
'Stop skipping lines when errors occur.
On Error Goto 0
Sub HandleErr()
'Do we need to trap an error?
If Err.Number <> 0 Then
MsgBox "Error in HandleErr: " & Err.Number & " - " & Err.Description '### Err.Number becomes zero.
'Clear current error now you have trapped it.
Err.Clear
WScript.Quit Err.Number
End If
End Sub
I'm trying to deploy an application through SCCM 2012 for Windows7 (x86 and x64) that requires to notify the user that his Microsoft Outlook should be closed before to continue with the installation. It could be either with a Timer or a (Yes / No) choice, then if the user press Yes then it will close Outlook and will continue with the installation otherwise it will send a log file saying the the user cancelled the installation but it can be retried at any time.
So far I just have the installation script that works only to install the applications using a command line script. So, it will just execute some MSI's installations and Windows updates, and then it quits.
The script I have that creates the pop up and that can be called by my CMD file is the following VBScript and was taken from a TechNet article.
Const TIMEOUT = 7
Set objShell = WScript.CreateObject("WScript.Shell")
Set objFS = WScript.CreateObject("Scripting.FileSystemObject")
strPath = Wscript.FullName
strFileVersion = objFS.GetFileVersion(strPath)
iRetVal = objShell.Popup(Wscript.FullName & vbCrLf & _
"File Version: " & _
strFileVersion & vbCrLf & _
"Would you like to close Outlook application and continue with the installation?" _
,TIMEOUT,"Outlook Validation",vbYesNo + vbQuestion)
Select Case iRetVal
Case vbYes
Set objFile = objFS.GetFile(strPath)
objShell.Popup WScript.FullName & vbCrLf & vbCrLf & _
"File Version: " & strFileVersion & vbCrLf & _
"File Size: " & Round((objFile.Size/1024),2) & _
" KB" & vbCrLf & _
"Date Created: " & objFile.DateCreated & vbCrLf & _
"Date Last Modified: " & objFile.DateLastModified & _
vbCrLf,TIMEOUT
Wscript.Quit
Case vbNo
Wscript.Quit
Case -1
WScript.StdOut.WriteLine "Popup timed out."
Wscript.Quit
End Select
So I don't know if there's any useful example that I can use and customize it from there. I'm clueless, blindfolded, I don't see the light. Well you understand my frustration.
Any ideas, examples or links will be really appreciated!!
Thanks & kind regards.
Joel.
This is one way.
Set objWMIService = GetObject("winmgmts:\\.\root\cimv2")
Set colItems = objWMIService.ExecQuery("Select * From Win32_Process")
For Each objItem in colItems
'msgbox objItem.name & " " & objItem.CommandLine
If LCase(objItem.name) = "outlook.exe" then
If Msgbox("Close Outlook", 33, "Install") = 1 then
objItem.terminate
End If
End If
Next
VBScript's Help file - https://www.microsoft.com/en-au/download/details.aspx?id=2764
For help with the WMI object use wmic at the command prompt.
wmic process get /? (same as wmic path win32_process get /?) and wmic process call /? list properties and methods.
Here my procedure which closes outlook before modifying the profile.
Is is part of a logon script. The show is a logging and informing procedure.
sub CloseOutlook
on error resume next 'to be able to log and continue
dim objWMIService, colProcessList, objProcess, sResult, oShell
set oShell = WScript.CreateObject("WScript.Shell")
set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\.\root\cimv2")
set colProcessList = objWMIService.ExecQuery ("Select * from Win32_Process Where Name = 'OUTLOOK.EXE'")
for Each objProcess in colProcessList
show "outlook is being closed"
objProcess.Terminate()
if Err <> 0 then
show "Error while closing outlook: " & err.Description
end if
sResult = oShell.Popup("Outlook is being closed, profile is configured")
next
end sub
If you want confirmation from the user you will have to use a MsgBox instead.
I'd recommend not faffing about with warnings and closing Outlook, but instead configure the advert to run when no users are logged in. Less chance for problems or accidentally miss-clicked "oh no you lost my emails" situations.
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.
Is theres a way to hide a vb6 error? I have a program that prompt an error but everything goes fine. Any help will be highly appreciated. Thanks.
UPD the code:
Set WshShell = CreateObject("WScript.Shell") 'passing the values to program2
strCommand = """" & App.Path & "\Prog2.exe """ & strArgs(0) & ";" & Trim$(.cFileName) & ";" & strArgs(1) WshShell.Run strCommand
you may suppress exception raising in vb in the following way:
on error resume next
doSomeDangerousStuff
if err.Number <> 0 then
'here is some optional error handling code
MsgBox "Exception occured: " & Err.Description
end if
on error goto 0
after the declaration of your function/sub put
on error goto error_filter
'your code is here'
before the ending of your sub/function put this
error_filter:
if err.description = (the error description you want to eliminate) then
exit sub
end if
I have VB6 application , I want to put some good error handling finction in it which can tell me what was the error and exact place when it happened , can anyone suggest the good way to do this
First of all, go get MZTools for Visual Basic 6, its free and invaluable. Second add a custom error handler on every function (yes, every function). The error handler we use looks something like this:
On Error GoTo {PROCEDURE_NAME}_Error
{PROCEDURE_BODY}
On Error GoTo 0
Exit {PROCEDURE_TYPE}
{PROCEDURE_NAME}_Error:
LogError "Error " & Err.Number & " (" & Err.Description & ") in line " & Erl & _
", in procedure {PROCEDURE_NAME} of {MODULE_TYPE} {MODULE_NAME}"
Then create a LogError function that logs the error to disc. Next, before you release code add Line Numbers to every function (this is also built into MZTools). From now on you will know from the Error Logs everything that happens. If possible, also, upload the error logs and actually examine them live from the field.
This is about the best you can do for unexpected global error handling in VB6 (one of its many defects), and really this should only be used to find unexpected errors. If you know that if there is the possibility of an error occurring in a certain situation, you should catch that particular error and handle for it. If you know that an error occurring in a certain section is going to cause instability (File IO, Memory Issues, etc) warn the user and know that you are in an "unknown state" and that "bad things" are probably going happen. Obviously use friendly terms to keep the user informed, but not frightened.
a simple way without additional modules, useful for class modules:
pre-empt each function/subs:
On Error Goto Handler
handler/bubbleup:
Handler:
Err.Raise Err.Number, "(function_name)->" & Err.source, Err.Description
voila, ghetto stack trace.
I use a home-grown Error.bas module to make reporting and re-raising less cumbersome.
Here's its contents (edited for length):
Option Explicit
Public Sub ReportFrom(Source As Variant, Optional Procedure As String)
If Err.Number Then
'Backup Error Contents'
Dim ErrNumber As Long: ErrNumber = Err.Number
Dim ErrSource As String: ErrSource = Err.Source
Dim ErrDescription As String: ErrDescription = Err.Description
Dim ErrHelpFile As String: ErrHelpFile = Err.HelpFile
Dim ErrHelpContext As Long: ErrHelpContext = Err.HelpContext
Dim ErrLastDllError As Long: ErrLastDllError = Err.LastDllError
On Error Resume Next
'Retrieve Source Name'
Dim SourceName As String
If VarType(Source) = vbObject Then
SourceName = TypeName(Source)
Else
SourceName = CStr(Source)
End If
If LenB(Procedure) Then
SourceName = SourceName & "." & Procedure
End If
Err.Clear
'Do your normal error reporting including logging, etc'
MsgBox "Error " & CStr(ErrNumber) & vbLf & "Source: " & ErrSource & vbCrLf & "Procedure: " & SourceName & vbLf & "Description: " & ErrDescription & vbLf & "Last DLL Error: " & Hex$(ErrLastDllError)
'Report failure in logging'
If Err.Number Then
MsgBox "Additionally, the error failed to be logged properly"
Err.Clear
End If
End If
End Sub
Public Sub Reraise(Optional ByVal NewSource As String)
If LenB(NewSource) Then
NewSource = NewSource & " -> " & Err.Source
Else
NewSource = Err.Source
End If
Err.Raise Err.Number, NewSource, Err.Description, Err.HelpFile, Err.HelpContext
End Sub
Reporting an error is as simple as:
Public Sub Form_Load()
On Error Goto HError
MsgBox 1/0
Exit Sub
HError:
Error.ReportFrom Me, "Form_Load"
End Sub
Reraising an error is as simple as calling Error.Reraise with the new source.
Although it is possible to retrieve the Source and Procedure parameters from the call stack if you compile with symbolic debug info, it's not reliable enough to use in production applications
ON ERROR GOTO
and the
Err
object.
There is a tutorial here.
Yes, take Kris's advice and get MZTools.
You can add line numbers to section off areas of complex procedures, which ERL will report in the error handler, to track down which area is causing the error.
10
...group of statements
20
...group of statements
30
...and so on
Use on
dim errhndl as string
on error goto errhndl
errhndl:
msgbox "Error"
Use the On Error statement and the Err object.