这是一个完整的TSR例程,读者可把自己的的应用程序添加在实例程序中相应的地方。但有两点需要注意:
1。在TSR程序内部,不要使用动态内存分配函数,因为该类函数从堆中分配内存,而堆在初始化时作为未驻留的空间被释放掉。如需内存分配,最好在TSR初始化时分配好,之后再保留内存块的指针。
2。不要在TSR程序内部使用exit()函数,以防非正常退出。
/***********************************************************************************
以下是一个完整的TSR例程,读者可把自己的的应用程序添加在实例程序中相应的地方。但有两点需要注意:
1。在TSR程序内部,不要使用动态内存分配函数,因为该类函数从堆中分配内存,而堆在初始化时作为未驻留的空间被释放掉。如
需内存分配,最好在TSR初始化时分配好,之后再保留内存块的指针。
2。不要在TSR程序内部使用exit()函数,以防非正常退出。
完整的实例程序如下:
***********************************************************************************/
/*TSR驻留程序设计框架*/
#include
#include
#include
#include
#include
#include
#include
/*一些数据的定义*/
#define STACK_SIZE 8192 /*栈的大小*/
#define KEYBOARD 0x60 /*键盘数据端口*/
#define PSP_TERMINATE 0x0a /*PSP中的结束地址*/
#define PSP_PARENTPSP 0x16 /*PSP中的父进程的PSP*/
#define PSP_ENVIRON 0x2C /*PSP中的环境块的地址*/
#define SCANCODE 52 /*字符.的键码*/
#define ALTKEY 8 /*ALT键的代码,用ALT+.的组合键来激活此TSR程序*/
#define PARAGRAPHS(x) ((FP_OFF(x)+15)>>4)
/*变量的定义*/
char far *indos_ptr=0; /*指向DOS安全标志的指针*/
char far *crit_err_ptr=0; /*指向致命错误标志的指针*/
char far *stack_ptr; /*TSR栈的指针*/
char far *dta1; /*前台进程的磁盘传输区地址*/
char far *dta2;
unsigned ss_save; /*用于保存堆栈段寄存器*/
unsigned sp_save; /*用于保存堆栈指针寄存器*/
static int active=0; /*TSR激活标志*/
static int popup_dosBusy=0; /*该标志为1,表明DOS繁忙时热键被按*/
static int int28_active=0; /*该标志为1,表明INT28处于活动状态*/
static int int13_active=0; /*该标志为1,表明INT13处于活动状态*/
static unsigned foreground_psp; /*前台进程的PSP*/
static unsigned long terminateAddr; /*删除TSR时使用*/
static int key; /*读键盘端口时使用*/
extern int sign; /*删除TSR程序时,汇编子程序使用的返回标志*/
static union REGS r;
static struct SREGS s;
static struct DOSERROR dosErr;
typedef struct
{
unsigned bp,di,si,ds,es,dx,cx,bx,ax,ip,cs,flags;
}INTERRUPT_REGS;
/*保存原中断的函数指针*/
void interrupt (*old_int8)(void); /*时钟中断*/
void interrupt (*old_int9)(void); /*键盘中断*/
void interrupt (*old_int1B)(void); /*CTRL+BREAK中断*/
void interrupt (*old_int23)(void); /*CTRL+C中断*/
void interrupt (*old_int24)(void); /*严重错误中断*/
void interrupt (*old_int28)(void); /*DOS空闲中断*/
void interrupt (*old_int62)(void); /*通讯中断*/
void interrupt (*old_int13)(void); /*磁盘读写中断*/
/*函数原型声明*/
void interrupt far new_int8(void);
void interrupt far new_int9(void);
void interrupt far new_int1B(void);
void interrupt far new_int23(void);
void interrupt far new_int24(INTERRUPT_REGS p);
void interrupt far new_int28(void);
void interrupt far communicate(INTERRUPT_REGS p);
void interrupt far new_int13(INTERRUPT_REGS p);
void help_message(void);
int dosBusy(void); /*如果DOS繁忙,该函数返回非零值*/
int int28DosBusy(void); /*如果DOS安全标志为1,并且致命错误标志非零,该函数返回非零值*/
void initIndos(void); /*初始化DOS安全标志的指针*/
void tsr_active(void); /*TSR激活函数*/
void tsr_exit(void); /*TSR退出函数*/
void do_deinstall(char *program); /*判断TSR是否删除的函数*/
void set_stack(void); /*保存栈*/
void restore_stack(void); /*恢复栈*/
void setpsp(unsigned segpsp); /*设置PSP*/
void setExtErr(struct DOSERROR near * err); /*设置扩展错误信息*/
int deinstall(void); /*删除TSR函数,该函数用汇编子程序编写*/
/*绝对恢复原中断的函数*/
int unLinkVect(int vect,void interrupt (*newint)(),
void interrupt (*oldint)());
/*主函数*/
void main(int argc,char *argv[])
{
union REGS regs1,regs2;
struct SREGS sregs;
unsigned memory; /*驻留内存大小的变量*/
unsigned far *fp; /*指向环境块段址的指针*/
initIndos(); /*初始化DOS安全标志和致命错误标志*/
switch(argc)
{
case 1:
if(getvect(0x61)!=NULL)
{
puts("TSR program already installed!n");
system("PAUSE");
exit(1);
}
break;
case 2:
if(toupper(argv[1][0])=='D' && (getvect(0x62)!=NULL))
do_deinstall(argv[0]);
else help_message();
break;
default:
help_message();
}
/*设置TSR自己的栈*/
stack_ptr=malloc(STACK_SIZE);
if(stack_ptr==NULL)
{
printf("Not enough memory!n");
system("PAUSE");
exit(1);
}
stack_ptr+=STACK_SIZE;
/*保留中断向量*/
old_int8=getvect(0x08);
old_int9=getvect(0x09);
old_int13=getvect(0x13);
old_int28=getvect(0x28);
old_int62=getvect(0x62);
/*设置中断向量*/
setvect(0x08,new_int8);
setvect(0x09,new_int9);
setvect(0x13,new_int13);
setvect(0x28,new_int28);
setvect(0x62,communicate);
/*释放掉环境块*/
fp=MK_FP(_psp,PSP_ENVIRON);
freemem(*fp);
/*计算驻留空间,并释放掉多余的堆空间*/
segread(&sregs); /*读取段寄存器*/
memory=sregs.ds+PARAGRAPHS(stack_ptr)-_psp;
setblock(_psp,memory); /*调整内存空间*/
keep(0,memory); /*驻留*/
}
/*主函数结束*/
/*捕获INT08时钟中断*/
void interrupt far new_int8()
{
(*old_int8)(); /*连接原中断*/
/*判断TSR能否弹出*/
if(!active && popup_dosBusy && !dosBusy() && !int13_active)
{
popup_dosBusy=0;
active=1;
enable();
tsr_active();
active=0;
}
}
/*捕获INT09键盘中断*/
void interrupt far new_int9()
{
if(!active)
{
if(inportb(0x60)==SCANCODE) /*从端口读取扫描码*/
{
if((bioskey(2)&ALTKEY)==ALTKEY) /*读取移位状态*/
{
/*清键盘缓冲区*/
key=inportb(0x61);
outportb(0x61,key);
outportb(0x20,0x20); /*告诉系统键盘中断结束*/
if(!int13_active) /*如果没有磁盘读写,可以弹出TSR程序*/
{
popup_dosBusy=0;
active=1;
old_int9();
tsr_active();
active=0;
}
else
{
popup_dosBusy=1;
(*old_int9)();
}
}
else
(*old_int9)();
}
else
(*old_int9)();
}
else
(*old_int9)();
}
/*捕获INT13磁盘读写中断*/
void interrupt far new_int13(INTERRUPT_REGS p)
{
int13_active++;
(*old_int13)();
p.ax=_AX;
p.cx=_CX;
p.dx=_DX;
p.flags=_FLAGS;
--int13_active;
}
/*捕获INT1B CTRL+BREAK中断*/
void interrupt far new_int1B()
{
/*Do Nothing*/
}
/*捕获INT23 CTRL+C中断*/
void interrupt far new_int23()
{
/*Do Nothing*/
}
/*捕获INT24严重错误中断*/
void interrupt far new_int24(INTERRUPT_REGS p)
{
p.ax=0x03; /*告诉DOS忽略该错误*/
}
/*捕获INT28 DOS空闲中断*/
void interrupt far new_int28()
{
int28_active++;
/*判断TSR能否弹出*/
if(popup_dosBusy && (!int28DosBusy()) && !active && !int13_active)
{
active=1;
tsr_active();
active=0;
}
int28_active--;
(*old_int28)();
}
/*通讯中断函数*/
void interrupt far communicate(INTERRUPT_REGS p)
{
terminateAddr=((long)p.bx<<16)+p.dx;
if(!active)
{
enable();
tsr_exit(); /*退出驻留程序*/
active=1;
}
}
void help_message(void) /*帮助信息*/
{
printf("The correct way of using this TSR program is:n");
printf("TSR[d]n");
printf("TSR d stands for deinstalling the TSR program.n");
printf("TSR stands for installing the program.n");
system("PAUSE");
exit(1);
}
void tsr_active(void) /*TSR激活函数*/
{
set_stack(); /*设置自己的栈*/
if(dosBusy() && !int28_active)
popup_dosBusy=1; /*设置标志以便下一次INT08,INT28激活TSR*/
else
{
popup_dosBusy=0;
/*保存INT1B,INT23,INT24中断*/
old_int1B=getvect(0x1B);
old_int23=getvect(0x23);
old_int24=getvect(0x24);
/*设置新的INT1B,INT23,INT24中断*/
setvect(0x1B,new_int1B);
setvect(0x23,new_int23);
setvect(0x24,new_int24);
/*PSP切换*/
foreground_psp=getpsp();
setpsp(_psp);
/*DTA切换*/
dta1=getdta();
dta2=MK_FP(_psp,0x80);
setdta(dta2);
/*保存前台进程的扩展错误信息*/
dosexterr(&dosErr);
/*清除键盘缓冲区*/
/*此处设置你的TSR应用程序*/
/*下面是一个极简单的示例,仅显示几个字符*/
printf("TSR program has been installed!n");
sound(532);
delay(100);
nosound();
/*恢复前台进程的扩展错误信息*/
setExtErr((struct DOSERROR near *)&dosErr);
/*恢复前台进程的DTA*/
setdta(dta1);
/*恢复前台进程的PSP*/
setpsp(foreground_psp);
/*恢复INT1B,INT23,INT24中断*/
setvect(0x1B,old_int1B);
setvect(0x23,old_int23);
setvect(0x24,old_int24);
}
restore_stack(); /*恢复栈*/
}
/*设置PSP的函数*/
void setpsp(unsigned segpsp)
{
union REGS regs;
if(!crit_err_ptr)
return;
*crit_err_ptr=0xff;
regs.h.ah=0x50; /*0x50号中断函数用于设置PSP*/
regs.x.bx=segpsp;
intdos(®s,®s);
*crit_err_ptr=0;
}
/*设置扩展错误信息函数*/
void setExtErr(struct DOSERROR near *err)
{
r.x.ax=0x5D0A;
r.x.bx=0x00;
segread(&s);
r.x.dx=(int)err;
intdosx(&r,&r,&s);
}
void initIndos(void)
{
union REGS regs;
struct SREGS sregs;
regs.h.ah=0x34;
intdosx(®s,®s,&sregs);
indos_ptr=MK_FP(sregs.es,regs.x.bx);
regs.x.ax=0x5D06;
intdosx(®s,®s,&sregs);
crit_err_ptr=MK_FP(sregs.ds,regs.x.si);
}
/*判断DOS是否繁忙*/
int dosBusy(void)
{
if(indos_ptr && crit_err_ptr)
return (*crit_err_ptr || *indos_ptr);
else
return 0xFFFF;
}
int int28DosBusy(void)
{
if(indos_ptr && crit_err_ptr)
return (*crit_err_ptr || (*indos_ptr>1));
else
return 0xFFFF;
}
int unLinkVect(int vect,void interrupt (*newint)(),
void interrupt (*oldint)())
{
if(newint==getvect(vect))
{
setvect(vect,oldint);
return 0;
}
return 1;
}
void tsr_exit(void)
{
set_stack(); /*设置自己的栈空间*/
if(!(unLinkVect(8,new_int8,old_int8) | unLinkVect(9,new_int9,old_int9) |
unLinkVect(0x28,new_int28,old_int28) | unLinkVect(0x13,new_int13,old_int13) |
unLinkVect(0x62,communicate,old_int62)))
{
/*把当前活动进程的PSP设置为TSR进程PSP中的父本PSP*/
*(int far *)(((long)_psp<<16)+PSP_PARENTPSP)=getpsp();
/*设置TSR进程PSP中的结束地址*/
*(long far *)(((long)_psp<<16)+PSP_TERMINATE)=terminateAddr;
/*设置TSR进程的PSP为当前活动进程的PSP*/
setpsp(_psp);
/*结束程序,退出到父本程序*/
bdos(0x4C,0,0);
}
}
void do_deinstall(char *program)
{
fputs(program,stdout);
deinstall();
switch(sign) /*判断汇编子程序的返回标志*/
{
case 1:
puts("Deactivated but not removed!n");
break;
case 2:
puts("Deinstalled!n");
break;
default:
puts("Unexpected errors!n");
break;
}
system("PAUSE");
exit(0);
}
/*******************C程序调用的两个汇编语言程序*****************************/
/**************************************************************************
_TEXT SEGMENT BYTE PUBLIC 'CODE' ;正文段
_TEXT ENDS
_DATA SEGMENT WORD PUBLIC 'DATA' ;段描述
_DATA ENDS
CONST SEGMENT WORD PUBLIC 'CONST'
CONST ENDS
_BSS SEGMENT WORD PUBLIC 'BSS'
_BSS ENDS
DGROUP GROUP _DATA,CONST,_BSS ;组描述
ASSUME CS:_TEXT,DS:DGROUP,SS:DGROUP
_DATA SEGMENT
PUBLIC _SIGN
_SIGN DW 0 ;返回到C程序中的标志变量
_DATA ENDS
PUBLIC _deinstall
EXTRN _ss_save:NEAR
EXTRN _sp_save:NEAR
_TEXT SEGMENT
_deinstall PROC FAR
PUSH SI
PUSH DI
PUSH BP
MOV WORD PTR _ss_save,SS ;保存主程序的栈
MOV WORD PTR _sp_save,SP
MOV CS:_ds_save,DS ;保存数据段
MOV BX,CS
MOV DX,OFFSET terminateAddr ;BX:DX指向程序的结束地址
INC _sign
INT 62H ;调用Communicate
JMP SHORT noterminate
terminateAddr:
MOV AX,CS:_ds_save
MOV DS,AX
INC _sign
MOV SS,WORD PTR _ss_save ;恢复栈
MOV SP,WORD PTR _sp_save
noterminate:
POP BP
POP DI
POP SI
RET
_deinstall ENDP
_ds_save DW 0
_TEXT ENDS
END
_TEXT SEGMENT BYTE PUBLIC 'CODE' ;正文段
_TEXT ENDS
_DATA SEGMENT WORD PUBLIC 'DATA' ;段描述
_DATA ENDS
CONST SEGMENT WORD PUBLIC 'CONST'
CONST ENDS
_BSS SEGMENT WORD PUBLIC 'BSS'
_BSS ENDS
DGROUP GROUP _DATA,CONST,_BSS ;组描述
ASSUME CS:_TEXT,DS:DGROUP,SS:DGROUP
PUBLIC _set_stack
PUBLIC _restore_stack
EXTRN _ss_save:NEAR
EXTRN _sp_save:NEAR
EXTRN _stack_ptr:NEAR
_TEXT SEGMENT
_set_stack PROC FAR
POP AX ;保存返回值的偏移地址
POP BX ;保存返回值的段地址
MOV WORD PTR _ss_save,SS ;保存前台程序的栈
MOV WORD PTR _sp_save,SP
MOV SS,WORD PTR _stack_ptr+2 ;设置TSR程序的栈
MOV SP,WORD PTR _stack_ptr
PUSH BX ;正常返回
PUSH AX
RET
_set_stack ENDP
_restore_stack PROC FAR
POP CX ;保存返回值的偏移地址
POP BX ;保存返回值的段地址
MOV WORD PTR _stack_ptr+2,SS ;保存TSR程序的栈
MOV WORD PTR _stack_ptr,SP
MOV SS,WORD PTR _ss_save ;恢复前台进程的栈
MOV SP,WORD PTR _sp_save
PUSH BX ;正常返回
PUSH CX
RET
_restore_stack ENDP
_TEXT ENDS
END
**************************************************************************/