home *** CD-ROM | disk | FTP | other *** search
/ Monster Media 1994 #1 / monster.zip / monster / PROG_BAS / PBFIXES2.ZIP / README.TXT < prev    next >
Text File  |  1994-01-11  |  10KB  |  194 lines

  1.      How to handle Control-Break and Control-C in a PowerBASIC program 
  2.  
  3.                               By Ray Crumrine                         
  4.  
  5.  
  6.  
  7.      Control-C and Control-Break are strange beasts.  PowerBASIC 
  8.      allows you to specify $OPTION CNTLBREAK OFF in your program to 
  9.      disable Control-Break checking.  A piece of literature I read 
  10.      somewhere about QuickBASIC said "pressing Control-C never 
  11.      interrupts a BASIC program".  Neither statement is completely 
  12.      true. 
  13.  
  14.      If you disable Control-Break checking with $OPTION CNTLBREAK OFF, 
  15.      the resulting compiled program will not be stopped when the user 
  16.      presses Control-Break but when your program ends, DOS will 
  17.      display ^C and newline after the DOS prompt is displayed.  The 
  18.      DOS programmers reference, 2nd edition states this is because 
  19.      pressing Control-Break "forces a Ctrl-C character into the 
  20.      keyboard buffer that DOS maintains (separate from the one 
  21.      maintained by BIOS). DOS then discovers that Ctrl-C the next time 
  22.      it checks for a character" (after your program exits, the first 
  23.      time the DOS prompt is displayed).  While this is only cosmetic 
  24.      and really does no harm, it makes your program look like it has a 
  25.      bug in it. 
  26.  
  27.      The problem with Control-C is a more subtle, but insidious one.  
  28.      The real fault here may lie with PowerBASIC, but I am not sure.  
  29.      DOS allows its operation to be aborted at times if the user 
  30.      presses Ctrl-C.  This may be OK or even desirable when one is 
  31.      working at the DOS prompt, but since most work done with a PC is 
  32.      done while using an external program (such as yours) it is 
  33.      imperative that you not allow DOS to shut down your program.  
  34.      This is because when DOS shuts down, interrupt vectors "hooked" 
  35.      by PowerBASIC will be left "hanging" and the computer will crash 
  36.      completely very soon thereafter. 
  37.  
  38.      Did you ever look at your DOS manual and see a reference to the 
  39.      BREAK command?  It is a little used command that nobody pays much 
  40.      attention to.  Simply put, if BREAK=OFF then DOS only checks to 
  41.      see if Ctrl-C has been pressed when it writes to the screen or 
  42.      when it is waiting for input from the keyboard.  If BREAK=ON then 
  43.      DOS checks to see if the user has pressed Ctrl-C ALL of the time.  
  44.      BREAK defaults to OFF unless you have the BREAK=ON command in 
  45.      either your CONFIG.SYS file or AUTOEXEC.BAT file.  It can also be 
  46.      turned off/on by any program that runs on your PC.  
  47.  
  48.      Having said all that, let me explain the two problems with Ctrl-C 
  49.      that can affect your PowerBASIC programs.  The first is simple.  
  50.      If BREAK=ON then your PowerBASIC program will display ^C and 
  51.      unceremoniously crash if the user presses Ctrl-C while reading 
  52.      from or writing to the disk. 
  53.  
  54.  
  55.      The second problem occurs when your program wants to use DOS for 
  56.      console output.  You might want to do this to access the ANSI.SYS 
  57.      driver.  You use the statement OPEN "CONS:" FOR OUTPUT AS #1 (or 
  58.      whatever file number you want to use) to do this.  Then when you 
  59.      want to print to the screen instead of using PRINT "Hello" you 
  60.      use PRINT #1, "Hello".  PowerBASIC sends the string "Hello" to 
  61.      DOS and DOS does the actual printing to the screen.  If the user 
  62.      presses Ctrl-C while DOS is writing to the screen, again you get 
  63.      ^C displayed on the screen and your program crashes.  It does not 
  64.      matter whether BREAK=ON or BREAK=OFF in this case. 
  65.  
  66.      There appear to me to be two ways to prevent these errors. The 
  67.      first would be to write your own Int 9 (keyboard) interrupt 
  68.      handler and prevent DOS from ever finding out Ctrl-Break or Ctrl-
  69.      C was pressed.  I suspect this is the method used by word 
  70.      processors and other programs that need complete control of the 
  71.      keyboard, but it is not easily accomplished and is probably 
  72.      overkill for most programs.  The second method involves either 
  73.      "hooking" the Ctrl-C and Ctrl-Break interrupts so that code of 
  74.      your own design runs when these two interrupts are called, or 
  75.      "patching" the DOS interrupt code with an Iret instruction so 
  76.      that when Int 1Bh or Int 23h is called, nothing happens.  This is 
  77.      the method I chose.  Your program can still detect if the user 
  78.      pressed either Ctrl-C or Ctrl-Break by using the INKEY$ function 
  79.      provided by PowerBASIC.  INKEY$ will return CHR$(0,0) if Ctrl-
  80.      Break was pressed and CHR$(3) if Ctrl-C was pressed. 
  81.  
  82.      There are two FUNCTIONS and one SUB in the file CTRLC.BAS.
  83.  
  84.      ----------
  85.      FUNCTION CbrkDisable% 
  86.      
  87.      This function requires no arguments and returns an integer 
  88.      result.  This should be one of the first executable statements in 
  89.      your program if not THE first.  The first thing it does is ask 
  90.      DOS for the address of the current Ctrl-C handler.  Then it saves 
  91.      the FIRST byte found at that address, and replaces it with an 
  92.      Iret instruction.  This prevents DOS from taking control and 
  93.      crashing our program if the user presses Ctrl-C. 
  94.  
  95.      Next we repeat the process for the Ctrl-Break vector.  Doing this 
  96.      prevents the ^C that would otherwise be displayed after our 
  97.      program ends, if the user presses Ctrl-Break.
  98.  
  99.  
  100.      There are just three items left to be done.  If the user presses 
  101.      Ctrl-C, DOS can STILL trash your screen by displaying ^C on the 
  102.      screen wherever the cursor is currently positioned.  There is NO 
  103.      way to prevent this, but we can minimize the likelihood that it 
  104.      will happen by setting the DOS BREAK flag OFF.  You may remember 
  105.      that when BREAK=OFF DOS only checks for Ctrl-C during screen 
  106.      output and keyboard input.  Since PowerBASIC does not use DOS for 
  107.      keyboard input, it can never trash our screen UNLESS we use
  108.      OPEN "CONS:" FOR OUTPUT AS #1 as described above which implies 
  109.      that we WANT to give give DOS a chance to intercept Ctrl-C.  So 
  110.      the third item of business for CbrkDisable is to ask DOS what the 
  111.      current state of the BREAK flag is and save it so we can restore 
  112.      it when our program is done, and then we set BREAK=OFF using a 
  113.      DOS call. 
  114.  
  115.      The next item of business is to ask DOS for the address of the 
  116.      current Critical error handler.  We save the segment address in 
  117.      the SHARED variable CEHSeg?? for use by the ClrErDev routine 
  118.      (described below).  
  119.  
  120.      The last thing CbrkDisable does is call the PowerBASIC routine 
  121.      SetOnExit and pass it the address of our routine CbrkRestore.  
  122.      PowerBASIC can be instructed to call as many as eight separate 
  123.      user procedures just before the program is exited.  By having 
  124.      SetOnExit call CbrkRestore for us, we ensure that DOS will be 
  125.      restored to its original state even if PowerBASIC shuts down 
  126.      prematurely due to an unexpected error.  The procedure is added 
  127.      to the list, and a true/false integer value is returned in ax to 
  128.      reflect the success of the operation.  A false value indicates 
  129.      that eight procedures have already been defined.  The value in ax 
  130.      is returned by CbrkDisable using the result% integer as an 
  131.      intermediary.  Note that PowerBASIC allows you to CALL a FUNCTION 
  132.      just like a SUB if you are not interested in the return value.  
  133.      This is true of CbrkDisable. 
  134.  
  135.      ----------
  136.      SUB CbrkRestore 
  137.      
  138.      This is a PRIVATE routine known only in this module.  We can do 
  139.      this because this sub will only be called by SetOnExit, and 
  140.      PowerBASIC accesses it by address.  By making it private it can 
  141.      co-exist with another routine with the same name without any 
  142.      conflict. 
  143.  
  144.      This routine undoes the changes that CbrkDisable made during 
  145.      startup of the program by in effect POKEing the two bytes back 
  146.      into the Int 1Bh and Int 23h interrupt routines and restoring the 
  147.      DOS BREAK flag to its original state, thus restoring DOS to its 
  148.      original state completely. 
  149.  
  150.  
  151.      ----------
  152.      FUNCTION CritErr%() 
  153.      
  154.      This function gives you a "hook" into the PowerBASIC V3.0c critical error 
  155.      handling system.  PowerBASICs ERDEV function can be used after CALL 
  156.      INTERRUPT to determine if a DOS critical error occurred.  However, there 
  157.      are a couple of problems with the way ERDEV works.  The most significant 
  158.      is the fact that PowerBASIC does not provide a way to clear ERDEV once an 
  159.      error has occurred.  The second one is mostly a matter of personal 
  160.      preference, and that is the error codes returned by ERDEV are not 
  161.      converted to match the error numbers that are returned by DOS Int 21h, 
  162.      function 59h (Get extended error).  
  163.      
  164.      Function CritErr% does three things: 
  165.      1: Returns logical TRUE (-1) if a critical error occurred during the DOS 
  166.         call or FALSE (0) if there was none. 
  167.      2: If there was an error, I add 12h to the flag value and store the 
  168.         number back into PowerBASICs ERDEV variable location.  This allows you 
  169.         to simply use ERDEV to retrieve the error code.  Making the error 
  170.         value match Int 21h function 59h makes for cleaner error handling in 
  171.         your program and easier generation of error messages.  
  172.      3: Lastly, if there was an error I clear the PowerBASIC critical error 
  173.         flag for the next CALL INTERRUPT.
  174.  
  175.      CbrkDisable MUST be called before CritErr can be used, otherwise the 
  176.      computer will probably CRASH!  Here is the syntax for using CritErr% 
  177.  
  178.      DECLARE FUNCTION CbrkDisable%()
  179.      DECLARE FUNCTION CritErr%()
  180.      
  181.      'Call CbrkDisable FIRST
  182.      success% = CbrkDisable%     'If you want to know if SetOnExit worked
  183.            or                        
  184.      CALL CbrkDisable            'If you're sure it did
  185.  
  186.      CALL INTERRUPT &h21
  187.      IF CritErr% THEN
  188.        CriticalErrNum% = ERDEV
  189.      END IF 
  190.  
  191.      I can be reached on the PowerBASIC BBS or at
  192.      (217) 223-8767 evenings 
  193.      (217) 221-6194 business
  194.