{----------------------------------------------------------------------------- ---> Project DelphiWorks <--- Version 1.4.1 ----------------------------------------------------------------------------- Unit: dwDrives.pas Description: Disk and drive operation functions Author: Codehunter Works Release: 1.4.1 State: Unstable History: 30.10.2004, 15:46:12 - Bug fixed: dwDriveFilesystemName: Return value trimmed 02.02.2005, 16:19:35 - changed dwHardDrivesLong: function to procedure, return value to param, added driveletter to output 02.02.2005, 16:52:15 - changed dwHardDrives: function to procedure, return value to param 20.07.2006, 21:54:11 - changed dwDriveTracksCount: Division by zero bug fixed -----------------------------------------------------------------------------} unit dwDrives; interface uses dwTypes, Classes; function dwApplicationHostDrive: Char; function dwAvailableDrives: TStringList; function dwCloseCD(Drive: Char): Boolean; function dwCompareDiskStruct(Drive: Char; var BPC, SPC, BPS, FC, TC: Integer; var FS, TS, DSN: Int64): Boolean; function dwDiskInDrive(Drive: Char): Boolean; function dwDiskWriteProtected(Drive: Char): Boolean; function dwDriveBytesPerCluster(Drive: Char): Integer; function dwDriveBytesPerSector(Drive: Char): Integer; function dwDriveCasePreserved(Drive: Char): Boolean; function dwDriveCylinders(Drive: Char): Integer; function dwDriveFATCount(Drive: Char): Integer; function dwDriveFilesystemName(Drive: Char): String; function dwDriveFreeClusters(Drive: Char): Integer; function dwDriveFreeSpace(Drive: Char): Int64; function dwDriveHeadCount(Drive: Char): Integer; function dwDriveHiddenSectors(Drive: Char): Integer; function dwDriveIndexToLetter(Drive: Byte): Char; function dwDriveLetterToIndex(Drive: Char): Byte; function dwDriveMaxFilenameLength(Drive: Char): Integer; function dwDriveReservedSectors(Drive: Char): Integer; function dwDriveRootEntries(Drive: Char): Integer; function dwDrivesCount: Integer; function dwDriveSectorsPerCluster(Drive: Char): Integer; function dwDriveSectorsPerFAT(Drive: Char): Integer; function dwDriveSectorsPerTrack(Drive: Char): Integer; function dwDriveSerialNumber(Drive: Char): String; function dwDriveSerialNumberInt(Drive: Char): Integer; function dwDriveTotalClusters(Drive: Char): Integer; function dwDriveTotalSpace(Drive: Char): Int64; function dwDriveTracksCount(Drive: Char): Integer; function dwDriveVolumeName(Drive: Char): String; function dwEjectRemovable(const Drive: Char): TDWEjectRemovableResult; function dwFloppyReady(const Drive: char): Boolean; function dwGetDeviceParamBlock(Drive: Char; var ParamBlock: TDeviceParamBlock): Word; function dwGetDriveShellInfo(const Drive: Char): TDWDriveShellInfo; function dwGetVolumeInformationX (Drive: Char; var V : TDriveInformation): Boolean; function dwOpenCD(Drive: Char): Boolean; function dwSetDeviceParamBlock(Drive: Char; var ParamBlock: TDeviceParamBlock): Word; function dwSystemHostDrive: Char; function dwValidateDriveLetter(Drive: Char): Char; function dwValidDriveLetter(Drive: Char): Boolean; function dwVWin32(CtlCode: TVWin32CtlCode; var Regs: TDiocRegisters): Boolean; procedure dwHardDrives(AList: TStrings); procedure dwHardDrivesLong(AList: TStrings); implementation uses dwMathematics, MMSystem, ShellAPI, SysUtils, Windows; function dwGetDriveShellInfo(const Drive: Char): TDWDriveShellInfo; var SHFileInfo : TSHFileInfo; begin ShGetFileInfo (PChar(Drive + ':\'), 0, SHFileInfo, SizeOf (TSHFileInfo), SHGFI_TYPENAME or SHGFI_DISPLAYNAME or SHGFI_SYSICONINDEX or SHGFI_ICON); with result do begin Icon := SHFileInfo.hIcon; Image := SHFileInfo.iIcon; DisplayName := SHFileInfo.szDisplayName; TypeName := SHFileInfo.szTypeName end end; function dwEjectRemovable(const Drive: Char): TDWEjectRemovableResult; var hDevice : THandle; Nb : DWORD; Reg : TDiocRegisters; VersionInfo : TOSVersionInfo; const VWIN32_DIOC_DOS_IOCTL = 1; IOCTL_STORAGE_EJECT_MEDIA = 2967560; begin result:= dwejeUnsupported; VersionInfo.dwOSVersionInfoSize := SizeOf(TOSVersionInfo); GetVersionEx(VersionInfo); if VersionInfo.dwPlatformId = VER_PLATFORM_WIN32_NT then hDevice := CreateFile(PChar('\\.\' + Drive + ':'), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0) else hDevice := CreateFile('\\.\VWIN32', 0, 0, nil, 0, FILE_FLAG_DELETE_ON_CLOSE, 0); if hDevice = INVALID_HANDLE_VALUE then RaiseLastWin32Error; if VersionInfo.dwPlatformId = VER_PLATFORM_WIN32_NT then begin if not DeviceIoControl(hDevice, IOCTL_STORAGE_EJECT_MEDIA, nil, 0, nil, 0, Nb, nil) then RaiseLastWin32Error; end else begin with Reg do begin EAX := $440D; EBX := Byte(Drive) - $40; ECX := $0849; end; DeviceIoControl(hDevice, VWIN32_DIOC_DOS_IOCTL, @Reg, SizeOf(Reg), @Reg, SizeOf(Reg), Nb, nil); if Reg.Flags and 1 = 1 then case Reg.EAX of $01: result:= dwejeUnsupported; $B1: result:= dwejeVolumeLocked; $B2: result:= dwejeUnremovable; $B5: result:= dwejeRequestFailed; end else result:= dwejeAllReady; end; CloseHandle(hDevice); end; function dwApplicationHostDrive: Char; begin result:= ParamStr(0)[1]; end; function dwAvailableDrives: TStringList; var i: Integer; C: String; DType: Integer; DriveString: String; begin result:= TStringList.Create; (* Zeichen 65 = A und 90 = Z -- Schleife durch alle möglichen Laufwerke *) for i := 65 to 90 do begin (* Aktuellen Schleifenindex in entsprechenden Laufwerksbezeichner wandeln *) C := chr(i)+':\'; (* Die Funktion GetDriveType() ermittelt den Medientyp des Laufwerks *) DType := GetDriveType(PChar(C)); (* Ermittelten Medientyp auswerten und entsprechende Meldung generieren *) case DType of 0: DriveString:= C+' Unbekannter Laufwerkstyp'; 1: DriveString:= C+' Kein Stammverzeichnis gefunden'; DRIVE_REMOVABLE: DriveString:= C+' Wechseldatenträger'; DRIVE_FIXED: DriveString:= C+' Festplatte'; DRIVE_REMOTE: DriveString:= C+' Netzwerklaufwerk'; DRIVE_CDROM: DriveString := C+' CD-ROM Laufwerk'; DRIVE_RAMDISK: DriveString := C+' RAM Disk'; end; (* Gültige Laufwerksbezeichner in Liste aufnehmen *) if not ((DType = 0) or (DType = 1)) then result.Add(DriveString); end; end; function dwCloseCD(Drive: Char): Boolean; var Res: MciError; OpenParm: TMCI_Open_Parms; Flags: DWORD; S: string; DeviceID: Word; begin Result := False; S := Drive + ':'; Flags := mci_Open_Type or mci_Open_Element; with OpenParm do begin dwCallback := 0; lpstrDeviceType := 'CDAudio'; lpstrElementName := PChar(S); end; Res := mciSendCommand(0, mci_Open, Flags, Longint(@OpenParm)); if Res = 0 then Exit; DeviceID := OpenParm.wDeviceID; try Res := mciSendCommand(DeviceID, MCI_SET, MCI_SET_DOOR_CLOSED, 0); if Res = 0 then Exit; Result := True; finally mciSendCommand(DeviceID, mci_Close, Flags, Longint(@OpenParm)); end; end; function dwDrivesCount: Integer; var TempList: TStringList; begin TempList:= dwAvailableDrives; result:= TempList.Count; TempList.Free; end; function dwDiskInDrive(Drive: Char): Boolean; var DriveNumber: Byte; ErrorMode : Word; begin result := FALSE; DriveNumber := ORD( UpCase(Drive) ); ErrorMode := SetErrorMode(SEM_FAILCRITICALERRORS); try if DiskSize(DriveNumber-ORD('A')+1) <> -1 then result:= TRUE; finally SetErrorMode(ErrorMode) end; end; function dwDiskWriteProtected(Drive: Char): Boolean; var ErrorMode: Word; PathName : String; TempName : String; begin ErrorMode := SetErrorMode(SEM_FAILCRITICALERRORS); try Assert(Upcase(drive) in ['A'..'Z'], 'Invalid drive specification'); PathName := drive + ':\'; SetLength(TempName, MAX_PATH+1); GetTempFileName(PChar(PathName), 'RWRO', 0, PChar(TempName)); result := (GetLastError = Windows.ERROR_WRITE_PROTECT); if not result then result := not SysUtils.DeleteFile(TempName); finally SetErrorMode(ErrorMode) end; end; function dwDriveSectorsPerCluster(Drive: Char): Integer; var RootPath: String; SectorsPerCluster, BytesPerSector, NumFreeClusters, TotalClusters: DWord; begin Drive:= dwValidateDriveLetter(Drive); RootPath:= Drive + ':\'; if GetDiskFreeSpace(PChar(RootPath), SectorsPerCluster, BytesPerSector, NumFreeClusters, TotalClusters) then result:= SectorsPerCluster else result:= -1; end; function dwDriveBytesPerSector(Drive: Char): Integer; var RootPath: String; SectorsPerCluster, BytesPerSector, NumFreeClusters, TotalClusters: DWord; begin Drive:= dwValidateDriveLetter(Drive); RootPath:= Drive + ':\'; if GetDiskFreeSpace(PChar(RootPath), SectorsPerCluster, BytesPerSector, NumFreeClusters, TotalClusters) then result:= BytesPerSector else result:= -1; end; function dwDriveBytesPerCluster(Drive: Char): Integer; begin result:= dwDriveBytesPerSector(Drive) * dwDriveSectorsPerCluster(Drive); end; function dwDriveFreeClusters(Drive: Char): Integer; var RootPath: String; SectorsPerCluster, BytesPerSector, NumFreeClusters, TotalClusters: DWord; begin Drive:= dwValidateDriveLetter(Drive); RootPath:= Drive + ':\'; if GetDiskFreeSpace(PChar(RootPath), SectorsPerCluster, BytesPerSector, NumFreeClusters, TotalClusters) then result:= NumFreeClusters else result:= -1; end; function dwDriveTotalClusters(Drive: Char): Integer; var RootPath: String; SectorsPerCluster, BytesPerSector, NumFreeClusters, TotalClusters: DWord; begin Drive:= dwValidateDriveLetter(Drive); RootPath:= Drive + ':\'; if GetDiskFreeSpace(PChar(RootPath), SectorsPerCluster, BytesPerSector, NumFreeClusters, TotalClusters) then result:= TotalClusters else result:= -1; end; function dwDriveFreeSpace(Drive: Char): Int64; begin result:= DiskFree(dwDriveLetterToIndex(Drive)); end; function dwDriveTotalSpace(Drive: Char): Int64; begin result:= DiskSize(dwDriveLetterToIndex(Drive)); end; function dwDriveSerialNumber(Drive: Char): String; var DI: TDriveInformation; TempStr: String; begin Drive:= dwValidateDriveLetter(Drive); dwGetVolumeInformationX(Drive, DI); TempStr:= IntToHex(DI.VolumeSerialNumber, 8); result:= Copy(TempStr, 1, 4) + '-' + Copy(TempStr, 5, 4); end; function dwDriveSerialNumberInt(Drive: Char): Integer; var DI: TDriveInformation; begin Drive:= dwValidateDriveLetter(Drive); dwGetVolumeInformationX(Drive, DI); result:= DI.VolumeSerialNumber; end; function dwDriveVolumeName(Drive: Char): String; var DI: TDriveInformation; begin Drive:= dwValidateDriveLetter(Drive); dwGetVolumeInformationX(Drive, DI); result:= DI.VolumeName; end; function dwDriveMaxFilenameLength(Drive: Char): Integer; var DI: TDriveInformation; begin Drive:= dwValidateDriveLetter(Drive); dwGetVolumeInformationX(Drive, DI); result:= DI.MaximumComponentLength; end; function dwDriveCasePreserved(Drive: Char): Boolean; var DI: TDriveInformation; begin Drive:= dwValidateDriveLetter(Drive); dwGetVolumeInformationX(Drive, DI); result:= TRUE;//([FS_CASE_IS_PRESERVED] in DI.FileSystemFlags); end; function dwDriveSectorsPerTrack(Drive: Char): Integer; var DPB: TDeviceParamBlock; begin Drive:= dwValidateDriveLetter(Drive); dwGetDeviceParamBlock(Drive, DPB); result:= DPB.BiosParamBlock.SectorsPerTrack; end; function dwDriveReservedSectors(Drive: Char): Integer; var DPB: TDeviceParamBlock; begin Drive:= dwValidateDriveLetter(Drive); dwGetDeviceParamBlock(Drive, DPB); result:= DPB.BiosParamBlock.ReservedSectors; end; function dwDriveTracksCount(Drive: Char): Integer; var TotalSectors, TracksOn, SectorsPerTrack, DriveHeads: Integer; begin result:= 0; Drive:= dwValidateDriveLetter(Drive); TotalSectors:= dwRoundUp(dwDriveTotalClusters(Drive) * dwDriveSectorsPerCluster(Drive)); SectorsPerTrack:= dwDriveSectorsPerTrack(Drive); if SectorsPerTrack > 0 then begin TracksOn:= dwRoundUp(TotalSectors div SectorsPerTrack)+1; DriveHeads:= dwDriveHeadCount(Drive); if DriveHeads > 0 then begin result:= dwRoundUp(TracksOn div DriveHeads); end; end; end; function dwDriveHeadCount(Drive: Char): Integer; var DPB: TDeviceParamBlock; begin Drive:= dwValidateDriveLetter(Drive); dwGetDeviceParamBlock(Drive, DPB); result:= DPB.BiosParamBlock.NumHeads; end; function dwDriveFATCount(Drive: Char): Integer; var DPB: TDeviceParamBlock; begin Drive:= dwValidateDriveLetter(Drive); dwGetDeviceParamBlock(Drive, DPB); result:= DPB.BiosParamBlock.NumFats; end; function dwDriveRootEntries(Drive: Char): Integer; var DPB: TDeviceParamBlock; begin Drive:= dwValidateDriveLetter(Drive); dwGetDeviceParamBlock(Drive, DPB); result:= DPB.BiosParamBlock.NumRootEntries; end; function dwDriveHiddenSectors(Drive: Char): Integer; var DPB: TDeviceParamBlock; begin Drive:= dwValidateDriveLetter(Drive); dwGetDeviceParamBlock(Drive, DPB); result:= DPB.BiosParamBlock.HiddenSectors; end; function dwDriveCylinders(Drive: Char): Integer; var DPB: TDeviceParamBlock; begin Drive:= dwValidateDriveLetter(Drive); dwGetDeviceParamBlock(Drive, DPB); result:= DPB.NumCylinders; end; function dwDriveSectorsPerFAT(Drive: Char): Integer; var DPB: TDeviceParamBlock; begin Drive:= dwValidateDriveLetter(Drive); dwGetDeviceParamBlock(Drive, DPB); result:= DPB.BiosParamBlock.SectorsPerFat; end; function dwDriveFilesystemName(Drive: Char): String; var DI: TDriveInformation; begin Drive:= dwValidateDriveLetter(Drive); dwGetVolumeInformationX(Drive, DI); result:= Trim(DI.FileSystemName); end; procedure dwHardDrives(AList: TStrings); var I: Integer; DriveString: String; DType: Integer; // DriveString: String; begin if not Assigned(AList) then exit; AList.Clear; (* Zeichen 65 = A und 90 = Z -- Schleife durch alle möglichen Laufwerke *) for I:= 65 to 90 do begin (* Aktuellen Schleifenindex in entsprechenden Laufwerksbezeichner wandeln *) DriveString:= Chr(i) + ':\'; (* Die Funktion GetDriveType() ermittelt den Medientyp des Laufwerks *) DType:= GetDriveType(PChar(DriveString)); (* Gültige Laufwerksbezeichner in Liste aufnehmen *) if (DType = DRIVE_FIXED) then begin AList.Add(DriveString); end; end; end; procedure dwHardDrivesLong(AList: TStrings); var I: Integer; DriveLetter: String; DType: Integer; DriveString: String; begin if not Assigned(AList) then exit; AList.Clear; (* Zeichen 65 = A und 90 = Z -- Schleife durch alle möglichen Laufwerke *) for I:= 65 to 90 do begin (* Aktuellen Schleifenindex in entsprechenden Laufwerksbezeichner wandeln *) DriveLetter:= Chr(i)+':\'; (* Die Funktion GetDriveType() ermittelt den Medientyp des Laufwerks *) DType:= GetDriveType(PChar(DriveLetter)); case DType of DRIVE_FIXED: begin DriveString:= PChar(dwDriveVolumeName(Chr(I))) + ' (' + DriveLetter + ')'; AList.Add(DriveString); end; end; end; end; function dwOpenCD(Drive: Char): Boolean; var Res: MciError; OpenParm: TMCI_Open_Parms; Flags: DWORD; S: string; DeviceID: Word; begin Result := False; S := Drive + ':'; Flags := mci_Open_Type or mci_Open_Element; with OpenParm do begin dwCallback := 0; lpstrDeviceType := 'CDAudio'; lpstrElementName := PChar(S); end; Res := mciSendCommand(0, mci_Open, Flags, Longint(@OpenParm)); if Res = 0 then Exit; DeviceID := OpenParm.wDeviceID; try Res := mciSendCommand(DeviceID, MCI_SET, MCI_SET_DOOR_OPEN, 0); if Res = 0 then Exit; Result := True; finally mciSendCommand(DeviceID, mci_Close, Flags, Longint(@OpenParm)); end; end; function dwSystemHostDrive: Char; var SysDir: Pchar; begin SysDir:= StrAlloc(MAX_PATH); GetWindowsDirectory(SysDir, MAX_PATH+1); result:= SysDir[0]; end; //--------------------------------------------------------------------- function dwValidDriveLetter(Drive: Char): Boolean; begin result:= not ((Ord(UpCase(Drive)) < 65) or (Ord(UpCase(Drive)) > 90)); end; function dwValidateDriveLetter(Drive: Char): Char; begin if dwValidDriveLetter(Drive) then result:= UpCase(Drive) else result:= 'C'; end; function dwDriveLetterToIndex(Drive: Char): Byte; begin Drive:= dwValidateDriveLetter(Drive); result:= (Ord(Drive) - 64); end; function dwDriveIndexToLetter(Drive: Byte): Char; begin if (Drive > 0) and (Drive < 27) then result:= UpCase(Chr(Drive)) else result:= 'C'; end; function dwGetVolumeInformationX (Drive: Char; var V : TDriveInformation): Boolean; var EM : integer; begin EM := SetErrorMode (SEM_FAILCRITICALERRORS); result:= FALSE; Drive:= dwValidateDriveLetter(Drive); try with V do begin SetLength (VolumeName, MAX_PATH); SetLength (FileSystemName, MAX_PATH); VolumeSerialNumber := 0; MaximumComponentLength := 0; FileSystemFlags := 0; GetVolumeInformation (PChar (Drive+':\'), PChar (VolumeName), MAX_PATH, @VolumeSerialNumber, MaximumComponentLength, FileSystemFlags, PChar (FileSystemName), MAX_PATH); end; finally SetErrorMode (EM); end; end; function dwVWin32(CtlCode: TVWin32CtlCode; var Regs: TDiocRegisters): Boolean; var hDevice: THandle; Count: DWORD; begin hDevice := CreateFile('\\.\VWIN32', 0, 0, nil, 0, FILE_FLAG_DELETE_ON_CLOSE, 0); Result := DeviceIoControl(hDevice, Ord(CtlCode), @Regs, SizeOf(Regs), @Regs, SizeOf(Regs), Count, nil); CloseHandle(hDevice); end; function dwGetDeviceParamBlock(Drive: Char; var ParamBlock: TDeviceParamBlock): Word; var Regs: TDiocRegisters; begin with Regs do begin EAX := $440D; EBX := Ord(UpCase(Drive)) - Ord('@'); ECX := $0860; EDX := LongInt(@ParamBlock); dwVWin32(ccVWin32IntIoctl, Regs); if (Flags and 1) <> 0 then Result := LoWord(EAX) else Result := 0; end; end; function dwSetDeviceParamBlock(Drive: Char; var ParamBlock: TDeviceParamBlock): Word; var Regs: TDiocRegisters; begin with Regs do begin EAX := $440D; EBX := Ord(UpCase(Drive)) - Ord('@'); ECX := $0840; EDX := LongInt(@ParamBlock); dwVWin32(ccVWin32IntIoctl, Regs); if (Flags and 1) <> 0 then Result := LoWord(EAX) else Result := 0; end; end; function dwFloppyReady(const Drive: char): Boolean; var DrvNum: byte; EMode: Word; begin result := false; DrvNum := ord(Drive); if DrvNum >= ord('a') then dec(DrvNum,$20); EMode := SetErrorMode(SEM_FAILCRITICALERRORS); try if DiskSize(DrvNum-$40) <> -1 then result := true else messagebeep(0); finally SetErrorMode(EMode); end; end; function dwCompareDiskStruct(Drive: Char; var BPC, SPC, BPS, FC, TC: Integer; var FS, TS, DSN: Int64): Boolean; begin result:= ((BPC = dwDriveBytesPerCluster(Drive)) and (SPC = dwDriveSectorsPerCluster(Drive)) and (BPS = dwDriveBytesPerSector(Drive)) and (FC = dwDriveFreeClusters(Drive)) and (TC = dwDriveTotalClusters(Drive)) and (FS = dwDriveFreeSpace(Drive)) and (TS = dwDriveTotalSpace(Drive)) and (DSN = dwDriveSerialNumberInt(Drive))); end; end.