INF: APPNOTE: Example of TSR Program

ID Number: Q28568

5.00 5.10 6.00 6.00a 6.00ax 7.00

MS-DOS

Summary:

The following article shows how to use Microsoft C to write a

terminate-and-stay-resident (TSR) program. The article also

illustrates the use of the following C run-time functions:

_dos_setvect

_dos_getvect

_dos_keep

_chain_intr

spawnXXX

More Information:

The following example shows how to use Microsoft C to write a TSR

program:

/**************************************************************/

/* */

/* DirZap.h */

/* */

/* This header file defines global variables, macros, */

/* function pointers, and function prototypes */

/* necessary for the DirZap.c program. */

/* */

/**************************************************************/

/* Global Variable For Macro SHIFTL(x, n)*/

long _ADDRESS;

/* Macro Definitions */

#define INT5 0x5

#define INT21 0x21

#define SHIFTL(x, n) (_ADDRESS = 0L, _ADDRESS = (long) x, _ADDRESS << n)

#define HIBYTE(x) (((unsigned) (x) >> 8) & 0xff)

#define REGPAK unsigned es, unsigned ds, unsigned di, unsigned si,\

unsigned bp, unsigned sp, unsigned bx, unsigned dx,\

unsigned cx, unsigned ax, unsigned ip,unsigned cs,\

unsigned flags

/* Function Pointers */

void (interrupt far *save_dir_adr)();

/* Saves address of the original interrupt service routine. */

void (interrupt far *set_dir_adr)();

/* This function pointer gets set to the address of the new

interrupt service routine 'set_dir'. */

void (interrupt far *reset_dir_adr)();

/* This function pointer gets set to the address of the new

interrupt service routine 'reset_dir'. */

/* Function Declarations */

void cdecl interrupt far set_dir(REGPAK);

/* This is the new service routine, which zaps the directory

interrupt routines. */

void interrupt far reset_dir(void);

/* This routine toggles between setting and disabling the

directory interrupt routines. */

unsigned _get_memsize(void);

/* This function gets the number of bytes to keep resident. */

short _set_shell(void);

/* Sets an MS-DOS shell. */

/**************************************************************/

/* */

/* DirZap.c */

/* */

/* This is an illustration of a TSR program. */

/* It traps and zaps the directory interrupts for */

/* MkDir, RmDir, and ChDir. It also illustrates how */

/* to set an MS-DOS shell by executing a new version of */

/* COMMAND.COM. */

/* */

/* Copyright Ó Microsoft Corp 1988. All rights */

/* reserved. */

/* */

/* To run, do the following: */

/* */

/* 1. EXEPACK DirZap to save memory. */

/* 2. Type DirZap at the MS-DOS prompt. DirZap sets */

/* an MS-DOS shell and is not yet active. */

/* 3. When "exit" is typed, DirZap is invoked. */

/* */

/* The PRINT SCREEN key now toggles */

/* DirZap on and off, but no memory has been */

/* freed. */

/* */

/**************************************************************/

/* */

/* NOTE: */

/* */

/* THIS PROGRAM, ITS USE, OPERATION, AND SUPPORT ARE PROVIDED */

/* "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR */

/* IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED */

/* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR */

/* PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE */

/* OF THIS PROGRAM IS WITH THE USER. IN NO EVENT SHALL */

/* MICROSOFT BE LIABLE FOR ANY DAMAGES INCLUDING, WITHOUT */

/* LIMITATION, ANY LOST PROFITS, LOST SAVINGS OR OTHER */

/* INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING FROM THE USE */

/* OR INABILITY TO USE SUCH PROGRAM, EVEN IF MICROSOFT HAS */

/* BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES OR FOR ANY */

/* CLAIM BY ANY OTHER PARTY. */

/* */

/**************************************************************/

#include <dos.h>

#include <stdio.h>

#include <stdlib.h>

#include <process.h>

#include <dirzap.h>

extern unsigned _psp;

/* Pre-defined varible, _psp = segment address of PSP. */

unsigned far *psp_pointer;

/* Used to retrieve the memsize to stay resident. */

short hot_key=1;

/* Flag to toggle DirZap on and off once it is invoked. */

void main(void);

void main()

{

if (_set_shell())

{

/* Set trap for directory interrupts. */

save_dir_adr = _dos_getvect(INT21);

/* Save original routine address. */

set_dir_adr = set_dir;

/* Get address of new (user defined) routine. */

_dos_setvect(INT21, set_dir_adr);

/* Revector to new service routine. */

/* Set trap for PRINT SCREEN interrupt. */

reset_dir_adr = reset_dir;

/* Get address of new routine. */

_dos_setvect(INT5, reset_dir_adr);

/* Revector to new routine. */

/* Blast off into memory and reside until

power down or CTRL+ALT+DEL. */

_dos_keep(0, _get_memsize());

}

else

puts("problems running DirZap.exe, COMSPEC not found!");

}

void cdecl interrupt far set_dir(REGPAK)

{

/*

Trap directory interrupts MkDir, RmDir, and ChDir and zap the

string entered by the user. DS:DX points to the string, so change it

to a string of length 0. WARNING: When compiled at high warning

levels, several warnings are generated. This is because several

elements of REGPAK are not referenced. These warnings should be

ignored.

*/

if (HIBYTE(ax) == 0x39 || HIBYTE(ax) == 0x3A || HIBYTE(ax) == 0x3B)

dx=0;

_chain_intr(save_dir_adr);

}

void interrupt far reset_dir()

{

if (hot_key)

{

hot_key=0;

_dos_setvect(INT21, save_dir_adr); /* Reset initial vector */

}

else

{

hot_key=1;

_dos_setvect(INT21, set_dir_adr); /* Install DirZap again */

_chain_intr(set_dir_adr); /* Chain to the Zapper function */

}

}

unsigned _get_memsize()

{

psp_pointer = (int far *) SHIFTL(_psp, 16); /*Get segment of the PSP*/

return(psp_pointer[1] - _psp); /* Amount of memory to stay resident. */

}

short _set_shell()

{

char *_COMSPEC_;

_COMSPEC_ = getenv("COMSPEC"); /* Get value of COMSPEC. */

if (_COMSPEC_) /* If not equal to NULL. */

{

puts("type 'EXIT' to invoke DirZapper...");

spawnlp(P_WAIT, _COMSPEC_, NULL); /* Invoke COMMAND.COM. */

return(1);

}

return(0);

}

Additional reference words: 5.00 5.10 6.00 6.00a 6.00ax 7.00