www.pudn.com > DriveRescuev1.8.zip > Ldiskio.pas


{ 
  Logical Disk Access for Delphi 
  (Absolute disk read/write under Windows 95/98/ME and NT/2000/XP) 
  Written 2001 by Alexander Grau 
 
  Contact: alexander_grau@web.de 
 
} 
 
unit LDISKIO; 
 
interface 
 
const 
  { Media types } 
  LMEDIA_TYPE_UNKNOWN   = 0; 
  LMEDIA_TYPE_FLOPPY    = 1; 
  LMEDIA_TYPE_REMOVABLE = 2; 
  LMEDIA_TYPE_FIXED     = 3; 
  LMEDIA_TYPE_CDROM     = 4; 
 
  { Media attributes } 
  LMEDIA_ATTR_REMOVABLE = 1; 
 
type 
  PLogDriveParams = ^TLogDriveParams; 
  TLogDriveParams = record 
    MediaType        : word;       { see equals above } 
    MediaAttr        : word;       { see equals above } 
    Heads            : longword; 
    TracksPerHead    : longword; 
    SectorsPerTrack  : longword; 
    BytesPerSector   : longword; 
    TotalPhysSec     : longword; 
  end; 
 
 
(* -------------- published functions --------------------------------- *) 
 
   // bDrive:  The MS-DOS logical drive number. 1 = A, 2 = B, 3 = C, etc. 
 
   function ReadLogicalSectors (bDrive: BYTE; 
                                dwStartSector: LONGWORD; 
                                wSectors: WORD; 
                                lpSectBuff: pointer): BOOLEAN; 
 
   function WriteLogicalSectors (bDrive: BYTE; 
                                  dwStartSector: LONGWORD; 
                                  wSectors: WORD; 
                                  lpSectBuff: pointer): BOOLEAN; 
 
   function GetLogDriveParams(bDrive: BYTE; params: PLogDriveParams): boolean; 
 
var 
  optUseINT25: boolean; 
 
(* ------------------------------------------------------------------ *) 
 
 
implementation 
 
  uses windows, sysutils, math; 
 
 
  // -------- Windows 9X specific... ------------------------------------------------------- 
 
  const 
    VWIN32_DIOC_DOS_IOCTL     =1; 
    VWIN32_DIOC_DOS_INT25     =2; // Performs the Absolute Disk Read command (Interrupt 25h) 
    VWIN32_DIOC_DOS_INT26     =3; // Performs the Absolute Disk Write command (Interrupt 26h) 
    VWIN32_DIOC_DOS_DRIVEINFO =6; // Performs Interrupt 21h Function 730X commands. This value is supported in Windows 95 OEM Service Release 2 and later. 
 
   // Intel x86 processor status flag 
    CARRY_FLAG = 1; 
 
  type 
    PDIOC_REGISTERS = ^DIOC_REGISTERS; 
    DIOC_REGISTERS = packed record 
       reg_EBX: DWORD; 
       reg_EDX: DWORD; 
       reg_ECX: DWORD; 
       reg_EAX: DWORD; 
       reg_EDI: DWORD; 
       reg_ESI: DWORD; 
       reg_Flags: DWORD; 
    end; 
 
    PDISKIO = ^ DISKIO; 
    DISKIO = packed record 
      dwStartSector: DWORD;   // starting logical sector number 
      wSectors     : WORD;    // number of sectors 
      lpBuffer     : pointer; // address of read/write buffer 
    end; 
 
    PDOSDPB = ^DOSDPB; 
    DOSDPB = packed record 
      specialFunc: BYTE;      // 
      devType    : BYTE;      // 
      devAttr    : WORD;      // 
      cCyl       : WORD;      // number of cylinders 
      mediaType  : BYTE;      // 
      cbSec      : WORD;      // Bytes per sector 
      secPerClus : BYTE;      // Sectors per cluster 
      cSecRes    : WORD;      // Reserved sectors 
      cFAT       : BYTE;      // FATs 
      cDir       : WORD;      // Root Directory Entries 
      cSec       : WORD;      // Total number of sectors in image 
      bMedia     : BYTE;      // Media descriptor 
      secPerFAT  : WORD;      // Sectors per FAT 
      secPerTrack: WORD;      // Sectors per track 
      cHead      : WORD;      // Heads 
      cSecHidden : DWORD;     // Hidden sectors 
      cTotalSectors: DWORD;   // Total sectors, if cbSec is zero 
      reserved: array[0..5] of BYTE // 
    end; 
 
 
  // ------ INT2F.VXD specific... ------------------------------------------------ 
  const 
    DIOC_ISCDROM         = 1; 
    DIOC_READSECTORS     = 2; 
 
  type 
    cdromstruc = packed record  { Important! Delphi is not allowed to align to 32-Bit here! 
                               (otherwise something goes wrong...) } 
      drv   : byte; 
      LBA   : longword; 
      blocks: byte; 
      buf   : pointer; 
    end; 
 
  // -------- Windows NT specific... ------------------------------------------------------- 
  (*typedef enum _MEDIA_TYPE { 
     Unknown,                // Format is unknown 
     F5_1Pt2_512,            // 5.25", 1.2MB,  512 bytes/sector 
     F3_1Pt44_512,           // 3.5",  1.44MB, 512 bytes/sector 
     F3_2Pt88_512,           // 3.5",  2.88MB, 512 bytes/sector 
     F3_20Pt8_512,           // 3.5",  20.8MB, 512 bytes/sector 
     F3_720_512,             // 3.5",  720KB,  512 bytes/sector 
     F5_360_512,             // 5.25", 360KB,  512 bytes/sector 
     F5_320_512,             // 5.25", 320KB,  512 bytes/sector 
     F5_320_1024,            // 5.25", 320KB,  1024 bytes/sector 
     F5_180_512,             // 5.25", 180KB,  512 bytes/sector 
     F5_160_512,             // 5.25", 160KB,  512 bytes/sector 
     RemovableMedia,         // Removable media other than floppy 
     FixedMedia,             // Fixed hard disk media 
     F3_120M_512,            // 3.5", 120M Floppy 
     F3_640_512,             // 3.5" ,  640KB,  512 bytes/sector 
     F5_640_512,             // 5.25",  640KB,  512 bytes/sector 
     F5_720_512,             // 5.25",  720KB,  512 bytes/sector 
     F3_1Pt2_512,            // 3.5" ,  1.2Mb,  512 bytes/sector 
     F3_1Pt23_1024,          // 3.5" ,  1.23Mb, 1024 bytes/sector 
     F5_1Pt23_1024,          // 5.25",  1.23MB, 1024 bytes/sector 
     F3_128Mb_512,           // 3.5" MO 128Mb   512 bytes/sector 
     F3_230Mb_512,           // 3.5" MO 230Mb   512 bytes/sector 
     F8_256_128              // 8",     256KB,  128 bytes/sector 
  } MEDIA_TYPE, *PMEDIA_TYPE;*) 
 
  type 
    PLARGE_INTEGER = ^LARGE_INTEGER; 
    LARGE_INTEGER = packed record 
	LowPart: dword; 
	HighPart: dword; 
    end; 
 
    PDISK_GEOMETRY = ^TDISK_GEOMETRY; 
    TDISK_GEOMETRY = packed record 
      Cylinders: LARGE_INTEGER; 
      MediaType: dword; 
      TracksPerCylinder: dword; 
      SectorsPerTrack: dword; 
      BytesPerSector: dword; 
    end; 
 
  const 
    FILE_DEVICE_DISK               =  $00000007; 
    FILE_DEVICE_MASS_STORAGE       =  $0000002d; 
    FILE_ANY_ACCESS                =  0; 
    FILE_READ_ACCESS               =  $0001;     // file & pipe 
 
    METHOD_BUFFERED                =  0; 
 
    IOCTL_DISK_BASE                = FILE_DEVICE_DISK; 
    IOCTL_STORAGE_BASE             = FILE_DEVICE_MASS_STORAGE; 
    IOCTL_DISK_GET_DRIVE_GEOMETRY  = ( ((IOCTL_DISK_BASE) SHL 16) OR ((FILE_ANY_ACCESS) SHL 14) OR (($0000) SHL 2) OR (METHOD_BUFFERED) ); 
    IOCTL_DISK_CHECK_VERIFY        = ( ((IOCTL_DISK_BASE) SHL 16) OR ((FILE_READ_ACCESS) SHL 14)OR (($0200) SHL 2) OR (METHOD_BUFFERED) ); 
    IOCTL_STORAGE_CHECK_VERIFY     = ( ((IOCTL_STORAGE_BASE) SHL 16)OR((FILE_READ_ACCESS)SHL 14)OR (($0200) SHL 2)   OR (METHOD_BUFFERED) ); 
    IOCTL_DISK_GET_MEDIA_TYPES     = ( ((IOCTL_DISK_BASE) SHL 16)   OR((FILE_ANY_ACCESS)SHL 14) OR (($0300) SHL 2)   OR (METHOD_BUFFERED) ); 
 
  // ------------------------------------------------------------------------------ 
 
  const 
    TEMPSECTORS = 128; 
 
 
  var 
    CDHandle:   thandle;    // Win9X only: current CD-ROM handle (INT2F.VXD) 
    W95Handle:  thandle;    // Win9X only: current handle (VWIN32.VXD) 
    NTHandle:   thandle;    // WinNT only: current handle 
    NTDrive:      byte;     // WinNT only: drive currently opened 
    NT_DRV_Params : TLogDriveParams; // WinNT only: current drive params 
    NT_ShiftBase  : byte; //WinNT only: NT_DRV_Params.BytesPerASector in Bit-Shifts  
 
    fWin95OSR2orLater: boolean; 
    fWinNT           : boolean; 
 
    tempbuf: array[0..512*TEMPSECTORS-1] of byte; 
 
    ExitSave: Pointer; 
 
 
  (* --- some forward declarations ----------------------------------------- *) 
 
 
   //----------- under Windows 95 OEM Service Release 2 and later... ----------- 
   function NewReadSectors (bDrive: BYTE; 
                            dwStartSector: LONGWORD; 
                            wSectors: WORD; 
                            lpSectBuff: pointer): BOOLEAN; forward; 
 
  function NewWriteSectors (bDrive: BYTE; 
                             dwStartSector: LONGWORD; 
                             wSectors: WORD; 
                             lpSectBuff: pointer): BOOLEAN; forward; 
 
 
  // ------------- CD-ROM absolute read sector --------------------------------- 
  function IsCDROM(drv: byte): boolean; forward; 
  function ReadCDROMSectors(drv: byte; LBA: longword; blocks: byte; buf: pointer; 
    ErrorDlg: boolean): boolean; forward; 
 
  // -------------- under Windows NT... ---------------------------------------- 
  function NT_Read ( bDrive: BYTE; 
                     dwStartSector: LONGWORD; 
                     wSectors: WORD; 
                     lpSectBuff: pointer): BOOLEAN; forward; 
 
  function NT_Write ( bDrive: BYTE; 
                     dwStartSector: LONGWORD; 
                     wSectors: WORD; 
                     lpSectBuff: pointer): BOOLEAN; forward; 
 
 
  (* --- helper functions --------------------------------------------------- *) 
 
 
  function IsWin95OSR2orLater: boolean; 
  var 
    os: TOSVersionInfo; 
    res: boolean; 
  begin 
    ZeroMemory(@OS,SizeOf(OS)); 
    OS.dwOSVersionInfoSize:=SizeOf(OS); 
    GetVersionEx(OS); 
 
    res:=false; 
 
    // from Microsoft Programmer's Guide to Win95: 
    // "The GetVersionEx function fills the members of an OSVERSIONINFO data structure. If the dwPlatformId member of that structure 
    // is VER_PLATFORM_WIN32_WINDOWS, and the low word of the dwBuildNumber member is greater than 1080, the system is running 
    // Windows 95,OEM Service Release 2 or a later release of Windows 95." 
    // if the there are problems with the following detection use above one... 
 
    if os.dwPlatformId = VER_PLATFORM_WIN32_WINDOWS then 
    begin 
      if (OS.dwMajorVersion >= 4) and (OS.dwMinorVersion>0) then res:=true 
        else begin 
          if (OS.dwMajorVersion=4) and (OS.dwMinorVersion=0) then 
          begin 
            if (Trim(OS.szCSDVersion)='B') then res:=true; 
          end; 
        end; 
    end; 
 
    result:=res; 
  end; 
 
 
  function IsWinNT: boolean; 
  var 
    info: TOSVersionInfo; 
  begin 
    IsWinNT:=false; 
    info.dwOSVersionInfoSize:=sizeof(TOSVersionInfo); 
    if GetVersionEx(info) then 
    begin 
      if info.dwPlatformId = VER_PLATFORM_WIN32_NT then IsWinNT:=true; 
    end; 
  end; 
 
 
   (*------------------------------------------------------------------ 
   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. 
   ------------------------------------------------------------------*) 
   function ReadLogicalSectors (bDrive: BYTE;  
                                dwStartSector: LONGWORD; 
                                wSectors: WORD; 
                                lpSectBuff: pointer): BOOLEAN; 
   var 
      fResult:        BOOL; 
      cb:             DWORD; 
      reg:            DIOC_REGISTERS; 
      dio:            DISKIO; 
 
   begin 
      if fWinNT then 
      begin 
        result:=NT_read( bDrive, dwStartSector, wSectors, lpSectBuff); 
        exit; 
      end; 
 
      // CD-ROM (MSCDEX/CDFS) ? 
      if IsCDROM( bDrive ) then 
      begin 
        result:=ReadCDROMSectors( bDrive, dwStartSector, wSectors, lpSectBuff, TRUE); 
        exit; 
      end; 
 
      if optUseINT25 then 
      begin 
        // Windows 95 OEM Service Release 2 and later ? 
        if fWin95OSR2orLater then 
        begin 
          result:=NewReadSectors( bDrive, dwStartSector, wSectors, lpSectBuff); 
          exit; 
        end; 
 
        fillchar(reg, sizeof(DIOC_REGISTERS), 0); 
        fillchar(dio, sizeof(DISKIO), 0); 
 
        dio.dwStartSector := dwStartSector; 
        dio.wSectors      := wSectors; 
        dio.lpBuffer      := lpSectBuff; 
 
        reg.reg_EAX := bDrive - 1;   // Int 25h drive numbers are 0-based. 
        reg.reg_EBX := DWORD(@dio); 
        reg.reg_ECX := $FFFF;        // use DISKIO struct 
 
        fResult := DeviceIoControl(W95Handle, VWIN32_DIOC_DOS_INT25, 
                                  @reg, sizeof(reg), 
                                  @reg, sizeof(reg), cb, 0); 
 
        // Determine if the DeviceIoControl call and the read succeeded. 
        fResult := fResult AND (reg.reg_Flags AND CARRY_FLAG=0); 
 
        result:=fResult; 
      end else result:=FALSE; 
   end; 
 
 
   (*------------------------------------------------------------------ 
   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. 
   ------------------------------------------------------------------*) 
   function WriteLogicalSectors (bDrive: BYTE;  
                                  dwStartSector: LONGWORD; 
                                  wSectors: WORD; 
                                  lpSectBuff: pointer): BOOLEAN; 
   var 
     fResult: BOOL; 
     cb     : DWORD; 
     reg    : DIOC_REGISTERS; 
     dio    : DISKIO; 
 
   begin 
      if fWinNT then 
      begin 
        result:=NT_write( bDrive, dwStartSector, wSectors, lpSectBuff); 
        exit; 
      end; 
 
      if optUseINT25 then 
      begin 
        // Windows 95 OEM Service Release 2 and later ? 
        if fWin95OSR2orLater then 
        begin 
          result:=NewWriteSectors(bDrive, dwStartSector, wSectors, lpSectBuff); 
          exit; 
        end; 
 
        fillchar(reg, sizeof(DIOC_REGISTERS), 0); 
        fillchar(dio, sizeof(DISKIO), 0); 
 
        dio.dwStartSector := dwStartSector; 
        dio.wSectors      := wSectors; 
        dio.lpBuffer      := lpSectBuff; 
 
        reg.reg_EAX := bDrive - 1;   // Int 26h drive numbers are 0-based. 
        reg.reg_EBX := DWORD(@dio); 
        reg.reg_ECX := $FFFF;        // use DISKIO struct 
 
        fResult := DeviceIoControl(W95Handle, VWIN32_DIOC_DOS_INT26, 
                                  @reg, sizeof(reg), 
                                  @reg, sizeof(reg), cb, 0); 
 
        // Determine if the DeviceIoControl call and the write succeeded. 
        fResult := fResult AND (reg.reg_Flags AND CARRY_FLAG=0); 
 
        result:=fResult; 
      end else result:=FALSE; 
   end; 
 
 
   (*------------------------------------------------------------------ 
   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. 
   ------------------------------------------------------------------*) 
   function NewReadSectors (bDrive: BYTE; 
                            dwStartSector: LONGWORD; 
                            wSectors: WORD; 
                            lpSectBuff: pointer): BOOLEAN; 
 
   var 
     fResult: BOOL; 
     cb     : DWORD; 
     reg    : DIOC_REGISTERS; 
     dio    : DISKIO; 
 
   begin 
     fillchar(reg, sizeof(DIOC_REGISTERS), 0); 
     fillchar(dio, sizeof(DISKIO), 0); 
 
     dio.dwStartSector := dwStartSector; 
     dio.wSectors      := wSectors; 
     dio.lpBuffer      := lpSectBuff; 
 
     reg.reg_EAX := $7305;   // Ext_ABSDiskReadWrite 
     reg.reg_EBX := DWORD(@dio); 
     reg.reg_ECX := DWORD(-1); 
     reg.reg_EDX := bDrive;  // Int 21h, fn 7305h drive numbers are 1-based 
 
     fResult := DeviceIoControl(W95Handle, VWIN32_DIOC_DOS_DRIVEINFO, 
                               @reg, sizeof(reg), 
                               @reg, sizeof(reg), cb, 0); 
 
     // Determine if the DeviceIoControl call and the read succeeded. 
     fResult := fResult AND (reg.reg_Flags AND CARRY_FLAG=0); 
 
     result:=fResult; 
   end; 
 
 
   (*------------------------------------------------------------------ 
   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. 
   ------------------------------------------------------------------*) 
  function NewWriteSectors (bDrive: BYTE;  
                             dwStartSector: LONGWORD; 
                             wSectors: WORD; 
                             lpSectBuff: pointer): BOOLEAN; 
    var 
     fResult: BOOL; 
     cb     : DWORD; 
     reg    : DIOC_REGISTERS; 
     dio    : DISKIO; 
 
   begin 
     fillchar(reg, sizeof(DIOC_REGISTERS), 0); 
     fillchar(dio, sizeof(DISKIO), 0); 
 
     dio.dwStartSector := dwStartSector; 
     dio.wSectors      := wSectors; 
     dio.lpBuffer      := lpSectBuff; 
 
     reg.reg_EAX := $7305;   // Ext_ABSDiskReadWrite 
     reg.reg_EBX := DWORD(@dio); 
     reg.reg_ECX := DWORD(-1); 
     reg.reg_EDX := bDrive;  // Int 21h, fn 7305h drive numbers are 1-based 
 
     reg.reg_ESI := $6001;   // Normal file data (See function 
                             // documentation for other values) 
 
 
     fResult := DeviceIoControl(W95Handle, VWIN32_DIOC_DOS_DRIVEINFO, 
                               @reg, sizeof(reg), 
                               @reg, sizeof(reg), cb, 0); 
 
     // Determine if the DeviceIoControl call and the write succeeded. 
     fResult := fResult AND (reg.reg_Flags AND CARRY_FLAG=0); 
 
     result:=fResult; 
   end; 
 
 
 
   // Get Logical Drive Parameters 
   function GetLogDriveParams(bDrive: BYTE; params: PLogDriveParams): boolean; 
   var 
     h:   tHANDLE; 
     reg:  DIOC_REGISTERS; 
     cb:   DWORD; 
     res:  boolean; 
     dpb:  DOSDPB; 
     hDevice: thandle; 
     dg:   TDISK_GEOMETRY; 
   begin 
      res:=false; 
      if (fWinNT) then 
      begin 
        // ---------- Windows NT... ---------------------------------------------- 
        hDevice := CreateFile(pchar('\\.\'+chr(ord('A')+bDrive-1)+':'), 0, FILE_SHARE_WRITE, 
          nil, OPEN_EXISTING, 0, 0); 
        if hDevice <> INVALID_HANDLE_VALUE then 
        begin 
          { if NT CD-ROM } 
          if GetDriveType(pchar(chr(ord('A')+bDrive-1)+':\'))=DRIVE_CDROM then 
          begin 
            fillchar(params^, sizeof(TLogDriveParams), 0); 
            params^.MediaType:=LMEDIA_TYPE_CDROM; 
            params^.MediaAttr:=LMEDIA_ATTR_REMOVABLE; 
            params^.BytesPerSector:=2048; 
            result:=true; 
            exit; 
          end; 
          { if NT Floppy, Harddisk, etc. }  
          res := DeviceIoControl(hDevice, 
             IOCTL_DISK_GET_DRIVE_GEOMETRY, nil, 0, 
             @dg, sizeof(TDISK_GEOMETRY), cb, nil); 
          CloseHandle(hDevice); 
          if res then 
          begin 
            params^.MediaAttr:=0; 
            params^.Heads:=dg.cylinders.lowpart; 
            params^.TracksPerHead:=dg.trackspercylinder; 
            params^.SectorsPerTrack:=dg.sectorspertrack; 
            params^.BytesPerSector:=dg.bytespersector; 
            params^.TotalPhysSec:=dg.cylinders.lowpart * dg.TracksPerCylinder * dg.SectorsPerTrack; 
            case dg.MediaType of 
              0:             params^.MediaType:=LMEDIA_TYPE_UNKNOWN; 
              1..10, 13..22: begin 
                               params^.MediaType:=LMEDIA_TYPE_FLOPPY; 
                               params^.MediaAttr:=LMEDIA_ATTR_REMOVABLE; 
                             end; 
              11:            begin 
                               params^.MediaType:=LMEDIA_TYPE_REMOVABLE; 
                               params^.MediaAttr:=LMEDIA_ATTR_REMOVABLE; 
                             end; 
              12:            params^.MediaType:=LMEDIA_TYPE_FIXED; 
            end; 
          end; 
        end; 
      end else 
      begin 
        //  --------- Windows 9X... -------------------------------------------- 
        if IsCDROM(bDrive) then 
        begin 
          fillchar(params^, sizeof(TLogDriveParams), 0); 
          params^.MediaType:=LMEDIA_TYPE_CDROM; 
          params^.MediaAttr:=LMEDIA_ATTR_REMOVABLE; 
          params^.BytesPerSector:=2048; 
          result:=true; 
          exit; 
        end; 
 
        if optUseINT25 then 
        begin 
          dpb.specialFunc := 0;  // return default type; do not hit disk 
 
          reg.reg_EBX   := bDrive;       // BL = drive number (1-based) 
          reg.reg_EDX   := DWORD(@dpb);  // DS:EDX -> DPB 
          reg.reg_ECX   := $0860;        // CX = Get DPB 
          reg.reg_EAX   := $440D;        // AX = Ioctl 
          reg.reg_Flags := CARRY_FLAG;   // assume failure 
 
          // Make sure both DeviceIoControl and Int 21h succeeded. 
          res:=(DeviceIoControl (W95handle, VWIN32_DIOC_DOS_IOCTL, @reg, 
                               sizeof(reg), @reg, sizeof(reg), cb, 0) 
                               AND (reg.reg_Flags AND CARRY_FLAG=0)); 
          if res then 
          begin 
            params^.MediaAttr:=0; 
            params^.Heads:=dpb.cHead; 
            params^.TracksPerHead:=dpb.cCyl; 
            params^.SectorsPerTrack:=dpb.secPerTrack; 
            params^.BytesPerSector:=dpb.cbSec; 
            params^.TotalPhysSec:=dpb.cCyl * dpb.cHead * dpb.secPerTrack; 
            case dpb.devType of 
              0..4, 7,8: params^.MediaType:=LMEDIA_TYPE_FLOPPY; 
              5:         params^.MediaType:=LMEDIA_TYPE_FIXED; 
              6,9:       if (dpb.devAttr AND 1) = 0 then params^.MediaType:=LMEDIA_TYPE_REMOVABLE 
                          else params^.MediaType:=LMEDIA_TYPE_UNKNOWN; 
            end; 
            if (dpb.devAttr AND 1) = 0 then params^.MediaAttr:=LMEDIA_ATTR_REMOVABLE; 
          end; 
        end; 
       end; 
      result:=res; 
   end; 
 
 
 
  // ----------------------------------------------------------------------------------- 
  //     MSCDEX  /  CDFS (INT2F.VXD) ... 
  // ----------------------------------------------------------------------------------- 
 
 
  function IsCDROM(drv: byte):Boolean; 
  var 
    res: boolean; 
    inbuf: byte; 
    outbuf: byte; 
    cb: dword; 
  begin 
    inbuf:=drv-1; 
    res:=DeviceIoControl(CDhandle, DIOC_ISCDROM, 
        @inbuf, 1, 
        @outbuf, 1, cb, nil); 
 
    result:=res AND (outbuf=1); 
  end; 
 
 
  function ReadCDROMSectors(drv: byte; LBA: longword; blocks: byte; buf: pointer; 
    ErrorDlg: boolean): boolean; 
  var 
    res: boolean; 
    struc: cdromstruc; 
    cb: dword; 
    tempbuf: array[0..2047] of byte; 
    count: integer; 
    msgRes: integer; 
 
  begin 
    count:=0; 
 
    struc.Drv    := drv-1; 
    struc.LBA    := LBA; 
    struc.blocks := 1; //blocks; 
    struc.buf    := {buf;} @tempbuf; 
    repeat 
      repeat 
        res:=DeviceIoControl(CDhandle, DIOC_READSECTORS, 
            @struc, sizeof(cdromstruc), 
            nil, 0, cb, nil); 
        msgRes := id_abort; 
        if (NOT res) AND (ErrorDlg) then 
        begin 
          msgRes:=messagebox(0, pchar('Error reading sector, '+#13#10+'drv:'+inttostr(drv)+' LBA:'+inttostr(LBA) 
            +' blocks:'+inttostr(blocks) +#13#10#13#10 
            +' Abort, Retry or Ignore?'), pchar('MSCDEX/CDFS CD-ROM read error '+inttostr(windows.GetLastError)), 
               mb_applmodal or mb_iconwarning or mb_abortretryignore); 
 
        end; 
      until NOT ((ErrorDlg) AND (msgRes = id_Retry)); 
      if (NOT res) AND (ErrorDlg) AND (msgRes = id_ignore) then res:=true; 
      if res then move(tempbuf, buf^, 2048); 
 
      inc(longword(buf),2048); 
      inc(count); 
      inc(struc.LBA); 
    until (NOT res) OR (count >= blocks); 
 
    result:=res; 
  end; 
 
 
   // ------------------------------------------------------------------------- 
 
   //        same stuff for Windows NT . . . 
 
   // ------------------------------------------------------------------------- 
 
 
  (* -- open / close drive handle ------------------------------------------ *) 
 
  function NT_changeDrive(bDrive: BYTE; ReadOnly: boolean): boolean; 
  var 
    hDevice: thandle; 
  begin 
    // notice: under WinNT each drive has to be opened - under Win9X/ME one handle is for all drives 
    if (NThandle <> INVALID_HANDLE_VALUE) then 
    begin 
      if NTdrive = bDrive then 
      begin 
        result:=true; 
        exit; 
      end else 
      begin 
        CloseHandle(NThandle); 
      end; 
    end; 
 
    if ReadOnly then 
      hDevice := CreateFile(pchar('\\.\'+chr(ord('A')+bDrive-1)+':'), GENERIC_READ, FILE_SHARE_READ OR FILE_SHARE_WRITE, 
       nil, OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH, 0) 
    else 
      hDevice := CreateFile(pchar('\\.\'+chr(ord('A')+bDrive-1)+':'), GENERIC_WRITE, FILE_SHARE_READ OR FILE_SHARE_WRITE, 
       nil, OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH, 0); 
    NThandle:=hDevice; 
    NTdrive:=bDrive; 
 
    GetLogDriveParams(bDrive, @nt_drv_params); 
    NT_ShiftBase:=round(log2(nt_drv_params.bytespersector)); 
 
    result:=(hDevice <> INVALID_HANDLE_VALUE); 
  end; 
 
 
 
  function NT_Read ( bDrive: BYTE; 
                     dwStartSector: LONGWORD; 
                     wSectors: WORD; 
                     lpSectBuff: pointer): BOOLEAN; 
  var 
    res: boolean; 
    bytestoread, numread, transfer: dword; 
    dwpointer: dword; 
    ldistancelow, ldistancehigh : dword; 
    DestSector : longword; 
  begin 
    res:=false; 
 
    if NT_changeDrive(bDrive, true) then 
    begin 
      ldistanceLow:=longword(dwStartSector) SHL NT_ShiftBase; 
      ldistanceHigh:=longword(dwStartSector) SHR (32-NT_ShiftBase); 
 
      dwpointer:=SetFilePointer(NThandle, ldistancelow, @ldistancehigh, FILE_BEGIN); 
      if dwPointer <> $FFFFFFFF then 
      begin 
        bytestoread:=wSectors*nt_drv_params.bytespersector; 
        repeat 
          transfer:=bytestoread; 
          if (transfer > TEMPSECTORS * nt_drv_params.bytespersector) then 
            transfer:=TEMPSECTORS * nt_drv_params.bytespersector; 
          res:=ReadFile(NThandle, tempbuf, transfer, numread, nil); 
          if res then res:=boolean(numread=transfer); 
          if res then move(tempbuf, lpSectBuff^, transfer); 
          inc(longword(lpSectBuff),transfer); 
          dec(bytestoread, transfer); 
        until (NOT res) OR (bytestoread = 0); 
      end; 
    end; 
    result:=res; 
  end; 
 
 
  function NT_Write ( bDrive: BYTE; 
                     dwStartSector: LONGWORD; 
                     wSectors: WORD; 
                     lpSectBuff: pointer): BOOLEAN; 
  var 
    res: boolean; 
    bytestoread, numread: dword; 
    dwpointer: dword; 
    ldistancelow, ldistancehigh: dword; 
  begin 
    res:=false; 
 
    if NT_changeDrive(bDrive, false) then 
    begin 
      ldistanceLow:=dword(dwStartSector SHL 9); 
      ldistanceHigh:=dword(dwStartSector SHR (32-9)); 
      dwpointer:=SetFilePointer(NThandle, ldistancelow, @ldistancehigh, FILE_BEGIN); 
      if dwPointer <> $FFFFFFFF then 
      begin 
        bytestoread:=wSectors*nt_drv_params.bytespersector; 
        res:=WriteFile(NThandle, lpSectBuff^, bytestoread, numread, nil); 
        res:=(res AND (numread =bytestoread)); 
      end; 
    end; 
    result:=res; 
  end; 
 
 
 
  procedure MyExit; 
  begin 
    ExitProc := ExitSave;            { first restore old vector } 
 
    if NOT (fWinNT) then 
    begin 
      // Win9X... 
       if W95Handle <> INVALID_HANDLE_VALUE then 
         CloseHandle(W95Handle); 
       if CDHandle <> INVALID_HANDLE_VALUE then 
         CloseHandle(CDHandle); 
    end else 
    begin 
      // WinNT... 
       if NThandle <> INVALID_HANDLE_VALUE then 
         CloseHandle(NThandle); 
    end; 
  end; 
 
 
 
begin 
  optUseINT25:=TRUE; 
 
  W95Handle := INVALID_HANDLE_VALUE; 
  NTHandle  := INVALID_HANDLE_VALUE; 
 
  fWin95OSR2orLater:=IsWin95OSR2orLater; 
  fWinNT           :=IsWinNT; 
 
  ExitSave := ExitProc; 
  ExitProc := @MyExit; 
 
  if NOT (fWinNT) then 
  begin 
    W95Handle := CreateFile('\\.\vwin32', 
        0, 0, nil, 0, FILE_FLAG_DELETE_ON_CLOSE, 0); 
    if W95handle = INVALID_HANDLE_VALUE then 
      MessageBox(0, 'Error loading "VWIN32.VXD (INT25/26)"', 'Error', mb_IconExclamation + mb_ok); 
 
    CDhandle:=CreateFile('\\.\INT2F.VXD', 
       0, 0, nil, 0,  FILE_FLAG_DELETE_ON_CLOSE, 0); 
    if CDhandle = INVALID_HANDLE_VALUE then 
      MessageBox(0, 'Error loading "INT2F.VXD" (MSCDEX/CDFS)', 'Error', mb_IconExclamation + mb_ok); 
  end; 
 
end.