Sources
Delphi Russian Knowledge Base
DRKB - это самая большая и удобная в использовании база знаний по Дельфи в рунете, составленная Виталием Невзоровым

Модуль для работы с дисковыми драйверами (На уровне секторов)

01.01.2007
{ Автор : NikNet
  MAIL   : NikNet@yandex.ru}
 
Unit Disk;
 
Interface
 
Uses Windows,SysUtils;
 
VAR
 dsBytePerSector : word = 512; // Установите размер сектора
 
function ReadLogicalSector  (Drive: Byte; Sector: Int64; Count: Word; Var Buffer): Boolean;
function WriteLogicalSector (Drive: Byte; Sector: Int64; Count: Word; Var Buffer): Boolean;
 
function ReadPlysicalSector (HddNumber: Byte; Sector:Int64; Count:word;  Var Buffer): DWORD;
function WritePlysicalSector(HddNumber: Byte; Sector:Int64; Count:word;  Var Buffer): DWORD;
 
 
Implementation
 
 
 
  TYPE
   PDIOC_Registers = ^TDIOC_Registers;
   TDIOC_Registers = record
     case Byte of
      0: (EBX,EDX,ECX,EAX,EDI,ESI,Flags: DWord);
      1: (BX,BXE,DX,DXE,CX,CXE,AX,AXE,DI,DIE,SI,SIE: Word);
     end;
 
  TReadWritePacket = packed record
    StartSector: DWord;
    Sectors    : Word;
    Buffer     : Pointer;
  end;
 
const
 
  VWIN32_DEVICE_NAME        = '\\.\VWIN32';
  VWIN32_DIOC_CLOSE         = 0; { Close the device.                           }
  VWIN32_DIOC_DOS_IOCTL     = 1; { MS-DOS device I/O control function,         }
                                 { interrupt 21h function 4400h through 4411h  }
  VWIN32_DIOC_DOS_INT25     = 2; { MS-DOS absolute disk read command,          }
                                 { interrupt 25h.                              }
  VWIN32_DIOC_DOS_INT26     = 3; { MS-DOS absolute disk write command,         }
                                 { interrupt 26h.                              }
  VWIN32_DIOC_DOS_INT13     = 4; { Low-level BIOS disk functions,              }
                                 { interrupt 13h.                              }
  VWIN32_DIOC_DOS_DRIVEINFO = 6; { MS-DOS Interrupt 21h new function 730x.     }
                                 { Supported only by Windows 95 OSR2 and later.}
 
  FLAG_CARRY = $00000001;
 
  // Error codes
  ERROR_NON                  = $0000; { no error                               }
  // MS-DOS/Windows error codes:
  ERROR_INVALID_FUNCTION     = $0001; { invalid function number                }
  ERROR_FILE_NOT_FOUND       = $0002; { file not found                         }
  ERROR_ACCESS_DENIED        = $0005; { specified access denied on drive       }
  ERROR_INVALID_DRIVE        = $000F; { invalid drive number                   }
  ERROR_MEDIA_NOT_LOCKED     = $00B0; { media is not locked in drive           }
  ERROR_MEDIA_LOCKED         = $00B1; { media is locked in drive               }
  ERROR_MEDIA_NOT_REMOVABLE  = $00B2; { media is not removable                 }
  ERROR_LOCKE_COUNT_EXCEEDED = $00B4; { media locke count exceeded             }
  ERROR_EJECT_REQUEST_FAILED = $00B5; { valid media eject request failed       }
 
  // Interrupt 13h/25h/26h error codes:
  ERROR_BAD_COMMAND          = $10001; { bad command                           }
  ERROR_BAD_ADDRESS_MARK     = $10002; { bad address mark                      }
  ERROR_WRITE_PROTECTED      = $10003; { write-protected disk                  }
  ERROR_SECTOR_NOT_FOUND     = $10004; { requested sector not found            }
  ERROR_RESET_FAILED         = $10005; { reset failed                          }
  ERROR_DISK_CHANGED         = $10006; { disk changed (floppy disk)            }
  ERROR_PARAMETER_FAILED     = $10007; { drive parameter activity failed       }
  ERROR_DMA_FAILURE          = $10008; { DMA failure/overrun                   }
  ERROR_DMA_SEGMENT_FAULT    = $10009; { attempted DMA across 64K boundary     }
  ERROR_BAD_SECTOR_DETECTED  = $1000A; { bad sector detected                   }
  ERROR_BAD_TRACK_DETECTED   = $1000B; { bad track detected                    }
  ERROR_INVALID_MEDIA        = $1000C; { invalid media or unsupported track    }
  ERROR_INVALID_SECTORS      = $1000D; { invalid number of sectors on format   }
  ERROR_CONTROL_DATA         = $1000E; { control data address mark detected    }
  ERROR_DMA_ARBITRATION      = $1000F; { DMA arbitration level out of range    }
  ERROR_DATA_ERROR           = $10010; { data error (uncorrectable CRC or ECC) }
  ERROR_DATA_ECC_CORRECTED   = $10011; { data ECC corrected                    }
  ERROR_CONTROLLER_FAILED    = $10020; { controller failed                     }
  ERROR_SEEK_FAILED          = $10040; { seek operation failed                 }
  ERROR_DEVICE_FAILED        = $10080; { device failed to respond (timeout)    }
  ERROR_DRIVE_NOT_READY      = $100AA; { drive not ready                       }
  ERROR_UNDEFINED            = $100BB; { undefined error                       }
  ERROR_WRITE_FAULT          = $100CC; { write fault                           }
  ERROR_STATUS_REGISTER      = $100E0; { status register error                 }
  ERROR_SENSE_FAILED         = $100FF; { sense operation failed                }
 
  // VWIN32 custom error codes:
  ERROR_UNKNOWN              = $00100000; { unknown error                      }
  ERROR_OPENING_DEVICE       = $00300000; { error trying to open device        }
 
  { Lock permission codes: }
  LOCK_ALLOW_WRITING  = $0001; { Allow write operations in level 1 lock. Write }
                               { operations are always blocked in level 2 & 3  }
                               { lock.                                         }
  LOCK_BLOCK_MAPPING  = $0002; { Block new file mappings in level 1 & 2 lock.  }
                               { New file mappings are always blocked in level }
                               { 3 lock.                                       }
                               { Read operations are always allowed in level   }
                               { 1 & 2 lock, and blocked in level 3 lock.      }
  LOCK_FOR_FORMATTING = $0004; { Locks the volume for formatting. Specified    }
                               { when a level 0 lock is obtained for the second}
                               { time.                                         }
 
Var
 
  VWIN32Error: DWord;
  VWIN32Device : THandle;
 
 
function VWIN32DIOC(ControlCode: Integer; Registers: PDIOC_Registers): Boolean;
var
  BytesReturned : DWord;
 
  function OpenDevice: Boolean;
  begin
     VWIN32Error := ERROR_NON;
    if (VWIN32Device = INVALID_HANDLE_VALUE) or (VWIN32Device = 0) then
    begin
      VWIN32Device := CreateFile(VWIN32_DEVICE_NAME,0,0,nil,0,FILE_FLAG_DELETE_ON_CLOSE,0);
      if (VWIN32Device = INVALID_HANDLE_VALUE) then
      VWIN32Error := ERROR_OPENING_DEVICE;
    end;
     Result := VWIN32Error = ERROR_NON;
  end;
 
  procedure CloseDevice;
  begin
    if (VWIN32Device <> INVALID_HANDLE_VALUE) then CloseHandle(VWIN32Device);
    VWIN32Device := INVALID_HANDLE_VALUE;
    Result := true;
  end;
 
begin
  VWIN32Error := ERROR_NON;
  if ControlCode = VWIN32_DIOC_CLOSE then CloseDevice
  else if OpenDevice then begin
    Result := DeviceIoControl(VWIN32Device,ControlCode,Registers,SizeOf(Registers^),
                              Registers,SizeOf(Registers^),BytesReturned,nil);
    if not result then VWIN32Error := ERROR_UNKNOWN;
  end
  else Result := false;
end;
 
 
function LockLogicalVolume(Drive: Byte; Level, Permission: Byte): Boolean;
var
  Registers: TDIOC_Registers;
begin
  VWIN32Error := ERROR_NON;
  Result := false;
  if (Level > 1) then Permission := $00
  else Permission := Permission and $07;
    Registers.EAX := $440D;
    Registers.EBX := (Level shl 8) or Drive;
    Registers.ECX := $484A;
    Registers.EDX := Permission;
    Registers.Flags := $00000000;
    if (VWIN32DIOC(VWIN32_DIOC_DOS_IOCTL,@Registers)) then
      if ((Registers.Flags and FLAG_CARRY) = 0) then Result := true;
  if not Result then
  begin
    Registers.EAX := $440D;
    Registers.EBX := (Level shl 8) or Drive;
    Registers.ECX := $084A;
    Registers.EDX := Permission;
    Registers.Flags := $00000000;
    if (VWIN32DIOC(VWIN32_DIOC_DOS_IOCTL,@Registers)) then begin
      if ((Registers.Flags and FLAG_CARRY) = 0) then Result := true
      else VWIN32Error := Registers.AX;
    end;
  end;
end;
 
function UnlockLogicalVolume(Drive: Byte): Boolean;
var
  Registers: TDIOC_Registers;
begin
  VWIN32Error := ERROR_NON;
  Result := false;
    Registers.EAX := $440D;
    Registers.EBX := Drive;
    Registers.ECX := $486A;
    Registers.Flags := $00000000;
    if (VWIN32DIOC(VWIN32_DIOC_DOS_IOCTL,@Registers)) then
      if ((Registers.Flags and FLAG_CARRY) = 0) then Result := true;
  if not Result then
  begin
    Registers.EAX := $440D;
    Registers.EBX := Drive;
    Registers.ECX := $086A;
    Registers.Flags := $00000000;
    if (VWIN32DIOC(VWIN32_DIOC_DOS_IOCTL,@Registers)) then
      if ((Registers.Flags and FLAG_CARRY) = 0) then Result := true
      else VWIN32Error := Registers.AX;
  end;
end;
 
 
function ReadHDD9x(HDD: Byte; Sector: DWord; Count: Word; Var Buffer): Boolean;
TYPE
 TPacket       = packed record
    PacketSize   : Byte;
    Unesed1      : Byte;
    NumSector    : Byte;
    Unesed2      : Byte;
    Buffer       : POINTER;
    Sector       : comp;
  end;
 
var
  Registers        : TDIOC_Registers;
  Packet           : TPacket;
begin
  Packet.PacketSize := 16   ; Packet.Unesed1 := 0;
  Packet.NumSector  := Count   ; Packet.Unesed2 := 0;
  Packet.Buffer     := @Buffer ; Packet.Sector  := Sector;
  VWIN32Error := ERROR_NON;
  Result := false;
  Registers.EAX := $4200; // Read one sector
  Registers.ECX := $0000; // Cyl 0; Sec 1
  Registers.EDX := $0080; // Head 0; Dev. Num 80h ( HDD0 )
  Registers.ESI := DWORD(@Buffer);     // Set ES:BX to Buffer
  VWIN32DIOC(VWIN32_DIOC_DOS_INT13,@Registers);
end;
 
 
function Read9xSector(Drive: Byte; Sector: DWord; Count: Word; Var Buffer): Boolean;
var
  Registers        : TDIOC_Registers;
  ReadWritePacket  : TReadWritePacket;
begin
  VWIN32Error := ERROR_NON;
  Result := false;
    ReadWritePacket.StartSector := Sector;
    ReadWritePacket.Sectors := Count;
    ReadWritePacket.Buffer := @Buffer;
 
    Registers.EAX   := $7305;
    Registers.EBX   := DWord(@ReadWritePacket);
    Registers.ECX   := $FFFFFFFF;
    Registers.EDX   := Drive;
    Registers.ESI   := $0000;
    Registers.Flags := $00000000;
 
    if VWIN32DIOC(VWIN32_DIOC_DOS_DRIVEINFO,@Registers) then
      if ((Registers.Flags and FLAG_CARRY) = 0) then Result := true;
 
  if (not Result) then
  begin
    ReadWritePacket.StartSector := Sector;
    ReadWritePacket.Sectors := Count;
    ReadWritePacket.Buffer := @Buffer;
 
    Registers.EAX := Drive-1;
    Registers.EBX := DWord(@ReadWritePacket);
    Registers.ECX := $FFFFFFFF;
    Registers.Flags := $00000000;
 
    if VWIN32DIOC(VWIN32_DIOC_DOS_INT25,@Registers) then begin
      if ((Registers.Flags and FLAG_CARRY) = 0) then Result := true
      else VWIN32Error := $10000 or (Registers.AX and $00FF);
    end;
  end;
end;
 
 
 
function Write9xSector(Drive: Byte; Sector: DWord; Count: Word; Var Buffer; Mode: Byte): Boolean;
var
  Registers      : TDIOC_Registers;
  ReadWritePacket: TReadWritePacket;
begin
  VWIN32Error := ERROR_NON;
  Result := false;
  if (LockLogicalVolume(Drive, 1, LOCK_ALLOW_WRITING)) then
  begin
      ReadWritePacket.StartSector := Sector;
      ReadWritePacket.Sectors := Count;
      ReadWritePacket.Buffer := @Buffer;
 
      Registers.EAX := $7305;
      Registers.EBX := DWord(@ReadWritePacket);
      Registers.ECX := $FFFFFFFF;
      Registers.EDX := Drive;
      Registers.ESI := $0001 or (Mode and $6000);
      Registers.Flags := $00000000;
      if (VWIN32DIOC(VWIN32_DIOC_DOS_DRIVEINFO,@Registers)) then
        if ((Registers.Flags and FLAG_CARRY) = 0) then Result := true;
    if (not Result) then
    begin
      ReadWritePacket.StartSector := Sector;
      ReadWritePacket.Sectors := Count;
      ReadWritePacket.Buffer := @Buffer;
      Registers.EAX := Drive-1;
      Registers.EBX := DWord(@ReadWritePacket);
      Registers.ECX := $FFFFFFFF;
      Registers.Flags := $00000000;
      if (VWIN32DIOC(VWIN32_DIOC_DOS_INT26,@Registers)) then
      begin
        if ((Registers.Flags and FLAG_CARRY) = 0) then Result := true
        else VWIN32Error := $10000 or (Registers.AX and $00FF);
      end;
    end;
    UnlockLogicalVolume(Drive);
  end;
end;
 
 
function __Mul(a,b: DWORD; var HiDWORD: DWORD): DWORD; // Result = LoDWORD
asm
  mul edx
  mov [ecx],edx
end;
 
 
 
function ReadNTSector(Drive: Byte; Sector,SectorCount: DWord; Var Buffer): Boolean;
var
  hDrive: THandle;
  DriveRoot: string;
  br,TmpLo,TmpHi: DWORD;
begin
  Result := False;
  TmpLo:=0;
  TmpHi:=TmpLo;
 
  DriveRoot := '\\.\' + Chr(64 + Drive) + ':';
  hDrive := CreateFile(
    PAnsiChar(DriveRoot), GENERIC_READ,
    FILE_SHARE_READ or FILE_SHARE_WRITE,
    nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
    if (hDrive = INVALID_HANDLE_VALUE) then Exit;
    TmpLo :=__Mul(Sector,dsBytePerSector,TmpHi);
    if SetFilePointer(hdrive,TmpLo,@TmpHi,FILE_BEGIN) = TmpLo then
    begin
     if ReadFile(hdrive,Buffer,dsBytePerSector*SectorCount,br,nil) then
     Result := BR = (dsBytePerSector*SectorCount);
  end;
      CloseHandle(hDrive);
end;
 
 
function WriteNTSector(Drive: Byte; Sector,SectorCount: DWord; Var Buffer): Boolean;
var
  hDrive: THandle;
  DriveRoot: string;
  bw,TmpLo,TmpHi: DWORD;
begin
  Result := False;
  DriveRoot := '\\.\' + Chr(64 + Drive) + ':';
  hDrive := CreateFile(
    PAnsiChar(DriveRoot), GENERIC_WRITE,
    FILE_SHARE_READ or FILE_SHARE_WRITE,
    nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
   if (hDrive = INVALID_HANDLE_VALUE) then Exit;
  TmpLo :=__Mul(Sector,dsBytePerSector,TmpHi);
  if SetFilePointer(hdrive,TmpLo,@TmpHi,FILE_BEGIN) = TmpLo then
  begin
   if not WriteFile(hdrive,Buffer,dsBytePerSector*SectorCount,bw,nil) then Exit;
   Result := bw = (dsBytePerSector*SectorCount);
  end;
   CloseHandle(hDrive);
end;
 
function ReadLogicalSector  (Drive: Byte; Sector: Int64; Count: Word; Var Buffer): Boolean;
begin
 case Win32Platform of
  VER_PLATFORM_WIN32_WINDOWS: Result:= Read9xSector(Drive, Sector, Count, Buffer);
  VER_PLATFORM_WIN32_NT     : Result:= ReadNTSector(Drive, Sector, Count, Buffer);
 End;
end;
 
function WriteLogicalSector  (Drive: Byte; Sector: Int64; Count: Word; Var Buffer): Boolean;
begin
 case Win32Platform of
  VER_PLATFORM_WIN32_WINDOWS: Result:= Write9xSector(Drive, Sector, Count, Buffer,1);
  VER_PLATFORM_WIN32_NT     : Result:= WriteNTSector(Drive, Sector, Count, Buffer);
 End;
end;
 
 
function ReadPlysicalSector (HddNumber: Byte; Sector:Int64; Count:word;  Var Buffer): DWORD;
var
  hFile: THandle;
  br,TmpLo,TmpHi: DWORD;
begin
  Result := 0;
  hFile := CreateFile(PChar('\\.\PhysicalDrive'+IntToStr(HddNumber)),
    GENERIC_READ,FILE_SHARE_READ,nil,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
  if hFile = INVALID_HANDLE_VALUE then Exit;
  TmpLo := __Mul(Sector,dsBytePerSector,TmpHi);
  if SetFilePointer(hFile,TmpLo,@TmpHi,FILE_BEGIN) = TmpLo then
  begin
    Count := Count*dsBytePerSector;
    if ReadFile(hFile,Buffer,Count,br,nil) then Result := br;
  end;
  CloseHandle(hFile);
end;
 
function WritePlysicalSector (HddNumber: Byte; Sector:Int64; Count:word;  Var Buffer): DWORD;
var
  hFile: THandle;
  bw,TmpLo,TmpHi: DWORD;
begin
  Result := 0;
  hFile := CreateFile(PChar('\\.\PhysicalDrive'+IntToStr(HddNumber)),
    GENERIC_WRITE,FILE_SHARE_READ,nil,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
  if hFile = INVALID_HANDLE_VALUE then Exit;
  TmpLo := __Mul(Sector,dsBytePerSector,TmpHi);
  if SetFilePointer(hFile,TmpLo,@TmpHi,FILE_BEGIN) = TmpLo then
  begin
    Count := Count*dsBytePerSector;
    if WriteFile(hFile,Buffer,Count,bw,nil) then Result := bw;
  end;
  CloseHandle(hFile);
end;
 
 
 
  Function GetLogic(C,H,S,Hmax,Smax:Int64):comp;
  BEGIN
   GetLogic:=(C*Hmax+H)*Smax+S-1;
  END;
 
 
  Procedure GetPhysical(N,Hmax,Smax:Int64; var C,H,S:Int64);
  BEGIN
   C:=N div (Hmax*Smax);
   H:=(N-C*Hmax*Smax) div Smax;
   S:=N-(C*Hmax+H)*Smax+1;
  END;
 
  PROCEDURE UnPackCylSec(CSec:word; var Cyl,Sec: Word);
  Begin
    Cyl := (CSec and 192) shl 2+CSec shr 8;
    Sec := CSec and 63;
  end;
 
  FUNCTION PackCylSec(Cyl,Sec: Word): Word;
  BEGIN
   PackCylSec := Sec+(Cyl and $300) shr 2+(Cyl shl 8)
  END;
 
 
end.