BUG: Int 21 Read/Write Track on Logical Drive Fails on OSR2

ID: Q174569

The information in this article applies to:

SYMPTOMS

On Windows 95 OEM Service Release2 (OSR2), Read Track on Logical Drive (Int 21h function 440Dh minor code 61h) and Write Track on Logical Drive (Int 21h function 440Dh minor code 41h), do not work when called through the DeviceIoControl API. When these functions are called, both DeviceIoControl and the Int 21h functions succeed, but the data is not read or written. However, both functions work correctly when called from Win16-based and MS- DOS based applications.

On the retail version of Windows 95, both functions work when called through DeviceIOControl as well as Win16-based and MS-DOS based applications.

CAUSE

Windows 95 OEM Service Release 2 has a bug that affects Int 21h function 440Dh minor codes 61h and 41h only when called through DeviceIoControl.

RESOLUTION

There are two ways to work around this bug:

1. Use DeviceIoControl with VWIN32_DIOC_DOS_INT25 and

   VWIN32_DIOC_DOS_INT26 to issue Absolute Disk Read (Int 25h) and
   Absolute Disk Write (Int 26h) respectively. This method works
   for FAT12 and FAT16 volumes. This method is compatible with the
   retail release version of Windows 95, but will not work on
   FAT32 volumes.

2. Use DeviceIoControl with VWIN32_DIOC_DOS_DRIVEINFO to issue
   Ext Absolute Disk Read & Write (Int 21h function 7305h). This
   method works for FAT12, FAT16, and FAT32 volumes, but is not
   backward compatible with the retail release version of
   Windows 95.

STATUS

Microsoft has confirmed this to be a bug in the Microsoft products listed at the beginning of this article. We are researching this bug and will post new information here in the Microsoft Knowledge Base as it becomes available.

MORE INFORMATION

Win32 and console applications running on Windows 95 and its successors use the DeviceIoControl() API to issue MS-DOS Interrupt 21h functions to perform low-level disk I/O. Such applications usually are disk utilities that must bypass the file system in order to perform activities such as formatting and defragmenting disks.

Windows 95 and its successors provides several functions to access sectors on a logical drive. These functions correspond to those available to MS-DOS and Win16-based applications, including Int 21h function 440Dh functions, Int 25h, and Int 26h. In addition, Windows 95 OEM Service Release 2 (OSR2) includes a new function, Int 21h function 7305h.

If your application must be compatible with the retail release version of Windows 95, but still support FAT32, then it should check the operating system version. If Windows 95 OSR2 is running, your application can call Int 21h function 7305h. If it is running on the retail release of Windows 95, it should use Int 25h and Int 26h. The following code demonstrates how to call these functions.

Sample Code

   #include <windows.h>

   #define VWIN32_DIOC_DOS_INT25     2
   #define VWIN32_DIOC_DOS_INT26     3
   #define VWIN32_DIOC_DOS_DRIVEINFO 6

   typedef struct _DIOC_REGISTERS {
       DWORD reg_EBX;
       DWORD reg_EDX;
       DWORD reg_ECX;
       DWORD reg_EAX;
       DWORD reg_EDI;
       DWORD reg_ESI;
       DWORD reg_Flags;
   } DIOC_REGISTERS, *PDIOC_REGISTERS;

   #define CARRY_FLAG 1

   #pragma pack(1)
   typedef struct _DISKIO {
      DWORD  dwStartSector;   // starting logical sector number
      WORD   wSectors;        // number of sectors
      DWORD  dwBuffer;        // address of read/write buffer
   } DISKIO, * PDISKIO;
   #pragma pack()

   /*------------------------------------------------------------------
   ReadLogicalSectors (hDev, bDrive, dwStartSector, wSectors, lpSectBuff)

   Purpose:
      Reads sectors from a logical drive.  Uses Int 25h.

   Parameters:
      hDev
         Handle of VWIN32

      bDrive
         The MS-DOS logical drive number. 1 = A, 2 = B, 3 = C, etc.

      dwStartSector
         The first logical sector to read

      wSectors
         The number of sectors to read

      lpSectBuff
         The caller-supplied buffer that will contain the sector data

   Return Value:
      Returns TRUE if successful, or FALSE if failure.

   Comments:
      This function does not validate its parameters.
   ------------------------------------------------------------------*/ 
   BOOL ReadLogicalSectors (HANDLE hDev,
                            BYTE   bDrive,
                            DWORD  dwStartSector,
                            WORD   wSectors,
                            LPBYTE lpSectBuff)
   {
      BOOL           fResult;
      DWORD          cb;
      DIOC_REGISTERS reg = {0};
      DISKIO         dio = {0};

      dio.dwStartSector = dwStartSector;
      dio.wSectors      = wSectors;
      dio.dwBuffer      = (DWORD)lpSectBuff;

      reg.reg_EAX = bDrive - 1;    // Int 25h drive numbers are 0-based.
      reg.reg_EBX = (DWORD)&dio;
      reg.reg_ECX = 0xFFFF;        // use DISKIO struct

      fResult = DeviceIoControl(hDev, VWIN32_DIOC_DOS_INT25,
                                &reg, sizeof(reg),
                                &reg, sizeof(reg), &cb, 0);

      // Determine if the DeviceIoControl call and the read succeeded.
      fResult = fResult && !(reg.reg_Flags & CARRY_FLAG);

      return fResult;
   }

   /*------------------------------------------------------------------
   WriteLogicalSectors (hDev, bDrive, dwStartSector, wSectors, lpSectBuff)

   Purpose:
      Writes sectors to a logical drive. Uses Int 26h

   Parameters:
      hDev
         Handle of VWIN32

      bDrive
         The MS-DOS logical drive number. 1 = A, 2 = B, 3 = C, etc.

      dwStartSector
         The first logical sector to write

      wSectors
         The number of sectors to write

      lpSectBuff
         The caller-supplied buffer that contains the sector data

   Return Value:
      Returns TRUE if successful, or FALSE if failure.

   Comments:
      This function does not validate its parameters.
   ------------------------------------------------------------------*/ 
   BOOL WriteLogicalSectors (HANDLE hDev,
                             BYTE   bDrive,
                             DWORD  dwStartSector,
                             WORD   wSectors,
                             LPBYTE lpSectBuff)
   {
      BOOL           fResult;
      DWORD          cb;
      DIOC_REGISTERS reg = {0};
      DISKIO         dio = {0};

      dio.dwStartSector = dwStartSector;
      dio.wSectors      = wSectors;
      dio.dwBuffer      = (DWORD)lpSectBuff;

      reg.reg_EAX = bDrive - 1;    // Int 26h drive numbers are 0-based.
      reg.reg_EBX = (DWORD)&dio;
      reg.reg_ECX = 0xFFFF;        // use DISKIO struct

      fResult = DeviceIoControl(hDev, VWIN32_DIOC_DOS_INT26,
                                &reg, sizeof(reg),
                                &reg, sizeof(reg), &cb, 0);

      // Determine if the DeviceIoControl call and the write succeeded.
      fResult = fResult && !(reg.reg_Flags & CARRY_FLAG);

      return fResult;
   }

   /*------------------------------------------------------------------
   NewReadSectors(hDev, bDrive, dwStartSector, wSectors, lpSectBuff)

   Purpose:
     Reads the specified number of sectors into a caller-supplied
     buffer. Uses Int 21h function 7305h

   Parameters:
     hDev
        Handle of VWIN32

     bDrive
        The MS-DOS logical drive number. 0 = default, 1 = A, 2 = B,
        3 = C, etc.

     dwStartSector
        The first sector to read.

     wSectors
        The number of sectors to read.

     lpSectBuff
        The caller-supplied buffer to read into.

   Return Value:
     Returns TRUE if successful, or FALSE if failure.

   Comments:
     This function does not validate its parameters.  It assumes that
     lpSectBuff is allocated by the caller and is large enough to
     hold all of the data from all of the sectors being read.
   ------------------------------------------------------------------*/ 
   BOOL NewReadSectors (HANDLE hDev,
                        BYTE   bDrive,
                        DWORD  dwStartSector,
                        WORD   wSectors,
                        LPBYTE lpSectBuff)
   {
     BOOL           fResult;
     DWORD          cb;
     DIOC_REGISTERS reg = {0};
     DISKIO         dio;

     dio.dwStartSector = dwStartSector;
     dio.wSectors      = wSectors;
     dio.lpBuffer      = (DWORD)lpSectBuff;

     reg.reg_EAX = 0x7305;   // Ext_ABSDiskReadWrite
     reg.reg_EBX = (DWORD)&dio;
     reg.reg_ECX = -1;
     reg.reg_EDX = bDrive;   // Int 21h, fn 7305h drive numbers are 1-based

     fResult = DeviceIoControl(hDev, VWIN32_DIOC_DOS_DRIVEINFO,
                               &reg, sizeof(reg),
                               &reg, sizeof(reg), &cb, 0);

     // Determine if the DeviceIoControl call and the read succeeded.
     fResult = fResult && !(reg.reg_Flags & CARRY_FLAG);

     return fResult;
   }

   /*------------------------------------------------------------------
   NewWriteSectors(hDev, bDrive, dwStartSector, wSectors, lpSectBuff)

   Purpose:
     Writes the specified number of sectors from a caller-supplied
     buffer. Uses Int 21h function 7305h

   Parameters:
     hDev
        Handle of VWIN32

     bDrive
        The MS-DOS logical drive number. 0 = default, 1 = A, 2 = B,
        3 = C, etc.

     dwStartSector
        The first sector to write.

     wSectors
        The number of sectors to write.

     lpSectBuff
        The caller-supplied buffer from which to write.

   Return Value:
     Returns TRUE if successful, or FALSE if failure.

   Comments:
     This function does not validate its parameters.  It assumes that
     lpSectBuff is allocated by the caller and is large enough to
     hold all of the data to be written.
   ------------------------------------------------------------------*/ 
   BOOL NewWriteSectors (HANDLE hDev,
                        BYTE   bDrive,
                        DWORD  dwStartSector,
                        WORD   wSectors,
                        LPBYTE lpSectBuff)
   {
     BOOL           fResult;
     DWORD          cb;
     DIOC_REGISTERS reg = {0};
     DISKIO         dio;

     dio.dwStartSector = dwStartSector;
     dio.wSectors      = wSectors;
     dio.lpBuffer      = (DWORD)lpSectBuff;

     reg.reg_EAX = 0x7305;   // Ext_ABSDiskReadWrite
     reg.reg_EBX = (DWORD)&dio;
     reg.reg_ECX = -1;
     reg.reg_EDX = bDrive;   // Int 21h, fn 7305h drive numbers are 1-based

     reg.reg_ESI = 0x6001;   // Normal file data (See function
                             // documentation for other values)

     fResult = DeviceIoControl(hDev, VWIN32_DIOC_DOS_DRIVEINFO,
                               &reg, sizeof(reg),
                               &reg, sizeof(reg), &cb, 0);

     // Determine if the DeviceIoControl call and the write succeeded.
     fResult = fResult && !(reg.reg_Flags & CARRY_FLAG);

     return fResult;
   }

REFERENCES

"Microsoft MS-DOS Programmer's Reference", Version 6.0, pages 140-144, 313- 314, 321-322.

Online Documentation:

Microsoft Win32 SDK, Programming Tools and Guides; Programmer's Guide to Windows 95; Windows 95 Reference; FAT32 API Reference; Functions; Int 21h Function 7305h Ext_AbsDiskReadWrite (FAT32)

Additional query words: format logical drive low level ioctl bios

Keywords          : kbAPI kbKernBase kbGrpKernBase 
Platform          : Win95
Issue type        : kbbug

Last Reviewed: October 4, 1997