论坛交流
首页办公自动化| 网页制作| 平面设计| 动画制作| 数据库开发| 程序设计| 全部视频教程
应用视频: Windows | Word2007 | Excel2007 | PowerPoint2007 | Dreamweaver 8 | Fireworks 8 | Flash 8 | Photoshop cs | CorelDraw 12
编程视频: C语言视频教程 | HTML | Div+Css布局 | Javascript | Access数据库 | Asp | Sql Server数据库Asp.net  | Flash AS
当前位置 > 文字教程 > C语言程序设计教程
Tag:新手,函数,指针,数据类型,对象,Turbo,入门,运算符,数组,结构,二级,,tc,游戏,试题,问答,编译,视频教程

C语言编程常见问题解答之杂项(Miscellaneous)(1)

文章类别:C语言程序设计 | 发表日期:2008-9-24 14:36:30


    本书试图覆盖有关C语言编程的每一个重要主题,并且希望所介绍的内容都是有用的和可以理解的。但是,在这样一本简洁的书中,是不可能介绍象计算机这样复杂的事物的每一个侧面的。因此,本章将讨论其它章中还未涉及到的一些杂项问题,并给出答案。

    20.1 怎样获得命令行参数:
    每当你运行一个DOS或Windows程序时,都会生成一个程序段前缀(Program SegmentPrefix,简称PSP)。当DOS程序的装入程序把程序复制到RAM中来执行时,它先把256个字节分配给PSP,然后把可执行代码复制到紧接着PSP的内存区域中。PSP中包含了DOS为了执行一个程序所需要的各种各样的信息,其中的一部分数据就是命令行。PSP中偏移量为128
的那个字节中存放着命令行中的字符个数,接下来的127个字节中存放着命令行本身。这也正是DOS把你能在其提示行中输入的字符个数限制在127个之内的原因——因为它为命令行分配的存储空间只有那么多。遗憾的是,PSP的命令行缓冲区中并没有存放可执行程序的名字——而只存放着在可执行程序名后键入的字符(包括空格符)。例如,假如你在DOS提示行中键入以下命令:
    XCOPY AUTOEXEC.BAT  AUTOEXEC.BAK
    假设XCOPY.EXE存放在c驱动器的DOS目录下,则XCOPY.EXE的PSP命令行缓冲区中将包含以下信息:
    AUTOEXEC.BAT  AUTOEXEC.BAK
    注重,命令行中紧接着"XCOPY"的空格符也被复制到PSP的缓冲区中。
    除了不能在PSP中找到可执行程序名外,PSP还有一个不足之处——在命令行中能看到的对于输出或输入的重定向,在PSP的命令行缓冲区中是无法看到的,也就是说,你无法从PSP中得知你的程序是否被重定向过。
    到现在为止,你应该熟悉在C程序中可以通过argc和argv来获取一些有关信息,但是,这些信息是怎样从DOS的装入程序传给argv指针的呢?这是由程序的启动代码来完成的。启动代码在main()函数的第一行代码之前被执行,在其执行期间,它调用一个名为__setargv()的函数,把程序名和命令行从PSP和DOS环境中复制到mai‘n()函数的argv指针所指向的缓冲区中。你可以在xLIBCE.LIB文件中找到_setargv()函数,对于Small,Medium和Large这三种存储模式,这里的“x”分别为“S”,“M”和“L”。在生成可执行程序时,上述库文件会自动被连接进来。除了复制argv参数的内容外,c的启动代码还要完成其它一些工作。当启动代码执行完毕后,main()函数中的代码就开始执行了。
    在DOS中的情况是这样的,那么在Windows中的情况又是怎样的呢?实际上,在Windows中的情况大致上和在DOS中的一样。当执行一个Windows程序时,与DOS的装入程序一样,Windows的装入程序也会建立一个PSP,其中包含了与DOS的PSP中相同的信息。主要的区别是此时命令行被复制到lpszCmdLine参数中,它是WinMain()函数的参数表中的第三个(也是倒数第二个)参数。在Windows C的xLIBCEW.LIB库文件中包含了启动函数setargv(),它负责把命令行信息复制到lpszCmdLine缓冲区中。同样,这里的“x”也表示程序所使用的存储模式。在Quick c中,函数_setargv()包含在库文件xLIBCEWQ.LIB中。
    尽管DOS程序和Windows程序的命令行信息的治理方式基本相同,但是传给你的C程序的命令行的格式在安排上稍有不同。在DOS中,启动代码获得以空格符为分隔符的命令行后,就把每个参数转换为其自身的以NULL为终止符的字符串。因此,你可把argv原型化为一个指针数组(char* argv[]),并通过从O到n这些下标值来访问每个参数,其中n等于命令行中的参数个数减去1。你也可以把argv原型化为一个指向指针的指针(char **argv),并通过增减argv的值来访问每一个参数。
    在Windows中,传给c程序的命令行是一个LPSTR类型或char_far*类型,其中的每一个参数都用空格符隔开,就象你在DOS提示行中键入这些字符后所看到的那样(实际上,在Windows中不可能真正键入这些字符,而是通过双击应用程序图标这样的方式来启动一个程序)。为了访问Windows命令行中的各个参数,你必须人工地访问lpszCmdLine所指向的存储区,并分隔存放在该处的参数,或者使用strtok()这样的函数,每次处理一个参数。
    假如你富于探索精神,你可以仔细地研究PSP本身,并从中获取命令行信息。为此,你可以像下面这样来使用DOS中断21H(此处使用Microsoft C):
# include <stdio. h>
# incIude <dos. h>
main(int argc,char **argv)
{
      union REGS regs ;                  / * DOS register access struct * /
      char far * pspPtr;                 / * pointer to PSP * /
      int cmdLineCnt;                    / *num of chars in cmd line * /
      regs. h. ah=0x62;                  /*use DOS interrupt 62 *;
      int86(0x21 ,®s,&egs) ;          / *call DOS * /
      FP-SEG(pspPtr) =regs. x. bx ;      / *save PSP segment * /
      FP_OFF(pspPtr)=0xS0;               / * set pointer offset * /
      / * * pspPtr now points to the command-line count byte * /
      cmdLineCnt== * pspPtr ;
    需要注重的是,在Small存储模式下,或者在只有一个代码段的汇编程序中,由DOS返回到BX寄存器中的值就是程序代码段的地址值;在Large模式的c程序中,或者在多个代码段的汇编程序中,所返回的值是程序中包含PSP的那个代码段的段地址值。假如你已经建立了一个指向这个数据的指针,你就可以在程序中使用这个数据了。

    请参见:
    20.2 程序总是可以使用命令行参数吗?

    20.2 程序总是可以使用命令行参数吗?
    今天,通常你可以认为你的程序可以使用命令行参数。但是,在DOS 2.O版以前,存储在PSP中的命令行信息与现在稍有不同(它不能从命令行中分离出输入或输出重定向数据),而
且由argv[O]所指向的数据中并不一定包含可执行程序的路径名。直到DOS发展到3.o版,它才提供了(或者说至少公开了)用来检索PSP的中断62H。因此,你至少可以认为,在运行DOS3.0或更高版本的PC上,你的程序总是可以获得命令行参数的。
    假如你的程序运行在DOS 3.0或更高的版本下,你基本上就可以任意处理命令行参数了,因为这些信息已存入栈中供你使用。显然,适用于栈中数据的常规的数据操作规则同样也适用于存入栈中的命令行参数。然而,假如你的编译程序不提供argv参数,例如当你用汇编语言或者某种不提供argv参数的编译程序编写程序时,真正的问题就出现了。在这种情况下,你将不得不自己找出检索命令行参数的方法,而使用DOS中断62H就是一种很方便 的方法。
    假如你要用DOS中断62H来检索指向命令行的指针,你必须明白该指针所指向的数据是供DOS使用的,并且正在被DOS使用。尽管你可以检索这些数据,但你不可以改变它们。假如在程序中需要随时根据命令行参数作出决定,那么在使用这些数据之前应该先把它们复制到一个局部缓冲区中,这样就可以随意处理这些数据,而不用担心会与DOS发生冲突了。实际上,这种技巧同样适用于带argv参数的c程序。位于main()函数之外的函数需要使用命令行参数的情况并不少见,为了使这些函数能引用这些数据,main()函数必须把这些数据存为全局型,或者通过(再次)入栈把它们传递给需要使用它们的函数。因此,假如你想使用命令行参数,一种好的编程习惯就是把它们存入一个局部缓冲区中。

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

    20.3 “异常处理(exception handling)”和“结构化异常处理(structured exception handling)”有什么区别?
    总的来说,结构化异常处理和异常处理之间的区别就是Microsoft对异常处理程序在实现上的不同。所谓的“普通”C++异常处理使用了三条附加的c++语句:try,catch和throw。这些语句的作用是,当正在执行的程序出现异常情况时,答应一个程序(异常处理程序)试着找到该程序的一个安全出口。异常处理程序可以捕捉任何数据类型上的异常情况,包括C++类。这三条语句的实现是以针对异常处理的ISO WG21/ANSI X3J16 C++标准为基础的,Microsoft C++支持基于这个标准的异常处理。注重,这个标准只适用于C++,而不适用于C。
    结构化异常处理是Microsoft c/c++编译程序的一种功能扩充,它的最大好处就是它对C和C++都适用。Microsoft的结构化异常处理使用了两种新的结构:try—except和try-finally。这两种结构既不是ANSI c++标准的子集,也不是它的父集,而是异常处理的另一种实现(Microsoft会继续在这方面努力的)。try—except结构被称为异常处理(exception handling),tryfinally结构被称为终止处理(termination handling)。try—except语句答应应用程序检索发生异常情况时的机器状态,在向用户显示出错信息时,或者在调试程序时,它能带来很大的方便。在程序的正常执行被中断时,try—finally语句使应用程序能确保去执行清理程序。尽管结构化异常处理有它的优点,但它也有缺点——它不是一种ANSI标准,因此,与使用ANSI异常处理的程序相比,使用结构化异常处理的程序的可移植性要差一些。假如你想编写一个真正的C++应用程序,那么你最好使用ANSI异常处理(即使用try,catch和throw语句)。

    20.4 怎样在DOS程序中建立一个延时器(delay timer)?
    我们这些程序员很幸运,因为Microsoft公司的专家认为建立一个独立于硬件的延时器是一个好主意。显然,这样做的目的在于,无论程序运行在什么速度的计算机上,都可产生一段固定的延迟时间。下面的程序说明了如何在DOS中建立一个延时器:
# include <stdio. h>
# include %dos. h>
# include <stdlib. h>
void main(int argc,char ** argv)
{
      union REGS regs:
      unsiged long delay;
      delay = atoI(argv[1]) ;        / * assume that there is an argument * /
      / * multiply by 1 for microsecond-granularity delay * /
      / * multiply by 1000 for millisecond-granularity delay * /
      / * multiply by 1000000 for second-granularity delay * /
      delay * =1000000;
      regs. x. ax = 0x8600 ;
      regs. x. cx= (unsigned int )((delay & 0xFFFF0000L)>>16) ;
      regs. x. dx = (unsigned int ) (delay & 0xFFFF)
      int86 (0x15, ®s, ®s) ;
}
    上例通过DOS中断15H的86H功能来实现延时,延迟时间以微秒为单位,因此,上述延时程序认为你可能要使用一个非常大的数字,于是它分别用CX和DX寄存器来接受延时值的高16位和低16位。上述延时程序最多可以延时49.亿微妙---大约等于1.2小时。
    上例假设延时值以微秒为单位,它把延时值乘以一百万,因此在命令行中可以输入以秒为单位的延时值。例如,“delay 10”命令将产生10秒的延时。

    请参见:    
    21.2 怎样在Windows程序中建立一个延时器?

    20.5 Kernighan和Ritchie是谁?
    Kernighan和Ritchie是指Brian w.Kernighan和Dennis M.Ritchie两人,他们是《C程序设计语言(The C Programming Language)》一书的作者。这是一本广为人知的书,许多人都亲切地称之为“K&R手册”、“白皮书”、“K&R圣经”或其它类似的名字。这本书最初是由Prentice—Hall于1978年出版的。Dennis为运行在DEC PDP-11主机上的UNIX操作系统开发了c程序设计语言。70年代初,Dennis和Brian都在AT&T Bell实验室工作,c和“K&R手册”就是在那时推出的。从某种意义上来讲,C是以Ken Thompson于1970年所写的程序设计语言和Martin Richards于1969年所写的BCPL语言为模型的。

    20.6 怎样生产随机数?
    尽管在计算机中并没有一个真正的随机数发生器,但是可以做到使产生的数字的重复率很低,以至于它们看起来是随机的。实现这一功能的程序叫做伪随机数发生器。
    有关如何产生随机数的理论有许多,这里不讨论这些理论及相关的数学知识。因为讨论这一主题需要整整一本书的篇幅。这里要说的是,不管你用什么办法实现随机数发生器,你都必须给它提供一个被称为“种子(seed)”的初始值,而且这个值最好是随机的,或者至少是伪随机的。“种子”的值通常是用快速计数寄存器或移位寄存器来生成的。
    在本书中,笔者将介绍c语言所提供的随机数发生器的用法。现在的c编译程序都提供了一个基于一种ANSI标准的伪随机数发生器函数,用来生成随机数。Microsoft和Borland都是通过rand()和srand()函数来支持这种标准的,它们的工作过程如下:
    (1)首先,给srand()提供一个“种子”,它是一个unsignde int类型,其取值范围是从0到65,535 ;
    (2)然后,调用rand(),它会根据提供给srand()的“种子”值返回一个随机数(在0到32,767之间);
    (3)根据需要多次调用rand(),从而不断地得到新的随机数;
    (4)无论什么时候,你都可以给srand()提供一个新的“种子”,从而进一步“随机化"rand()的输出结果。
    这个过程看起来很简单,问题是假如你每次调用srand()时都提供相同的“种子”值,那么你将会得到相同的“随机”数序列。例如,在以17为“种子”值调用srand()之后,在你首次调用rand()时,你将得到随机数 94;在你第二次和第三次调用rand()时,你将分别得到26,602和30,017。这些数看上去是相当随机的(尽管这只是一个很小的数据点集合),但是,在你再次以17为“种子”值调用srand()之后,在对rand()的前三次调用中,所得到的返回值仍然是94、
26,602和30,017,并且此后得到的返回值仍然是在对rand()的第一批调用中所得到的其余的返回值。因此,只有再次给srand()提供一个随机的“种子”值,才能再次得到一个随机数。
    下面的例子用一种简单而有效的办法来产生一个相当随机的“种子”值——当天的时间值。
# include <stdlib. h>
# include <stdio. h>
# include <sys/types. h>
# include <sys/timeb. h>
void main (void)
{
     int i ;
     unsigned int seedVal;
     struct_timeb timeBuf ;
      _ftime (&timeBuf) ;
      seedVal = ( ( ( ( (unsigned int)timeBuf, time & 0xFFFF) +
                       (unsigned int)timeBuf, millitm) ^
                       (unsigned int)timeBuf, millitm) ;
     srand ((unsigned int)seedVal) ;
     for(i=O;i<lO;++i)
          printf (" %6d\n" ,rand ( ) ) ;
}
    上例先是调用_ftime()来检索当前时间,并把它的值存入结构成员timeBuf.time中,当前时间的值从1970年1月1日开始以秒计算。在调用了_ftime()之后,在结构timeBuf的成员millitm中还存入了在当前那一秒已经度过的毫秒数,但在DOS中这个数字实际上是以百分之一秒来计算的。然后,把毫秒数和秒数相加,再和毫秒数进行一次异或运算。你可以对这两个结构成员施加更多的逻辑运算,以控制seedVal的取值范围,并进一步加强它的随机性,但上例所用的逻辑运算已经足够了。
    注重,在前面的例子中,rand()的输出并没有被限制在一个指定的范围内,假设你想建立一个彩票选号器,其取值范围是从1到44。你可以简单地忽略掉rand()所输出的在该范围之外的值,但这将花费许多时间去得到所需的全部(例如6个)彩票号码。假设你已经建立了一个令你满足的随机数发生器,它所产生的随机数据范围是从0到32,767(就象前文中提到过的那样),而你想把输出限制在1到44之间,下面的例子就说明了如何来完成这项工作:
int i ,k ,range ;
int rain, max ;
double j ;
min=1;                               /* 1 is the minimum number allowed */
max=44;                              /* 44 is the maximum number allowed * /
range=max-min;                      / * r is the range allowed; 1 to 44 * /
i=rand();                             /* use the above example in this slot * /
/ * Normalize the rand() output (scale to 0 to 1) * /
/ * RAND_MAX is defined in stdlib, h * /
j= ((double)i/(double)RAND_MAX) ;
/ * Scale the output to 1 to 44 * /
i= (int)(j * (double)range) ;
i+ =min;
  上例把输出的随机数限制在1到44之间,其工作原理如下:
    (1)得到一个在O到RAND_MAX(32,767)之间的随机数,把它除以RAND_MAX,从而产生一个在0到1之间的校正值;
    (2)把校正值乘以所需要的范围值(在本例中为43,即44减去1),从而产生一个在O到43之间的值;
    (3)把该值和所要求的最小值相加,从而使该值最终落在正确的取值范围——1到44之内。
    你可以用不同的min和max值来验证这个例子,你会发现它总是会正确地产生在新的rain和max值之间的随机数。

    20.7 什么时候应该使用32位编译程序?
    32位编译程序应该在32位操作系统上使用。由32位编译程序生成的32位程序比16位程序运行得更快,这正是任何32位的东西都很热门的原因。
    有那么多不同版本的Microsoft Windows,它们和哪种编译程序组成最佳搭配呢?
Windows 3.1和Windows for Workgroups 3.11是16位的操作系统;Microsoft Visual C++1.x是16位编译程序,它所生成的程序能在Windows 3.1上运行。Microsoft Windows NT和Windows 95是32位操作系统;Microsoft Visual C++2.o是32位编译程序,它所生成的32位程序能在这两种操作系统上运行。由Visual C++1.x生成的16位程序不仅能在Windows 3.1上运行,也能在Windows NT和Windows 95上运行。
    然而,由Visual 2.O生成的32位程序无法在Windows 3.1上运行。这就给Microsoft提出了一个难题——它希望每个人都使用它的32位编译程序,但是有6千万台计算机所使用的Windows版本无法运行32位程序。为了克服这个障碍,Microsoft设计了一个叫做Win32s的转换库,用来完成由32到16位的转换,从而使由Visual C++生成的32位程序能在
Windows 3.1和Windows for Workgroups上运行。Win32s是专门为在Windows 3.1(和WFW)上运行而设计的,它不能用在Windows NT和Windows 95上,因为它们不需要为运行32位程序作什么转换。有了Win32s,你就可以用Visual C++2.0生成一个既能在Windows 3.1(和WFW)上运行,又能在Windows NT上运行的程序了。
    最后还有一个问题,即编译程序本身的问题。Visual C++1.x是一个16位Windows程序,它既能在Windows 3.1上运行并生成16位程序,也能在Windows 95和Windows NT上运行并生成同样的程序。但是,作为一个32位程序,Visual C++2.O无法在Windows 3.1上运行,即使装上了Win32s也无法运行,因为出于方便的目的,Microsoft认为在启动Visual C++2.O之前,你一定运行在Windows 95或Windows NT上。
    总而言之,在Windows 3.1,Windows for Workgroups,Windows 95或Windows NT上运行Visual c++1.x(更新的版本为1.51)而生成的16位程序能在任何版本的Windows上运行。由Visual C++2.O生成的32位程序在Windows 95或Windows NT上运行得很快,但它在Windows 3.1(和WFW)下也能很好地运行。
    对于Borland C/C++,Borland的Turbo C++3.1版是版本较新的16位编译程序,Borland c++4.5版(注重该名称中没有"Turbo一词)是32位Windows编译程序。这两种编译程序都包含编译程序、Borland的OWL C++类和一个出色的集成化调试程序。

    20.8 怎样中断一个Windows程序?
    没有一个普通的方法可以中断一个Windows程序,从而让你去完成一项必需的任务(假如这就是你的目标的话)。Windows 3.x是一种不支持优先调度的多任务操作系统,换句话说,它是一种合作性(cooperative)的多任务操作系统——原因之一就是你无法简单地从当前正在由处理器控制着的一个Windows程序中抢占一段时间。假如你看一下Windows API,你会发现PeekMessage()和WaitMessage()这样一些函数,在Microsoft的帮助文档中有这样一段介绍这些函数的话:
    函数GetMessage(), PeekMessage()和WaitMessage()把控制权交给别的应用程序,答应别的应用程序运行的唯一方法就是使用这些函数。假如一个应用程序长时间不调用这些函数,那么它就阻止了别的应用程序的运行。
    尽管这样,你还是可以中断一个Windows程序的。一种办法是在你的Windows程序中建立一个定时器,它每隔一段时间就会发出信号,只要你的程序处于激活的状态,那么在定时器发出信号的时候,你就能获得时间来完成各种所需的工作。但是,从技术角度来讲,这种方法并没有中断一个程序的处理过程——它只是应用了Windows的合作性多任务性能。假如你需要中断一个Windows程序,你可以使用过滤函数(filter function,也叫做hook function,即钩子函数)。Windows中的过滤函数与DOS中的中断处理程序相似(见20.12和20.17),你可用它“挂上(hook)”某个Windows事件,并在该事件发生时执行相应的任务。实际上,你可以用一个使用了一个或多个可用的钩子(hook)的过滤器来监视Windows中的几乎每一条消息。下面的例子使用了键盘钩子,因为它要使你能随时按一个特定的组合键来中断一个程序。该例挂上了键盘事件链,并且在Ctrl+Alt+F6键按下时打开一个消息框。不管当前正在执行的是哪一个应用程序,该例都能起作用。
 # include <dos. h>
 # include <windows. h>
 DWORD FAR PASCAL  __loadds KeyBoardProc(int,WORD,DWORD);
 static FARPROC nextKeyboardFilter=NULL;
                                                            
 BOOL shiftKeyDown ,ctrlIKeyDown ;
 # define REPEAT-COUNT       0x000000FF       / * test key repeat * /
 # define KEY_WAS_UP         0x80000000        / * test WM_KEYUP * /
 # define ALT KEY_DWN        0x20000000        / * test ALT key state * /
 # define EAT_THE_KEY         1                   /* swallow keystroke * /
 # define SEND_KEY ON         0                  /* act on keystroke ~ /
 BOOL useAltKey=TRUE;        / * use Alt key in sequence * /
 BOOL useCtrlKey=TRUE;       / * also use Ctrl key * /
 BOOL useShiftKey=FALSE;      / * don't use Shift key * /
 / * Entry point into the DLL. Do all necessary initialization here * /
 int FAR PASCAL LibMain (hModule,wDataSeg,cbHeapSize,lpszCmdLine)
 HANDLE hModule ;
 WORD wDataSeg ;
 WORD cbHeapSize ;
 LPSTR lpszCmdLine ;
       / * initialize key state variables to zero * /
       shiftKeyDown = 0 ;
       ctrlKeyDown = 0 ;
       return 1 ;
   /* The keyboard filter searches for the hotkey key sequence.
      If it gets it, it eats the key and displays a message box.
      Any other key is sent on to Windows. * /
 DWORD FAR PASCAL    _loadds
 KeyBoardProc (int nCode, WORD wParam, DWORD lParam)
     BOOL fCallDefProc ;
     DWORD dwResult = 0 ;
     dwResult=SEND_KEY_ON ;       / * default to send key on * /
     fCallDefProc=TRUE;               / * default to calling DefProc * /
switch (nCode) {
     case HC ACTION :
     case HC-NOREMOVE :
           / * If key is Shift , save it * /
           if (wParam= = (WORD)VK_SHIFT) (
                 shiftKeyDown = ( (1Param & KEY_WAS_UP) ? 0 : 1 ) ;
                 break ;
           / * If key is Ctrl,save it * /
           else if(wParam= = (WORD)VK_CONTROL) {
                 ctrlKeyDown=((lParam & KEY WAS UP)? 0:1);
                 break ;
           / * If key is the F6 key,act on it * /
           else if(wParam= = (WORD)VK_F6) {
                  / * Leave if the F6 key was a key release and not press * /
                  if (lParam * KEY_WAS_UP) break;
                  / * Make sure Alt key is in desired state, else leave * /
                  if((useAltKey) && ! (lParam & ALT_KEY_DOWN) ) {
                       break ;
                  else if((!useAltKey) && (lParam & ALT KEY_DOWN)){
                       break ;
                  / * Make sure Shift key in desired state, else leave * /
                  if(useShiftKey && ! shiftKeyDown){
                       break ;
                  else if ( !useShiftKey && shiftKeyDown) {
                       break ;
                  / * Make sure Ctrl key in desired state, else leave * /
                  if(useCtrlKey && ! ctrlKeyDown){
                       break ;
                  else if ( !useCtrlKey && ctrlKeyDown) {
                      break ;
              }
                 / * Eat the keystroke, and don't call DefProc * /
                 dwResult = EAT_THE_KEY;
                 fCallDefProc =FALSE ;
                 / * We made it, so Ctrl+Alt+F6 was pressed! * /
                 MessageBox (NULL, (LPSTR)" You pressed Ctrl + Alt + F6!" , (LPSTR)" Keyboard
                                              Hook" ,MB_OK) ;
                 break ;
              }
        default :
                 fCallDefProc = TRUE ;
                 break ;
    }
if((nCode<0) | (fCallDefProc && (nextKeyboardFilter ! =NULL)))
     dwResult = DefHookProc (nCode, wParam, lParam,&nextKeyboardFilter) ;
     return (dwResult) ;
}
/ * This function is called by the application to set up or tear
     down the filter function hooks.  * /
void FAR PASCAL
SetupFilters (BOOL install)
{
     if (install) {
           next KeyboardFilter = SetWindowsHook (WH-KEYBOARD,
                                                            (FARPROC) KeyBoardProc) ;
    }
     else {
           UnhookWindowsHook (WH-KEYBOARD, (FARPROC)KeyBoardProe) ;
           nextKeyboardFitter = NULL ;
    }
}
    Microsoft强调最好把过滤函数放在DLL中而不是放在应用程序中(注重在DLL中有LibMain()而没有WinMain()),为了实现上述应用,你需要编写一个普通的Windows应用程序来调用SetupFilters()函数——当该函数的参数值为TRUE时,该程序就开始监视键盘输入;当该函数的参数值为FALSE时,该程序就停止监视键盘输入。假如该程序被激活并且调用了SetupFilters(TRUE),回调函数KeyBoardProc()就会接收所有的键盘输入,而不管你是否正在运行其它的Windows程序。假如你按下Ctrl+Altq+F6,屏幕上就会出现一个小的消息框,通知你这些键被你按下了。在按下这些键的那一刹那,正在运行的那个程序被中断了。
  注重,在DOS Shell下,键盘过滤函数不会接收键盘输入,但是在出现象询问你是否真的要退出Windows这样的系统模式对话框时,它会接收键盘输入并且中断该对话框。

视频教程列表
文章教程搜索
 
C语言程序设计推荐教程
C语言程序设计热门教程
看全部视频教程
购买方式/价格
购买视频教程: 咨询客服
tel:15972130058