choice.com 应用推广三部曲

类别:软件工程 点击:0 评论:0 推荐:

===========================
choice.com 应用推广三部曲
Will Sort - 2005/04/18
Updated:2005/05/14
===========================

序言
==============================================================================

  在 choice.com 出现之前,用户控制批处理运行过程的途径通常被限制在命令行参
数和环境变量上,虽然有个别的编程者会使用debug 的汇编脚本或者 date 、time等一
些可以接受键盘输入的特殊命令来实现运行中接受用户输入的参数,但是它们不是使用
方法很复杂,就是适用的场合很有限,或者还有其他一些诸如此类的问题。

  但是,自从 choice.com 出现后,菜单选择立即成为批处理程序交互的重要手段。
程序中开始大量出现由 echo/choice/if errorlevel goto 所组成的结构范式。然而,
随着 choice.com 不断深入的应用,大家在享受它所带来的便利时,也渐渐无法忍受它
所带来的不便。

  这不便中最主要的,就是 choice.com 仅支持字母和数字等可以指定候选字符的按
键,而对经常会在菜单中用到的回车、空格、Esc 、光标移动、翻页等特殊按键却无法
用字母进行指定,也就无法在菜单中使用它们。

  然而,事实真的如此吗?不妨看看下文会给我们带来哪些收获。
==============================================================================

===========================
三部曲之一:自欺欺人
Will Sort - 2005/04/18
Updated:2005/04/23
===========================

==============================================================================

  首先,让我们进入命令行,当出现命令提示符后,尝试敲入并执行下面的语句:

  choice /n/s/c:Q  Press [PageDown] to quit:

  当按下回车后,我们看到了提示,然后根据提示按下 PageDown 键,然后我们发现
了什么?程序运行结束了!并且在提示语的后面出现了一个大写的字母 Q。


  哦,太神奇了,我发现了新大陆!

  不,这只是新大陆的一个小岛而已。事实上,大部分的特殊按键在 choice.com 中
都对应着一个字母、数字或者其他可以显示出来的字符。

  以下是一些常用的特殊按键对应字符的简明列表:
  ------------------------------------------------------------------------
  ; [F1]   < [F2]     = [F3]    > [F4]    ? [F5]
  @ [F6]   A [F7]     B [F8]    C [F9]    D [F10]

  R [Insert]          G [Home]            I [Page Up]
  S [Delete]          O [End]             Q [Page Down]

                      H [UP Arrow]
  K [Left Arrow]      P [Down Arrow]      M [Right Arrow]

  27 [Esc]            8 [Back Space]
  ------------------------------------------------------------------------

  我们可以再根据上表中提供的字符做新的尝试:

  choice /n/s/c:; Press [F1] to quit:

  哈,又成功了!再试:

  choice /n/s/c:< Press [F2] to quit:

  啊,出错了!找不到文件,怎么回事?
  嗯,你用的小于号是系统定义的文件重定向符号,它把 Press当成文件重定向了。
  那怎么办?
  简单,在小于号两边加上双引号,系统不认识小于号就行了,就像这样:

  choice /n/s/c:"<" Press [F2] to quit:

  哈,真是这样呢!
  那么,你可以再多试一些练习一下。
  好,我试,我试,我试试试。

  choice /n/s/c:= Press [F3] to quit: ---〉成功!
  choice /n/s/c:> Press [F4] to quit: ---〉失败!
  哦,不对,改:
  choice /n/s/c:">" Press [F4] to quit: ---〉成功!
  choice /n/s/c:? Press [F5] to quit: ---〉成功!

  好了,我会用了,但是,这到底是怎么回事呢?

  原因嘛,深究起来比较复杂;简单的说,是因为这些特殊的按键会产生一种系统可
以识别的扩展编码,一般是由0 或者224 和另一数字编码组成。而 choice.com 接受这
些按键时,会将编码中认为无效的0 或者224 过滤掉,而剩下后面的数字编码,而这部
分数字又恰好会被误认为是某个可显示字符的 ASCII编码。以开始提到的 PageDown 来
说,它的扩展编码就是“0;81”,而与此相对应的,字母 Q的 ASCII编码也是81,于是
choice.com就将这 PageDown 和 Q这两个键混淆了。其它“F1”和分号、“F2”和小于
号等等都是类似的原因。这也是本节篇名“自欺欺人”的由来。
  等等,你说什么“扩展编码”,那是什么东东?
  “扩展编码”实际上是 MS-DOS 从键盘读取按键时,经由键盘扫描码略加修改转换
后的一种按键的内部编码,通过这种转换后的编码形式,它可以兼容地处理 ASCII字符
键和非 ASCII字符键(就是在 ASCII码表中找不到对应编码的按键)。
  呵,似懂非懂啊,还是算了吧。

  等一下,表里面最后一个 Esc ,前面怎么是个二位数?

  哦,你观察的倒真仔细。是这样的, Esc还有 BackSpace,它们与 PageDown 有所
不同。那就是 Esc和 BackSpace它们都是 ASCII字符键,也就是说它们在 ASCII码表中
可以找到对应的编码,正是表中所列的 27 和 8。而这个数字同时也是它们的扩展编码
只有一串数字,没有0 或者 224打头,这就意味着,它们在独立地在 ASCII码表中占用
两个位置,也就没有可能冒充其他的字符。
  同时,因为他们在命令行和各种编辑器中都被赋予了特殊的含义(命令行中 Esc用
来重写命令, BackSpace用来向前删除字符),所以就无法通过按键直接输入字符来指
定候选键。但是,实际上 choice.com 是可以接受 27 和 8这样的编码的,所以我们只
要想办法将这两个键变成字符输进命令行就可以完成任务了。

  唔,那么怎么把它们输进去呢?
  这就要请出我们命令行工具的不老悍将—— edit 了。
  edit,就是那个DOS 下面打字的软件吗?

  对,准确的说, edit 是一个文本编辑器;它有个很实用的功能,就是用 Ctrl+P
输入特殊字符。比如,我们要把 Esc当作字符输进去,就可以先按下Ctrl+P,放开后再
按Esc ,这时一个向左的小箭头就会出现了,它就是Esc 在 ASCII码表中对应的字符;
同样,按下 Ctrl+P 、 BackSpace后就会出现一个带圆孔的长方块,那就是 BackSpace
对应的字符了。

  输进去以后怎么办呢?
  这样,我们可以把上面的 choice.com 语句写进批处理程序中,然后将 /c 后面的
候选字符换成我们刚刚学会输入的那两个字符,就像下面这样:

  @choice /n/s/c: Press [Esc] or [BackSpace] to quit:

  将上面这个句子保存为批处理程序,比如“Esc-BkSp.bat”。然后,在命令行中执
行它。然后按 Esc 和 BackSpace 试试看。

  唉,终于成功了!好累啊,不过也蛮有成功感的。

  嗯,到这里,我们的第一部曲也该就此结束了。现在,我们应该可以解决一些以前
用 choice.com 时无法解决的问题。但是,仍然还有一些问题,比如我们一直没讲到回
车键怎么在 choice.com 中使用,它无论在命令行还是在批处理中都被当作了语句结束
的标志,因而始终无法作为候选字符使用,还有很多其它的一些类似的特殊按键(例如
Tab )因为同样的原因而无法使用。
  而这些,都将留在第二部曲“瞒天过海”中探讨和解决。

  最后,作为本节内容的附赠品,一篇详细标明更多特殊按键扩展编码以及替代字符
的列表以附件形式列于其后,附件中还包括 EscBksp.bat和 PgDw.bat 用以测试是否支
持 [Esc]/[BackSpace]/[PageDown] 等特殊的按键。在 EscBksp.bat中使用了自动获取
 [Esc]/[BackSpace]这两个特殊按键对应字符的方法。
------------------------------------------------------------------------------

编后语:

  其实,就我自己而言,很不习惯也很不喜欢写这样风格的文字,短短的一篇技术短
文硬是被拖拽成了又长又臭的搞笑散文 :< 没办法,为了照顾大多数初学者,只好牺牲
一下我的个人品味了 :) 不过,在后两节文字中,随着技术含量的增加,同时也是为了
照顾我的个人情绪,我将会适当的提高文章的理解水平底线,低于此线的朋友们,在此
先说声抱歉了。
==============================================================================

===========================
三部曲之二:瞒天过海
Will Sort - 2005/04/24
Updated:2005/04/30
===========================

==============================================================================

  在上节中我们留下一个题目,那就是如何让 choice.com 接受并正确处理回车键以
及其他一些系统保留的特殊键。我的答案是键盘重定义,就是将键盘上的特殊键重定义
为我们可以利用的常规键,比如字母或者数字。

  这需要使用从 MS-DOS 时代起便出现的一个设备驱动程序 ANSI.SYS 。它是为了支
持美国国家标准学会(ANSI)所制订的一套关于键盘和屏幕控制的标准而设计的,具有
相当丰富的键盘和屏幕控制功能,详细内容可以参阅附件中的文档 ANSI_SYS.TXT 。利
用它所提供的重定义键(实际上是重定义键的扩展编码)的功能,可以将回车重定义为
字母‘y’,从而实现我们的初衷。但是要使用这个功能,我们需要预先做一些准备。

在 MS-DOS 和 Win98 中准备使用 ANSI.SYS
--------------------------------------

  在 MS-DOS 和 Win98的命令行环境中,我们使用 ANSI.SYS 需要通过一个启动配置
文件来加载它,那就是 config.sys ,它是一个纯文本文件,通常位于系统所在盘的根
目录,可以用“记事本”或者上节提到的“edit”来编辑它。我们可以在这个文件的最
后添加一行如下的文字:

  device=c:\Windows\Command\ANSI.SYS /x

  其中的 C:\Windows\Command 可能需要替换成 ANSI.SYS 文件实际所在的目录。比
如你是 MS-DOS 的用户,那么 ANSI.SYS 很可能在 C:\DOS 下。

  编辑完成后,重新启动计算机就可以在 MS-DOS 或者 Win 98 的命令行环境下使用
 ANSI.SYS 所提供的各种功能特性了。

在 Win2K 和 WinXP 中准备使用 ANSI.SYS
-------------------------------------

  在 Win2K和 WinXP的命令行环境中,已不再使用 config.sys ,但是可以在系统路
径下的 System32 目录中找到一个替代品,那就是 config.nt,它用于配置 MS-DOS 模
拟环境(command.com)。它也是纯文本文件,所以也可以用记事本打开并编辑它。我
们需要给它加上与 MS-DOS 和 Win9x 系统相类似的文字:

  device=%SystemRoot%\system32\ANSI.SYS /x

  其中的 %SystemRoot% 是个全局的环境变量,我们可以直接使用它引用系统路径,
而不需要人为地修改它。

  与 MS-DOS 和 Win9x 的命令行环境不同的是,我们还需要修改 config.nt 文件中
的一个细节,那就是将“REM DOSONLY”一行中的“REM”去除。这样做的原因是,ANSI
是一个基于 MS-DOS 的终止并驻留(TSR)内存的程序,它需要一个纯粹的运行 MS-DOS
程序的环境,否则就会无法正常的使用。而 DOSONLY正是创建这个环境的必要开关,所
以我们去掉它前面的注释命令,以使它在 config.sys 中生效。

  所有的设置修改完成后,就可以立即在运行中输入 “command”(不是 CMD),此
时有 ANSI 支持的命令行环境就被启动了。

开始体验 ANSI.SYS
-----------------

  准备了这么久,我们终于可以使用 ANSI.SYS 带给我们的特性了,请将下面的代码
拷贝并保存到批处理文件中。

  @echo off
  echo [13;'y'p-------- Start -------------
  choice Press Enter or Y to quit: /c:y /n
  echo --------- End --------------
  pause

  然后在我们刚才启动的有 ANSI 支持的命令行环境下,执行这个批处理,看到提示
后我们按回车键:此时,程序运行结束,并在提示语后出现一个大写字母 'Y'。

  我们分析一下程序,第一行是将命令回显关闭,第二行就用了 ANSI 的重定义键功
能, echo 后面的向左箭头就是上节中提到的 Esc 键的 ASCII 字符,它和'[' 联合使
用,标志着随后的序列开始使用 ANSI 的功能,它们不会被 echo ,而会被 ANSI 发现
并转变其含义,因此这个序列被称为转义序列,Esc 也被称为转义字符。第三行是供我
们测试的 choice 语句,其中仅仅定义了一个候选字符‘y’;第四行是恢复回车原来
的定义;第五行实现暂停。

  而转义序列“13;'y'p”的意思,我们应该可以猜得出来,那就是将回车重定义为
字母‘y',回车的表示正是上节中提到的扩展编码,而字母‘y'被单引号括了起来,表
示它是一个 ASCII 字符,而非字符的编码;至于紧跟其后的字母‘p’正是重定义键的
功能标志。而后面的 -Start- 并不是转义序列的一部分,将会原样输出。

  我们也可以实现更多地重定义按键,比如制表符键、光标键、删除键、回删键等,
详细的代码请参阅附件中的 Remap2.bat ,我们可以在命令行中执行它,然后测试一
下我们的按键和输出结果是否一一匹配。

如何在我需要时才使用 ANSI.SYS
-----------------------------

  以上对 config.sys 和 config.nt 的修改将会使以后运行所有的 MS-DOS 程序都
可以享用 ANSI.SYS 所带来的特性,但同时也会始终占用命令行环境下约 4K 的内存空
间。我们可能并不想这样,因为我们并不经常需要 ANSI.SYS 的支持,当我们不需要它
时,总是不希望它浪费我们宝贵的内存空间。

  要解决这个问题,我们可以在系统启动以后再加载它。在 MS-DOS 环境中,当需要
使用 ANSI.SYS 的时候,一般使用 device.exe 或者其他有相似功能的第三方程序在命
令行加载 ANSI.SYS ,不需要的时候也同样可以卸载它。

  在 Win9x的命令行环境中,我们多了一种选择,那就是为需要使用 ANSI.SYS 的程
序编辑自己专属的启动配置文件:在程序的右键“属性”菜单中,选择“程序->高级->
MS-DOS方式->指定新的 MS-DOS 配置”,然后在 config.sys 的对话框中添加 device
语句即可。但是这个方法的缺点是启动和退出这个程序时,都会重新启动系统以进入新
的命令行环境。而在WinXP 中,没有了 Win9x的缺点,我们可以为需要使用 ANSI.SYS
的程序创建一个指向 MS-DOS 程序的快捷方式(.PIF文件):在程序所在文件夹的空白
处点击右键,选择“新建->快捷方式”,在文本框中填入 command,然后一路回车,在
出现“MS-DOS方式”之后,在右键“属性”菜单中,修改程序名为 Remap2.bat ,修改
启动配置文件为我们自己制作的启动配置文件,比如附件中的 Remap_XP.PIF 使用的便
是 %SystemRoot%\System32 下的 ANSI_SYS.NT(对应CONFIG.NT)和 ANSI_BAT.NT(对
应AUTOEXEC.BAT),它们可以在附件中 ANSI_XP 文件夹中找到。

  这一节我们讲述了如何通过 ANSI 的重定义键功能来实现 CHOICE 接受任意按键,
除了极个别的按键之外(如 Ctrl+Break 或者 Ctrl+Alt+Del ),我们已经可以处理各
种常用的特殊键。下一节,我们将涉及 choice 的另一个应用需求,那就是默认按键的
优化。
------------------------------------------------------------------------------

编后语:

  可能对于很多人来说,本节是比较枯燥的一节,因为它的文字量远远大于代码量,
以至于让很多兄弟少了很多实践锻炼的素材,也许将 Remap2 贴上是个办法,但我并不
愿意如此做。下一节,这种情况就会完全反过来了,请大家也有所准备。

  另外,本节的内容很久前就筹划好了,大概是在回复了“联合 DOS论坛”的一位站
友关于 choice.com 接受回车键的问题帖之后。只是后来觉得这种方法有很多的缺陷,
我们必须预先配置命令行环境,然后才能在这个环境中运行相应的程序;而对于 XP 等
NT类环境来说,这种配置无法全局有效,对 config.nt的修改只能影响 command.com,
而无法影响直接点击运行的批处理程序,因为它们默认是启用 cmd作为命令解释器的。
直到不久前发现 XP 中也可以借助 .PIF 文件进行启动配置,我才下定决心将此节继续
完成并公布于众。

  因为花了很多时间编写并测试一些代码实例,以及其他一些无关的工作,所以本节
出得稍微晚了一些。如果没有意外,第三节可能会快一些,因为我将不会做太多的文字
说明。
==============================================================================

===========================
三部曲之三:借花献佛
Will Sort - 2005/05/03
Updated:2005/05/15
===========================

==============================================================================

  在上一节中,我们基本完成了 choice 接受任意按键的任务,现在我们将目标定位
在 choice 的其他功能扩展上。而探讨的焦点就在 choice 的缺省按键功能上。

  当我们用 /t 开关指定了缺省按键后,经常会有一个问题,那就是一旦按下了某个
候选字符未指定的按键,就会终止缺省按键的倒计时。这是程序固有的细节,我们很难
通过外部的办法来改变它,于是,我们所能使用的最佳方法就是从内部修改它。

  修改一个可执行程序,并不是一件十分神秘高深的事情,虽然它有可能是一件枯燥
乏味的事情:)但是只要选对合适的工具,这项工作也将会进行的顺利和简单。可执行程
序是一个二进制文件,在 Windows下有很多人选择 UltraEdit或者 WinHex 来修改它,
但是,在这个案例中,为了兼顾 MS-DOS 的使用者,同时也考虑到修改过程的自动化,
我决定使用 DOS平台下的程序调试器—— DEBUG.EXE。

  作为 DOS下的调试工具, DEBUG.EXE有着相当广泛的用途。一方面它是一个二进制
文件编辑器,另一方面它又是一个汇编与反汇编器,同时也担当着用户与底层硬件的交
互平台,特别是在早期的逆向工程领域中,它扮演着举足轻重的角色,因此有着“屠龙
宝刀”的诨号,我们在这里使用它来扩展 choice 的使用功能,只能说是牛刀小试。关
于它子命令的用法说明,可以在网络上找到许多相当优秀的教程文章。

  修改程序之前,我们首先需要熟悉程序的流程,尤其是修改处的细节流程。我们运
用 debug的 d/u/g/t/p/ 等命令可以了解到 choice.com 的流程大致如下:

------------------------------------------------------------------------------
   CS:0100  跳至0211程序区 jump 0211 program area
   CS:0103  数据区 date area
   CS:0211  检查系统版本号 Verify MS-DOS version is 4.0 or later
   CS:0236  获取大写字母表 Get Upper Case Table address/bias
   CS:0262  分析命令行 Parse command line
   CS:03C7  转换小写字母 convert choices and default to Upper Case
   CS:03E9  检查缺省选择 verify specified default is in Choices
   CS:0401  显示选择提示 Display prompt
   CS:0442  等待按键或倒计时结束 wait for timeout or keypress
   CS:0476  接受按键 get key
   CS:04A4  显示按键并设置错误码后退出 got key, set errorlevel, quit
------------------------------------------------------------------------------
 
  进而分析我们关心的倒计时终止部分:
------------------------------------------------------------------------------
   CS:0442  计时秒数若为0 则跳至0476等待按键,否则取当前秒数后继续
   CS:0451  如果有案件则跳至0476,否则继续
   CS:045D  如当前秒数变化,则计时秒数减一后继续,否则跳至0451循环
   CS:046B  若计时秒数为0 则模拟按下缺省键并跳至0485,否则跳至0451循环
   CS:0476  接收按键
   CS:0485  搜索按键是否候选,如果是跳至04A4,如果否跳至049C
   CS:049C  响铃后跳至0476(* 注意此处 *)
   CS:04A4  显示并设置按键后退出
------------------------------------------------------------------------------

  从上面的分析可以看出,问题的关键在于 049C--04A4 之间的错误按键处理段。在
此处,程序响铃(即输出字符 07 )后,跳到了 0476 继续接收按键,从此不再计时。
解决的办法很简单,只需要将这个跳转地址改为 0451/045D的任意一处即可。通过 u命
令反汇编,确定这个跳转命令的地址 04A2 ,这样我们只要通过 a 4a2将汇编代码 jmp
476 替换为 jmp 451即可。

  此外,我们还可以考虑其它的一些功能上的扩展。比如无效按键不能终止倒计时,
在用户需要谨慎选择的时候,就需要一个特有按键(比如 ESC)停止倒计时;又比如,
按下一个常用键(比如回车或者空格),可以直接选择任意设定的缺省按键。

  这些功能的实现都不是很复杂,但前提是操作者要有一定的汇编编程和程序调试经
验。当然,谁也不可能要求所有的 DOS用户都能熟悉甚至了解 DEBUG子命令和 8086 代
码集。所以对于大多数人,本文提供了更为简洁的操作方式——批处理。这个程序可以
在附件的 X3 文件夹下见到,名为 ModChc.bat ,点击它即可以自动地根据可以找到的
choice.com生成一个新的 choicex.com。这个新的程序也可以在附件中的同样位置直接
找到,它业已实现了我在上面所提到的三项扩展。

  关于 xchoice.com更详细准确的描述如下:
------------------------------------------------------------------------------
  1.如果指定缺省选择键,键入无效选择键不会终止取缺省选择时的倒计时
  2.如果指定缺省选择键,可通过键入回车或空格键终止计时并立即返回缺省选择键
  3.如果指定缺省选择键,可通过键入跳出键终止计时并等待至键入有效选择键
  4.指定缺省选择键时,设置等待时间为 0,可实现仅在按回车或空格时取缺省选择
   键而不会限时。如:choice /c:qwert /t:w,0
  5.当指定跳出、回车或者空格键为选择键时,其终止计时的功能将相应失效
------------------------------------------------------------------------------

  如果程序附件的链接失效,或者你无法得到附件,那么可以根据下文提供的修改脚
本自己用 debug修改 choice.com ,命令格式如下:

  debug choice.com < choicex.asd > nul

  注意:在修改前请自己备份原程序
------------------------------------------------------------------------------
:: ChoiceX.asd - Choice 扩展 DEBUG 汇编脚本
:: Will Sort - 14:48 2005-5-15 - Debug
:: Modifition:
::   1.Not terminates timeout when press invalid choice key
::   2.press ESC to terminate timeout
::   3.press CR or SPACE to choose default choice key
a 047D
JNZ     0482        ; Call 0A80 when press control key
CALL    0A80        ; get second byte of scancode of control key
CALL    0A85        ; process event of press ESC, CR, SPACE

a 04A2
JMP     0451        ; Not terminate timeout when press invalid choice key       

a 0A80
MOV     AH,08       ; get char again
INT     21
RET
CMP     AL,1B       ; if press ESC
JZ      0A94        ; YES, terminate timeout
CMP     AL,0D       ; if press CR
JZ      0A91        ; Yes, goto set default choice
CMP     AL,20       ; if press SPACE
JNZ     0A99        ; No, goto return
MOV     AL,[018A]   ; set default choice
MOV     BYTE PTR [0189],00    ; set timeout is zero
RET

n ChoiceX.com
w
q
:: Please reserved this line.
------------------------------------------------------------------------------

编后语:

  第三部曲的准备,其实很早前就开始了,只是中途不耐于繁琐的文档整理而罢手,
直到这次执笔编写应用扩展三部曲,才又将它从故纸堆里翻了出来,花了几天时间重新
整理文档,又删除了一些复杂而不实用的修改(比如用十六进制的 ASCII编码指定候选
按键),简化了许多代码,终于成了现在的模样。

  至此,《 choice.com 应用扩展三部曲》已全部结束。回顾近一个月的编写历程,
仍不免留有一些遗憾,但已基本达成我的初衷,应该可以给自己有所交待了。希望它能
够对大家有所启发和帮助,也希望大家能对它提出自己宝贵的意见和建议,联系地址可
通过帖子上方的邮件功能找到。

  最后,在此真诚地祝愿所有的读者健康、快乐!
==============================================================================

本文地址:http://com.8s8s.com/it/it31432.htm