I came to some VBScript examples, and I saw the statement On Error Resume Next basically at the beginning of the script.
What does it do?
It basically tells the program when you encounter an error just continue at the next line.
It's worth noting that even when On Error Resume Next is in effect, the Err object is still populated when an error occurs, so you can still do C-style error handling.
On Error Resume Next
DangerousOperationThatCouldCauseErrors
If Err Then
WScript.StdErr.WriteLine "error " & Err.Number
WScript.Quit 1
End If
On Error GoTo 0
When an error occurs, the execution will continue on the next line without interrupting the script.
It means, when an error happens on the line, it is telling vbscript to continue execution without aborting the script. Sometimes, the On Error follows the Goto label to alter the flow of execution, something like this in a Sub code block, now you know why and how the usage of GOTO can result in spaghetti code:
Sub MySubRoutine()
On Error Goto ErrorHandler
REM VB code...
REM More VB Code...
Exit_MySubRoutine:
REM Disable the Error Handler!
On Error Goto 0
REM Leave....
Exit Sub
ErrorHandler:
REM Do something about the Error
Goto Exit_MySubRoutine
End Sub
It enables error handling. The following is partly from https://msdn.microsoft.com/en-us/library/5hsw66as.aspx
' Enable error handling. When a run-time error occurs, control goes to the statement
' immediately following the statement where the error occurred, and execution
' continues from that point.
On Error Resume Next
SomeCodeHere
If Err.Number = 0 Then
WScript.Echo "No Error in SomeCodeHere."
Else
WScript.Echo "Error in SomeCodeHere: " & Err.Number & ", " & Err.Source & ", " & Err.Description
' Clear the error or you'll see it again when you test Err.Number
Err.Clear
End If
SomeMoreCodeHere
If Err.Number <> 0 Then
WScript.Echo "Error in SomeMoreCodeHere:" & Err.Number & ", " & Err.Source & ", " & Err.Description
' Clear the error or you'll see it again when you test Err.Number
Err.Clear
End If
' Disables enabled error handler in the current procedure and resets it to Nothing.
On Error Goto 0
' There are also `On Error Goto -1`, which disables the enabled exception in the current
' procedure and resets it to Nothing, and `On Error Goto line`,
' which enables the error-handling routine that starts at the line specified in the
' required line argument. The line argument is any line label or line number. If a run-time
' error occurs, control branches to the specified line, making the error handler active.
' The specified line must be in the same procedure as the On Error statement,
' or a compile-time error will occur.
On Error Statement - Specifies that when a run-time error occurs, control goes to the statement immediately following the statement. How ever Err object got populated.(Err.Number, Err.Count etc)
On Error Resume Next means that On Error, It will resume to the next line to resume.
e.g. if you try the Try block, That will stop the script if a error occurred
Related
I have a page that calls a classic ASP/VB script using the jQuery Ajax function, which allows handlers to be specified for success and error results. Those handlers receive back the response from the ASP script, which just executes some SQL code to insert a record into a database. If there's an SQL error that indicates a duplicate key violation, I want to replace the generated error message with a friendly one, using the code below. As I've discovered, that doesn't work because the code never reaches the "if conn.Errors.Count" line. If the SQL generates an error, the code immediately returns with the error message, which includes the line number of the "conn.Execute" line. Is there a way to get this to do what I want?
set conn=CreateObject("ADODB.Connection")
conn.Open ConnString
conn.Execute "INSERT ... " (long statement omitted for readability)
if conn.Errors.Count> 0 then
if instr(conn.Errors(0).Description, "duplicate key") > 0 then
Response.Write "Unable to add herb - code already exists"
else
Response.Write conn.Errors(0).Description
end if
else ' success
Response.Write "Herb added"
end if
conn.Close
set conn=nothing
as others have pointed out, the best solution is to use "on error resume next". so your code would look something like:
on error resume next
set conn=CreateObject("ADODB.Connection")
conn.Open ConnString
conn.Execute "INSERT ... " (long statement omitted for readability)
if Err.Number > 0 then
if instr(Err.Description, "duplicate key") > 0 then
Response.Write "Unable to add herb - code already exists"
else
Response.Write conn.Errors(0).Description
end if
else ' success
Response.Write "Herb added"
end if
conn.Close
set conn=nothing
on error goto 0 '-- this will remove error handling for the rest of the page and can be considered optional in this case
While using On Error Resume Next,it skips the encountered error and move to the next line.
If we use err.Number and err.Description it shows message and number related to the error.
My question is: what if it faces more than on error...then how it will show?
On Error Resume Next
intDivideByZero
floatDivideByZero
If err.Number <> 0 Then
msgbox "the error number is : " & err.Number
msgbox "the error description is : " & err.Description
End If
On error Resume 0
VBScript error handling is rather limited. You will need to put an error handler after each line where an error can occur. Also, the Err object isn't automatically reset after an error, so you need to do that yourself. Otherwise the object will still indicate an error after the next statement, even if none occured there.
On Error Resume Next
intDivideByZero
If Err Then
WScript.Echo "0x" & Hex(Err.Number) & ": " & Err.Description
Err.Clear
End If
floatDivideByZero
If Err Then
WScript.Echo "0x" & Hex(Err.Number) & ": " & Err.Description
Err.Clear
End If
On Error Goto 0
You can simplify that a little bit by wrapping the handler in a procedure and calling that procedure after each statement:
Sub Trap
If Err Then
WScript.Echo "0x" & Hex(Err.Number) & ": " & Err.Description
Err.Clear
End If
End Sub
On Error Resume Next
intDivideByZero : Trap
floatDivideByZero : Trap
On Error Goto 0
I try several version of Wscript exec method.
If i use a cmd.exe /C MyRequest, the execution does not report an error if the MyRequest is failing but return the error if cmd.exe /c is not used.
I was thinking that cmd.exe is reporting the return code of the call to the MPyRequest but seems not. How to retreive the return code in this case.
Here is the simplified version of my test (comment direct version to have the non failure)
Environnement will be mainly windows 7 (normaly no other system, maybe XP)
' Missing.cmd does not exist to force the failure test
'version with cmd.exe (CmdDir content is a valid and working cmd.exe)
ExecCmd = CmdDir & " /c Missing.cmd 1"
' direct version
ExecCmd = "Missing.cmd 1"
Set objShell = CreateObject("WScript.Shell")
On Error Resume Next
Set oExec = objShell.exec( ExecCmd)
' -- Post treatment ---------------------------
If ( err.Number = 0) Then
If ( oExec.ExitCode = 0 ) Then
' No error
wscript.echo "Execution OK"
Else ' Exit with error
wscript.echo "Error :" & oExec.ExitCode
end if
Else ' error on exec itself
wscript.echo "Execution. Error on object at call: " & err.Number _
& " Source: " & Err.Source & " Desc: " & Err.Description
end if
Solution based on all your reply (thanks all) [ #Damien, #Ekkehard.Horner, #Hans Passant]
check error (via on error and err.Number) for vbs internal error like bad variable/method call. This should be at exec call sub level (so not at main process if exec call is in a function/subroutine)
exec.status to wait until it change to 1. Wait is a following process checking the status, not like a shell.sleep
during this wait, catch stdout/stderr with a AtEndOfStream if life info is needed (if not, read after the close of the process)
for a timeout process, use a cycle of shell.sleep with an exit (of cycle) if timeout or other event is trapped (I use a counter associate to a clock time/sleep time in this case) and exit the loop if trigger occur associate with a exec.Terminate to kill the process in this case (depending of your need ...)
You need to check the ExitCode property.
If it is actually returning an error code, perhaps you are processing exitcode to early. Maybe add in oexec.StdOut.ReadAll() to make it wait until completion.
Perhaps you could add in something like this
....
....
Set oExec = objShell.exec(ExecCmd)
oExecResult = oexec.StdOut.ReadAll()
SessionExitCode = oexec.ExitCode
' -- Post treatment ---------------------------
If ( err.Number = 0) Then
If ( SessionExitCode = 0 ) Then
' No error
wscript.echo "Execution OK"
Else ' Exit with error
wscript.echo "Error :" & SessionExitCode
end if
Else ' error on exec itself
....
....
I developed an application in VB6. In client's environment it raises runtime errors which I can't reproduce under debugger. Is there any way to get the stacktrace or location of error?
I created log file and
I used
Err.Description,Err.Source
but it gives blank values.
Please help me.
my method(......
On Error GoTo Error_Handler
.........
Error_Handler :
writeToLogFile(Err.Source,Err.Description)
You've probably done something to clear the Err object before writing to the log file. This is very, very easy to do. What you'll want to do is as soon as you detect an error has occurred, grab the error message before doing anything else. Then pass the error message to whatever logging routine you're using. E.g.:
Dim sMsg As String
On Error Goto ErrHandler
' ...code here...
Exit Function
ErrHandler:
sMsg = "Error #" & Err.Number & ": '" & Err.Description & "' from '" & Err.Source & "'"
GoLogTheError sMsg
BTW, thanks for your guys' answers helping me. I'm about half a decade late to the game of VB6. I don't do windows unless forced to. ;)
Anyhow, when doing your error checking, say among 3000 individual record query insertions, I learned a couple tricks. Consider this block of code:
'----- order number 1246-------
On Error Goto EH1246:
sSql="insert into SalesReceiptLine ( CustomerRefListID,TemplateRe..."
oConnection.Execute sSQL
sSql="SELECT TxnID FROM SalesReceiptLine WHERE RefNumber='1246'..."
oRecordset.Open sSQL, oConnection
sTxnId = oRecordset(0)
oRecordset.Close
sSql="INSERT INTO SalesReceiptLine (TxnId,SalesReceiptLineDesc,Sal..."
oConnection.Execute sSQL
EH1246:
IF Err.Number<>0 THEN
sMsg = sMsg & "Order # 1246; sTxnId = " & sTxnId & _
vbCrLf & Err.Number & ": " & Err.Description & vbCrLf
sErrOrders = sErrOrders & "1246,"
End If
On Error GoTo -1
'----- order number 1247-------
On Error Goto EH1247:
When not testing for Err.Number, you'll get a 0: on every order handled. (maybe you don't want that). The On Error GoTo -1 resets the error so that it will work again. Apparently, Err only works "once".
I wrote a php script to build the VB6 source code to run some 8000 odbc queries... :P
Do you definitely, positively have an Exit Function just above the Error_Handler:?
my method(......
On Error GoTo Error_Handler
........
Exit Sub
Error_Handler :
writeToLogFile(Err.Source,Err.Description)
"Exit Sub" should be added before you handle the Error_Handler function.....
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.