[comp.os.msdos.programmer] Identifying the max. number of drives.

rob@cs.mu.oz.au (Robert Wallen) (07/31/90)

I'm writing a program that displays a window showing a list of "A:,B:,
C: ..." for the user to select a drive from.  Problem is how do you work
out what drives are installed in a system.  I can can get the number of
floppies from the equipment list function.  If I ask DOS the max. drive
number I always get 5 drives.

I have been thinking of reading the partition map to see how many drives
are on 1 hard disk, but this seems like overkill and wouldn't work for
machines that have 2 physical hard disks.

It would also be possible to read a sector from each disk to see if the
disk was there but this would take ages.

What would you do to identify and networked volumes?

Seems like someone must have done this before.

Can anyone help?

Thanks in advance, Rob Wallen

feit@cs.odu.edu (Mark A. Feit) (08/01/90)

In article <3102@murtoa.cs.mu.oz.au> rob@cs.mu.oz.au (Robert Wallen) writes:

   I'm writing a program that displays a window showing a list of "A:,B:,
   C: ..." for the user to select a drive from.  Problem is how do you work
   out what drives are installed in a system.  I can can get the number of
   floppies from the equipment list function.  If I ask DOS the max. drive
   number I always get 5 drives.

The five-drives thing is just how the user has MAXDRIVE set...  We
have ours set to 'n', which will yield 16.  Obviously, this is no
good.

   I have been thinking of reading the partition map to see how many drives
   are on 1 hard disk, but this seems like overkill and wouldn't work for
   machines that have 2 physical hard disks.

That it is.

   It would also be possible to read a sector from each disk to see if the
   disk was there but this would take ages.
   What would you do to identify and networked volumes?

No need to do anything special, really...  IMHO, applications should
be able to use networked drives just like anything else, so long as
they're properly attached.  Takes one more headache out of support
when somebody calls up bawling "it won't work on my Novell system."

   Seems like someone must have done this before.
   Can anyone help?

Yep on both counts.  Try this on for size:

-------------8<-----Trim Here------------------

#define	DEBUGdrive_is_valid	0  /* 01 to show diags on stderr */

#define DR_INVALID		0
#define DR_VALID		1
#define DR_DONTREAD		2

/* drive_is_valid()
 *
 * Determine if a drive (named by 'drive') exists or not.  If it's A or B,
 * go through the BIOS to see if it physically exists (avoiding DOS's "Insert
 * dist for drive B" messages).  Otherwise, go through DOS.
 *
 * Return Value:	DR_INVALID (0) if the drive is not valid.
 *			DR_VALID if Valid.
 *			Result will be ORed with DR_DONTREAD is the drive
 *			 is not able to be read bust exists anyway.
 */
static int drive_is_valid( char drive )
{
	union REGS	regs;
	struct diskinfo_t	disk_tee;
	unsigned i;

	#if DEBUGdrive_is_valid
	fprintf( stderr, "drive_is_valid( %c )\n", drive );
	#endif

	drive = toupper( drive );

	/* Check for physical A: or B:, go through DOS for anything else. */
	if ( (drive == 'A') || (drive == 'B') ) {

		#if DEBUGdrive_is_valid
		fprintf( stderr, "drive_is_valid() Going through BIOS\n" );
		#endif

		disk_tee.drive    = toupper(drive) - 'A';
		disk_tee.head     = 1;
		disk_tee.track    = 1;
		disk_tee.sector   = 1;
		disk_tee.nsectors = 1;
		disk_tee.buffer   = NULL;

		i = _bios_disk( _DISK_VERIFY, &disk_tee ) >> 8;
		#if DEBUGdrive_is_valid
		fprintf( stderr, "drive_is_valid()  _bios_disk returned %04x\n", i );
		#endif

		/* If error or invalid, try again */
		switch ( i ) {

			/* Nada Problemo */
			case 0x00:
			case 0x06:		/* Disk's been changed, big deal */
				#if DEBUGdrive_is_valid
				fprintf( stderr, "drive_is_valid()  Drive OK (No error)\n" );
				#endif
				return DR_VALID;

			/* Errors where the drive is invalid */

			case 0x07:
			case 0x0C:
			case 0x20:
			case 0xBB:
			case 0xE0:
				#if DEBUGdrive_is_valid
				fprintf( stderr, "drive_is_valid() DRIVE IS INVALID.\n" );
				#endif
				return DR_INVALID;

			/* Everything else (Not ready, etc.) */
			default:
				{
				#if DEBUGdrive_is_valid
				fprintf( stderr, "drive_is_valid() Drive OK but not readable.\n" );
				#endif
				}
				return DR_VALID|DR_DONTREAD;
		}
	}

	#if DEBUGdrive_is_valid
	fprintf( stderr, "drive_is_valid() Checking through DOS\n" );
	#endif

	/* Check anything C: or greater by going through DOS */
	regs.h.ah = 0x36;		/* DOS Get diskfree */
	regs.h.dl = drive - '@';	/* 0=Default, 1=A, etc */
	int86( 0x21, &regs, &regs );	/* Call DOS */

	#if DEBUGdrive_is_valid
	fprintf( stderr, "drive_is_valid() Drive is %s\n",
		( regs.x.ax != 0xffff ? "VALID" : "INVALID" ) );
	#endif

	return ( regs.x.ax != 0xffff ? DR_VALID : DR_INVALID );
}


-------------8<-----Trim Here------------------

A little explanation is in order for this routine.  First, it goes
through the BIOS for A and B to see if the drives physically exist.  I
didn't want that "Insert disk for drive B, press ENTER" message to pop
up on the screen and mess things up, so that's how that works.  For
anything C: or greater (okay, so I didn't take having three floppies
into account, but that;s a rarity these days and this code will work
with it anyway), it goes through DOS to get the free space.  If it
fails for any reason other than being invalid (not ready, etc), the
routine will return DR_VALID ORed with DR_DONTREAD.

Hope this helps.

						- Mark

................................... ................................... 
: Mark A. Feit                     : feit@cs.odu.edu                  :
: Old Dominion University CS Dept. : feit@xanth.UUCP                  :
: Norfolk, Virginia, U.S.A., Earth : "So where's my lunch, anyway?"   :
................................... ................................... 
  Y
:)8 G-G-G-D-E-C
--

						- Mark

................................... ................................... 
: Mark A. Feit                     : feit@cs.odu.edu                  :
: Old Dominion University CS Dept. : feit@xanth.UUCP                  :
: Norfolk, Virginia, U.S.A., Earth : "So where's my lunch, anyway?"   :
................................... ................................... 
  Y
:)8 G-G-G-D-E-C

gchamber@nfsun.UUCP (Glenn Chambers) (08/01/90)

In article <FEIT.90Jul31140200@oswald.cs.odu.edu> feit@cs.odu.edu (Mark A. Feit) writes:
>A very nice bit of work to determine whether a drive exists or not.
>
>/* drive_is_valid()
> *
> * Determine if a drive (named by 'drive') exists or not.  If it's A or B,
> * go through the BIOS to see if it physically exists (avoiding DOS's "Insert
> * dist for drive B" messages).  Otherwise, go through DOS.
> *
> * Return Value:	DR_INVALID (0) if the drive is not valid.
> *			DR_VALID if Valid.
> *			Result will be ORed with DR_DONTREAD is the drive
> *			 is not able to be read bust exists anyway.
> */
>static int drive_is_valid( char drive )
>{
>  /* Code omitted for brevity. */
>}

The code works quite nicely in a non-networked environment.  I don't have
access to a Novell net, so the following comments may not apply in that
case.

In PC-NFS networks (using in my case the software sold by Sun Microsystems)
drives between the last physical drive, and drive 'V' are created by the
network driver, then 'mounted' from the file server.  This mount process
assigns remote file systems to any unused device in the list.  It is
therefore possible to have the following as the 'valid' device list:

    A: C: D: E: N: O: V:

(I.e. a floppy disk, 3-partition hard disk, "new software", "old software",
and network printer (pc-nfs uses drives t-v to map lpt1-3)).

Attempting to access an allocated but unmounted drive produces the dreaded
'Abort, Retry, Ignore' dialog.

The solution involves writing a dummy Critical Error Handler for DOS, that
always returns 'fail', instead of prompting the user.  I wrote one once,
but that project got shelved, and I can't lay my hands on the code at the
moment.  I'm sure one of the net.msdos.gods can provide details on how to
write the beast.  I seem to remember that MSC includes some type of support
but I can't remember.

If it's any consolation, the various WINDOWS versions we've played with
over the years don't even try to get it right.  They blithely display
drive K in the directory window, even though there is nothing mounted on
that drive.

--------------
Glenn Chambers                                  This is me speaking.
Intelligent Technology Group                    If my company were speaking,
Pittsburgh, PA.                                 my lips wouldn't be moving.
gchamber@nfsun.UUCP or ...!uunet!nfsun!gchamber

TOMIII@MTUS5.BITNET (Thomas Dwyer III) (08/01/90)

You can get gobs of neat information from INT 21, AH=52.  To find the
number of block devices (drives) in a system, look at offset 20h from
the pointer returned by ES:BX.

Example:

#include <dos.h>

main()
{
        union REGS regs;
        struct SREGS sregs;
        unsigned char far *ptr;

        regs.h.ah = 0x52;
        intdosx(&regs, &regs, &sregs);

        ptr = (unsigned char far *) (((unsigned long) sregs.es<<16)+regs.x.bx);

        printf("Number of block devices: %d\n", (int) *(ptr+0x20));
        printf("Value of LASTDRIVE: %d\n", (int) *(ptr+0x21));

        return(0);
}