⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 第20章 杂项.txt

📁 我自己整理的c语言教程 来自 c语言之家
💻 TXT
📖 第 1 页 / 共 5 页
字号:
      ax=AX;                     /* use return value from switch() */
}
/ * avoids calling DOS to print characters * /
void
OutputString(char * str)
{
      int i ;
      regs. h. ah = 0x0E ;
      regs. x. bx = 0 ;
      for(i=strlen(str) ; i>0; i--,str++){
             regs. h. al= * str;
             int86 (0xl0, &regs, &regs) ;
     }
}
    上述程序是这两个程序中的TSR程序。这个程序中有一个NewCommVector()函数,它被安装在中断63H(63H通常是一个可用的向量)处作为中断服务程序。当它被安装好后,它就可以接收命令了。switch语句用来处理输入的命令,并作出相应的反应。笔者随意选择了0x1O和0x30来代表这样两条命令:“从ES:BX处复制数据,并打印到屏幕上,CX中的数值为打印次数”;“脱离中断63H,并停止接收命令”。下面是第二个程序——向中断63H发送命令的程序(注意它必须在Large模式下编译)。
# include <stdlib. h>
# include <dos. h>
# define COMM VECTOR 0x63
union REGS regs;
struct SREGS segregs ;
char buffer[80];
char _far * buf=(char_far *)buffer;
main (int argc,char * * argv)
{
      intcnt;
      cnt = (argo= =1 ? 1:atoi(argv[1])) ;
      strcpy (bur, "Hello There\r\n" ) ;
      regs. x. ax= 0x10;
      regs. x. cx=cnt ;
      regs. x. bx=FP OFF(buf);
      segregs, es=FP SEG(buf) ;
      int86x(COMM_VECTOR ,&regs, &segregs) ;
      printf ("TSR returned %d\n" ,regs. x. ax) ;
}
     你可能会认为这个短小的程序看上去和那些通过调用int 21或int 10来在DOS中设置或检索信息的程序差不多。如果你真的这么想,那就对了。唯一的区别就是现在你所用的中断号是63H,而不是21H或10H。上述程序只是简单地调用前文中的TSR程序,并要求后者把es:bX所指向的字符串打印到屏幕上,然后,它把中断处理程序(即那个TSR程序)的返回值打印到屏幕上。
    当字符串"Hello There”被打印到屏幕上后,在两个程序之间传递数据的全部必要步骤就都完成了。这个例子的真正价值在于它能够举一反三。现在你能很轻松地编写一个这样的程序,它将发送一条类似于“把要求你打印的最后一个字符串传递给我”的命令。你所要做的就是在前述TSR程序的switch语句中加入这条命令,然后再写一个程序来发送这条命令。此外,你也可以在第二个程序中利用20.11中所介绍的system()或spawn()函数来启动前述TSR程序。由于TSR程序会检查自己是否已被装入,因此你只需装入一次TSR程序,就可以多次运行第二个程序了。在所有要和前述TSR程序通信的程序中,你都可以使用这里所说的方法。
    在建立前述TSR程序时,需要有几个前提条件。其一就是没有其它重要的中断服务程序也在处理中断63H。例如,笔者原来在程序中使用的是中断67H,结果该程序能正常装入并运行,但此后笔者就无法编译程序了,因为Microsoft用来运行C编译程序的DOS扩展程序也要使用中断67H。在笔者发送了命令0x30(让程序卸载自身)后,编译程序又能正常运行了,因为DOS扩展程序的中断处理程序已被该程序恢复了。
    第二个前提条件与驻留检查在关。笔者假设永远不会有另一个中断处理程序使用和NewCommVector()相同的近程型地址,尽管这种巧合的可能性极小,但读者应该知道该程序并不是万无一失的。在该程序中,笔者特意让NewCommVector()使用自己的栈,以避免它运行在调用它的程序的栈上,但是,笔者还是假设调用所需的任何函数都是安全的。注意,该程序没有调用printf(),因为它占用较多的内存,并且要调用DOS(int 21)来打印字符。在该程序中,当中断63H发生时,笔者不知道DOS是否可以被调用,因此不能假设可以使用DOS调用。
注意,在该程序中,可以调用那些没有用到DOS int21服务程序的函数来完成所需的任务,如果必须使用一个DOS服务程序,你可以在中断63H发生时检查DOS忙标志,以确定当时DOS是否可以被调用。最后,对dos_keep()作一点说明:该函数要求知道在程序退出时要在内存中保留多少段(每段16字节)数据。在本例这个TSR程序中,提供给该函数的段数(276)稍大于整个可执行程序的大小。当你的程序变大时,提供给该函数的段数也必须增大,否则就会出现一些异常现象。

    请参见:
    20.10 怎样在一个程序后面运行另一个程序?
    20.1l 怎样在一个程序执行期间运行另一个程序?
    20.15 本书的有些例子程序有许多缺陷,为什么不把它们写得更好?

    20.13 怎样判断正在运行的程序所在的目录?
    我们这些DOS程序员是很幸运的,因为DOS程序的装入程序会提供正在运行的可执行文件的路径全名。这个路径全名是通过指针argv[0]提供的,mai‘n()函数的argv变量指向该指针。只需去掉路径全名中的文件名,你就得到了正在运行的程序所在的目录。下面的例子演示了这种技巧:
# include <stdio. h>
# include <stdlib. h>
# include <string. h>
void main(int argc, char ** argv)
{
      char execDir [80];
      int i,t;
      / * set index into argv[0] to slash character prior to appname * /
      for(i= (strlen(argv[0])-1) ;
                 ((argv[O][i] ! ='/' ) && (argv[O][i]! =' \\' ));--i) ;
      / * temporarily truncate argv[] * /
      t =argv[O][i] ;
      argv[O][i]= O ;
       / * copy directory path into local buffer * /
      strcpy(execDir ,argv[O]) ;
       /* put back original character for sanity's sake * /
    argvEO]Ei]=t;
}

    请参见:
    20.1  怎样获得命令行参数?

    20.14 怎样找到程序中的重要文件(数据库,配置文件,等等)?
    DOS提供了一对函数,用来在一个目录下查找一个任何类型的文件。你可以查找普通文件、档案文件、隐含文件、系统文件、只读文件、目录文件,甚至卷标文件。下面这个小例子说明了如何在当前目录下查找一个特定文件:
# include <stdio. h>
# include <dos. h>
void main(void)
{
     struct    find_t myFile ;
     _dos_findfirst ("MYFILE. INI" ,_A_NORMAL ,&-nyFile) ;
     while (_dos_findnext (&myFile) == 0)
        printf("Found file %s of size %s\n", myFile, name,myFile, size) ;
}
    这个例子说明了函数_dos_findfirst()和_dos_findnext()是如何工作的。你可以进入一个目录,然后象上例这样用这两个函数查找一个指定名字的文件。这两个函数还允许使用通配符“*”和“?”,如果你用“*”作为文件名,它们就会返回一个目录中的所有文件。如果你要查找硬盘上的每一个文件,则要把上例中的代码放到一个递归的函数中,由它来进入每个子目录并查找指定的文件。

    20.15 本书的有些例子程序有许多缺陷,为什么不把它们写得更好?
    本书的有些例子尽管是完整的程序,但是比较短小,因此往往不是实用的、注释完整的程序。这样做的原因是为了尽量给读者提供明确的、知识性强的,但又非常简洁的答案。如果例子很长,它们就会破坏全书的连贯性,并且不利于读者把握基本的学习目标。

    请参见:    .
    20.12 怎样把数据从一个程序传给另一个程序?
    20.14 怎样找到程序中的重要文件(数据库,配置文件,等等)?


    20.16 怎样使Ctrl+Break失效?
    有好几种方法可以使Ctrl+Break功能失效,这里只讨论两种最常用的方法。
    第一种方法是用DOS来解除Ctrl+Break功能。你可能用DOS中断21H的33H函数来得到或设置Ctrl+Break检查标志,该标志告诉DOS是否在Ctrl+Break按下时对其进行处理。下面的例子说明了这种方法:
# include <stdio. h>
# include <dos. h>
void main(int argc,char **argv)
{
      union REGS regs;
      int ctrlBreakFlag ;
      / * find out the curre.nt state of the flag * /
      regs. x. ax= 0x3300 ;                       / * subfunction 0 gets flag state */
      int86 (0x21, &regs, &regs) ;
      ctrlBreakFlag == rags. h. dl ;                 / * save flag value from DL * /
      / * set the state of the flag to disable Ctrl+Break * /
      regs. x. ax=0x3301;
      regs. h. dl = 0 ;                              / * disable checking * /
      int86(0x21,&regs, &regs) ;              /* subfunction 1 sets flag state * /
}
    上例首先调用DOS来查询Ctrl+Break检查标志的当前状态,并将其存入ctrlBreakFlag中。DOS调用的返回值存在DL中,如果解除了Ctrl+Break检查,则该值为O;如果允许Ctrl+Break检查,则该值为1。接着,上例清除DL并调用DOS的设置Ctrl+Break标志函数来解除Ctrl+Break检查。在调用上述函数重新设置Ctrl+Break标志之前,此次所设置的状态将
一直保留。上例没有用到子函数02(AX一0x3302),该函数能同时得到并设置Ctrl+Break标志的状态,为了完成这项任务,你应该把0x3302放到AX中,把要求的Ctrl+Break标志状态放到DL中,然后进行中断,并把原来的状态从DL中存入ctrlBreakFlag中。
    第二种方法是使用信号(signal)。信号是从过去的UNIX时代继承下来的。信号函数的作用是在某些事件发生时通知程序员,这些事件之一就是用户中断——在DOS下就是Ctrl+Break事件。下面的例子说明了如何用Microsoft的signal()函数来捕获Ctrl+Break事件并作出反应(假设允许Ctrl+Break检查):
# include <[stdio. h>
# include <signal. h>
int exitHandler (void) ;
int main (int argc,char ** argv)
{
      int quitFlag = 0 ;
      / * Trap all Ctrl+Breaks * /
      signal (SIGINT, (void (__cdecl * ) (int))exitHandler);
      / * Sit in infinite loop until user presses Ctrl+Break * /
      while (quitFlag = = 0)
      printf (" % s\", (argv > 1 ) ? argv [ 1 ] : "Waiting for Ctrl + Break" ) ;
}
/ * Ctrl+Break event handler function * /
int exitHandler ()
{
     char ch ;
     / * Disable Ctrl+Break handling while inside the handler * /
     signal (SIGINT, SIG_ IGN ) ;
      / * Since it was an "interrupt",clear keyboard input buffer * /
     fflush(stdin) ;
      / * Ask if user really wants to quit program * /
     printf("\nCtrl+Break occurred. Do you wish to exit this program?
     →(Y or N)");
      / * Flush output buffer as well * /
     fflush (stdout) ;
      / * Get input from user, print character and newline * /
     ch =getche ( ) ;
      printf("\n" ) ;
      / * If user said yes, leave program * /
      if(toupper (eh) = 'Y' )
           exit (0) ;
      / * Reenable Ctrl+Break handling * /
      signal (SIGINT, (void (__cdecl * ) (int))exitHandler) ;
      return(0) ;
}

    上例的好处在于每当按下Ctrl+Break时都会调用一个函数,也就是说,你可以选择将要作出的反应一一你可以忽略这个事件,相当于解除Ctrl+Break功能;你也可以作出所需的其它任何反应;上例的另一个好处是当程序退出时,Ctrl+Break的正常操作就会恢复,不需要人为的干预。

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -