Page 2 sur 2

Re: Accès direct Secteur de disque

Publié : jeu. 22/juin/2017 20:42
par Ollivier
Ça me rappelle ceci :

Code : Tout sélectionner

g=c800:5
Exécution complètement obsolète aujourd'hui sous Debug.com pour faire un pré-formatage matériel (Esdi et Seagate STxxxx). Des centaines d'heures de formatage de disques durs de 55 méga. Ça doit sûrement parler à certains d'entre vous.

Re: Accès direct Secteur de disque

Publié : ven. 23/juin/2017 7:53
par djes
Ollivier a écrit :Ça me rappelle ceci :

Code : Tout sélectionner

g=c800:5
Exécution complètement obsolète aujourd'hui sous Debug.com pour faire un pré-formatage matériel (Esdi et Seagate STxxxx). Des centaines d'heures de formatage de disques durs de 55 méga. Ça doit sûrement parler à certains d'entre vous.
Ouaip ! :)

Re: Accès direct Secteur de disque

Publié : ven. 23/juin/2017 9:17
par Micoute
Oui, je me souviens vaguement

Re: Accès direct Secteur de disque

Publié : jeu. 21/juin/2018 7:07
par Pierre Bellisle
Salut Jérome,
Àprès une année, j'imagine que tu as déjà trouvé la réponse... À tous hasards...

Pour lire un disque "physique" dans son intégralité, tu peut utiliser CreateFile("\\.\PhysicalDrive0"... (PhysicalDrive1, PhysicalDrive2, etc.)
CreateFile("\\.\PhysicalDrive0", GENERIC_READ OR GENERIC_WRITE, FILE_SHARE_READ OR FILE_SHARE_WRITE, BYVAL 0, OPEN_EXISTING, 0, 0)
Ceci nécessite un manifeste qui réclamera la permission Admin - level='requireAdministrator'

Pour lire un volume, utilise CreateFile("\\.\C:" ("\\.\D:", "\\.\E:", etc.)
CreateFile("\\.\C:", GENERIC_READ OR GENERIC_WRITE, FILE_SHARE_READ OR FILE_SHARE_WRITE, BYVAL 0, OPEN_EXISTING, 0, 0)

Voici du code que j'ai écris en PowerBASIC, tu pourras convertir si tu en as envie.

Code : Tout sélectionner

'-------------------------------------------------------------------------------------

'Cylinder start at zero, heads start at zero, and sectors at one but IOCTL_DISK_GET_DRIVE_GEOMETRY
'return CylinderCount, HeadCount, and SectorsCount so no need to add one.
'The math is TotalSectorCount = Cylinders * Heads(or Tracks) * SectorsByTrack
'But the main disparity reason reside somewhere else...
'
'IOCTL_DISK_GET_DRIVE_GEOMETRY/DISK_GEOMETRY will return CHS value that are depreciated
'by today standard. Becose of the way it was structured CHS is unable to give correct size
'for big disk, Microsoft is now using "Logical block adressing" or LBA wich is simply a sector number.
'The first LBA have number zero, unlike CHS where first sector is one.
'
'For a USB drive, since there is no mechanics, meaning no real cylinder, head, or sectorByTrack,
'the size don't have to fit exactly in a CHS sheme. So Microsoft took a simple approach
'for IOCTL_DISK_GET_DRIVE_GEOMETRY under modern Windows, always return 255 heads, always
'return 63 sectorsByTrack, always return 512 BytesPerSector and use the next formula
'to return calculated cylinder count...
'
'Cylinder = DiskSizeInBytes \ 255 * 63 * 512
'Note the "\" for integer division wich is not the same as "/" for Floating-point division.
'Here is what you got: 63 = 520,093,696 \ 255 * 63 * 512
'
'This way the cylinder count will always be small enough to stop legacy software to try to
'access a USB drive past the real end. They did not developped a sofisticated formula to get
'closer to the real size becose the main purpose is simply to protect.
'
'Now, to get the size of your USB stick, use IOCTL_DISK_GET_LENGTH_INFO/GET_LENGTH_INFORMATION
'as you did or use IOCTL_DISK_GET_DRIVE_GEOMETRY_EX/DISK_GEOMETRY_EX.
'The CHS geometry is still present for legacy purpose but there is also DiskSize
'as LARGE_INTEGER wich will give you the disk size in bytes.
'
'LbaCount = DiskSizeInByte / BytesBySector
'
'Now you can access any sector from zero to LbaCount - 1 using SetFilePointer/ReadFile.
'
'Note that modern hard disk work with 4096 bytes chunks, the main reason for the need
'of aligned partitions, but all this is transparent in the actual case.
'
'Note also that the old code posted at Disk sector Read-Write on any OS use
'the legacy CHS way and need to be updated.
'_____________________________________________________________________________

'This is a demo to show how to directly access
'a hard disk under any NT version of windows.

'Use it at your own risk, very easy to destroy the content of a hard-disk
'if you are not familiar with direct disk access.

'Accessing an NT system is fairly easy using CreateFile("\\.\PhysicalDrive0"...

'Thank to Semen Matusovski, William Burns and Lance Edmonds

'Pierre Bellisle
'_____________________________________________________________________________

#COMPILE EXE '#Win 9.07#
#REGISTER NONE
#DIM ALL
#INCLUDE "Win32Api.inc"
#INCLUDE "CommCtrl.inc"
#RESOURCE "SectorReader10.pbr" 'Set manifest to get admin right

GLOBAL hDlg AS DWORD

$AppName         = "Direct disk read"

%LabelDisk       = 101
%LabelDiskCount  = 102
%LabelBlockCount = 103
%LabelCyl        = 104
%LabelHead       = 105
%LabelSect       = 106
%LabelMeg        = 107
%LabelStatus     = 108
%ComboDisk       = 201
%ButtonRead      = 301
%ButtonTry       = 302
%EditBlockIndex  = 401
%EditHex         = 402
%OptionPhysical  = 501
%OptionLogical   = 502
%UpDownH         = 601

%SECURITY_NT_AUTHORITY          = 005
%BlockSize                      = 512
%IOCTL_DISK_GET_DRIVE_GEOMETRY  = &H00070000
%FILE_DEVICE_DISK               = &h07              '%IOCTL_DISK_GET_LENGTH_INFO
%IOCTL_DISK_BASE                = %FILE_DEVICE_DISK '%IOCTL_DISK_GET_LENGTH_INFO
%METHOD_BUFFERED                = 0                 '%IOCTL_DISK_GET_LENGTH_INFO
%FILE_READ_ACCESS               = &h01              '%IOCTL_DISK_GET_LENGTH_INFO
%IOCTL_DISK_GET_LENGTH_INFO     = &h7405C           '%IOCTL_DISK_GET_LENGTH_INFO
%FSCTL_ALLOW_EXTENDED_DASD_IO   = &H00090083        'CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 32, METHOD_NEITHER,  FILE_ANY_ACCESS)


%Unknown        = 00 'Format is unknown
%F5_1Pt2_512    = 01 '5.25", 1.2MB,  512 bytes/sector
%F3_1Pt44_512   = 02 '3.5",  1.44MB, 512 bytes/sector
%F3_2Pt88_512   = 03 '3.5",  2.88MB, 512 bytes/sector
%F3_20Pt8_512   = 04 '3.5",  20.8MB, 512 bytes/sector
%F3_720_512     = 05 '3.5",  720KB,  512 bytes/sector
%F5_360_512     = 06 '5.25", 360KB,  512 bytes/sector
%F5_320_512     = 07 '5.25", 320KB,  512 bytes/sector
%F5_320_1024    = 08 '5.25", 320KB,  1024 bytes/sector
%F5_180_512     = 09 '5.25", 180KB,  512 bytes/sector
%F5_160_512     = 10 '5.25", 160KB,  512 bytes/sector
%RemovableMedia = 11 'Removable media other than floppy
%FixedMedia     = 12 'Fixed hard disk media
%F3_120M_512    = 13 '3.5",   120M Floppy
%F3_640_512     = 14 '3.5" ,  640KB,  512 bytes/sector
%F5_640_512     = 15 '5.25",  640KB,  512 bytes/sector
%F5_720_512     = 16 '5.25",  720KB,  512 bytes/sector
%F3_1Pt2_512    = 17 '3.5" ,  1.2Mb,  512 bytes/sector
%F3_1Pt23_1024  = 18 '3.5" ,  1.23Mb, 1024 bytes/sector
%F5_1Pt23_1024  = 19 '5.25",  1.23MB, 1024 bytes/sector
%F3_128Mb_512   = 20 '3.5" MO 128Mb   512 bytes/sector
%F3_230Mb_512   = 21 '3.5" MO 230Mb   512 bytes/sector
%F8_256_128     = 22 '8",     256KB,  128 bytes/sector
%F3_200Mb_512   = 23 '3.5",   200M Floppy (HiFD)
%F3_240M_512    = 24 '3.5",   240Mb Floppy (HiFD)
%F3_32M_512     = 25 '3.5",   32Mb Floppy

TYPE GET_LENGTH_INFORMATION
 Length AS QUAD
END TYPE

TYPE DISK_GEOMETRY
 Cylinders         AS QUAD
 MediaType         AS DWORD '0 unknown, 11 RemovableMedia, 12 FixedMedia, else floppy
 TracksPerCylinder AS DWORD
 SectorsPerTrack   AS DWORD
 BytesPerSector    AS DWORD
END TYPE

DECLARE FUNCTION SetFilePointerEx LIB "Kernel32.dll" ALIAS "SetFilePointerEx" _
(BYVAL hFile AS DWORD, BYVAL liDistanceToMove AS QUAD, lpNewFilePointer AS QUAD, BYVAL dwMoveMethod AS DWORD) AS LONG
'_____________________________________________________________________________

FUNCTION IsUserLocalAdmin() AS LONG 'IsUserAnAdmin api may not be there in future Windows release, so doing it the safe way.
 LOCAL NtAuthority         AS SID_IDENTIFIER_AUTHORITY
 LOCAL AdministratorsGroup AS LONG
 LOCAL IsMember            AS LONG

 NtAuthority.value(5) = %SECURITY_NT_AUTHORITY 'SECURITY_NT_AUTHORITY = 5

 IF AllocateAndInitializeSid(NtAuthority, 2, %SECURITY_BUILTIN_DOMAIN_RID, _
                             %DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, _
                             AdministratorsGroup) THEN
    IF CheckTokenMembership(%NULL, BYVAL AdministratorsGroup, IsMember) THEN
      FUNCTION = IsMember
    END IF
    FreeSid(BYVAL AdministratorsGroup)
 END IF

END FUNCTION
'_____________________________________________________________________________

FUNCTION GeometryMediaTypeGet(BYVAL MediaType AS DWORD) EXPORT AS STRING

 SELECT CASE MediaType
   CASE %Unknown        : FUNCTION = "Format is unknown"
   CASE %F5_1Pt2_512    : FUNCTION = "A 5.25"" floppy, with 1.2MB and 512 bytes/sector."
   CASE %F3_1Pt44_512   : FUNCTION = "A 3.5"" floppy, with 1.44MB and 512 bytes/sector."
   CASE %F3_2Pt88_512   : FUNCTION = "A 3.5"" floppy, with 2.88MB and 512 bytes/sector."
   CASE %F3_20Pt8_512   : FUNCTION = "A 3.5"" floppy, with 20.8MB and 512 bytes/sector."
   CASE %F3_720_512     : FUNCTION = "A 3.5"" floppy, with 720KB and 512 bytes/sector."
   CASE %F5_360_512     : FUNCTION = "A 5.25"" floppy, with 360KB and 512 bytes/sector."
   CASE %F5_320_512     : FUNCTION = "A 5.25"" floppy, with 320KB and 512 bytes/sector."
   CASE %F5_320_1024    : FUNCTION = "A 5.25"" floppy, with 320KB and 1024 bytes/sector."
   CASE %F5_180_512     : FUNCTION = "A 5.25"" floppy, with 180KB and 512 bytes/sector."
   CASE %F5_160_512     : FUNCTION = "A 5.25"" floppy, with 160KB and 512 bytes/sector."
   CASE %RemovableMedia : FUNCTION = "Removable media other than floppy."
   CASE %FixedMedia     : FUNCTION = "Fixed hard disk media."
   CASE %F3_120M_512    : FUNCTION = "A 3.5"" floppy, with 120MB and 512 bytes/sector."
   CASE %F3_640_512     : FUNCTION = "A 3.5"" floppy, with 640KB and 512 bytes/sector."
   CASE %F5_640_512     : FUNCTION = "A 5.25"" floppy, with 640KB and 512 bytes/sector."
   CASE %F5_720_512     : FUNCTION = "A 5.25"" floppy, with 720KB and 512 bytes/sector."
   CASE %F3_1Pt2_512    : FUNCTION = "A 3.5"" floppy, with 1.2MB and 512 bytes/sector."
   CASE %F3_1Pt23_1024  : FUNCTION = "A 3.5"" floppy, with 1.23MB and 1024 bytes/sector."
   CASE %F5_1Pt23_1024  : FUNCTION = "A 5.25"" floppy, with 1.23MB and 1024 bytes/sector."
   CASE %F3_128Mb_512   : FUNCTION = "A 3.5"" floppy, with 128MB and 512 bytes/sector."
   CASE %F3_230Mb_512   : FUNCTION = "A 3.5"" floppy, with 230MB and 512 bytes/sector."
   CASE %F8_256_128     : FUNCTION = "An 8"" floppy, with 256KB and 128 bytes/sector."
   CASE %F3_200Mb_512   : FUNCTION = "A 3.5"" floppy, with 200MB and 512 bytes/sector. (HiFD.)"
   CASE %F3_240M_512    : FUNCTION = "A 3.5"" floppy, with 240MB and 512 bytes/sector. (HiFD.)"
   CASE %F3_32M_512     : FUNCTION = "A 3.5"" floppy, with 32MB and 512 bytes/sector."
 END SELECT

END FUNCTION
'_____________________________________________________________________________

FUNCTION MediaTypeGet(BYVAL MediaType AS DWORD) EXPORT AS STRING

 SELECT CASE MediaType
   CASE %DRIVE_UNKNOWN     : FUNCTION = "DRIVE_UNKNOWN"
   CASE %DRIVE_NO_ROOT_DIR : FUNCTION = "DRIVE_NO_ROOT_DIR"
   CASE %DRIVE_REMOVABLE   : FUNCTION = "DRIVE_REMOVABLE"
   CASE %DRIVE_REMOTE      : FUNCTION = "DRIVE_REMOTE"
   CASE %DRIVE_CDROM       : FUNCTION = "DRIVE_CDROM"
   CASE %DRIVE_RAMDISK     : FUNCTION = "DRIVE_RAMDISK"
   CASE %DRIVE_FIXED       : FUNCTION = "DRIVE_FIXED"
 END SELECT

END FUNCTION
'_____________________________________________________________________________

FUNCTION GetVolumeInfo(zDisk AS ASCIIZ * 4)AS LONG
 LOCAL zVolumeName        AS ASCIIZ * 21
 LOCAL zFileSystem        AS ASCIIZ * 21
 LOCAL VolumeSerialNumber AS DWORD
 LOCAL FileNameMaxLen     AS DWORD
 LOCAL FileSystemFlag     AS LONG

 IF GetVolumeInformation(zDisk, _
                         zVolumeName, 20, _
                         VolumeSerialNumber, _
                         FileNameMaxLen, _
                         FileSystemFlag, _
                         zFileSystem, 20) = %FALSE THEN
 ELSE
   FUNCTION = %TRUE 'Media inserted
 END IF
END FUNCTION
'_____________________________________________________________________________________

FUNCTION GetPhysicalDiskId(BYREF sBuffer AS STRING, BYVAL BufferLen AS DWORD) EXPORT AS DWORD
 LOCAL DiskGeometry      AS DISK_GEOMETRY
 LOCAL sPhysicalDiskList AS STRING
 LOCAL PhysicalDiskIndex AS DWORD
 LOCAL PhysicalDiskCount AS DWORD
 LOCAL hDevice           AS DWORD

 DO
   hDevice = CreateFile("\\.\PhysicalDrive" & FORMAT$(PhysicalDiskIndex), _ 'Could be a HD, flash, CdRom, etc.
                         %GENERIC_READ, %FILE_SHARE_READ OR %FILE_SHARE_WRITE, BYVAL 0, %OPEN_EXISTING, 0, 0)
   IF hDevice = %INVALID_HANDLE_VALUE THEN
     EXIT DO 'No more devices
   ELSE
     IF DeviceIoControl(hDevice, %IOCTL_DISK_GET_DRIVE_GEOMETRY, BYVAL 0, _ 'Will be TRUE if device is a disk
                        BYVAL 0, BYVAL VARPTR(DiskGeometry), SIZEOF(DISK_GEOMETRY), 0, BYVAL 0 ) THEN
       sPhysicalDiskList = sPhysicalDiskList & FORMAT$(PhysicalDiskIndex, "00") & ":" 'Disk are zero based, Result example: "00:01:04:06"
       INCR PhysicalDiskCount
     END IF
     CloseHandle(hDevice)
     INCR PhysicalDiskIndex
   END IF
 LOOP
 IF PhysicalDiskCount THEN
   sPhysicalDiskList = LEFT$(sPhysicalDiskList, -1) 'Remove last ":"
   sBuffer           = LEFT$(sPhysicalDiskList, BufferLen) 'Respect buffer lenght
 ELSE
   sBuffer = ""
 END IF
 FUNCTION = PhysicalDiskCount

END FUNCTION
'_____________________________________________________________________________

FUNCTION GetLogicalDiskId(BYREF sBuffer AS STRING, BYVAL BufferLen AS DWORD) EXPORT AS DWORD
 LOCAL sLogicalDiskList      AS STRING
 LOCAL sLogicalFixedDiskList AS STRING
 LOCAL LogicalDiskIndex      AS DWORD
 LOCAL LogicalDiskCount      AS DWORD
 LOCAL LogicalFixedDiskCount AS DWORD
 LOCAL LogicalDiskListLen    AS DWORD
 LOCAL zDrive                AS ASCIIZ * 4

 sLogicalDiskList   = NUL$(26 * 4 + 1)
 LogicalDiskListLen = GetLogicalDriveStrings(LEN(sLogicalDiskList), BYVAL STRPTR(sLogicalDiskList)) 'C:\<NULL>D:\<NULL>R:\<NULL><NULL>
 IF LogicalDiskListLen <= BufferLen THEN 'Success
   REPLACE $NUL WITH "" IN sLogicalDiskList
   LogicalDiskCount = LogicalDiskListLen / 4
   FOR LogicalDiskIndex = 0 TO LogicalDiskCount - 1
     zDrive = MID$(sLogicalDiskList, LogicalDiskIndex * 3 + 1, 3)
     SELECT CASE GetDriveType(zDrive)
       CASE %DRIVE_UNKNOWN
       CASE %DRIVE_NO_ROOT_DIR
       CASE %DRIVE_REMOTE
       CASE %DRIVE_CDROM
       CASE %DRIVE_RAMDISK
      'CASE %DRIVE_REMOVABLE
       CASE %DRIVE_FIXED, %DRIVE_REMOVABLE
         IF GetVolumeInfo(zDrive) THEN 'Is there a media inserted
           sLogicalFixedDiskList = sLogicalFixedDiskList & zDrive
           INCR LogicalFixedDiskCount
         END IF
     END SELECT
   NEXT
   sBuffer  = sLogicalFixedDiskList
 ELSE 'sBuffer is too small, LogicalDiskListLen is the required size
   sBuffer  = ""
 END IF
 FUNCTION = LogicalFixedDiskCount

END FUNCTION
'_____________________________________________________________________________

FUNCTION GetPhysicalDiskGeometry(BYVAl hDisk AS DWORD, BYREF DiskGeometry AS DISK_GEOMETRY) EXPORT AS DWORD

 'Does not work on USB FAT16
 IF DeviceIoControl(hDisk, %IOCTL_DISK_GET_DRIVE_GEOMETRY, BYVAL 0, _
                    BYVAL 0, BYVAL VARPTR(DiskGeometry), SIZEOF(DISK_GEOMETRY), 0, BYVAL 0 ) THEN
   FUNCTION = %TRUE
 END IF

END FUNCTION
'_____________________________________________________________________________

FUNCTION OpenPhysicalDisk(BYVAL PhysicalDiskId AS DWORD, BYVAL WritePermission AS LONG) EXPORT AS DWORD
 LOCAL zDisk AS ASCIIZ * 20

 zDisk = "\\.\PhysicalDrive" & FORMAT$(PhysicalDiskId) 'Physical disk as "\\.\PhysicalDrive0"

 IF WritePermission THEN 'Read & Write
   FUNCTION = CreateFile(zDisk, %GENERIC_READ OR %GENERIC_WRITE, _
                                %FILE_SHARE_READ OR %FILE_SHARE_WRITE, BYVAL 0, %OPEN_EXISTING, 0, 0)
 ELSE 'Read only
   FUNCTION = CreateFile(zDisk, %GENERIC_READ, _
                                %FILE_SHARE_READ OR %FILE_SHARE_WRITE, BYVAL 0, %OPEN_EXISTING, 0, 0 )
 END IF

END FUNCTION
'_____________________________________________________________________________

FUNCTION OpenLogicalDisk(BYVAL sLogicalDisk AS STRING, BYVAL WritePermission AS LONG) EXPORT AS DWORD
 LOCAL zDisk AS ASCIIZ * 8

 zDisk =  "\\.\" & LEFT$(sLogicalDisk, 2) 'Logical disk as "\\.\C:"
 IF WritePermission THEN 'Read & Write
   FUNCTION = CreateFile(zDisk, %GENERIC_READ OR %GENERIC_WRITE, _
                                %FILE_SHARE_READ OR %FILE_SHARE_WRITE, BYVAL 0, %OPEN_EXISTING, 0, 0)
 ELSE 'Read only
   FUNCTION = CreateFile(zDisk, %GENERIC_READ, _
                                %FILE_SHARE_READ OR %FILE_SHARE_WRITE, BYVAL 0, %OPEN_EXISTING, 0, 0 )
 END IF

END FUNCTION
'_____________________________________________________________________________

FUNCTION GetPhysicalDiskBlockCount(BYVAL hDisk AS DWORD) EXPORT AS QUAD
 LOCAL DiskGeometry AS DISK_GEOMETRY
 LOCAL OldErrMode   AS LONG

 'DeviceIoControl(BYVAL hDisk, %FSCTL_ALLOW_EXTENDED_DASD_IO, BYVAL %NULL, 0, _
 '                BYVAL %NULL, 0, BytesReturned, BYVAL %NULL)
 'Remarks: A call using the FSCTL_ALLOW_EXTENDED_DASD_IO control code should only be used with great caution
 '         by programmers familiar with the underlying structure of a hard disk drive and file system.
 '         Improper use or inaccurate checking in subsequent write operations to the partition
 '         can result in damage to data on the partition, or destruction of the entire partition.
 '         The FSCTL_ALLOW_EXTENDED_DASD_IO control code is used to signal the file system driver
 '         not to perform any I/O boundary checks on read or write calls made with the specified handle.
 '         FSCTL_ALLOW_EXTENDED_DASD_IO allows access to hidden sectors, a part of the partition
 '         that might exist between the first sector of the partition (the boot parameter block)
 '         and the first useful sector of the partition. FSCTL_ALLOW_EXTENDED_DASD_IO also
 '         allows access to lost clusters, which might exist between the last useful cluster
 '         and the end of the partition.

 OldErrMode = SetErrorMode(%SEM_FAILCRITICALERRORS) 'Prevent pop-up dialogs if device is not present or loaded
 IF DeviceIoControl(hDisk, %IOCTL_DISK_GET_DRIVE_GEOMETRY, BYVAL 0, _
                    BYVAL 0, BYVAL VARPTR(DiskGeometry), SIZEOF(DISK_GEOMETRY), 0, BYVAL 0 ) THEN
   FUNCTION  = DiskGeometry.Cylinders * DiskGeometry.TracksPerCylinder * DiskGeometry.SectorsPerTrack
 END IF
 SetErrorMode(OldErrMode)

END FUNCTION
'_____________________________________________________________________________

FUNCTION GetLogicalDiskBlockCount(BYVAL sDisk AS STRING) EXPORT AS QUAD
 LOCAL FreeBytesForUser AS QUAD 'Available to user
 LOCAL UserDiskSize     AS QUAD 'Available to user
 LOCAL FreeByteOnDisk   AS QUAD
 LOCAL OldErrMode       AS LONG

 'DeviceIoControl(BYVAL hDisk, %FSCTL_ALLOW_EXTENDED_DASD_IO, BYVAL %NULL, 0, _ '
 '                BYVAL %NULL, 0, BytesReturned, BYVAL %NULL)
 'Remarks: A call using the FSCTL_ALLOW_EXTENDED_DASD_IO control code should only be used with great caution
 '         by programmers familiar with the underlying structure of a hard disk drive and file system.
 '         Improper use or inaccurate checking in subsequent write operations to the partition
 '         can result in damage to data on the partition, or destruction of the entire partition.
 '         The FSCTL_ALLOW_EXTENDED_DASD_IO control code is used to signal the file system driver
 '         not to perform any I/O boundary checks on read or write calls made with the specified handle.
 '         FSCTL_ALLOW_EXTENDED_DASD_IO allows access to hidden sectors, a part of the partition
 '         that might exist between the first sector of the partition (the boot parameter block)
 '         and the first useful sector of the partition. FSCTL_ALLOW_EXTENDED_DASD_IO also
 '         allows access to lost clusters, which might exist between the last useful cluster
 '         and the end of the partition.

 OldErrMode = SetErrorMode(%SEM_FAILCRITICALERRORS) 'Prevent pop-up dialogs if device is not present or loaded

 GetDiskFreeSpaceEx(BYVAL STRPTR(sDisk), FreeBytesForUser, UserDiskSize, FreeByteOnDisk)
 FUNCTION = UserDiskSize / %BlockSize
 SetErrorMode(OldErrMode)

END FUNCTION
'_____________________________________________________________________________

FUNCTION DiskRead(BYVAL hDisk AS DWORD, BYVAL BlockIndex AS QUAD, BYREF sBuffer AS STRING, BYVAL BufferLen AS DWORD) EXPORT AS DWORD
 LOCAL FilePointer    AS QUAD
 LOCAL NewFilePointer AS QUAD
 LOCAL BytesReturned  AS DWORD

 FilePointer = BlockIndex * %BlockSize
 SetFilePointerEx(hDisk, FilePointer, NewFilePointer, %FILE_BEGIN)
 IF ReadFile(hDisk, BYVAL STRPTR(sBuffer), BufferLen, BytesReturned, BYVAL %NULL) THEN
   FUNCTION = BytesReturned
 END IF

END FUNCTION
'_____________________________________________________________________________

FUNCTION DiskWrite(BYVAL hDisk AS DWORD, BYVAL BlockIndex AS QUAD, BYREF sBuffer AS STRING, BYVAL BufferLen AS DWORD) EXPORT AS DWORD
 LOCAL FilePointer    AS QUAD
 LOCAL NewFilePointer AS QUAD
 LOCAL BytesWritten   AS DWORD

 FilePointer = BlockIndex * %BlockSize
 SetFilePointerEx(hDisk, FilePointer, NewFilePointer, %FILE_BEGIN)
 IF WriteFile(hDisk, BYVAL STRPTR(sBuffer), BufferLen, BytesWritten, BYVAL %NULL) THEN
   FUNCTION = BytesWritten
 END IF

END FUNCTION
'_____________________________________________________________________________

FUNCTION CloseDisk(BYREF hDisk AS DWORD) EXPORT AS DWORD

 CloseHandle(hDisk)
 FUNCTION = GetLastError()
 hDisk = 0

END FUNCTION
'_____________________________________________________________________________

FUNCTION StringToHexView(sString AS STRING) AS STRING 'See D:\Basic\Bas\SRC\Hex\HexToStrGary03.bas
 LOCAL  sBuffer    AS STRING
 LOCAL  LooperLine AS LONG
 LOCAL  LooperChar AS LONG
 LOCAL  CharPos    AS LONG
 LOCAL  StringLen  AS LONG

 StringLen  = LEN(sString)
 FOR LooperLine = 0 TO StringLen - 1 STEP 16

   sBuffer = sBuffer & HEX$(LooperLine, 4) & ": "  'Offset

   FOR LooperChar = 1 TO 16 'Hex
     CharPos = LooperLine + LooperChar
     IF CharPos <= StringLen THEN
       sBuffer = sBuffer & HEX$(ASC(sString, CharPos), 2) & $SPC
     END IF
   NEXT

   '000000: 31 32 33 34 35 36 37 38 39 30 41 42 43 44 45 46 ; 1234567890ABCDEF
   sBuffer = sBuffer & SPACE$(54 - (LEN(sBuffer) MOD 74)) & "; "

   FOR LooperChar = 1 TO 16 'Ascii
     CharPos = LooperLine + LooperChar
     IF CharPos <= StringLen THEN
       SELECT CASE ASC(sString, CharPos)
         CASE 0, 1, 9, 10, 13, 27, 28, 29, 30, 31, _ '"Courier New"
              127, 129, 140, 141, 143, 144, 152, 157
           sBuffer = sBuffer & "."
         CASE ELSE
           sBuffer = sBuffer & MID$(sString, CharPos, 1)
       END SELECT

     END IF
   NEXT
   sBuffer = sBuffer & $CRLF
 NEXT
 FUNCTION = sBuffer

END FUNCTION
'______________________________________________________________________________

CALLBACK FUNCTION DlgProc()
 LOCAL  pNotifyMessageHeader         AS NMHDR POINTER
 LOCAL  pNotifyMessageUpDown         AS NM_UPDOWN POINTER
 LOCAL  MinMaxInfoPtr                AS MINMAXINFO POINTER
 DIM    UpDownAccelleration(0 TO 7)  AS UDACCEL
 LOCAL  DiskGeometry                 AS DISK_GEOMETRY
 STATIC sLogicalDisk                 AS STRING
 LOCAL  sBuffer                      AS STRING
 STATIC sDisk                        AS STRING
 LOCAL  BlockIndex                   AS QUAD
 STATIC BlockCount                   AS QUAD
 LOCAL  UpDownAccellerationStepCount AS DWORD
 STATIC hDisk                        AS DWORD
 STATIC PhysicalDisk                 AS DWORD
 STATIC Disk                         AS DWORD
 STATIC DiskCount                    AS DWORD
 STATIC DiskError                    AS LONG
 LOCAL  ReadCount                    AS LONG
 LOCAL  Looper                       AS LONG
 STATIC UpDownValH                   AS LONG
 STATIC UpDownDeltaH                 AS LONG
 STATIC UpDownStartPosition          AS LONG

 SELECT CASE CBMSG

   CASE %WM_INITDIALOG
     UpDownStartPosition = 0 - 2147483648 + 1 'Needed for 2 tb, LONG -2,147,483,648 to +2,147,483,647  (+1 so the control limit will be respected)
     'Sets the acceleration for the up-down control
     UpDownAccelleration(0).nSec = 1
     UpDownAccelleration(0).nInc = 1
     UpDownAccelleration(1).nSec = 2
     UpDownAccelleration(1).nInc = 10
     UpDownAccelleration(2).nSec = 3
     UpDownAccelleration(2).nInc = 100
     UpDownAccelleration(3).nSec = 4
     UpDownAccelleration(3).nInc = 1000
     UpDownAccelleration(4).nSec = 5
     UpDownAccelleration(4).nInc = 10000
     UpDownAccelleration(5).nSec = 6
     UpDownAccelleration(5).nInc = 100000
     UpDownAccelleration(6).nSec = 7
     UpDownAccelleration(6).nInc = 1000000
     UpDownAccelleration(7).nSec = 8
     UpDownAccelleration(7).nInc = 10000000
     UpDownAccellerationStepCount = 8
     CONTROL SEND hDlg, %UpDownH, %UDM_SETACCEL, UpDownAccellerationStepCount, VARPTR(UpDownAccelleration(0))

     PostMessage(hDlg, %WM_COMMAND, MAKDWD(%OptionPhysical, %BN_CLICKED), GetDlgItem(hDlg, %OptionPhysical))

   CASE %WM_COMMAND
     SELECT CASE LOWRD(CBWPARAM)

       CASE %OptionLogical, %OptionPhysical
         IF CBCTLMSG = %BN_CLICKED OR CBCTLMSG = 1 THEN

           BlockIndex = 0
           CONTROL SET TEXT hDlg, %EditBlockIndex, "0"
           CONTROL GET CHECK hDlg, %OptionPhysical TO PhysicalDisk
           COMBOBOX RESET hDlg, %ComboDisk
           IF PhysicalDisk THEN
             sBuffer = NUL$(3 * 26)
             DiskCount = GetPhysicalDiskId(sBuffer, LEN(sBuffer))
             IF DiskCount = 0 THEN
               MessageBox(hDlg, BYCOPY "No disk fatal error", BYCOPY $AppName, %MB_ICONINFORMATION OR %MB_OK)
               PostMessage(hDlg, %WM_SYSCOMMAND, %SC_CLOSE, BYVAL 0)
             ELSE
               FOR Looper = 1 TO DiskCount
                 COMBOBOX ADD hDlg, %ComboDisk, PARSE$(sBuffer, ":", Looper)
               NEXT
               Disk = VAL(PARSE$(sBuffer, ":", 1)) 'Set to first disk, zero based
               COMBOBOX SELECT hDlg, %ComboDisk, 1 'Set to first disk, zero based
             END IF
           ELSE 'OptionLogical
             sBuffer = NUL$(3 * 26)
             DiskCount = GetLogicalDiskId(sBuffer, LEN(sBuffer))
             IF DiskCount = 0 THEN
               MessageBox(hDlg, BYCOPY "No disk fatal error", BYCOPY $AppName, %MB_ICONINFORMATION OR %MB_OK)
               PostMessage(hDlg, %WM_SYSCOMMAND, %SC_CLOSE, BYVAL 0)
             ELSE
               FOR Looper = 1 TO DiskCount
                 COMBOBOX ADD hDlg, %ComboDisk, PARSE$(sBuffer, "\", Looper) & "\"
               NEXT
               sDisk = PARSE$(sBuffer, "\", 1) & "\" 'Set to first disk,
               COMBOBOX SELECT hDlg, %ComboDisk, 1
             END IF
           END IF
           CONTROL SET TEXT hDlg, %LabelDiskCount, "Disk found: " & STR$(DiskCount)
           PostMessage(hDlg, %WM_COMMAND, MAKDWD(%ComboDisk, %CBN_EDITCHANGE), GetDlgItem(hDlg, %ComboDisk))
         END IF

       CASE %ComboDisk
         IF CBCTLMSG = %CBN_EDITCHANGE OR CBCTLMSG = 1 THEN
           IF hDisk THEN CloseDisk(hDisk)
           Disk       = 0
           BlockCount = 0
           BlockIndex = 0
           CONTROL SET TEXT hDlg, %EditBlockIndex, FORMAT$(BlockIndex) '"0"
           IF PhysicalDisk THEN
             CONTROL GET TEXT hDlg, %ComboDisk TO sBuffer
             Disk       = VAL(sBuffer)
             hDisk      = OpenPhysicalDisk(Disk, %FALSE) '%FALSE for open in read only mode
             BlockCount = GetPhysicalDiskBlockCount(hDisk)
             IF GetPhysicalDiskGeometry(hDisk, DiskGeometry) THEN
               CONTROL SET TEXT hDlg, %LabelCyl,  "Cyl : "  & FORMAT$(DiskGeometry.Cylinders, "0,")
               CONTROL SET TEXT hDlg, %LabelHead, "Head: "  & FORMAT$(DiskGeometry.TracksPerCylinder)
               CONTROL SET TEXT hDlg, %LabelSect, "Sect: "  & FORMAT$(DiskGeometry.SectorsPerTrack)
             END IF
           ELSE 'Logical disk
             COMBOBOX GET TEXT hDlg, %ComboDisk TO sDisk 'sLogicalDisk
             hDisk      = OpenLogicalDisk(sDisk, %FALSE) '%FALSE for open in read only mode
             BlockCount = GetLogicalDiskBlockCount(sDisk)
             CONTROL SET TEXT hDlg, %LabelCyl,  ""
             CONTROL SET TEXT hDlg, %LabelHead, ""
             CONTROL SET TEXT hDlg, %LabelSect, ""
           END IF
           CONTROL SET TEXT hDlg, %LabelMeg,  "Bytes: " & FORMAT$(BlockCount * %BlockSize, "0,")
           CONTROL SEND hDlg, %UpDownH, %UDM_SETRANGE32, UpDownStartPosition, _ 'LONG -2,147,483,648 '
                                                         UpDownStartPosition + BlockCount - 1
           CONTROL SEND hDlg, %UpDownH, %UDM_SETPOS32, 0, UpDownStartPosition 'Reset to start pos
           CONTROL SET TEXT hDlg, %LabelBlockCount, "Block: " & FORMAT$(BlockCount, "0,")
           DIALOG POST hDlg, %WM_COMMAND, %ButtonRead, %BN_CLICKED
         END IF

       CASE %EditBlockIndex
         IF HIWRD(CBWPARAM) = %EN_CHANGE THEN
             CONTROL GET TEXT hDlg, %EditBlockIndex TO sBuffer
             BlockIndex = VAL(sBuffer)
             IF BlockIndex > BlockCount - 1 THEN BlockIndex = BlockCount - 1
             IF BlockIndex < 0 THEN BlockIndex = 0
             CONTROL SEND hDlg, %UpDownH, %UDM_SETPOS32, 0, UpDownStartPosition + BlockIndex
         END IF

       CASE %ButtonRead, %IDOK
         IF CBCTLMSG = %BN_CLICKED OR CBCTLMSG = 1 THEN
           IF DiskError = %FALSE THEN
             CONTROL GET TEXT hDlg, %EditBlockIndex TO sBuffer
             BlockIndex = VAL(sBuffer)
             IF BlockIndex > BlockCount - 1 THEN BlockIndex = BlockCount - 1
             IF BlockIndex < 0 THEN BlockIndex = 0
             CONTROL SET TEXT hDlg, %EditBlockIndex, FORMAT$(BlockIndex)
             CONTROL SEND hDlg, %UpDownH, %UDM_SETPOS32, 0, UpDownStartPosition + BlockIndex
             sBuffer = NUL$(%BlockSize) '4096
             ReadCount = DiskRead(hDisk, BlockIndex, sBuffer, LEN(sBuffer))
             IF ReadCount = 0 THEN
               DiskError = %TRUE
               CONTROL SET TEXT hDlg, %EditHex, ""
               MessageBox(hDlg, "Disk error on block " & FORMAT$(BlockIndex, "0,"), _
                          $AppName, %MB_ICONINFORMATION OR %MB_OK)
               DiskError = %FALSE
             ELSE
               CONTROL SET TEXT hDlg, %EditHex, StringToHexView(sBuffer)
             END IF
           END IF
         END IF

     END SELECT

   CASE %WM_NOTIFY
     pNotifyMessageHeader = CBLPARAM
     IF @pNotifyMessageHeader.Code = %UDN_DELTAPOS THEN
        IF (@pNotifyMessageHeader.idFrom = %UpDownH) THEN
           pNotifyMessageUpDown = CBLPARAM
           UpDownDeltaH = @pNotifyMessageUpDown.iDelta
        END IF
     END IF

   CASE %WM_VSCROLL, %WM_HSCROLL
     IF GetDlgCtrlID(CBLPARAM) = %UpDownH THEN
       LOCAL UpDownError AS LONG
       UpDownValH = SendMessage(CBLPARAM, %UDM_GETPOS32, %FALSE, UpDownError) '+ UpDownStartPosition
       CONTROL SET TEXT hDlg, %EditBlockIndex, FORMAT$(UpDownValH - UpDownStartPosition) '-2147483648 'Needed for 2 tb, LONG -2,147,483,648 to +2,147,483,647
       DIALOG POST hDlg, %WM_COMMAND, %ButtonRead, %BN_CLICKED
     END IF

   CASE %WM_DESTROY
     IF hDisk THEN CloseDisk(hDisk)

  END SELECT

END FUNCTION
'_____________________________________________________________________________

FUNCTION PBMAIN()
 LOCAL hFont             AS DWORD
 LOCAL hIconBig          AS DWORD
 LOCAL hIconSmall        AS DWORD
 LOCAL DiskGeometry      AS DISK_GEOMETRY
 LOCAL hDisk             AS DWORD
 LOCAL PhysicalDiskCount AS DWORD
 LOCAL PhysicalDiskId    AS DWORD
 LOCAL LogicalDiskCount  AS DWORD
 LOCAL ReadCount         AS DWORD
 LOCAL BlockCount        AS QUAD
 LOCAL BlockIndex        AS QUAD
 LOCAL sDisk             AS STRING
 LOCAL sBuffer           AS STRING

 IF IsUserLocalAdmin = %FALSE THEN
   MessageBox(%HWND_DESKTOP, "Please restart program as administrator.", $AppName & " - Error", %MB_OK)
 ELSE
   DIALOG FONT "Segoe UI", 9
   DIALOG NEW %HWND_DESKTOP, $AppName & " for Windows NT/2000/XP/Vista/Seven/Eight/Ten",,, _
   398, 360, %WS_CAPTION OR %WS_MINIMIZEBOX OR %WS_SYSMENU, 0 TO hDlg

   CONTROL ADD FRAME, hDlg, -1, "", 02, 0, 394, 58, %BS_LEFT OR %BS_TOP, %WS_EX_LEFT
   CONTROL ADD OPTION, hDlg, %OptionPhysical, "&Physical", 7, 6, 35, 9, %BS_LEFT OR %BS_VCENTER OR %WS_TABSTOP OR %WS_GROUP, %WS_EX_LEFT
   CONTROL ADD OPTION, hDlg, %OptionLogical, "&Logical", 47, 6, 35, 9, %BS_LEFT OR %BS_VCENTER OR %WS_TABSTOP, %WS_EX_LEFT
   CONTROL SET OPTION hDlg, %OptionPhysical, %OptionPhysical, %OptionLogical
   CONTROL ADD LABEL, hDlg, %LabelDiskCount, "Disk found:", 7, 18, 60, 9
   CONTROL ADD LABEL, hDlg, %LabelDisk, "Hard Disk", 7, 31, 50, 9
   CONTROL ADD COMBOBOX, hDlg, %ComboDisk, , 57, 29, 31, 72, _
   %CBS_DROPDOWNLIST OR %CBS_HASSTRINGS OR %CBS_SORT OR %WS_TABSTOP, %WS_EX_CLIENTEDGE OR %WS_EX_LEFT
   CONTROL ADD LABEL, hDlg, %LabelBlockCount, "LB:  00000000", 100, 7, 90, 9, %SS_NOTIFY
   CONTROL ADD LABEL, hDlg, %LabelMeg, "Meg: 00000000", 100, 17, 90, 9, %SS_NOTIFY
   CONTROL ADD LABEL, hDlg, %LabelCyl, "Cylinder: 00000000", 215, 7, 80, 9, %SS_NOTIFY
   CONTROL ADD LABEL, hDlg, %LabelHead, "Head:     00000", 215, 17, 80, 9, %SS_NOTIFY
   CONTROL ADD LABEL, hDlg, %LabelSect, "Sector:   00000", 215, 27, 80, 9, %SS_NOTIFY
   CONTROL ADD LABEL, hDlg, -1, "LogicalBlock", 300, 7, 90, 9
   CONTROL ADD TEXTBOX, hDlg, %EditBlockIndex, "0", 300, 17, 45, 12
   CONTROL ADD BUTTON, hDlg, %ButtonRead, "Read", 350, 16, 40, 13
   CONTROL ADD "msctls_updown32", hDlg, %UpDownH, "", 305, 32, 0, 0, %WS_CHILD OR %WS_VISIBLE OR %UDS_HORZ
   CONTROL SET SIZE hDlg, %UpDownH, 36, 15
   CONTROL ADD LABEL, hDlg, %LabelStatus, "Status", 47, 46, 250, 9

   CONTROL ADD TEXTBOX, hDlg, %EditHex, "", 3, 63, 393, 295, %WS_CHILD OR %WS_VISIBLE OR _
   %WS_TABSTOP OR %WS_HSCROLL OR %WS_VSCROLL OR %ES_LEFT OR %ES_MULTILINE OR _
   %ES_NOHIDESEL OR %ES_AUTOHSCROLL OR %ES_AUTOVSCROLL OR %ES_WANTRETURN, _
   %WS_EX_CLIENTEDGE OR %WS_EX_LEFT OR %WS_EX_LTRREADING OR %WS_EX_RIGHTSCROLLBAR
    hFont = CreateFont(16, 0, _      'Height, Width usually 0,
                       0, 0, _       'Escapement(angle), Orientation
                       0, 0, 0, 0, _ 'Bold, Italic, Underline, Strikethru
                       0, %OUT_TT_PRECIS, %CLIP_DEFAULT_PRECIS, %DEFAULT_QUALITY, %FF_DONTCARE, _
                       BYCOPY "Courier New") 'Consolas
   SendDlgItemMessage(hDlg, %EditHex, %WM_SETFONT, hFont, %TRUE)

   ExtractIconEx("shell32.dll", 8, BYVAL VARPTR(hIconBig), BYVAL VARPTR(hIconSmall), 1)
   SetClassLong(hDlg, %GCL_HICONSM, hIconSmall) 'Set an icon
   SetClassLong(hDlg, %GCL_HICON, hIconBig) 'Set an icon
   SendMessage(hDlg, %WM_SETICON, %ICON_SMALL, hIconSmall)
   SendMessage(hDlg, %WM_SETICON, %ICON_BIG, hIconBig)

   DIALOG SHOW MODAL hDlg CALL DlgProc

   DestroyIcon(hIconSmall)
   DestroyIcon(hIconBig)
   DeleteObject(hFont)

END IF

END FUNCTION
'_____________________________________________________________________________
'

Re: Accès direct Secteur de disque

Publié : jeu. 21/juin/2018 14:59
par caussatjerome
Et non je n'avais pas trouvé merci!

Re: Accès direct Secteur de disque

Publié : jeu. 21/juin/2018 22:17
par Pierre Bellisle
Voici un aperçu des options "physique" et "volume" du dialogue Direct disk read

Re: Accès direct Secteur de disque

Publié : dim. 07/janv./2024 6:25
par Pierre Bellisle
J'ai finalement pris le temps de convertir mon code d'accès disque en PureBASIC.
Plus joli et plus solide que le précédent.
Totalement SDK.

Image

Re: Accès direct Secteur de disque

Publié : lun. 08/janv./2024 2:48
par Pierre Bellisle
Voici le code qui vous donne accès à tous les secteurs d'un disque.
Notez que le moteur de recherche n'est absolument pas optimisé pour la vitesse, c'est un jouet amusant...

Code : Tout sélectionner

 
;Disk read code 2024-01-07
EnableExplicit ;Varaible must be declared
#PB_Compiler_ExecutableFormat    = #PB_Compiler_Executable
#PB_Compiler_IsMainFile          = 1

#BlockSize                       = 512
#SECURITY_NT_AUTHORITY           = 005
#IOCTL_DISK_GET_DRIVE_GEOMETRY   = $00070000
#FILE_DEVICE_DISK                = $07
#IOCTL_DISK_BASE                 = #FILE_DEVICE_DISK
#METHOD_BUFFERED                 = 0
#FILE_READ_ACCESS                = $01
#IOCTL_DISK_GET_LENGTH_INFO      = $7405C
#FSCTL_ALLOW_EXTENDED_DASD_IO    = $00090083

#AppName            = "Disk read"
#StaticDisk         = 101
#StaticDiskCount    = 102
#StaticBlockCount   = 103
#StaticCyl          = 104
#StaticHead         = 105
#StaticSect         = 106
#StaticMeg          = 107
#StaticSearchStatus = 108
#StaticDiskIdPart   = 109
#StaticPhysical     = 110
#StaticLogical      = 111
#ComboDisk          = 201
#ButtonRead         = 301
#ButtonSearchPrev   = 303
#ButtonSearchNext   = 304
#ButtonStop         = 305
#EditLogicalBlock   = 401
#EditHex            = 402
#EditSearch         = 403
#OptionPhysical     = 501
#OptionLogical      = 502
#UpDownHBlockIndex  = 601

Macro LoWord(WordWord) : (WordWord&$ffff)       : EndMacro ;ControlId = LoWord(wParam)

Macro HiWord(WordWord) : ((WordWord>>16)&$ffff) : EndMacro ;Action    = HiWord(wParam)

Macro MakeDword(WordLo, WordHi) : ((WordHi<<16)+WordLo) : EndMacro ;resultvar = MAK(datatype, loworderval, highorderval)

Macro GetLBA(LBA)
sBuffer = Space(SendMessage_(hEditLogicalBlock, #WM_GETTEXTLENGTH, 0, 0))
SendMessage_(hEditLogicalBlock, #WM_GETTEXT, Len(sBuffer) + 1, @sBuffer)
LBA = Val(sBuffer)
If LBA > BlockCount - 1 : LBA = BlockCount - 1 : EndIf
If LBA < 0 : LBA = 0 : EndIf
SendMessage_(hUpDown, #UDM_SETPOS32, 0, UpDownStartPosition + LBA)
EndMacro

Structure DISK_GEOMETRY
 Cylinders.q
 MediaType.l ;0 unknown, 11 RemovableMedia, 12 FixedMedia, else floppy
 TracksPerCylinder.l
 SectorsPerTrack.l
 BytesPerSector.l
EndStructure

Structure GET_LENGTH_INFORMATION
 Length.q
EndStructure
;_____________________________________________________________________________

Procedure.s FileDeviceTypeText(dwDevType.l)
 ;From a number, return a string describing the device
 Protected sDevType.s

 Select dwDevType
   Case $00000027 : sDevType = "8042_port"           ; #FILE_DEVICE_8042_PORT
   Case $00000032 : sDevType = "Acpi"                ; #FILE_DEVICE_ACPI
   Case $00000029 : sDevType = "Battery"             ; #FILE_DEVICE_BATTERY
   Case $00000001 : sDevType = "Beep"                ; #FILE_DEVICE_BEEP
   Case $0000002a : sDevType = "Bus-extender"        ; #FILE_DEVICE_BUS_EXTENDER
   Case $00000002 : sDevType = "Cd-rom"              ; #FILE_DEVICE_CD_ROM
   Case $00000003 : sDevType = "Cd-rom-file-system"  ; #FILE_DEVICE_CD_ROM_FILE_SYSTEM
   Case $00000030 : sDevType = "Changer"             ; #FILE_DEVICE_CHANGER
   Case $00000004 : sDevType = "Controller"          ; #FILE_DEVICE_CONTROLLER
   Case $00000005 : sDevType = "Datalink"            ; #FILE_DEVICE_DATALINK
   Case $00000006 : sDevType = "Dfs"                 ; #FILE_DEVICE_DFS
   Case $00000035 : sDevType = "Dfs-file-system"     ; #FILE_DEVICE_DFS_FILE_SYSTEM
   Case $00000036 : sDevType = "Dfs-volume"          ; #FILE_DEVICE_DFS_VOLUME
   Case $00000007 : sDevType = "Disk"                ; #FILE_DEVICE_DISK
   Case $00000008 : sDevType = "Disk-file-system"    ; #FILE_DEVICE_DISK_FILE_SYSTEM
   Case $00000033 : sDevType = "Dvd"                 ; #FILE_DEVICE_DVD
   Case $00000009 : sDevType = "File-system"         ; #FILE_DEVICE_FILE_SYSTEM
   Case $0000003a : sDevType = "Fips"                ; #FILE_DEVICE_FIPS
   Case $00000034 : sDevType = "Fullscreen-video"    ; #FILE_DEVICE_FULLSCREEN_VIDEO
   Case $0000000a : sDevType = "Inport-port"         ; #FILE_DEVICE_INPORT_PORT
   Case $0000000b : sDevType = "Keyboard"            ; #FILE_DEVICE_KEYBOARD
   Case $0000002f : sDevType = "Ks"                  ; #FILE_DEVICE_KS
   Case $00000039 : sDevType = "Ksec"                ; #FILE_DEVICE_KSEC
   Case $0000000c : sDevType = "Mailslot"            ; #FILE_DEVICE_MAILSLOT
   Case $0000002d : sDevType = "Mass-storage"        ; #FILE_DEVICE_MASS_STORAGE
   Case $0000000d : sDevType = "Midi-in"             ; #FILE_DEVICE_MIDI_IN
   Case $0000000e : sDevType = "Midi-out"            ; #FILE_DEVICE_MIDI_OUT
   Case $0000002b : sDevType = "Modem"               ; #FILE_DEVICE_MODEM
   Case $0000000f : sDevType = "Mouse"               ; #FILE_DEVICE_MOUSE
   Case $00000010 : sDevType = "Multi-unc-provider"  ; #FILE_DEVICE_MULTI_UNC_PROVIDER
   Case $00000011 : sDevType = "Named-pipe"          ; #FILE_DEVICE_NAMED_PIPE
   Case $00000012 : sDevType = "Network"             ; #FILE_DEVICE_NETWORK
   Case $00000013 : sDevType = "Network-browser"     ; #FILE_DEVICE_NETWORK_BROWSER
   Case $00000014 : sDevType = "Network-file-system" ; #FILE_DEVICE_NETWORK_FILE_SYSTEM
   Case $00000028 : sDevType = "Network-redirector"  ; #FILE_DEVICE_NETWORK_REDIRECTOR
   Case $00000015 : sDevType = "Null"                ; #FILE_DEVICE_NULL
   Case $00000016 : sDevType = "Parallel-port"       ; #FILE_DEVICE_PARALLEL_PORT
   Case $00000017 : sDevType = "Physical-netcard"    ; #FILE_DEVICE_PHYSICAL_NETCARD
   Case $00000018 : sDevType = "Printer"             ; #FILE_DEVICE_PRINTER
   Case $00000019 : sDevType = "Scanner"             ; #FILE_DEVICE_SCANNER
   Case $0000001c : sDevType = "Screen"              ; #FILE_DEVICE_SCREEN
   Case $00000037 : sDevType = "Serenum"             ; #FILE_DEVICE_SERENUM
   Case $0000001a : sDevType = "Serial-mouse-port"   ; #FILE_DEVICE_SERIAL_MOUSE_PORT
   Case $0000001b : sDevType = "Serial-port"         ; #FILE_DEVICE_SERIAL_PORT
   Case $00000031 : sDevType = "Smartcard"           ; #FILE_DEVICE_SMARTCARD
   Case $0000002e : sDevType = "Smb"                 ; #FILE_DEVICE_SMB
   Case $0000001d : sDevType = "Sound"               ; #FILE_DEVICE_SOUND
   Case $0000001e : sDevType = "Streams"             ; #FILE_DEVICE_STREAMS
   Case $0000001f : sDevType = "Tape"                ; #FILE_DEVICE_TAPE
   Case $00000020 : sDevType = "Tape-file-system"    ; #FILE_DEVICE_TAPE_FILE_SYSTEM
   Case $00000038 : sDevType = "Termsrv"             ; #FILE_DEVICE_TERMSRV
   Case $00000021 : sDevType = "Transport"           ; #FILE_DEVICE_TRANSPORT
   Case $00000022 : sDevType = "Unknown"             ; #FILE_DEVICE_UNKNOWN
   Case $0000002c : sDevType = "Vdm"                 ; #FILE_DEVICE_VDM
   Case $00000023 : sDevType = "Video"               ; #FILE_DEVICE_VIDEO
   Case $00000024 : sDevType = "Virtual-disk"        ; #FILE_DEVICE_VIRTUAL_DISK
   Case $00000025 : sDevType = "Wave-in"             ; #FILE_DEVICE_WAVE_IN
   Case $00000026 : sDevType = "Wave-out"            ; #FILE_DEVICE_WAVE_OUT
   Case $00000040 : sDevType = "Wpd"                 ; #FILE_DEVICE_WPD
   Default        : sDevType = "Unknown" + Str(dwDevType)
 EndSelect
 ProcedureReturn(sDevType)

EndProcedure
;_____________________________________________________________________________

Procedure.l IsUserAdmin()
 ;Check id Admin right are granted
 Protected NtAuthority.SID_IDENTIFIER_AUTHORITY
 Protected *FunctionPointer
 Protected pSidAdministrator.i
 Protected IsMember.l

 ;IsUserAnAdmin api may not be there in future Windows release, so doing it the following way.
 NtAuthority\value[5] = #SECURITY_NT_AUTHORITY ;SECURITY_NT_AUTHORITY = 5
 If AllocateAndInitializeSid_(@NtAuthority, 2, #SECURITY_BUILTIN_DOMAIN_RID,
                              #DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, @pSidAdministrator)
   If OpenLibrary(125, "AdvApi32.dll")
      *FunctionPointer = GetFunction(125, "CheckTokenMembership")
      CallFunctionFast(*FunctionPointer, #Null, pSidAdministrator, @IsMember)
    EndIf
    CloseLibrary(125)
    FreeSid_(pSidAdministrator)
 EndIf
 ProcedureReturn(IsMember)

EndProcedure
;_____________________________________________________________________________

Macro CreateControls
 ;Macro to declutter main window loop

 ;Get font
 NonClient\cbSize = SizeOf(NONCLIENTMETRICS)
 SystemParametersInfo_(#SPI_GETNONCLIENTMETRICS, SizeOf(NONCLIENTMETRICS), @NonClient, 0)
 LogicalFont = NonClient\lfMessageFont
 hFont       = CreateFontIndirect_(LogicalFont) ;Usually "Segoe UI", 9

 ;Create controls...
 hFrame01  = CreateWindowEx_(0, "Button", "", #WS_CHILD | #WS_VISIBLE | #BS_GROUPBOX | #BS_LEFT | #BS_TOP,
                             7, 0, 689, 109, hWnd,  -1, hInstance, 0)
 SendMessage_(hFrame01, #WM_SETFONT, hFont, 0)

 hStaticPhysical = CreateWindowEx_(0, "Static", "Physical", #WS_CHILD | #WS_VISIBLE | #SS_NOTIFY,
                                   28, 11, 50, 17, hWnd, #StaticPhysical, hInstance, 0)
 SendMessage_(hStaticPhysical, #WM_SETFONT, hFont, 0)
 hOptionPhysical = CreateWindowEx_(0, "Button", "&Physical", #WS_CHILD | #WS_VISIBLE | #WS_GROUP |
                                   #WS_TABSTOP | #BS_AUTORADIOBUTTON | #BS_LEFT | #BS_VCENTER,
                                   12, 11, 14, 16, hWnd, #OptionPhysical, hInstance, 0)

 hStaticLogical = CreateWindowEx_(0, "Static", "Logical", #WS_CHILD | #WS_VISIBLE | #SS_NOTIFY,
                                  98, 11, 50, 17, hWnd, #StaticLogical, hInstance, 0)
 SendMessage_(hStaticLogical, #WM_SETFONT, hFont, 0)
 hOptionLogical = CreateWindowEx_(0, "Button", "&Logical", #WS_CHILD | #WS_VISIBLE |
                                  #BS_AUTORADIOBUTTON | #BS_LEFT | #BS_VCENTER,
                                  82, 11, 14, 16, hWnd, #OptionLogical, hInstance, 0)

 hStaticDiskFound = CreateWindowEx_(0, "Static", "Disk found: ", #WS_CHILD | #WS_VISIBLE,
                                    12, 34, 105, 17, hWnd, #StaticDiskCount, hInstance, 0)
 SendMessage_(hStaticDiskFound, #WM_SETFONT, hFont, 0)

 hStaticHardDisk = CreateWindowEx_(0, "Static", "Hard Disk", #WS_CHILD | #WS_VISIBLE,
                                   12, 58, 80, 17, hWnd, -1, hInstance, 0)
 SendMessage_(hStaticHardDisk, #WM_SETFONT, hFont, 0)

 hComboDisk = CreateWindowEx_(0, "ComboBox", "", #WS_CHILD | #WS_VISIBLE | #WS_TABSTOP | #CBS_DROPDOWNLIST |
                              #CBS_SORT | #CBS_HASSTRINGS, 100, 54, 54, 160, hWnd, #ComboDisk, hInstance, 0)
 SendMessage_(hComboDisk, #WM_SETFONT, hFont, 0)

 hStaticBlock = CreateWindowEx_(0, "Static", "Block: 1,953,520,065", #WS_CHILD | #WS_VISIBLE | #SS_NOTIFY,
                                175, 13, 158, 17, hWnd, #StaticBlockCount, hInstance, 0)
 SendMessage_(hStaticBlock, #WM_SETFONT, hFont, 0)

 hStaticMeg = CreateWindowEx_(0, "Static", "Bytes: 1,000,202,273,280", #WS_CHILD | #WS_VISIBLE | #SS_NOTIFY,
                              175, 32, 158, 17, hWnd, -1, hInstance, 0)
 SendMessage_(hStaticMeg, #WM_SETFONT, hFont, 0)

 hStaticDiskIdPart = CreateWindowEx_(0, "Static", "", #WS_CHILD | #WS_VISIBLE | #SS_CENTERIMAGE |
                                     #SS_NOTIFY, 175, 51, 193, 18, hWnd, -1, hInstance, 0)
 SendMessage_(hStaticDiskIdPart, #WM_SETFONT, hFont, 0)

 hStaticCyl = CreateWindowEx_(0, "Static", "Cyl : 121,601", #WS_CHILD | #WS_VISIBLE |
                              #SS_NOTIFY, 376, 13, 140, 17, hWnd, -1, hInstance, 0)
 SendMessage_(hStaticCyl, #WM_SETFONT, hFont, 0)

 hStaticHead = CreateWindowEx_(0, "Static", "Head: 255", #WS_CHILD | #WS_VISIBLE |
                               #SS_NOTIFY, 376, 32, 140, 17, hWnd, -1, hInstance, 0)
 SendMessage_(hStaticHead, #WM_SETFONT, hFont, 0)

 hStaticSect = CreateWindowEx_(0, "Static", "Sect: 63", #WS_CHILD | #WS_VISIBLE |
                               #SS_NOTIFY, 376, 51, 140, 17, hWnd, -1, hInstance, 0)
 SendMessage_(hStaticSect, #WM_SETFONT, hFont, 0)

 hEditSearch = CreateWindowEx_(#WS_EX_CLIENTEDGE, "Edit", "", #WS_CHILD | #WS_VISIBLE | #WS_TABSTOP |
                           #ES_AUTOHSCROLL, 175, 71, 226, 21,  hWnd, #EditSearch, hInstance, 0)
 SendMessage_(hEditSearch, #WM_SETFONT, hFont, 0)
 SendMessage_(hEditSearch, #EM_SETCUEBANNER, 1, "Cheap search for ansi text")

 hButtonPrev = CreateWindowEx_(0, "Button", "Prev", #WS_CHILD | #WS_VISIBLE | #WS_TABSTOP,
                               403, 71, 35, 21, hWnd, #ButtonSearchPrev, hInstance, 0)
 SendMessage_(hButtonPrev, #WM_SETFONT, hFont, 0)

 hButtonNext = CreateWindowEx_(0, "Button", "Next", #WS_CHILD | #WS_VISIBLE | #WS_TABSTOP,
                               438, 71, 35, 21, hWnd, #ButtonSearchNext, hInstance, 0)
 SendMessage_(hButtonNext, #WM_SETFONT, hFont, 0)

 hButtonStop = CreateWindowEx_(0, "Button", "Stop", #WS_CHILD | #WS_VISIBLE | #WS_TABSTOP,
                               473, 71, 35, 21, hWnd, #ButtonStop, hInstance, 0)
 SendMessage_(hButtonStop, #WM_SETFONT, hFont, 0)

 hStaticLogicalBlock = CreateWindowEx_(0, "Static", "LogicalBlock",  #WS_CHILD | #WS_VISIBLE,
                                       525, 13, 68, 17, hWnd,  -1, hInstance, 0)
 SendMessage_(hStaticLogicalBlock, #WM_SETFONT, hFont, 0)

 hEditLogicalBlock = CreateWindowEx_(0, "Edit", "4294967294", #WS_CHILD | #WS_VISIBLE | #WS_TABSTOP |
                                     #ES_CENTER | #ES_NUMBER, 525, 32, 79, 15, hWnd, -1, hInstance, 0)
 SendMessage_(hEditLogicalBlock, #WM_SETFONT, hFont, 0)

 hButtonRead = CreateWindowEx_(0, "Button", "Read", #WS_CHILD | #WS_VISIBLE | #WS_TABSTOP,
                               613, 28, 70, 24, hWnd, #ButtonRead, hInstance, 0)
 SendMessage_(hButtonRead, #WM_SETFONT, hFont, 0)

 hUpDown = CreateWindowEx_(0, "MsCtls_UpDown32", "", #WS_CHILD | #WS_VISIBLE | #UDS_HORZ,
                           534, 60, 63, 28, hWnd, -1, hInstance, 0)
 SendMessage_(hUpDown, #WM_SETFONT, hFont, 0)

 hStaticSearchStatus = CreateWindowEx_(0, "Static", "Status: ", #WS_CHILD | #WS_VISIBLE,
                                       12, 86, 158, 19, hWnd, #StaticSearchStatus, hInstance, 0)
 SendMessage_(hStaticSearchStatus, #WM_SETFONT, hFont, 0)

 hEditHex = CreateWindowEx_(#WS_EX_CLIENTEDGE, "Edit", "0123", #WS_CHILD | #WS_VISIBLE |
                            #WS_TABSTOP | #ES_MULTILINE | #ES_WANTRETURN | #ES_READONLY |
                            #ES_NOHIDESEL, 5, 118, 688, 553, hWnd, #EditHex, hInstance, 0)
 hFixedFont = CreateFont_(16, 0,       ;Height, Width usually 0,
                          0, 0,        ;Escapement(angle), Orientation
                          0, 0, 0, 0,  ;Bold, Italic, Underline, Strikethru
                          0, #OUT_TT_PRECIS, #CLIP_DEFAULT_PRECIS, #DEFAULT_QUALITY, #FF_DONTCARE,
                          "Courier New") ;Fixed font
 SendMessage_(hEditHex, #WM_SETFONT, hFixedFont, 0)

EndMacro
;_____________________________________________________________________________

Procedure.q GetLogicalDiskBlockCount(sLogicalDisk.s)
 ;Get number of LBA LogicalBlockAddress on logical disk, aka "C:\"
 Protected FreeBytesForUser.q
 Protected UserDiskSize.q
 Protected FreeByteOnDisk.q
 Protected ByteOnDisk.GET_LENGTH_INFORMATION
 Protected OldErrMode.l
 Protected zLogicalDisk.s{8}
 Protected hDisk.i
 Protected BytesReturned.l

 OldErrMode = SetErrorMode_(#SEM_FAILCRITICALERRORS) ;Prevent pop-up dialogs if device is not present or loaded

 zLogicalDisk =  "\\.\" + Left(sLogicalDisk, 2) ;Logical disk as "\\.\C:"
 hDisk = CreateFile_(zLogicalDisk, #GENERIC_READ, #FILE_SHARE_READ |
                     #FILE_SHARE_WRITE, 0, #OPEN_EXISTING, 0, 0)
 If hDisk <> #INVALID_HANDLE_VALUE
   If DeviceIoControl_(hDisk, #IOCTL_DISK_GET_LENGTH_INFO, #Null, 0,
                       @ByteOnDisk, SizeOf(ByteOnDisk), @BytesReturned, #Null)
   EndIf
   CloseHandle_(hDisk)
 EndIf

 SetErrorMode_(OldErrMode)

 If ByteOnDisk\Length
   ProcedureReturn(ByteOnDisk\Length / #BlockSize)
 EndIf

EndProcedure
;_____________________________________________________________________________

Procedure.l GetPhysicalDiskId(pBuffer.i, BufferLen.l)
 ;Get physical disk count and id, id are not always consecutive
 Protected DiskGeometry.DISK_GEOMETRY
 Protected sBuffer.s
 Protected sPadZero.s
 Protected sPhysicalDiskList.s
 Protected PhysicalDiskIndex.l
 Protected PhysicalDiskCount.l
 Protected hDevice.i
 Protected ReturnedSize.l

 While 1
   ;Need to be Admin
   hDevice = CreateFile_("\\.\PhysicalDrive" + Str(PhysicalDiskIndex), ;Could be a HD, flash, CdRom, etc.
                         #GENERIC_READ, #FILE_SHARE_READ | #FILE_SHARE_WRITE, 0, #OPEN_EXISTING, 0, 0)
   If hDevice = #INVALID_HANDLE_VALUE
     Break ;No more devices
   Else
     If DeviceIoControl_(hDevice, #IOCTL_DISK_GET_DRIVE_GEOMETRY, 0, ;Will be TRUE if device is a disk / have media inserted
                         0, @DiskGeometry, SizeOf(DISK_GEOMETRY), @ReturnedSize, 0)
       sPadZero = "0" : If PhysicalDiskIndex > 9 : sPadZero = "" : EndIf
       sPhysicalDiskList = sPhysicalDiskList + sPadZero + Str(PhysicalDiskIndex) + ":" ;Disk are zero based, Result example: "00:01:04:06"
       PhysicalDiskCount = PhysicalDiskCount + 1
     EndIf
     CloseHandle_(hDevice)
     PhysicalDiskIndex = PhysicalDiskIndex + 1
   EndIf
 Wend

 If PhysicalDiskCount
   sPhysicalDiskList = Left(sPhysicalDiskList, Len(sPhysicalDiskList) - 1) ;Remove last ":", example: "00:01:04:06:"
   sBuffer           = Left(sPhysicalDiskList, BufferLen) ;Respect buffer lenght
 Else
   sBuffer = ""
 EndIf

 PokeS(pBuffer, sBuffer, PhysicalDiskCount * 3)
 ProcedureReturn(PhysicalDiskCount)

EndProcedure
;_____________________________________________________________________________

Procedure.l GetVolumeInfo(zDisk.s)
 ;Check if volume have a media available via GetVolumeInformation()
 Protected.s{21} zVolumeName
 Protected.s{21} zFileSystem
 Protected.l VolumeSerialNumber
 Protected.l FileNameMaxLen
 Protected.l FileSystemFlag

 If GetVolumeInformation_(zDisk, zVolumeName, 20, VolumeSerialNumber,
                          FileNameMaxLen, FileSystemFlag, zFileSystem, 20) = #False
   ;"No disk/media inserted !"
 Else
   ProcedureReturn(#True) ;Media inserted
 EndIf
EndProcedure
;______________________________________________________________________________

Procedure.l GetLogicalDiskId(pBuffer.i, BufferLen.l)
 ;Get disk drive letters designer, aka "C:\, D:\, H:\"
 Protected sLogicalDiskList.s
 Protected sLogicalFixedDiskList.s
 Protected sBuffer.s
 Protected LogicalDiskIndex.l
 Protected LogicalDiskCount.l
 Protected LogicalFixedDiskCount.l
 Protected LogicalDiskListLen.l
 Protected zDrive.s{4}

 sLogicalDiskList   = Space(26 * 4 + 1)
 LogicalDiskListLen = GetLogicalDriveStrings_(Len(sLogicalDiskList), @sLogicalDiskList) ;Aka C:\<NULL>D:\<NULL>R:\<NULL><NULL>
 If LogicalDiskListLen <= BufferLen ;Success
   LogicalDiskCount = LogicalDiskListLen / 4
   For LogicalDiskIndex = 0 To LogicalDiskCount - 1
     zDrive = PeekS(@sLogicalDiskList + LogicalDiskIndex * 8, 3) ;Aka C:\<NULL>D:\<NULL>R:\<NULL><NULL>
     Select GetDriveType_(zDrive)
       Case #DRIVE_UNKNOWN
       Case #DRIVE_NO_ROOT_DIR
       ;CASE #DRIVE_REMOTE
       Case #DRIVE_CDROM
       Case #DRIVE_RAMDISK
       ;CASE #DRIVE_REMOVABLE
       Case #DRIVE_FIXED, #DRIVE_REMOVABLE
         If GetVolumeInfo(zDrive) ;Is there a media inserted
           sLogicalFixedDiskList = sLogicalFixedDiskList + zDrive
           LogicalFixedDiskCount = LogicalFixedDiskCount + 1
         EndIf
     EndSelect
   Next
   sBuffer = sLogicalFixedDiskList
 Else ;sBuffer is too small, LogicalDiskListLen is the required size
   sBuffer = ""
 EndIf
 PokeS(pBuffer, sBuffer, LogicalFixedDiskCount * 4)
 ProcedureReturn(LogicalFixedDiskCount)

EndProcedure
;_____________________________________________________________________________

Procedure.l CloseDisk(phDisk.i)
 ;Clean up and close an open disk handle

 CloseHandle_(PeekI(phDisk.i))
 PokeI(phDisk, 0)
 ProcedureReturn(GetLastError_())

EndProcedure
;_____________________________________________________________________________

Procedure.l OpenPhysicalDisk(PhysicalDiskId.l, WritePermission.l)
 ;Get an handle to work with a physical disk
 Protected zDisk.s{20}

 zDisk = ReplaceString(zDisk, " ", Chr(0))
 zDisk = "\\.\PhysicalDrive" + Str(PhysicalDiskId) ;Physical disk aka "\\.\PhysicalDrive0"

 If WritePermission ;Read & Write
   ProcedureReturn(CreateFile_(zDisk, #GENERIC_READ | #GENERIC_WRITE,
                               #FILE_SHARE_READ | #FILE_SHARE_WRITE, 0, #OPEN_EXISTING, 0, 0))
 Else ;Read only
   ProcedureReturn(CreateFile_(zDisk, #GENERIC_READ,
                               #FILE_SHARE_READ | #FILE_SHARE_WRITE, 0, #OPEN_EXISTING, 0, 0))
 EndIf

EndProcedure
;_____________________________________________________________________________

Procedure.q GetPhysicalDiskBlockCount(hDisk.l)
 ;Get number of LBA LogicalBlockAddress on physical disk via an open handle
 Protected DiskGeometry.DISK_GEOMETRY
 Protected OldErrMode.l
 Protected ReturnedSize.l

 OldErrMode = SetErrorMode_(#SEM_FAILCRITICALERRORS) ;Prevent pop-up dialogs if device is not present or loaded
 If DeviceIoControl_(hDisk, #IOCTL_DISK_GET_DRIVE_GEOMETRY, 0,
                     0, @DiskGeometry, SizeOf(DISK_GEOMETRY), @ReturnedSize, 0 )
   ;Cyl  = DISK_GEOMETRY\Cylinders
   ;Head = DISK_GEOMETRY\TracksPerCylinder
   ;Sect = DISK_GEOMETRY\SectorsPerTrack
   ;LB   = DISK_GEOMETRY\Cyl * DiskGeometry\Head * DiskGeometry\Sect
   ProcedureReturn(DiskGeometry\Cylinders * DiskGeometry\TracksPerCylinder * DiskGeometry\SectorsPerTrack)
 EndIf
 SetErrorMode_(OldErrMode)

EndProcedure
;_____________________________________________________________________________

Procedure.l GetPhysicalDiskGeometry(hDisk.i, pDiskGeometry.i) ;DISK_GEOMETRY)
 ;Get a physical disk Cyl, Head, Sect, and logical block number
 Protected ReturnedSize.l

 If DeviceIoControl_(hDisk, #IOCTL_DISK_GET_DRIVE_GEOMETRY,  0,
                     0, pDiskGeometry, SizeOf(DISK_GEOMETRY), @ReturnedSize, 0)
   ;Cyl  = DISK_GEOMETRY\Cylinders
   ;Head = DISK_GEOMETRY\TracksPerCylinder
   ;Sect = DISK_GEOMETRY\SectorsPerTrack
   ;LB   = DISK_GEOMETRY\Cyl * DiskGeo\Head * DiskGeo\Sect
   ProcedureReturn(#True)
 EndIf

EndProcedure
;_____________________________________________________________________________

Procedure.i OpenLogicalDisk(sLogicalDisk.s, WritePermission.l, hStaticDiskIdPart.i)
 ;Get an handle to work with a volume logical disk, aka D:\
 Protected StorageDevice.STORAGE_DEVICE_NUMBER
 Protected zDisk.s{8}
 Protected hDisk.i
 Protected BytesReturned.l

 zDisk = "\\.\" + Left(sLogicalDisk, 2) ;Logical disk aka "\\.\C:"
 If WritePermission ;Read & Write
   hDisk = CreateFile_(zDisk, #GENERIC_READ | #GENERIC_WRITE,
                              #FILE_SHARE_READ | #FILE_SHARE_WRITE, 0, #OPEN_EXISTING, 0, 0)
 Else ;Read only
   hDisk = CreateFile_(zDisk, #GENERIC_READ,
                              #FILE_SHARE_READ | #FILE_SHARE_WRITE, 0, #OPEN_EXISTING, 0, 0 )
 EndIf
 ;Remarks: A call using the FSCTL_ALLOW_EXTENDED_DASD_IO control code should only be used with great caution
 ;         by programmers familiar with the underlying structure of a hard disk drive and file system.
 ;         Improper use or inaccurate checking in subsequent write operations to the partition
 ;         can result in damage to data on the partition, or destruction of the entire partition.
 ;         The FSCTL_ALLOW_EXTENDED_DASD_IO control code is used to signal the file system driver
 ;         not to perform any I/O boundary checks on read or write calls made with the specified handle.
 ;         FSCTL_ALLOW_EXTENDED_DASD_IO allows access to hidden sectors, a part of the partition
 ;         that might exist between the first sector of the partition (the boot parameter block)
 ;         and the first useful sector of the partition. FSCTL_ALLOW_EXTENDED_DASD_IO also
 ;         allows access to lost clusters, which might exist between the last useful cluster
 ;         and the end of the partition.
 DeviceIoControl_(hDisk, #FSCTL_ALLOW_EXTENDED_DASD_IO, #Null, 0,
                  #Null, 0, @BytesReturned, #Null)

 If DeviceIoControl_(hDisk, #IOCTL_STORAGE_GET_DEVICE_NUMBER, #Null, 0, ;XP+
                     @StorageDevice, SizeOf(StorageDevice), @BytesReturned, #Null)
   ;Remember, disk start at 0, partition start at 1
   SendMessage_(hStaticDiskIdPart, #WM_SETTEXT, 0,
                FileDeviceTypeText(StorageDevice\DeviceType) +
                ", device" + Str(StorageDevice\DeviceNumber) +
                ", partition " + Str(StorageDevice\PartitionNumber))
 Else
   SendMessage_(hStaticDiskIdPart, #WM_SETTEXT, 0, "No device number info" )
 EndIf

 ProcedureReturn(hDisk)

EndProcedure
;_____________________________________________________________________________

Procedure.q DiskWrite(hDisk.i, BlockIndex.q, psBuffer.i, BufferLen.l)
 ;Disk write procedure
 Protected FilePointer.q
 Protected NewFilePointer.q
 Protected BytesWritten.l

 ;Be carefull to not zap your disk if you implement and use this procedure

 FilePointer = BlockIndex * #BlockSize
 SetFilePointerEx_(hDisk, FilePointer, NewFilePointer, #FILE_BEGIN)
 If WriteFile_(hDisk, psBuffer, BufferLen, @BytesWritten, 0)
   ProcedureReturn(BytesWritten)
 EndIf

EndProcedure
;_____________________________________________________________________________

Procedure.q DiskRead(hDisk.i, BlockIndex.q, psBuffer.i, BufferLen.l)
 ;Disk read procedure
 Protected FilePointer.q
 Protected NewFilePointer.q
 Protected BytesReturned.l

 FilePointer = BlockIndex * #BlockSize
 SetFilePointerEx_(hDisk, FilePointer, NewFilePointer, #FILE_BEGIN)

 If ReadFile_(hDisk, psBuffer, BufferLen, @BytesReturned, 0)
   ProcedureReturn(BytesReturned)
 EndIf

EndProcedure
;_____________________________________________________________________________

Procedure.s DataToHexView(pString.i, ReadCount.l) ;See D:\Dev\Pow\Bas\SRC\Hex\HexToStrGary03.bas
 ;Construct two digit hex line number, 16 bytes value in 2 digit hex, a ";"
 ;and text representation of those 16 bytes
 ;A dot replace non visible character
 ;Example: "30: 83 C6 10 49 74 19 38 2C 74 F6 A0 B5 07 B4 07 8B ; .ÆIt8,tö µ´."
 Protected sBuffer.s
 Protected LineNumber.l
 Protected ChrIndex.l
 Protected CharPos.l
 Protected DataLen.l
 Protected ChrPeek.a

 DataLen = ReadCount
 sBuffer = Chr(13) + Chr(10)
 For LineNumber = 0 To DataLen - 1 Step 16
   sBuffer = sBuffer + "      " + Right("0" + Hex(LineNumber), 2) + ": "
   For ChrIndex = 0 To 15 ;Aka "C6 10 49 74 19 38 2C 74 F6 A0 B5 07 B4 07 8B F0"
     CharPos = LineNumber + ChrIndex
     If CharPos <= DataLen
       sBuffer = sBuffer + Right("0" + Hex(PeekA(pString + CharPos)), 2) + " " ;Hex part
     EndIf
   Next

   ;Example: "000000: 31 32 33 34 35 36 37 38 39 30 41 42 43 44 45 46 ; 1234567890ABCDEF"
   sBuffer = sBuffer + "; " ;Used for a sector of 512 bytes

   For ChrIndex = 1 To 16
     CharPos = LineNumber + ChrIndex - 1
     If CharPos <= DataLen
       ChrPeek = PeekA(pString + CharPos - 0)
       Select ChrPeek
         Case 0,1,9,10,13,27,28,29,30,31,127 To 159
           sBuffer = sBuffer + "."
         Default
           sBuffer = sBuffer + Chr(ChrPeek)
       EndSelect
     EndIf
   Next
   sBuffer = sBuffer + Chr(13) + Chr(10)
 Next
 ProcedureReturn(sBuffer)

EndProcedure
;_____________________________________________________________________________

Procedure.l EditSearch(hEditText.i, hEditSearch.i, Reverse.l)
 ;Simple, one way, case insensitive, and wide char
 ;Work by text via an edit control, caret position or selected text.
 ;SearchTextbox empty  + no sel = do nothing
 ;SearchTextbox filled + no sel = find text from sel pos
 ;SearchTextbox empty  + sel    = copy sel to Search Textbox and find text (until crlf or max-char) from sel pos
 ;SearchTextbox filled + sel    = copy sel to Search Textbox and find text (until crlf or max-char) from sel pos
 Protected sToFind.s
 Protected hEditTextMem.i, pEditTextMem.i, pFound.l
 Protected SelStart.l, SelEnd.l, ToFindLen.l ;, Offset.l

 hEditTextMem = SendMessage_(hEditText, #EM_GETHANDLE, 0, 0) ;Get the handle of text in memory
 pEditTextMem = LocalLock_(hEditTextMem)                     ;Get the pointer to that text

 ToFindLen = SendMessage_(hEditSearch, #WM_GETTEXTLENGTH, 0, 0)
 sToFind   = Space(ToFindLen)
 SendMessage_(hEditSearch, #WM_GETTEXT, ToFindLen + 1, @sToFind)
 SendMessage_(hEditText, #EM_GETSEL, @SelStart, @SelEnd)
 If ToFindLen = 0 and SelStart <> SelEnd ;Some text is selected
   sToFind   = PeekS(pEditTextMem + SelStart * 2, SelEnd - SelStart, #PB_Unicode)
   ToFindLen = Len(sToFind)
   If FindString(sToFind, Chr(13)) : sToFind = Left(sToFind, FindString(sToFind, Chr(13)) - 1) : EndIf ;Optionnal limit sToFind
   SendMessage_(hEditSearch, #WM_SETTEXT, 0, @sToFind)
 EndIf

 If ToFindLen
   Protected TextLen.l = SendMessage_(hEditText, #WM_GETTEXTLENGTH, 0, 0)
   If Reverse ;Search backward from the end
     If SelStart = SelEnd ;No selection made so caret position
       SelStart = SelStart - ToFindLen + 1
       If SelStart < 0 : SelStart = TextLen : EndIf ;Caret is at the text end
     EndIf ;Caret

     pFound = StrRStrI_(pEditTextMem, pEditTextMem + SelStart * 2, @sToFind) ;Search text position backward

     If pFound
       SelStart = (pFound - pEditTextMem) / 2 ;pFound is zero if nothing found
       SendMessage_(hEditText, #EM_SETSEL, SelStart, SelStart + ToFindLen)
     Else
       SelStart = 0
     EndIf
   Else ;Search forward ------------------------------------------------------------------------------------
     If SelStart = SelEnd ;No selection made so caret position
       SelStart = SelStart - ToFindLen
       If SelStart = 0 : SelStart = -2 : EndIf ;Caret at the text beginning
     EndIf ;Caret

     pFound = StrStrI_(pEditTextMem + (SelStart + ToFindLen) * 2, @sToFind) ;Search text position

     If pFound
       SelStart = (pFound - pEditTextMem) / 2 ;pFound is zero if nothing found
       SendMessage_(hEditText, #EM_SETSEL, SelStart, SelStart + ToFindLen)
     Else
       SelStart = 0
     EndIf
   EndIf
 Else
   SelStart = -1 ;No search text, nor selected text
 EndIf
 LocalUnlock_(hEditTextMem) ;LocalLock increments the lock count by one, use LocalUnlock to decrements when done.

 ProcedureReturn(SelStart)

EndProcedure
;_____________________________________________________________________________

Procedure WndProc(hWnd, uMsg, wParam, lParam)
 ;Main callback procedure
 Protected *pNotifyMessageUpDown.NM_UPDOWN
 Protected *CreateStruct.CREATESTRUCT
 Protected LogicalFont.LOGFONT
 Protected NonClient.NONCLIENTMETRICS
 Protected DiskGeometry.DISK_GEOMETRY
 Protected hDC.i, sBuffer.s, BlockIndex.q, BlockSearchIndex.q
 Protected UpDownAccellerationStepCount.l, ReadCount.l, Looper.l, ControlId.w, Action.w
 Static hInstance.i, hFrame01.i, hCombo01.i, hButton01.i, hOptionPhysical.i, pSector.i
 Static hOptionLogical.i, hStaticDiskFound.i, hStaticHardDisk.i, hComboDisk.i
 Static hStaticBlock.i, hStaticMeg.i, hStaticDiskIdPart.i, hStaticCyl.i, hStaticHead.i
 Static hStaticSect.i, hButtonPrev.i, hButtonNext.i, hStaticLogicalBlock.i, hEditLogicalBlock.i
 Static hButtonRead.i, hUpDown.i, hStaticSearchStatus.i, hEditHex.i, hEditSearch.i, hFont.i, hFixedFont.i
 Static hStaticPhysical.i, hStaticLogical.i, hEditBlockIndex.i, hBlueBrush.i, hButtonStop.i, hDisk.i
 Static StartPos.l, PhysicalDisk.l, Disk.l, DiskCount.l, DiskError.l, UpDownValH.l, UpDownDeltaH.l, UpDownStartPosition.l
 Static sDisk.s, BlockCount.q
 Dim UpDownAccelleration.UDACCEL(7)

 Select uMsg

   Case #WM_CREATE
     pSector = AllocateMemory(#BlockSize)
     *CreateStruct = lParam ;Get hInstance from CreateWindowEx_()
     hInstance = *CreateStruct\lpCreateParams
     hBlueBrush = CreateSolidBrush_($c0320a)
     CreateControls ;Macro for controls creation

     ;Sets the acceleration for the up-down control,
     ;after n seconds pushed, auto increment by nInc
     UpDownStartPosition          = 0 - 2147483648 + 1 ;Needed for 2 tb, LONG -2,147,483,648 to +2,147,483,647  (+1 so the control limit will be respected)
     UpDownAccelleration(0)\nSec  = 1 : UpDownAccelleration(0)\nInc = 1
     UpDownAccelleration(1)\nSec  = 2 : UpDownAccelleration(1)\nInc = 10
     UpDownAccelleration(2)\nSec  = 3 : UpDownAccelleration(2)\nInc = 100
     UpDownAccelleration(3)\nSec  = 4 : UpDownAccelleration(3)\nInc = 1000
     UpDownAccelleration(4)\nSec  = 5 : UpDownAccelleration(4)\nInc = 10000
     UpDownAccelleration(5)\nSec  = 6 : UpDownAccelleration(5)\nInc = 100000
     UpDownAccelleration(6)\nSec  = 7 : UpDownAccelleration(6)\nInc = 1000000
     UpDownAccelleration(7)\nSec  = 8 : UpDownAccelleration(7)\nInc = 10000000
     UpDownAccellerationStepCount = 8
     SendMessage_(hUpDown, #UDM_SETACCEL, UpDownAccellerationStepCount, @UpDownAccelleration(0))

     ;Click button programmatically to initiate
     PostMessage_(hWnd, #WM_COMMAND, MakeDword(#StaticPhysical, #BN_CLICKED), hStaticPhysical)
     ProcedureReturn(0)

   Case #WM_COMMAND
     ControlId = LoWord(wParam)
     Action    = HiWord(wParam)
     Select ControlId

       Case #OptionPhysical, #StaticPhysical
         If (Action = #BN_CLICKED) Or (Action = 1) ;BN_CLICKED = STN_CLICKED = 0
           If ControlId = #StaticPhysical
             CheckDlgButton_(hWnd, #OptionPhysical, #BST_CHECKED)
             CheckDlgButton_(hWnd, #OptionLogical, #BST_UNCHECKED)
           EndIf
           PhysicalDisk = IsDlgButtonChecked_(hWnd, #OptionPhysical) ;Set PhysicalDisk
           BlockIndex = 0
           SendMessage_(hEditLogicalBlock, #EM_SETSEL, 0, -1) ;Select all
           SendMessage_(hEditLogicalBlock, #EM_REPLACESEL, #True, "00") ;Replace selection
           SendMessage_(hComboDisk, #CB_RESETCONTENT, 0, 0)
           sBuffer = Space(3 * 26)
           DiskCount = GetPhysicalDiskId(@sBuffer, Len(sBuffer))
           SendMessage_(hStaticDiskFound, #WM_SETTEXT, 0, "Disk found: " + Str(DiskCount))
           If DiskCount = 0
             MessageBox_(hWnd, "No disk fatal error", #AppName, #MB_OK | #MB_TOPMOST)
             PostMessage_(hWnd, #WM_SYSCOMMAND, #SC_CLOSE, 0)
           Else
             For Looper = 1 To DiskCount ;example: "00:01:04:06"
               SendMessage_(hComboDisk, #CB_ADDSTRING, Looper - 1, Mid(sBuffer, 1 + ((Looper - 1) * 3), 2))
             Next
             Disk = Val(Left(sBuffer, 2))
             SendMessage_(hComboDisk, #CB_SETCURSEL, 0, 00)
           EndIf
           SendMessage_(hUpDown, #UDM_SETPOS32, 0, UpDownStartPosition) ;Reset to zero
           BlockIndex = 0
           SendMessage_(hEditLogicalBlock, #WM_SETTEXT, 0, FormatNumber(BlockIndex, 0, ".", ","))
           PostMessage_(hWnd, #WM_COMMAND, MakeDword(#ComboDisk, #CBN_SELCHANGE), hComboDisk)
         EndIf

       Case #OptionLogical, #StaticLogical
         If (Action = #BN_CLICKED) Or (Action = 1) ;BN_CLICKED = STN_CLICKED = 0
           If LoWord(wParam) = #StaticLogical
             CheckDlgButton_(hWnd, #OptionPhysical, #BST_UNCHECKED)
             CheckDlgButton_(hWnd, #OptionLogical, #BST_CHECKED)
           EndIf
           PhysicalDisk = IsDlgButtonChecked_(hWnd, #OptionPhysical) ;Set PhysicalDisk
           SendMessage_(hComboDisk, #CB_RESETCONTENT, 0, 0)
           sBuffer = Space(3 * 26)
           DiskCount = GetLogicalDiskId(@sBuffer, Len(sBuffer))
           SendMessage_(hStaticDiskFound, #WM_SETTEXT, 0, "Disk found: " + Str(DiskCount))
           If DiskCount = 0
             MessageBox_(hWnd, "No disk fatal error!", #AppName, #MB_OK | #MB_TOPMOST)
             PostMessage_(hWnd, #WM_SYSCOMMAND, #SC_CLOSE, 0)
           Else
             For Looper = 1 To DiskCount
               SendMessage_(hComboDisk, #CB_ADDSTRING, Looper - 1, Mid(sBuffer, 1 + ((Looper - 1) * 3), 3))
             Next
             sDisk = Left(sBuffer, 3)
             SendMessage_(hComboDisk, #CB_SETCURSEL, 0, 00)
           EndIf
           SendMessage_(hUpDown, #UDM_SETPOS32, 0, UpDownStartPosition) ;Reset to zero
           BlockIndex = 0
           SendMessage_(hEditLogicalBlock, #WM_SETTEXT, 0, FormatNumber(BlockIndex, 0, ".", ","))
           PostMessage_(hWnd, #WM_COMMAND, MakeDword(#ComboDisk, #CBN_SELCHANGE), hComboDisk)
         EndIf

       Case #ComboDisk
         If (Action = #CBN_SELCHANGE) Or (Action = 1)
           If hDisk : CloseDisk(@hDisk) : EndIf
           Disk       = 0
           BlockCount = 0
           BlockIndex = 0
           SendMessage_(hEditBlockIndex, #WM_SETTEXT, 0, Str(BlockIndex))
           If PhysicalDisk
             sBuffer = Space(SendMessage_(hComboDisk, #WM_GETTEXTLENGTH, 0, 0))
             SendMessage_(hComboDisk, #WM_GETTEXT, Len(sBuffer) + 1, @sBuffer)
             Disk = Val(sBuffer)
             hDisk = OpenPhysicalDisk(Disk, #False) ;#FALSE for open in read only mode
             BlockCount = GetPhysicalDiskBlockCount(hDisk)
             If GetPhysicalDiskGeometry(hDisk, @DiskGeometry)
               SendMessage_(hStaticCyl, #WM_SETTEXT, 0, "Cyl : " + FormatNumber(DiskGeometry\Cylinders, 0, ".", ","))
               SendMessage_(hStaticHead, #WM_SETTEXT, 0, "Head: " + FormatNumber(DiskGeometry\TracksPerCylinder, 0, ".", ","))
               SendMessage_(hStaticSect, #WM_SETTEXT, 0, "Sect: " + FormatNumber(DiskGeometry\SectorsPerTrack, 0, ".", ","))
               SendMessage_(hStaticDiskIdPart, #WM_SETTEXT, 0, "")
             EndIf
           Else ;Logical disk
             sDisk = Space(SendMessage_(hComboDisk, #WM_GETTEXTLENGTH, 0, 0))
             SendMessage_(hComboDisk, #WM_GETTEXT, Len(sDisk) + 1, @sDisk)
             hDisk      = OpenLogicalDisk(sDisk, #False, hStaticDiskIdPart) ;#FALSE for open in read only mode
             BlockCount = GetLogicalDiskBlockCount(sDisk)
             SendMessage_(hStaticCyl, #WM_SETTEXT, 0, 0)
             SendMessage_(hStaticHead, #WM_SETTEXT, 0, 0)
             SendMessage_(hStaticSect, #WM_SETTEXT, 0, 0)
             SendMessage_(hStaticDiskIdPart, #WM_SETTEXT, 0, 0)
           EndIf
           SendMessage_(hStaticMeg, #WM_SETTEXT, 0, "Bytes: " + FormatNumber(BlockCount * #BlockSize, 0, ".", ","))
           SendMessage_(hUpDown, #UDM_SETRANGE32, UpDownStartPosition, UpDownStartPosition + BlockCount - 1)
           SendMessage_(hUpDown, #UDM_SETPOS32, 0, UpDownStartPosition) ;Reset to zero
           SendMessage_(hStaticBlock, #WM_SETTEXT, 0, "Block: " + FormatNumber(BlockCount, 0, ".", ","))
           PostMessage_(hWnd, #WM_COMMAND, MakeDword(#ButtonRead, #BN_CLICKED), hButtonRead)
           SendMessage_(hUpDown, #UDM_SETPOS32, 0, UpDownStartPosition) ;Reset to zero
           BlockIndex = 0
           SendMessage_(hEditLogicalBlock, #WM_SETTEXT, 0, FormatNumber(BlockIndex, 0, ".", ","))

         EndIf

       Case #StaticBlockCount
         If (Action = #STN_DBLCLK) Or (Action = 1) ;Copy block count to logical block edit control
           SendMessage_(hEditLogicalBlock, #WM_SETTEXT, 0, FormatNumber(BlockCount - 1, 0, ".", ","))
         EndIf

       Case #EditLogicalBlock
         If (Action = #EN_CHANGE) Or (Action = 1)
             GetLBA(BlockIndex) ;Read LBA edit control new value
         EndIf

       Case #ButtonRead, #IDOK
         If (Action = #BN_CLICKED) Or (Action = 1)

           If DiskError = #False
             GetLBA(BlockIndex) ;Read LBA edit control
             SendMessage_(hEditLogicalBlock, #WM_SETTEXT, 0, FormatNumber(BlockIndex, 0, ".", ","))
             sBuffer = Space(#BlockSize) ;4096
             ReadCount = DiskRead(hDisk, BlockIndex, @sBuffer, Len(sBuffer))
             If ReadCount = 0
               DiskError = #True
               SendMessage_(hEditHex, #WM_SETTEXT, 0, 0)
               MessageBox_(hWnd, "Disk error on block " + FormatNumber(BlockIndex, 0, ".", ","),
                           #AppName, #MB_ICONINFORMATION | #MB_OK)
               DiskError = #False
             Else
               SendMessage_(hEditHex, #WM_SETTEXT, 0, DataToHexView(@sBuffer, ReadCount))
             EndIf
           EndIf
         EndIf

       Case #IDCANCEL ;Escape key
         Static SearchStop.l
         Static SearchStarted.l
         SearchStop = #True
         SendMessage_(hWnd, #WM_APP, 01, 0) ;Enable buttons

       Case #ButtonStop
         If (Action = #BN_CLICKED) Or (Action = 1)
           SearchStop = #True
           SendMessage_(hWnd, #WM_APP, 01, 0) ;Enable buttons
         EndIf

       Case #EditSearch
         If (Action = #EN_CHANGE) Or (Action = 1)
             GetLBA(BlockIndex)
         EndIf

       Case #ButtonSearchPrev, #ButtonSearchNext
         If (Action = #BN_CLICKED) Or (Action = 1)
           Protected Sector.s{#BlockSize}
           Protected FoundPos.l

           If SearchStarted
             SearchStop = #True
           Else
             SearchStarted = #True
             If DiskError = #False
               GetLBA(BlockSearchIndex)
               SendMessage_(hWnd, #WM_APP, 00, 0) ;Disable buttons
               If ControlId = #ButtonSearchNext
                 Repeat ;For/Next won't work with quad under 32 bit as in FOR BlockSearchIndex = BlockSearchIndex TO BlockCount - 1
                   FoundPos = EditSearch(hEditHex, hEditSearch, #False) ;#False for forward search
                   SendMessage_(hStaticSearchStatus, #WM_SETTEXT, 0, "Searching " + FormatNumber(BlockSearchIndex, 0, ".", ","))
                   If FoundPos = -1 ;No search text, nor selected text
                     MessageBox_(hWnd, "No search text, nor selected text!", #AppName, #MB_OK | #MB_TOPMOST)
                     SearchStop = #False
                     SendMessage_(hWnd, #WM_APP, 01, 0) ;Enable buttons
                     Break
                   ElseIf FoundPos ;FoundPos >= 0
                     SendMessage_(hStaticSearchStatus, #WM_SETTEXT, 0, "Found in " + FormatNumber(BlockSearchIndex, 0, ".", ","))
                     SearchStarted = #False
                     SendMessage_(hWnd, #WM_APP, 01, 0) ;Enable buttons
                     Break
                   Else ;FoundPos = 0, none or no more found
                     BlockSearchIndex = BlockSearchIndex + 1
                     If BlockSearchIndex = BlockCount - 1  Or SearchStop ;SearchStop Or
                       SearchStop = #False
                       SendMessage_(hWnd, #WM_APP, 01, 0) ;Enable buttons
                       Break
                     EndIf
                     ReadCount = DiskRead(hDisk, BlockSearchIndex, @Sector, #BlockSize)
                     If ReadCount = 0
                       DiskError = #True
                       SendMessage_(hEditHex, #WM_SETTEXT, 0, 0)
                       MessageBox_(hWnd, "Disk error on block " + FormatNumber(BlockIndex, 0, ".", ","),
                                   #AppName, #MB_OK | #MB_TOPMOST)
                       DiskError     = #False
                       SearchStarted = #False
                       SendMessage_(hWnd, #WM_APP, 01, 0) ;Enable buttons
                       Break
                     Else ;Read disk successfull
                       SendMessage_(hEditHex, #WM_SETTEXT, 0, DataToHexView(@Sector, #BlockSize))
                       SendMessage_(hEditLogicalBlock, #WM_SETTEXT, 0, FormatNumber(BlockSearchIndex, 0, ".", ","))
                       BlockIndex = BlockSearchIndex
                     EndIf
                   EndIf
                   WindowEvent() ;Let window bread
                 ForEver
               Else ;#ButtonSearchPrev clicked, going backward
                 Repeat ;For/Next won't work with quad under 32 bit as in FOR BlockSearchIndex = BlockSearchIndex TO BlockCount - 1
                   FoundPos = EditSearch(hEditHex, hEditSearch, #True) ;#True for reverse search
                   SendMessage_(hStaticSearchStatus, #WM_SETTEXT, 0, "Searching " + FormatNumber(BlockSearchIndex, 0, ".", ","))
                   If FoundPos = -1 ;No search text, nor selected text
                     MessageBox_(hWnd, "No search text, nor selected text!", #AppName, #MB_OK | #MB_TOPMOST)
                     SearchStop = #False
                     SendMessage_(hWnd, #WM_APP, 01, 0) ;Enable buttons
                     Break
                   ElseIf FoundPos ;Valid FoundPos >= 0
                     SendMessage_(hStaticSearchStatus, #WM_SETTEXT, 0, "Found in " + FormatNumber(BlockSearchIndex, 0, ".", ","))
                     SearchStarted = #False
                     SendMessage_(hWnd, #WM_APP, 01, 0) ;Enable buttons
                     Break
                   Else ;FoundPos = 0, none or no more found
                       SendMessage_(hStaticSearchStatus, #WM_SETTEXT, 0, "No found")
                       BlockSearchIndex = BlockSearchIndex - 1
                       If BlockSearchIndex = -1 Or SearchStop
                         SearchStop = #False
                         SendMessage_(hWnd, #WM_APP, 01, 0) ;Enable buttons
                         Break
                       EndIf
                       ReadCount = DiskRead(hDisk, BlockSearchIndex, @Sector, #BlockSize)
                       If ReadCount = 0
                         DiskError = #True
                         SendMessage_(hEditHex, #WM_SETTEXT, 0, 0)
                         MessageBox_(hWnd, "Disk error on block " + FormatNumber(BlockIndex, 0, ".", ","),
                                     #AppName, #MB_OK | #MB_TOPMOST)
                         DiskError     = #False
                         SearchStarted = #False
                         SendMessage_(hWnd, #WM_APP, 01, 0) ;Enable buttons
                         Break
                       Else ;Read disk successfull
                         SendMessage_(hEditHex, #WM_SETTEXT, 0, DataToHexView(@Sector, #BlockSize))
                         SendMessage_(hEditLogicalBlock, #WM_SETTEXT, 0, FormatNumber(BlockSearchIndex, 0, ".", ","))
                         BlockIndex = BlockSearchIndex
                       EndIf
                   EndIf
                   WindowEvent() ;Let window bread
                 ForEver
               EndIf
             EndIf
             SearchStarted = #False
           EndIf
         EndIf

     EndSelect

   Case #WM_APP
     If wParam ;Enable buttons
       EnableWindow_(hButtonStop, #False)
       EnableWindow_(hOptionPhysical, #True)
       EnableWindow_(hOptionLogical, #True)
       EnableWindow_(hComboDisk, #True)

       EnableWindow_(hButtonPrev, #True)
       EnableWindow_(hButtonNext, #True)

       EnableWindow_(hEditLogicalBlock, #True)
       EnableWindow_(hButtonRead, #True)

       EnableWindow_(hEditSearch, #True)
       EnableWindow_(hUpDown, #True)
     Else ;Disable buttons
       EnableWindow_(hButtonStop, #True)
       EnableWindow_(hOptionPhysical, #False)
       EnableWindow_(hOptionLogical, #False)
       EnableWindow_(hComboDisk, #False)

       EnableWindow_(hButtonPrev, #False)
       EnableWindow_(hButtonNext, #False)

       EnableWindow_(hEditLogicalBlock, #False)
       EnableWindow_(hButtonRead, #False)

       EnableWindow_(hEditSearch, #False)
       EnableWindow_(hUpDown, #False)
     EndIf
     RedrawWindow_(hButtonStop, #Null, #Null, #RDW_INVALIDATE)
     RedrawWindow_(hOptionPhysical, #Null, #Null, #RDW_INVALIDATE)
     RedrawWindow_(hOptionLogical, #Null, #Null, #RDW_INVALIDATE)
     RedrawWindow_(hComboDisk, #Null, #Null, #RDW_INVALIDATE)
     RedrawWindow_(hButtonPrev, #Null, #Null, #RDW_INVALIDATE)
     RedrawWindow_(hButtonNext, #Null, #Null, #RDW_INVALIDATE)
     RedrawWindow_(hEditLogicalBlock, #Null, #Null, #RDW_INVALIDATE)
     RedrawWindow_(hButtonRead, #Null, #Null, #RDW_INVALIDATE)
     InvalidateRect_(hButtonRead, #Null, #True) : UpdateWindow_(hButtonRead)
     RedrawWindow_(hEditSearch, #Null, #Null, #RDW_INVALIDATE)
     RedrawWindow_(hUpDown, #Null, #Null, #RDW_INVALIDATE)
     RedrawWindow_(hWnd, #Null, #Null, #RDW_INVALIDATE | #RDW_ALLCHILDREN)

   Case #WM_NOTIFY
     *pNotifyMessageUpDown = lParam ;NM_UPDOWN
     If *pNotifyMessageUpDown\hdr\Code = #UDN_DELTAPOS
        If *pNotifyMessageUpDown\hdr\HwndFrom = hUpDown
           UpDownDeltaH = *pNotifyMessageUpDown\iDelta
        EndIf
     EndIf

   Case #WM_VSCROLL, #WM_HSCROLL ;UpDownHBlockIndex
     ;nScrollCode   = LO(INTEGER, wParam) ;Scroll bar value
     ;nPos          = HI(INTEGER, wParam) ;Scroll box position
     ;hwndScrollBar = lParam              ;Handle of scroll bar
     ;IF GetDlgCtrlID_(lParam) = #UpDownHBlockIndex
     If lParam = hUpDown
       Protected UpDownError.l
       ;GetLBA(BlockIndex) ;Read LBA edit control new value
       UpDownValH = SendMessage_(lParam, #UDM_GETPOS32, #False, UpDownError)
       SendMessage_(hEditLogicalBlock, #WM_SETTEXT, 0, FormatNumber(UpDownValH - UpDownStartPosition, 0, ".", ",")) ;-2147483648 ;Needed for 2 tb, LONG -2,147,483,648 to +2,147,483,647
       PostMessage_(hWnd, #WM_COMMAND, MakeDword(#ButtonRead, #BN_CLICKED), hButtonRead)
     EndIf

   Case #WM_CTLCOLORSTATIC ;wParam is device context
     SetTextColor_(WPARAM, #Yellow)
     SetBkMode_(WPARAM, $c0320a)
     ProcedureReturn(hBlueBrush)

   Case #WM_CLOSE
     PostMessage_(hWnd, #WM_QUIT, 0, 0)

   Case #WM_DESTROY
     ;Clean-up
     FreeMemory(pSector)
     DeleteObject_(hFont)
     DeleteObject_(hFixedFont)
     DeleteObject_(hBlueBrush)
     PostQuitMessage_(0)

 EndSelect
 ProcedureReturn(DefWindowProc_(hWnd, uMsg, wParam, lParam))

EndProcedure
;_____________________________________________________________________________

 ;Create main window
 Define wce.WndClassEx
 Define WindowSize.Sizel
 Define msg.msg
 Define AppName.s
 Define hWnd.i
 Define hInstance.i

 AppName   = "SectorReader"
 hInstance = GetModuleHandle_(0)

 Define AdminLevel.l = IsUserAdmin()
 If AdminLevel = 0
   MessageBox_(hWnd, "Must be run as Admin!", #AppName, #MB_OK | #MB_TOPMOST)
 Else
   wce\cbSize        = SizeOf(WNDCLASSEX)
   wce\STYLE         = #CS_HREDRAW | #CS_VREDRAW
   wce\lpfnWndProc   = @WndProc()
   wce\cbClsExtra    = 0
   wce\cbWndExtra    = 0
   wce\hInstance     = hInstance
   wce\hIcon         = ExtractIcon_(GetModuleHandle_(""), "shell32.dll", 8)
   wce\hIconSm       = wce\hIcon
   wce\hCursor       = LoadCursor_(#Null, #IDC_ARROW)
   wce\hbrBackground = CreateSolidBrush_($c0320a)
   wce\lpszMenuName  = 0
   wce\lpszClassName = @AppName
   RegisterClassEx_(@wce)
   WindowSize\cx = 714
   WindowSize\cy = 706

   ;Create a window using the registered class
   hWnd = CreateWindowEx_(#WS_EX_CLIENTEDGE,
                          AppName, ;Window class name
                          AppName, ;Window caption
                          #WS_OVERLAPPED | #WS_BORDER | #WS_DLGFRAME | #WS_CAPTION | #WS_SYSMENU |
                          #WS_MINIMIZEBOX | #WS_CLIPSIBLINGS | #WS_VISIBLE,
                          (GetSystemMetrics_(#SM_CXSCREEN) - WindowSize\cx) / 2, ;Center position x
                          (GetSystemMetrics_(#SM_CYSCREEN) - WindowSize\cy) / 2, ;Centr position y
                          WindowSize\cx, ;x size
                          WindowSize\cy, ;y size
                          #HWND_DESKTOP, ;Parent window handle
                          0,             ;Window menu handle
                          hInstance,     ;Program instance handle
                          hInstance)     ;Creation parameters

   ShowWindow_(hWnd, #SW_SHOWDEFAULT)
   UpdateWindow_(hWnd)

   While GetMessage_(Msg, 0, 0, 0)
     If IsDialogMessage_(hWnd, Msg) = #False
       TranslateMessage_(Msg)
       DispatchMessage_(Msg)
     EndIf
   Wend
 EndIf
 DestroyIcon_(wce\hIcon)

 End msg\wParam
;_____________________________________________________________________________
;
; IDE Options = PureBasic 5.73 LTS (Windows - x86)
; CursorPosition = 1217
; FirstLine = 1203
; Folding = ----
; Markers = 1,190
; EnableAsm
; EnableXP
; EnableAdmin
; Executable = SectorReader.exe
; DisableDebugger
; CompileSourceDirectory
; EnableUnicode

Re: Accès direct Secteur de disque

Publié : lun. 08/janv./2024 10:02
par Ar-S
Merci pour ce partage. :)

Re: Accès direct Secteur de disque

Publié : lun. 08/janv./2024 16:36
par Pierre Bellisle
J'espère que le code plaira, notez qu'il faut le rouler en administrateur pour avoir les droits d'accès aux disques.
Une routine pour écrire est aussi incluse. Elle n'est jamais appelée dans le cadre de cette application.
Aux audacieux qui pourrait s'en servir, soyez prudent. Un disque USB pourrait être idéal pour expérimenter...
Amusez-vous!
:D

Re: Accès direct Secteur de disque

Publié : mar. 16/janv./2024 19:38
par Kwai chang caine
C'est cool comme code, y'en a pas souvent sur les forums 8O
Il marche niquel, bien que je n'ai pas les connaissances pour l'utiliser à fond :|
Merci beaucoup, j'aimerais avoir ne serait-ce, qu'une petite partie de tes connaissances :wink:
J'ai une question, sais-tu pourquoi en mode admin c'est le second debugger qui démarre automatiquement, on peut pas le faire avec le premier ?

Re: Accès direct Secteur de disque

Publié : lun. 22/janv./2024 22:03
par Pierre Bellisle
Salut Kwai,

Un petit truc, démarre ton IDE (PureBasic.exe) en mode administrateur, de plus, l'exécutable compilé en héritera,
donc, tu n'auras pas de demande de permission UAC à chaque compilation incluant exécution.
C'est utile pour tout code qui demande les droits d'admin...