Ctrl-C and Ctrl-Break Handlers and High-Level Languages

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.