Capturing the Ctrl-C and Ctrl-Break interrupts is straightforward when you are programming in assembly language. The process is only slightly more difficult with high-level languages, as long as you have enough information about the language's calling conventions that you can link in a small assembly-language routine as part of the program.
The BREAK.ASM listing in Figure 5-1 contains source code for a Ctrl-Break handler that can be linked with small-model Microsoft C programs running on an IBM PC compatible. The short C program in Figure 5-2 demonstrates use of the handler. (This code should be readily portable to other C compilers.)
page 55,132
title Ctrl-C & Ctrl-Break Handlers
name break
;
; Ctrl-C and Ctrl-Break handler for Microsoft C
; programs running on IBM PC compatibles
;
; by Ray Duncan
;
; Assemble with: C>MASM /Mx BREAK;
;
; This module allows C programs to retain control
; when the user enters a Ctrl-Break or Ctrl-C.
; It uses Microsoft C parameter-passing conventions
; and assumes the C small memory model.
;
; The procedure _capture is called to install
; a new handler for the Ctrl-C and Ctrl-Break
; interrupts (1bh and 23h). _capture is passed
; the address of a static variable, which will be
; set to true by the handler whenever a Ctrl-C
; or Ctrl-Break is detected. The C syntax is:
;
; static int flag;
; capture(&flag);
;
; The procedure _release is called by the C program
; to restore the original Ctrl-Break and Ctrl-C
; handler. The C syntax is:
; release();
;
; The procedure ctrlbrk is the actual interrupt
; handler. It receives control when a software
; int 1bh is executed by the ROM BIOS or int 23h
; is executed by MS-DOS. It simply sets the C
; program's variable to true (1) and returns.
;
args equ 4 ; stack offset of arguments,
; C small memory model
cr equ 0dh ; ASCII carriage return
lf equ 0ah ; ASCII linefeed
_TEXT segment word public 'CODE'
assume cs:_TEXT
public _capture
_capture proc near ; take over Ctrl-Break
; and Ctrl-C interrupt vectors
push bp ; set up stack frame
mov bp,sp
push ds ; save registers
push di
push si
; save address of
; calling program's "flag"
mov ax,word ptr [bp+args]
mov word ptr cs:flag,ax
mov word ptr cs:flag+2,ds
; save address of original
mov ax,3523h ; int 23h handler
int 21h
mov word ptr cs:int23,bx
mov word ptr cs:int23+2,es
mov ax,351bh ; save address of original
int 21h ; int 1bh handler
mov word ptr cs:int1b,bx
mov word ptr cs:int1b+2,es
push cs ; set DS:DX = address
pop ds ; of new handler
mov dx,offset _TEXT:ctrlbrk
mov ax,02523h ; set int 23h vector
int 21h
mov ax,0251bh ; set int 1bh vector
int 21h
pop si ; restore registers
pop di
pop ds
pop bp ; discard stack frame
ret ; and return to caller
_capture endp
public _release
_release proc near ; restore original Ctrl-C
; and Ctrl-Break handlers
push bp ; save registers
push ds
push di
push si
lds dx,cs:int1b ; get address of previous
; int 1bh handler
mov ax,251bh ; set int 1bh vector
int 21h
lds dx,cs:int23 ; get address of previous
; int 23h handler
mov ax,2523h ; set int 23h vector
int 21h
pop si ; restore registers
pop di ; and return to caller
pop ds
pop bp
ret
release endp
ctrlbrk proc far ; Ctrl-C and Ctrl-Break
; interrupt handler
push bx ; save registers
push ds
lds bx,cs:flag ; get address of C program's
; "flag variable"
; and set the flag "true"
mov word ptr ds:[bx],1
pop ds ; restore registers
pop bx
iret ; return from handler
ctrlbrk endp
flag dd 0 ; far pointer to caller's
; Ctrl-Break or Ctrl-C flag
int23 dd 0 ; address of original
; Ctrl-C handler
int1b dd 0 ; address of original
; Ctrl-Break handler
_TEXT ends
end
Figure 5-1. BREAK.ASM: A Ctrl-C and Ctrl-Break interrupt handler that can be linked with Microsoft C programs.
/*
TRYBREAK.C
Demo of BREAK.ASM Ctrl-Break and Ctrl-C
interrupt handler, by Ray Duncan
To create the executable file TRYBREAK.EXE, enter:
MASM /Mx BREAK;
CL TRYBREAK.C BREAK.OBJ
*/
#include <stdio.h>
main(int argc, char *argv[])
{
int hit = 0; /* flag for key press */
int c = 0; /* character from keyboard */
static int flag = 0; /* true if Ctrl-Break
or Ctrl-C detected */
puts("\n*** TRYBREAK.C running ***\n");
puts("Press Ctrl-C or Ctrl-Break to test handler,");
puts("Press the Esc key to exit TRYBREAK.\n");
capture(&flag); /* install new Ctrl-C and
Ctrl-Break handler and
pass address of flag */
puts("TRYBREAK has captured interrupt vectors.\n");
while(1)
{
hit = kbhit(); /* check for key press */
/* (MS-DOS sees Ctrl-C
when keyboard polled) */
if(flag != 0) /* if flag is true, an */
{ /* interrupt has occurred */
puts("\nControl-Break detected.\n");
flag = 0; /* reset interrupt flag */
}
if(hit != 0) /* if any key waiting */
{
c = getch(); /* read key, exit if Esc */
if( (c & 0x7f) == 0x1b) break;
putchÓ; /* otherwise display it */
}
}
release(); /* restore original Ctrl-C
and Ctrl-Break handlers */
puts("\n\nTRYBREAK has released interrupt vectors.");
}
Figure 5-2. TRYBREAK.C: A simple Microsoft C program that demonstrates use of the interrupt handler BREAK.ASM from Figure 5-1.
In the example handler, the procedure named capture is called with the address of an integer variable within the C program. It saves the address of the variable, points the Int 1BH and Int 23H vectors to its own interrupt handler, and then returns.
When MS-DOS detects a Ctrl-C or Ctrl-Break, the interrupt handler sets the integer variable within the C program to true (1) and returns. The C program can then poll this variable at its leisure. Of course, to detect more than one Ctrl-C, the program must reset the variable to zero again.
The procedure named release simply restores the Int 1BH and Int 23H vectors to their original values, thereby disabling the interrupt handler. Although it is not strictly necessary for release to do anything about Int 23H, this action does give the C program the option of restoring the default handler for Int 23H without terminating.