B_ONEXIT Is Not Always Automatic in QB.EXE or QBX.EXE; Restart (71234)



The information in this article applies to:

  • Microsoft QuickBASIC 4.0
  • Microsoft QuickBASIC 4.0b
  • Microsoft QuickBASIC 4.5
  • Microsoft BASIC Compiler for MS-DOS and OS/2 6.0
  • Microsoft BASIC Compiler for MS-DOS and OS/2 6.0b
  • Microsoft Basic Professional Development System for MS-DOS 7.0
  • Microsoft Basic Professional Development System for MS-DOS 7.1

This article was previously published under Q71234

SYMPTOMS

B_ONEXIT is a system-level Basic function designed to allow non-Basic routines in your program to take special actions when the Basic program ends. B_ONEXIT works as expected at the termination of Basic executable .EXE programs.

Note that the Basic run-time routine B_ONEXIT is not automatically invoked when your program ends if you are running inside the QB.EXE or QBX.EXE environment. B_ONEXIT is only invoked by the environment if you restart your program. This is correct behavior because the environment needs to maintain the final state of the program in case you want to view variable values or use the History debugging features.

However, after running a program, the QB.EXE or QBX.EXE environment fails to invoke B_ONEXIT when you perform an Open or New command from the File menu.

STATUS

Microsoft has confirmed this to be a bug in QB.EXE in Microsoft QuickBasic versions 4.00, 4.00b, and 4.50; in the QB.EXE environment that comes with Microsoft Basic Compiler versions 6.00 and 6.00b for MS-DOS (buglist6.00, buglist6.00b); and in the QBX.EXE (QuickBasic Extended) environment that comes with Microsoft Basic Professional Development System (PDS) versions 7.00 and 7.10 for MS-DOS (buglist7.00, buglist7.10). We are researching this problem and will post new information here in the Microsoft Knowledge Base as it becomes available.

MORE INFORMATION

In the QuickBasic environment, your program does not actually terminate by the END or SYSTEM statements, but instead, terminates when you perform an operation that requires QuickBasic's "binding" process to be invoked. Operations, such as major changes to the code (where QuickBasic prompts you with an optional restart message) or loading a new program, cause the old program to terminate, and then QuickBasic binds the new program.

The QuickBasic environment fails to call B_ONEXIT when you choose operations from the File menu (such as Open or New). As a workaround to ensure that B_ONEXIT is called consistently, you need to manually choose the Restart option from the Run menu immediately after the termination of your program in QB.EXE or QBX.EXE.

The following example uses an assembly routine to install a new keyboard interrupt vector and uses B_ONEXIT to restore the old keyboard interrupt when the program ends. When the following program is executed inside the QuickBasic environment and you perform a Load operation from the File menu after running the program, the program will terminate but B_ONEXIT will not be invoked, and the keyboard will be disabled. To correct the problem, you will either have to restart/reboot the computer, or use the mouse to select the Restart option from the Run menu, which will invoke B_ONEXIT and restore the keyboard interrupt.

This example shows a simple case of what could happen when B_ONEXIT is not called. The actual results will be unpredictable, depending on your code.

The following code samples were provided by a customer reporting the problem and are shown here as examples.

Code Example

Assembler Code (INT9.ASM)

 
     .model medium, basic

EXTRN     B_OnExit:FAR        ; in the QBX/BC run time

     .code

OldInt9        LABEL     DWORD     ; Old Keyboard interrupt vector
Old9Ofs        dw   0    ; handler address.
Old9Seg        dw   0

Int9Restore    PROC uses ds

; Auto-restore the vectors we may have changed. Registered with
; _onexit.

     cmp  Old9Seg, 0     ; did we get the vector
     jz   @f          ; No, lets jump to the return
     mov  ax, 2509H      ; otherwise, we restore the vector
     lds  dx, OldInt9
     INT  21H
     mov  Old9Seg, 0     ; and clear our old vector
@@:  ret

Int9Restore    ENDP

Int9Grabber    PROC

; Gets the original INT9 vector in case we change it during processing

     cmp  Old9Seg, 0          ; already have it?
     jnz  @f             ; yes, don't set it twice
                         ; and lets jump to the return
     mov  ax, 3509H
     INT  21H

     mov  Old9Seg, es         ; we are just going to chain
     mov  Old9Ofs, bx         ; the int9 through our own
                         ; handler.
     mov  ax, offset @code:Int9Restore
     push cs
     push ax
     call B_OnExit       ; register the restore routine
@@:  ret

Int9Grabber    ENDP
     END
				

Basic Code (Basic.BAS)

DEFINT A-Z
DECLARE SUB Int9Grabber ()
Int9Grabber ' Install Int9 handler, if not already installed
CLS
PRINT "Use the Open option on the File Menu"
PRINT "to lose all keyboard input."
END
				

Build Options

MASM INT9.ASM;
LINK /Q INT9.OBJ,,,BQLB40.LIB;
QB Basic.BAS /L INT9.QLB
				
BQLB40.LIB will be replaced by the appropriate Quick-library support module for the version of the product being used.

Modification Type:MinorLast Reviewed:8/16/2005
Keywords:KB71234