(********************************************************************) (* UNIT DRIVExx *) (* *) (* Version 1.01 *) (* *) (* Copyright (C) 1991 by NativSoft Computing *) (* *) (* 1155 College Ave *) (* Adrian, MI 49221 *) (* (517) 265-6080 *) (* CIS [71160,1045] *) (* *) (* Written by: Charles B. Little, Ph.D. *) (* Version: 1.01 *) (* Revision Date: 1 November 1991 *) (* Language: Turbo Pascal v6 *) (* *) (* ALL RIGHTS RESERVED *) (* *) (********************************************************************) INTRODUCTION: DRIVExx is a collection of tools, written in Turbo Pascal v6, for dealing with block devices in a DOS environment. To the best of our knowledge, no such tools exist in any "professional" programming toolboxes, in any programming language. The tools available here will allow any Pascal programmer who uses disk I/O to gain that extra measure of control over his or her application. At the very least they will have the ability to prevent access of a phantom drive so their nice user interface won't get overwritten by non-interceptable DOS messages like "Insert a disk..." While it may not be the last word on the subject, DRIVExx works. And just about everything you'd want your applications to know about drives is available WITHOUT ACCESSING THEM! The only exceptions to hitting drives are functions DiskWasChanged and GETDPB: DiskWasChanged will hit a drive ONLY if the drive supports changeline detection, and GETDPB will hit the disk only if you tell it to. Naturally, it is perfectly safe for GETDPB to hit a removable drive that does not have a disk in it. Much of the required information is available only in undocumented DOS functions and data structures. Wherever a documented function will return the same information, however, the undocumented function is still used as a redundancy check. Indeed, the source code for DRIVExx is probably at least 50% longer than need be due to extensive interlocking redundancy checking. All of the features of DRIVExx are demonstrated in the demo program DRVDEMO.PAS. The only exception to this is function MakeActive, whose description below should make clear why it is impractical to include it in DRVDEMO. CONDITIONS: DRIVExx.TPU is not free. IT IS SHAREWARE AND IT IS COPYRIGHTED. If you use it you are expected to pay for it -- that's how shareware works. However, you only pay the registration fee. Absolutely no royalties are expected or will be sought if you use it in any commercial program. It comes with the usual caveats (spelled out in the REGISTRATION/ORDER FORM) about suitability, limits of liability, etc. The registration fee as of 1 November 1991 is $15. Commented source code may be made available if demand is great enough, but fees and distribution agreements have not yet been established. Fees for DRIVExx should be accompanied by a completed REGISTRATION/ORDER FORM, found on the last page of this file. Since DRIVExx has not been tested it on every conceivable platform and under every flavor of DOS out there, special consideration (i.e., fee waiver or free upgrades) will be given to any user who reports bugs or anomalous behavior, and can suggest a fix. If you like it and find it useful you may distribute DRIVExx freely as long as all files in the original LZH file are distributed. Those files are: DRIVExx.TXT (this file) DRIVExx.TPU size = 20528, date = 11/1/91, time = 6:00pm DRVDEMO.PAS size = 10034, date = 11/1/91, time = 6:00pm DRVDEMO.EXE size = 28192, date = 11/1/91, time = 6:00pm Phone support and (cheap) upgrades will be available to registered users ONLY. REQUIREMENTS: DOS version: MS-DOS 3.0 to 5.0 Hardware: XT, AT, or PS/2 compatible PC. ACKNOWLEDGEMENTS: MS-DOS is a registered trademark of the Microsoft Corporation PC Tools Deluxe is a registered trademark of Central Point Software NCACHE is a registered trademark of Symantec Corporation Stacker is a registered trademark of Stac Electronics DESCRIPTION OF TYPES AND GLOBAL VARIABLES Only one variable type is defined in the Interface section of DRIVExx. "FakeDPB" is so named because it does not conform to the real DPB structure as defined under any single version of DOS. It nevertheless contains most useful information found in the actual DPB after DOS 3.0. TYPE FakeDPB = record ddunitnum : byte; Unit number within device driver bytespersex : word; Bytes/sector sexperclust : byte; Sectors/cluster numFATS : byte; Number of FAT copies on the disk RootdirEnts : word; Number of root directory entries FirstDataSec : word; First data sector on the disk numclusts : word; Number of data clusters on the disk sexperFAT : word; Sectors in each FAT mediabyte : byte; Media descriptor byte accessflag : byte; 0 if drive was accessed since bootup RootdirSex : word; Number of sectors in root directory end; Global Variables: DOSVER : real The currently running DOS version, expressed as a real number. This information is critical to the proper operation of DRIVExx, since many internal DOS data structures vary from one DDOS version to another. You should be aware that some DOS versions do not return a value to this function equal to the product's version number. For example, under MS DOS version 5 this function will return DOSVER = 5.00, but under DR DOS version 5 it will return DOSVER = 3.30! These *known* situations with non-MS versions are dealt with, but full compatibility is assured ONLY under MS-DOS or IBM's PC-DOS. Of course, even MS-DOS prior to version 5 can fool DRIVExx into a fatal error because Microsoft did not provide a function in these versions that "sees through" fake version numbers set by SETVER! DRIVExx assumes that DOSVER returns the TRUE version number; it can be ASSURED of finding the correct DOS version only with MS-DOS 5. DRDOS : boolean DRIVExx uses an undocumented (but DR-approved) function call to determine SPECIFICALLY whether DR DOS is running. This is important because DR DOS 5 is really a clone of MS DOS 3.3, with some internal data structures modified to accommodate volumes larger than 32M (ala Compaq DOS 3.31). Thus, programs running under DR DOS 5 can't count on internal (undocumented) data structures to be the same as in any SINGLE version of MS DOS. We also know that ASSIGN and SUBST are not reported the way they are in MS DOS, and suspect that other situations of "aliased" drives may be ambiguously reported under this OS. Therefore, when running under DR DOS we recommend calling the generic DriveIsAliased function and not bothering to try to distinguish between SUBST, ASSIGN, JOIN, NETWORK and IFS (installable file system). See function "DriveIsAliased" in the next section. DriveError : longint A global error code that is the sum of one or more error codes described below. A value of zero means no errors were detected during execution of the main procedure, UpdateDrives. Only three errors will cause immediate exit from UpdateDrives: Error 00001 - wrong DOS version (or OS/2), Error 00002 - not enough memory to create the variable DRIVES^, and Error 00004 - failure to find the address of DOS's List-of-Lists. All of these errors will result in all booleans set to false and all strings set to null, because they make it impossible to continue processing UpdateDrives. Less serious errors arise mostly as a result of redundancy checking on undocumented DOS functions and data structures, thus execution may continue and multiple errors can be reported. It would then be up to the programmer or user to determine how to proceed. In general it would not be wise to allow a program to proceed if DriveError were non-zero. The procedure ShowDriveError described at the end of the next section will interpret the individual error codes summed in DriveError. ANY NON-ZERO DRIVEERROR SHOULD BE REPORTED TO NATIVSOFT IMMEDIATELY! InternalFloppies : byte The number of BIOS-driven floppy drives, deduced from the equipment list word at $0000:$0410. NumBlockDevs : byte The number of block devices, most accurately described as the number of Drive Parameter Blocks set up during the processing of the CONFIG.SYS file. This is NOT ALWAYS the same as NumLogicalDrives since a logical drive can be "created" using SUBST or JOIN but won't necessarily have a DPB associated with it. Every block device also has a device driver associated with it, whose address can be found in the drive's DPB. DevDrvrChainValid : boolean; Some software, notably Norton's NCACHE, seems to relocate device drivers for all system disk drives. As a consequence, function DriveIsSwapped and variable BootableDrives are unreliable when NCACHE is loaded. However, DriveIsSwapped and BootableDrives are both reliable if the variable DevDrvrChainValid is TRUE. This is a problem that will be addressed in a future release of DRIVExx -- as soon as non-disclosure agreements are signed with Symantec. NumLogicaldrives : byte A short explanation of this variable is that NumLogicalDrives is simply the length of the string AllLogicalDrives, described below. AllLogicaldrives : string[26] Logical drives are those associated with real drives, virtual drives, phantom drives (including phantom drives created with DRIVER.SYS), and "aliased" drives created by SUBST or JOIN. For example, a two floppy system with a hard disk partitioned into C: and D: and a RAMdisk set up as E: would have 5 logical drives, A: thru E:, and AllLogicalDrives would be the string 'ABCDE'. Having the statement LASTDRIVE=Y in your CONFIG.SYS file and executing SUBST P: C:\DOS would give you 6 logical drives, A: thru E: AND P:, and thus AllLogicalDrives would have the value 'ABCDEP'. Note that P: is a "logical" drive, so NumLogicalDrives is 6 (A: thru E:, and P:), but it does not have a DPB associated with it so NumBlockDevs is 5 (A: thru E: only). BootableDrives : string[26] Driveletters of BIOS-driven drives, minus any phantom or aliased drives. The purpose in defining this variable was to compile a list of drives available AT THE TIME UPDATEDRIVES IS RUN from which one might be able to boot the system. This would exclude any device driven drive installed in CONFIG.SYS, but would include floppy disk B: if a REAL floppy drive exists, as well as hard disk partitions other than C:. It would also exclude the second of two physical hard disks if two were installed. There are circumstances under which this variable will be a null string. See "DevDrvrChainValid" above. Floppies, Hards : string[26] Strings of uppercase driveletters representing the drive types indicated. BiosDateString : string[8] The date of the system BIOS, in a non-zero-padded format: mm/dd/yy. Example 3/3/89 (not 03/03/89). DevDrvrChainValid : boolean; This variable will almost ALWAYS be TRUE, in which case you needn't pay attention to it. When this variable is FALSE, however, it just means that some program has been messing around with the addresses of the block device drivers. In this case "BootableDrives" should be a null string, and the function DriveIsSwapped should be ignored since its returned value will be meaningless. What programs are capable of this? The only one (now) know to do this is Norton's NCACHE. DESCRIPTION OF PROCEDURES AND FUNCTIONS This section describes all procedures and functions declared in the INTERFACE of unit DRIVExx. All functions below that require a character argument will accept either uppercase or lowercase driveletters; characters other than letters of the alphabet will ALWAYS cause boolean functions to return FALSE since each of these functions first calls DrivExists, which of course will return FALSE in such cases. PROCEDURE UpdateDrives Creates or updates the DRIVES^ array. This "dynamic" array is allocated on the heap and consists of one element for each valid drive (a total of NumLogicalDrives elements). Because the dynamic array technique requires range checking to be OFF, it's especially important not to use an invalid drive letter as an array index, as this may send your program into outer space. This is easily accomplished by not allowing direct access to DRIVES^ by keeping it in the IMPLEMENTATION section! All information in this array is accessible only through the functions described below. This is about as close to OOP as I ever care to get, thank you very much! UpdateDrives is called in the unit initialization, and need not be called directly unless your program shells out to DOS. It should be called upon return to your main program to guard against a situation in which a user might have SUBSTed or JOINed a drive, or accessed a phantom drive, while shelled out. FUNCTION DrivExists(drv:char) : boolean Self explanatory. Returns FALSE if the drive doesn't exist. If you type Q: at the DOS prompt and get the message "invalid drive specification", you'll also get FALSE with DrivExists(Q). Conversely, if you don't get "invalid drive specification" then DrivExists will return TRUE. Simple. FUNCTION DriveisNormal(drv:char) : boolean This function will return FALSE in only a few situations: 1) Returns FALSE for phantom, aliased and NONDOS drives. It is expedient for many applications to DEFINE these types of drives as non-normal. 2) Returns FALSE for any drive that does not support Interrupt 21 Function $4408 calls. This is somewhat arbitrary, but in my experience the only block devices that do not support $4408 calls are NONDOS drives (see next function below). 3) Returns FALSE if, at any time during the execution of UpdateDrives, a DOS or BIOS function call returns "invalid function" for a drive that is supposed to support that function. In this case you will almost certainly see a non-zero value for DriveError that will diagnose the problem. FUNCTION DriveisNONDOS(drv:char) : boolean NONDOS drives are somewhat rare in my experience. However, if you use a PC running IBM AS/400 or System38 emulation, you might have virtual drives created for "PC-Support". These are the only examples of NONDOS drives with which I am familiar. Just trying to access such a drive is dangerous, and trying to run DOS's CHKDSK on one will usually get you a "Memory allocation error. System halted" message. While there's not much we can do to teach IBM to write decent block device drivers, DRIVExx should make their lousy code easier to live with by avoiding it altogether. FUNCTION DriveisRemovable(drv:char) : boolean Self explanatory. Again, we have yet to see it incorrectly classify a drive. The TYPE of removable drive can be found with function RemovableDriveType described below. FUNCTION RemovableDrivetype(drv:char) : byte Returns: -3 : Int $13 function $08 failed -2 : Int $13 function $08 returned value not in range 0-4 -1 : Int $13 function $08 returned 0 for unknown reason 0 : type can't be determined (no error) or drive is NOT removable 1 : 5.25" 360K 2 : 5.25" 1.2M 3 : 3.5" 720K 4 : 3.5" 1.44M 5 : 3.5" 2.88M 6 : TAPE 7 : Bernoulli FUNCTION DriveisPhantom(drv:char) : boolean This function is somewhat problematic for DOS versions PRIOR to 3.2, in that "phantomness" in these environments has to be decided primarily based on the value of the byte at $0000:$0504. In one- floppy systems this byte is 0 if the single floppy was most recently accessed as A:, or 1 if it was most recently accessed as B:. However, some relatively ill-behaved programs such as PC Tools Deluxe version 6 will change this memory location to $FF in certain situations, rendering this phantom-finding method ineffective. Since DOS 3.2 phantom-finding has been safe and straightforward, so only well- behaved and documented functions are used when running under these newer operating systems. PROCEDURE MakeDriveActive(drv:char) Whenever a phantom drive exists -- whether it's drive B: in a one-floppy system, or one created by DRIVER.SYS -- this procedure will make it active so you don't see the obnoxious "Insert diskette... " message when you try to access it. This message is NOT interceptable; the only way to avoid it is to avoid trying to access a phantom drive. This procedure does that, AND ALSO CALLS UpdateDrives WHEN IT'S FINISHED!! FUNCTION DriveMappedTo(drv:char) : char If drv is a removable drive and is currently classified as phantom, this function will return the uppercase driveletter of the physical drive to which the phantom is currently mapped. FUNCTION DriveisSubsted(drv:char) : boolean FUNCTION DriveisJoined(drv:char) : boolean FUNCTION DriveisAssigned(drv:char) : boolean FUNCTION DriveisNetwork(drv:char) : boolean FUNCTION DriveisIFS(drv:char) : boolean FUNCTION DriveisAliased(drv:char) : boolean These are fairly self-explanatory. The last function in this group, DriveIsAliased, is a shortcut that returns true if a drive is SUBSTed, JOINed, ASSIGNed, NETWORK or IFS (installable file system). These functions have been tested extensively for SUBSTed, JOINed and ASSIGNed drives, but not for NETWORK or IFS drives as I have no access to such drives. If DriveIsAliased returns true for a drive, there is little RELIABLE information that can be obtained for it other than whether it's device driven or swapped, as DRVDEMO shows. Under DR DOS, only DriveIsAliased should be called, since the other "aliased" functions may not operate correctly. We KNOW this to be the case for ASSIGN and SUBST, but have not checked it out for NETWORK and IFS drives. FUNCTION DriveisDeviceDriven(drv:char) : boolean Any drive that's run by a device driver installed in the CONFIG.SYS file will return TRUE here. This is true for RAM disks as well as for REAL physical drives, like some 1.2M external 5.25" drives on IBM PS/2 machines. FUNCTION DriveisSwapped(drv:char) : boolean Useful for systems that have a real drive swapped with a device-driven drive (such as STACKER volumes), especially if one of the swapped drives is your bootable hard disk partition. See variable "DevDrvrChainValid" above for limitations. FUNCTION DriveisHard(drv:char) : boolean FUNCTION DriveisRAMDisk(drv:char) : boolean FUNCTION DriveisOtherfixed(drv:char) : boolean These are the only categories of non-removable drives; the functions are also self explanatory. We have yet to see a non-removable drive that couldn't be classified as either hard or RAM disk, but function DriveisOtherfixed is included to take care of this possibility. Instead of having three BOOLEAN functions for the three drive types, we COULD have defined a single BYTE function, NONRemovableType that returns 1 for hard disks, 2 for RAM disks and 3 for "other". In my own applications, though, I find it more convenient to have a single, direct test for a hard disk. FUNCTION ChangeLineSupported(drv:char) : boolean This should return true ONLY for removable drives, and then only if the device driver for the drive says it supports change line detection. FUNCTION DiskWasChanged(drv:char) : boolean This functions hits the disk only if there's support for changeline detection. And it makes no difference whether there's a disk in the drive -- no errors are generated if there isn't a disk in the drive. FUNCTION DriveSize(drv:char) : longint This function returns: -1 : if there's an error calculating disk size from DPB data 0 : usually only for a removable drive that hasn't been accessed, and so has no valid DPB data to use > 0 : disk size IN BYTES; if the disk is REMOVABLE, DriveSize returns the size of last disk accessed in drive FUNCTION CurrentDir(drv:char) : pathstr Returns the currently logged directory for the specified drive as a complete path, including driveletter, colon and final backslash, all in uppercase letters. If the drive doesn't exist a null string is returned. FUNCTION DefaultDrive : char Identifies the current default drive as an uppercase letter. FUNCTION GETDPB(drv:char; var d:fakeDPB; hit:boolean) : boolean; Puts useful drive data from DPB into d. If HIT is TRUE, the function will hit the disk to update the DPB data in memory. Otherwise it will return data already in memory, usually for the last disk that was accessed in the drive. GETDPB returns FALSE if problems were encountered (drv is an invalid drive, no disk in drive when HIT is set to TRUE, etc.) and all fields of d will be zeroed out. PROCEDURE ShowDriveError; Shows and interprets errors using global DriveError variable. PRODUCT SUPPORT Support is available TO REGISTERED USERS ONLY at (517)-265-6080 after 5pm Eastern (weekdays), or by U.S. Mail. Bug reports and/or suggestions for improvement are always welcome from anyone. However, no collect calls can be accepted. REGISTRATION/ORDER FORM COMPANY NAME: _______________________________________________________ ADDRESS: _______________________________________________________ _______________________________________________________ _______________________________________________________ WARRANTY DISCLAIMER: THIS PROGRAM IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OR MERCHANTABILITY AND FITNESS FOR A PARTICULAR REASON. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH THE CUSTOMER. THE CUSTOMER SHALL ASSUME THE ENTIRE COST OF SERVICING, REPAIR, AND CORRECTION. IN NO EVENT SHALL NativSoft BE LIABLE TO THE CUSTOMER FOR ANY DAMAGES, INCLUDING LOST PROFITS, LOST SAVINGS, OR OTHER INCIDENTAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF THE USE OR INABILITY TO USE SUCH PROGRAM, EVEN IF NativSoft OR AN AUTHORIZED REPRESENTATIVE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES, OR FOR ANY CLAIM BY ANOTHER PARTY. I HAVE READ THE ABOVE AGREEMENT AND BY MY SIGNATURE BELOW ACCEPT ITS TERMS. NAME: _______________________ SIGNATURE: ________________________ _____ Copies of DRIVExx.TPU @ $15.00 ea : $_______ (Michigan Residents add 4% sales tax) : $_______ TOTAL : $_______ Payment by check or money order in U.S. Dollars only. Make check or money order payable to NativSoft Computing. Mail this completed REGISTRATION form with payment to: NativSoft Computing 1155 College Ave. Adrian, MI 49221