Link to home
Start Free TrialLog in
Avatar of luciliacoelho
luciliacoelhoFlag for Portugal

asked on

on error handling Visual Foxpro

I'm trying to setup an error handling routine.
I have tried to set up a simple "On Error do MyerrorHandler" at the begining of my main program. Then I create a simple error condition changing the name of a VFox table to get a run time error and activate the MyErrorHandler. Nevertheless the on error code doesn't execute.
If I follow the Try Catch approach how sould I proceed.
Kind regards
Lucilia C
ASKER CERTIFIED SOLUTION
Avatar of Cyril Joudieh
Cyril Joudieh
Flag of Lebanon image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
As Cyril says, you were on the right track, but somehow either you chnaged a table name your code does not work on or the on error is reset somewhere. TRY CATCH is not meant for global error handling, it's just there to catch expected errors locally.

The "MyerrorHandler" must either be a prg on it's own, you set procedure to, before on error, and that procedure must stay known throughout the lifetime of the application, it's really better to make a procudure in the main.prg file, as it's safe, it's always on the call stack.

If you ant to test your error handling simply add a line of total rubbish, like:
lcCode = "sdljfsjdgsfkghks"
&lcCode

This compiles but errors at runtime.

Or do
USE hjkshdfgkjsfhgkh

Bye, Olaf.
Oh, and another very simple way to test error handling is to call ERROR 1 or any other defined Error number.

Bye, Olaf.
I am using following setup and appropriate routine - it always saves the error info into a temporary file and also to the errorlog.dbf which is later posted to the development site (this part was removed):

ON ERROR DO PrgError WITH ERROR(), SYS(16), LINENO()  && This must be at the beginning of the main program

The PrgError procedure must also be in the main program or in procedure file:
 
PROCEDURE PrgError
LPARAMETERS nError, cMethod, nLine

LOCAL lcOutFile, lcErrtext, lcAppVers, lcAppName, ldAppDate, ;
  laUsed[1], lnWarea, lnI, lnWAcount, loFormObject

lnWarea = SELECT(0)

lcOutFile = ADDBS(SYS(2023)) + SYS(2015) + '.ERR'

lcAppVers = IIF(TYPE("_screen.AppVersion")="C", _screen.AppVersion, "XXX")
lcAppName = IIF(TYPE("_screen.AppName")="C", _screen.AppName, "XXX")
ldAppDate = IIF(TYPE("_screen.AppDate")="D", _screen.AppDate, {//})

IF VARTYPE(m.gn_errCount) <> "N"
  RELEASE gn_errCount
  PUBLIC gn_errCount
  gn_errCount = 0
ENDIF

gn_errCount = m.gn_errCount + 1

TEXT TO lcErrText TEXTMERGE NOSHOW 
**********************************************************************************
<<"*"+PADC("Error log info from " + lcAppName + "  v" + lcAppVers, 80)+"*">>
**********************************************************************************
ERROR trap in  <<PROGRAM()>>   <<SYS(0)>>   <<DATETIME()>>
**********************************************************************************
Version: <<lcAppVers>>  <<ldAppDate>> 
ERROR <<nError>> in <<cMethod>>,  Line: <<nLine>> (ErrCnt <<gn_errCount>>)
<<MESSAGE()>>

Settings:
SET DATE <<SET("DATE")>>
SET CENTURY <<SET("CENTURY")>>
SET STRICTDATE TO <<SET("STRICTDATE")>>
SET COLLATE TO <<SET("COLLATE")>>
Code Page: <<CPCURRENT()>>
SET EXACT <<SET("EXACT")>>
SET ANSI <<SET("ANSI")>>
SET DATASESSION TO <<SET("DATASESSION")>>

Configuration File: (<<IIF(FILE("@:\CONFIG.FPW"), "In EXE", "On disk")>>)
<<SYS(2019)>>
Resource File: (<<SET("RESOURCE")>>)
<<SYS(2005)>>
Temporary folder: (<<LTRIM(TRANSFORM(DISKSPACE(JUSTDRIVE(SYS(2023))),'999999999999999'))>> bytes free)
<<SYS(2023)>>
Home folder: (<<LTRIM(TRANSFORM(DISKSPACE(JUSTDRIVE(HOME())),'999999999999999'))>> bytes free)
<<HOME()>>
*Data folder: (<<LTRIM(TRANSFORM(DISKSPACE(JUSTDRIVE(AppDataFolder())),'999999999999999'))>> bytes free)
*<<AppDataFolder()>>
VFP Version: (Language <<VERSION(3)>>)
<<VERSION(1)>>
OS Version:
<<OS(1) + " " + OS(5) + " " + OS(7)>>
Topmost window: <<WONTOP()>>
Active form: <<IIF(TYPE("_screen.ActiveForm.Name")= "C", _screen.ActiveForm.Name, "")>>

ENDTEXT


lcErrText = lcErrText + CHR(13)+CHR(10) + "Form list:" + CHR(13)+CHR(10)

FOR lnI = 1 TO _screen.FormCount
  loFormObject = _screen.Forms[lnI]
  lcErrText = lcErrText + loFormObject.Class + " " + loFormObject.Name + " " + ;
      IIF(TYPE("loFormObject.FormMode") = "C", loFormObject.FormMode, "") + " " + ;
      IIF(TYPE("loFormObject.ActiveControl.Name") = "C", loFormObject.ActiveControl.Name, "") + CHR(13)+CHR(10)
NEXT


lcErrText = lcErrText + CHR(13)+CHR(10) + "Workarea list:" + CHR(13)+CHR(10)

lnWAcount = AUSED(laUsed)
FOR lnI = 1 TO m.lnWAcount
  lcErrText = lcErrText + STR(laUsed[lnI, 2], 5) + " " + PADR(laUsed[lnI, 1], 40) + ;
     TRANSFORM(RECNO(laUsed[lnI, 2])) + '/' + TRANSFORM(RECCOUNT(laUsed[lnI, 2])) + " " + ;
     TRANSFORM(BOF(laUsed[lnI, 2])) + " " + TRANSFORM(EOF(laUsed[lnI, 2])) + CHR(13)+CHR(10)
NEXT

RELEASE laUsed

= STRTOFILE(lcErrText, lcOutFile)

LOCAL ARRAY laCallStack[1]
= ASTACKINFO(laCallStack)

LIST STATUS TO FILE (lcOutFile) ADDITIVE NOCONSOLE
LIST MEMORY TO FILE (lcOutFile) ADDITIVE NOCONSOLE
LIST OBJECTS TO FILE (lcOutFile) ADDITIVE NOCONSOLE

LOCAL lnErrId, loErrRec

*-- Save error info into a table if possible
IF VARTYPE(gc_site_code) = 'C' AND !EMPTY(m.gc_site_code)
  TRY
    IF USED("ERRORLOG") OR OpenTable("ERRORLOG", .F., .T., "ERRORLOG")
      IF USED("COUNTERS") OR OpenTable("COUNTERS", .F., .T., "COUNTERS")
        lnErrId = CreateNewPK("ERRORLOGID")
        USE IN SELECT("COUNTERS")
        IF m.lnErrId > 0
          SELECT ERRORLOG
          SCATTER MEMO NAME loErrRec BLANK
          loErrRec.Id = m.lnErrId
          loErrRec.LogInfo = FILETOSTR(lcOutFile)
          UpdateSystemColumns(loErrRec, .T.)
          IF FLOCK()
            INSERT INTO ERRORLOG FROM NAME loErrRec
          ENDIF
        ENDIF
      ENDIF
      USE IN SELECT("ERRORLOG")
    ENDIF
  CATCH
    USE IN SELECT("COUNTERS")
    USE IN SELECT("ERRORLOG")
  ENDTRY
  
  SELECT (lnWarea)
  
ENDIF

WAIT WINDOW "ERROR INFORMATION WAS RECORDED IN " + lcOutFile TIMEOUT 1

IF m.gn_errCount > 20
  ON ERROR CANCEL
  CLEAR EVENTS
  IF VERSION(2) = 0
    QUIT
  ELSE
    SUSPEND
  ENDIF
ENDIF

Open in new window

Disadvantage of this error procedure could be an automatic program continuation after the error occurence. It can cause unpredictable results sometimes but it ignores minor errors which do not affect program run in most of the cases. You may add some dialog to PrgError to let users decide if they want to continue or not. I am testing gn_errCount in regular code which allows to make this decision automatically.

I am using TRY - CATCH for "local error handling" but to use this structure as the global error handler should also be possible.

ON ERROR setting cannot be ignored in your code, so if an error occurs it must pass the program flow into the error routine.
Try RETURN or RETRY from within a Catch Block. It's not supported.

With these last lines of your error handler, you can debug when an error occurred and return to the line of error:

If _vfp.startmode=0
   set step on
   return
Endif

This is not possible from CATCH, so TRY .. CATCH is bad for global error handling alone because of that.

Bye, Olaf.
Avatar of luciliacoelho

ASKER

In fact when I tried to use directly the error message and send to the screen through a MessageBox, directly. The MyErrorHandling routine didn't work at all. The workaround I used was the following: The data gathered from an error is firstly writen in a errorlog table then I use this data to interact with the user.
It seems that the variables you were using are local to a certain procedure/function. It's best errors are logged in a database or text file. I also send them by email to the development team of course with the permission of the user.