Using the TERMINATE or STOP Statements within Dialog-based Applications

This document covers the following topics:


Introduction

The execution of the TERMINATE and STOP statements relies on stack-based exception handling in order to immediately abort the application’s execution, which is not guaranteed to work within event-driven (i.e., dialog-based) applications in consumer versions of Windows. In particular, this can be the case where one or more events on the Natural call stack have been synchronously raised in response to the corresponding system event(s). This is because the relevant section of the system call stack in such situations can contain Windows system code that has been compiled with a small performance optimization to not generate the required stack frame information at run-time.

Furthermore, because the Windows system code involved changes over time, a TERMINATE or STOP statement that works on one version of Windows is not guaranteed to continue to work on all later versions, or even on all later Service Packs for the same version.

For these reasons, the use of the TERMINATE and STOP statements in dialog-based applications is discouraged.

Solution

A universal solution is not available. However, in many cases, the problem can be circumvented by performing the termination asynchronously. For example, instead of coding a TERMINATE or STOP statement directly, one instead asynchronously invokes a user-defined event for the dialog via the CALL-DIALOG action, and places the TERMINATE or STOP statement within the event handler for the user-defined event, as illustrated in the example below.

In particular, this workaround is applicable in situations where no dialog boxes are active.

Example

Suppose that the CLOSE event for a modeless dialog contains a TERMINATE statement.

/* CLOSE event
TERMINATE

This TERMINATE statement fails in some Windows versions in the case where the user attempts to close the dialog via the Close (X) icon.

To solve this problem, we can firstly define a user-defined event for the dialog (e.g., with the name "TERMINATE"), and insert the TERMINATE statement there:

/* TERMINATE event
TERMINATE

Secondly, we replace the original TERMINATE statement in the CLOSE event with the following two statements:

/* CLOSE event
PROCESS GUI ACTION CALL-DIALOG WITH
  #DLG$WINDOW NULL-HANDLE 'TERMINATE' FALSE GIVING *ERROR
ESCAPE ROUTINE IMMEDIATE

The CALL-DIALOG action with the FALSE parameter causes the TERMINATE event to be executed asynchronously. The ESCAPE statement by-passes any subsequent event code and is absolutely necessary in this case to ensure that the DELETE-WINDOW action generated by the Dialog Editor is not performed, so that the dialog remains active until the TERMINATE event is received.