You call DosExitList to add to the exit list a routine that is to be given control when a process is ended (or finishes its execution). Multiple routines can be added to the list. When the process is ending, OS/2 transfers control to each address on the list.
If there are multiple addresses on the list, each function gets control in numerical order (with 0 being first and 0FFH being last), based on a value supplied by the application when it calls DosExitList. In case of duplicate entries for this parameter, the routines will be executed in LIFO (last in, first out) order.
DosExitList requires a function code that specifies an action and a pointer to the function that is to receive control upon termination.
The following code fragment adds the locally defined function SaveFiles to the exit list:
#define INCL_DOSPROCESS /* Process and thread values */ #include <os2.h> #define HF_STDOUT 1 /* Standard output handle */ VOID main(VOID) { . . . DosExitList(EXLST_ADD, (PFNEXITLIST) SaveFiles); . . . DosExit(EXIT_PROCESS, 0); } VOID APIENTRY SaveFiles(ULONG ulTermCode) { ULONG ulWritten; switch (ulTermCode) { case TC_EXIT: case TC_KILLPROCESS: DosWrite(HF_STDOUT, "Goodbye\r\n", 10, &ulWritten); break; case TC_HARDERROR: case TC_TRAP: break; } DosExitList(EXLST_EXIT, 0); }
Any function that you add to the list must take one parameter. The function can carry out any task, as shown in the preceding example, but as its last action it must call DosExitList, specifying the EXLST_EXIT constant. An exit-list function must not have a return value and must not call DosExit to end.
When an exit-list routine receives control, the parameter (located at ESP+4 on the stack) contains an indicator of why the process ended. The values returned are the same as those for termination codes returned by DosWaitChild or DosExecPgm requests. These values are:
TC_EXIT (0)
To execute the exit-list functions, OS/2 reassigns thread 1 after ending all other threads in the process. If thread 1 has already exited (for example, if it called DosExit without ending other threads in the process), the exit-list functions cannot be executed. In general, it is poor practice to end thread 1 without ending all other threads.
Before transferring control to the termination routines, OS/2 resets the stack to its initial value. Transfer is by way of an assembly language JMP instruction. The routine must be in the address space of the ending process. The termination routine at that address takes the necessary steps and then calls DosExitList with FunctionOrder=EXLST_EXIT. Control is then transferred to the next address in the invocation order of the exit list. When all such addresses have been processed, the process completes exiting. If a routine on the list does not call DosExitList at the completion of its processing, the process waits, and OS/2 prevents termination.
During DosExitList processing, the process is in a state of partial termination. All threads of the process are ended, except for the one executing the exit-list routines. To ensure good response to a user request to end a program, there should be minimal delay in completing termination. Termination routines should be short and fail-safe.
You can use DosExitList with the EXLST_REMOVE constant to remove a function from the list.
The designer of an exit-list routine must carefully consider which functions will be used by the routine. In general, calls to most OS/2 functions are valid in a DosExitList routine, but certain functions, such as DosCreateThread and DosExecPgm, are not.