I have a large and fairly sophisticated Winbatch program (> 5000 Lines) which I just recently decided I should start giving my users a "better crash screen" :)
However the moment I implemented a custom Error Handler via: IntControl(73,3,0,"ErrorHandler",0) ; SET Error Handler
every place where I take advantage of the concept of:
ErrorMode(@off)
A line of Code that may fail, but it important to this program that I try
Err = LastError()
ErrorMode (@Cancel)
if Err then
Things didn't go right... make live decisions/changes accordingly
end if
- which is over 50 different spots in my code
began sending ALL Errors to my Handler (regardless of the ErrorMode) ...
which of course then had NO idea what the "make changes accordingly" concept should be for the exact circumstance of a certain acceptable error
and just exploded
if I ask for the ErrorMode to be Off I don't want it to send errors to the default handler OR my Handler...
As soon as I removed any IntControl(73,x,x,x,x) everything went right back to normal
The only solution I see is to switch the default handler just before I use ErrorMode(@off), but
1) To me it just seems like a bug and I hate "accommodating bugs", AND
2) My code is extremely optimize to not waste CPU cycles (if possible) and I just can't bear to set ErrorHandlers back and forth so frivolously
Anyone have any idea what I may have done wrong, am I crazy, or is this really an "unexpected result" / bug in Winbatch
Aaron
=====
7/31/14
well I ran some tests and another solution (which I don't like) would be to flag the ErrorMode(@off) in my own way... like ErrMode = @off and then have my custom handler check for this flag and do nothing specific, set LstError = LastError()
something like
Gosub UDFS
IntControl(73,3,0,"ErrorHandler",0)
;Known Good Statements
; ErrorMode(@off)
ErrMode = @off
Command That May Fail, but this Program needs to Try
;ErrorMode(@Cancel)
ErrMode = @Cancel
;if LastError() then
if LstError then
; There was trouble, make live decisions/changes as appropriate
End if
Exit
:UDFS
#DefineSubroutine ErrorHandler(wberrorarray)
LstError = wberrorarray[0]
if isDefined(ErrMode) then
if ErrMode == @OFF then Return 1 ; do nothing
end if
; Do Anything needed before End User Interaction
ErrorRecipient = "axxlbxxith@bxxxhxxx.com"
ErrorSubject = "My Program Error Report"
MyExe = IntControl(1004, 0, 0, 0, 0) ; Get fully qualified name of the current WB program
MyVersion = Strreplace(FileVerInfo(MyExe, "", "#FileVersion" ),",",".")
;This is a our universal Error Handler
; We want to ask User if we can send an email
; We want to ask user if they want to restart PS
MyDialogFormat=`WWWDLGED,6.2`
MyDialogCaption=`MyProgram - v<MyVersion> CRITICAL ERROR`
MyDialogX=073
MyDialogY=080
MyDialogWidth=170
MyDialogHeight=094
MyDialogNumControls=006
MyDialogProcedure=`DEFAULT`
MyDialogFont=`Calibri|6656|40|34`
MyDialogTextColor=`DEFAULT`
MyDialogBackground=`DEFAULT,219|219|219`
MyDialogConfig=0
MyDialog001=`001,077,066,012,PUSHBUTTON,"BtnYesRestart",DEFAULT,"Yes, and then Restart",1,10,32,DEFAULT,DEFAULT,DEFAULT`
MyDialog002=`073,077,048,012,PUSHBUTTON,"BtnNoRestart",DEFAULT,"No, but Restart",2,20,DEFAULT,DEFAULT,DEFAULT,DEFAULT`
MyDialog003=`001,001,164,034,PICTURE,"Picture_2",DEFAULT,"Picture 2",DEFAULT,30,DEFAULT,DEFAULT,DEFAULT,"BannerLite_ERROR.bmp"`
MyDialog004=`015,035,136,020,STATICTEXT,"ErrorInstructions1",DEFAULT,"Wooops! This Program, well... Crashed, Choked, FAILED, you know... ""Critical Error"".",DEFAULT,40,512,DEFAULT,DEFAULT,DEFAULT`
MyDialog005=`001,055,166,020,STATICTEXT,"ErrorInstructions2",DEFAULT,"Would it be OK if This Program used your Email client to send a Bug Report (you will be able to review before send)",DEFAULT,40,512,DEFAULT,DEFAULT,DEFAULT`
MyDialog006=`127,077,038,012,PUSHBUTTON,"BtnNoClose",DEFAULT,"No, and Exit",3,30,DEFAULT,DEFAULT,DEFAULT,DEFAULT`
;ButtonPushed=Dialog("MyDialog")
MyDialogCaption = StrReplace(MyDialogCaption,"<MyVersion>",MyVersion)
ButtonPushed=Dialog("MyDialog")
if ButtonPushed == 3 then
; User just wants us to quit
ErrorMode(@off)
; do cleanup tasks
Exit
elseif ButtonPushed == 2 then
; User Wants us to Restart
ErrorMode(@off)
; Do Cleanup Tasks
Run(IntControl(1004, 0, 0, 0, 0),"")
Exit
else
;User Wants us to help file a bug report
ErrorMode(@off)
;KNOWN WBERRORARRAY LABELS AS OF 7/31/14
NumberofErrorFields = ArrInfo( wberrorarray, 1 )
ErrorLabels = ArrDimension( NumberofErrorFields )
ArrInitialize( ErrorLabels, "UnDefined Label" ) ; ANY NEW INFORMATION Winbatch adds to weberrorarray in the future will get this label
ErrorLabels[0] = "LastError()"
ErrorLabels[1] = "wberrorhandlerline"
ErrorLabels[2] = "wberrorhandleroffset"
ErrorLabels[3] = "wberrorhandlerassignment"
ErrorLabels[4] = "wberrorhandlerfile"
ErrorLabels[5] = "wberrortextstring"
ErrorLabels[6] = "wberroradditionalinfo"
ErrorLabels[7] = "wberrorinsegment"
ErrorLabels[8] = "wberrorhandlerlinenumber"
ErrorLabels[9] = "line number in the UDF where the error occurred or 0."
ErrorLabels[10] = "a positive number if reported line numbers are accurate, zero (0) if possibly inaccurate or -1 if run from WinBatch Studio, in which case both `wberrorhandlerlinenumber` and `line number in the UDF where the error occurred or 0.` will contain the line number of the error in the UDF."
ErrorLabels[11] = "Used only with error handler UDF method to set return value of the function the error occurred on."
ErrorText = ""
For I = 0 to (NumberofErrorFields - 1)
ErrorText = ItemInsert(ErrorLabels : ": " : wberrorarray,-1,ErrorText,@TAB)
Next I
ErrorText = "MyEXE: " : MyExe : @TAB : "MyVersion: " : MyVersion : @TAB : @TAB : ErrorText
ErrorText = StrReplace(ErrorText," ","%%20")
ErrorText = StrReplace(ErrorText,@TAB,"%%0A")
MailToLink = "Mailto:" : ErrorRecipient : "?subject=" : ErrorSubject : "&body=" : ErrorText
ShellExecute(MailToLink,"","",0,"Open") ; call the current mailto: handler...
; DO Cleanup tasks
Run(IntControl(1004, 0, 0, 0, 0),"")
Exit
end if
Exit
#EndSubroutine
return
If I take this approach I have to update my code making it unable to function with the default handler.
Maybe I am envisioning this wrong, but I would like to operate just like the default handler, but with my own GUI and user choices.
Aaron
This is probably a variation of what you are considering but maybe a bit cleaner...Perhaps just set a flag above the code you are trying and reset after so you know at what point the error occurred. This keeps all your error handling in one place.
Jim
IntControl(73....)
error_flag = 0 ;'Generic'
....
...
error_flag = 1 ;'Act1'
command that fails occasionally
error_flag = 0 ;'Generic'
....
error_flag = 2 ;'Act2'
command that fails occasionally
error_flag = 0 ;'Generic'
....
error_flag = 3 ;'Act3'
command that fails occasionally
error_flag = 0 ;'Generic'
....
#DefineSubroutine ErrorHandler(wberrorarray)
Switch(error_flag)
Case 0; Generic
Do whatever fall all other errors
Break
Case 1; Act1
Do for Act1
Break
Case 2; Act3
Do for Act1
Break
Case 3; Act 3
Do for Act3
Break
Case error_flag;
Do whatever fall all other errors or figure out why error_flag isn't accounted for here.
Break
EndSwitch
#EndSubRoutine
Jim's suggestion is valid, but a program with 50 possible 'crashes' doesn't sound that stable. You might also consider breaking down the lasterr() into recoverable/non-recoverable options.
This is expected behavior. There are two major methods to trap errors. The more powerful method is to use IntControl 73 to set an error handler to capture all types of errors. Where as, the older ErrorMode can only be used to capture minor errors.
If you really want to handle errors for the entire script, we recommend using the IntControl 73 function. If you only want to minor capture errors for a single function use ErrorMode only, to wrap that function call. For a list of the various error numbers, see the Window Interface Language help file or manual.
Reference: http://techsupt.winbatch.com/webcgi/webbatch.exe?techsupt/nftechsupt.web+Tutorials+Trap~Errors.txt