If you need a simple way to enable a user to cancel out of loops, in particular where external processing is involved, try the following, (VFP 7 and above).
If an external process is running, it is generally easier to allow the external process to complete, resolve any issues arising and then exit the loop.
Add a command button to a form and name it .cmdCancel, with .Enabled = .F. Add two properties to the form, .lQueryExit and .lCancelButton.
In the .MouseEnter() event of .cmdCancel put :-
THISFORM.lCancelButton = .T.
In the .MouseLeave() event of .cmdCancel put :-
THISFORM.lCancelButton = .F.
In the following code, the command button is enabled for a couple of seconds during which time, should a user click on .cmdCancel, the form property .lQueryExit becomes true enabling the relevant MESSAGEBOX dialog to take place.
The form property .lCancelButton only becomes true if the mouse click was detected whilst the mouse was over the command button .cmdCancel.
There is no requirement for code in the command button as the .Click() event was not fired but as far as the user is concerned it appears to have happened.
For simplicity, all the required code remains within the loop and in the example below, the user can skip a record, ignore and continue, or cancel the entire operation.
LOCAL inval1, lnSeconds, lnAnswer
DOEVENTS && Allow user to cancel WITH .cmdCancel .Enabled = .T. .Refresh() ENDW
lnSeconds = SECONDS() DO WHILE .T. inval1 = INKEY(0.06,'HM') IF SECONDS() > lnSeconds + 2.5 EXIT ENDI IF inval1 = 151 AND .lCancelButton .lQueryExit = .T. EXIT ENDIF ENDD
WITH .cmdCancel .Enabled = .F. .Refresh() ENDW
IF .lQueryExit lnAnswer = MESSAGEBOX( ; [Do you want to skip the deletion of ] ; + ALLT(TABLENAME.filename) ; + [?] ; + CHR(13) ; + CHR(13) ; + [If you answer 'Cancel', you will cancel the entire operation] ,; 3 + 32 + 256 ,; [Skip file deletion or cancel operation]) DO CASE CASE lnAnswer = 2 EXIT CASE lnAnswer = 6 .lQueryExit = .F. LOOP OTHERWISE .lQueryExit = .F. ENDC ENDI && Processing, internal and/or external, here ENDS
IF .lQueryExit TABLEREVERT(.T.,[TABLENAME]) ELSE TABLEUPDATE(.T.,.T.,[TABLENAME]) ENDI ENDW