I often have situations where error handlers go into endless loops, such as Can't Release Object, Do you want to Continue Yes/No, xxx is not an object, Can't release Object, Do you want to Continue Yes/No etc. etc.
Consider Manual Garbage Collection ?
I figure in situations like these it would be good to give the user another option, CANCEL and it would just execute some code that will bail out FOR SURE. I tried QUIT and CLEAR ALL, QUIT etc. or releasing the main application object, but they just continue with endless error loops.
There are other conditions that will prevent closure: pending transactions, dirty buffers, etc. Let's accumulate all of our shutdown code into one big prg.
ACTIVATE WINDOW COMMAND
SET SYSMENU TO DEFAULT
FOR each oFrm in _SCREEN.Forms
There were some errors in this code that the ON ERROR plError = .T. line made go unnoticed. Now it actually works. -- Mike Feltman
* Reset VFP back to normal - may need to be run twice
* sometimes. First time does not always catch things that
* can't be removed fully until all applications have stopped.
* You can avoid this problem many times by using the reset
* hotkey recommendations in the session notes from
* Error Handling and Debugging in VFE.
* Fletcher Johnson, Patrick Murphy, DVT, Toni M. Feltman, David Tansey
SET ESCAPE ON
FOR lnTransaction = 1 TO TXNLEVEL()
RELEASE ALL EXTENDED
MODIFY WINDOW SCREEN
* ZOOM WINDOW SCREEN MAX
SET TALK ON
SET STATUS BAR ON
SET RESOURCE ON
SET DEVELOPMENT ON
* SET SYSMENU NOSAVE
* SET SYSMENU TO DEFAULT
SET SYSMENU ON
* SET SYSMENU TO DEFAULT
*!* PROCEDURE CloseDataAll
* This will close and revert all the open tables in all
* the data sessions
* Special thanks again to Fletcher Johnson for this portion
* of the reset code which was the winner of the $100 poker
* chip contest from the Error Handling and Debugging session.
* David Tansey
* Fletcher Johnson - CIS 72647,1447
#define MAX_SESSIONS 200
LOCAL lnCurrentDataSession, ;
plError = .F.
lcErrorCode = ON( "error")
ON ERROR plError = .T.
FOR lnCurrentDataSession = 1 TO MAX_SESSIONS
SET DATASESSION TO ( lnCurrentDataSession )
*-- An error occured, must not have
*-- any data in this datasession
plError = .f.
*-- Ok, we have data here
FOR lnCurrentWorkArea = 1 to AUSED( laFiles )
IF CURSORGETPROP("BUFFERING") > 1
USE && close the table
plError = .F.
*-- Now reset the error handling code
IF NOT EMPTY( lcErrorCode )
ON ERROR &lcErrorCode
This is the version that I use that I have adapted from many sources including the above. It has been updated for VFP8 and multiple data sessions. I assume that data should be tableupdated if possible.
* Procedure CLOSEALL
* Closes, Clears and Resets most everything after an error or suspend
* I usually assign this routine to a function key using on key label
* Written in VFP8 by Ben Creighton 9/1/2005
* 1st: Clean up ON routines
Set Escape On
* 2nd: Trash and reset the menus
* These are done early in case any variables/fields are used in SKIP FOR
Deactivate Menu All
Set Sysmenu To Default
Set Sysmenu On
* Go through each session, rollback transactions,
* and tableupdate unless there is an error in which case revert
For xx=1 To Asessions(sessionList)
Set DataSession To sessionList[m.xx]
Do While Txnlevel() > 0
For yy=1 To Aused(usedA)
If CursorGetProp("Buffering") > 1
If !Tableupdate(2, .T.)
* Clear out memory first, close data later
* Miscellaneous stuff
Set Printer To
Set Printer Off
Set Talk Off
Set Help To
Set Help On
* Development Environment Specific Stuff
* POP KEY
A much easier way to do this is just kill the application process. I tried many solution to this problem, included Garbage Collection techniques and code like the one above, but killing the process turned to be the only reliable solution that works in ANY situation.
So now I have a function called Abend(), that I call when the user press the Cancel button in an error dialog:
ON ERROR foo=1
SET PROCEDURE TO WPLIB
Library WPLIB.PRG can be downloaded from here: http://www.victorespina.com/hs/files/vfp/dwn/wp2.zip. -- Victor Espina
Victor: A problem with your Abend routine is that it doesn't take care of pending table updates or transactions before attempting to CLOSE ALL. Also, I don't want to Kill the process when I am doing development since that would shutdown FoxPro. Finally, your web site address is not working. -- Ben Creighton
Ben: Thanks for reporting the broken link; its fixed. The idea behind Abend() is to be able to finish the current program smoothly, regardless any problem that may rise during the shutdown process. When you try to close a running program in VFP, closing any opened files or finish pending updates isn't a real problem: the problem is to terminate the program AFTER this tasks are done.. and here is when an abend() funcion become handy. Of course, you are right about that wouldn't be nice to terminate your VFP session when you are testing your program... all you have to do is don't call abend() funcion if you are in development mode (wich can be determinated in runtime using the Start Mode property of _VFP object). -- Victor Espina
See also: Clean Up Code
Contributors: Carl Karsten, Hermann Strijewski, DavidTansey, Mike Feltman, Ben Creighton, Victor Espina
Category Code Samples Category Application Design