Einzelnen Beitrag anzeigen

Alter Mann

Registriert seit: 15. Nov 2003
Ort: Berlin
947 Beiträge
 
Delphi 10.2 Tokyo Professional
 
#1

IOCTL_STORAGE_GET_DEVICE_NUMBER, DRIVE_GEOMETRY und DRIVE_LAYOUT

  Alt 7. Sep 2010, 13:48
Hallo,

hier ein kleines Beispiel für die Verwendung von IOCTL_STORAGE_GET_DEVICE_NUMBER, IOCTL_DISK_GET_DRIVE_GEOMETRY und IOCTL_DISK_GET_DRIVE_LAYOUT.
Damit es läuft braucht man die jwaWinIOCTL bzw. die entsprechenden Deklaration(en).

Kernstück ist die Unit uDevice:
Delphi-Quellcode:
unit uDevice;

interface

uses
  Classes, Windows, jwaWindows;

type
  TDeviceRec = record
    DeviceNr : DWORD;
    DeviceStr : String;
    DriveStr : String;
    PartitionStr : String;
    PartitionNr : DWORD;
    DeviceTyp : DEVICE_TYPE;
    DiskGeometry : DISK_GEOMETRY;
    PartitionData : PARTITION_INFORMATION;
  end;

  TDynDeviceRec = record
    DeviceNr : DWORD;
    DeviceStr : String;
    DiskGeometry : DISK_GEOMETRY;
    PartitionData : Array of PARTITION_INFORMATION;
    MediaTypeStr : String;
  end;

  TDeviceRecList = Array of TDeviceRec;
  TDynDeviceRecList = Array of TDynDeviceRec;

function GetAllLogicalDevices(var aDeviceRecList : TDeviceRecList) : Boolean;
function GetAllPhysicalDevices(var aDynDeviceRecList : TDynDeviceRecList) : Boolean;

implementation

uses SysUtils;

function MediaTypeToStr(aMediaType : MEDIA_TYPE) : String;
begin
  case aMediatype of
    Unknown : Result := 'UNKNOWN';
    F5_1Pt2_512 : Result := '5.25" floppy, with 1.2MB and 512 bytes/sector';
    F3_1Pt44_512 : Result := '3.5" floppy, with 1.44MB and 512 bytes/sector';
    F3_2Pt88_512 : Result := '3.5" floppy, with 2.88MB and 512 bytes/sector';
    F3_20Pt8_512 : Result := '3.5" floppy, with 20MB and 512 bytes/sector';
    F3_720_512 : Result := '3.5" floppy, with 720KB and 512 bytes/sector';
    F5_360_512 : Result := '5.25" floppy, with 360KB and 512 bytes/sector';
    F5_320_512 : Result := '5.25" floppy, with 320KB and 512 bytes/sector';
    F5_320_1024 : Result := '5.25" floppy, with 360KB and 1024 bytes/sector';
    F5_180_512 : Result := '5.25" floppy, with 180KB and 512 bytes/sector';
    F5_160_512 : Result := '5.25" floppy, with 160KB and 512 bytes/sector';
    RemovableMedia : Result := 'Removable media other than floppy';
    FixedMedia : Result := 'Fixed hard disk media';
    F3_120M_512 : Result := '3.5" floppy, with 120MB and 512 bytes/sector';
    F3_640_512 : Result := '3.5" floppy, with 640MB and 512 bytes/sector';
    F5_640_512 : Result := '5.25" floppy, with 640KB and 512 bytes/sector';
    F5_720_512 : Result := '5.25" floppy, with 720KB and 512 bytes/sector';
    F3_1Pt2_512 : Result := '3.5" floppy, with 1.2MB and 512 bytes/sector';
    F3_1Pt23_1024 : Result := '3.5" floppy, with 1.23MB and 1024 bytes/sector';
    F5_1Pt23_1024 : Result := '5.25" floppy, with 1.23KB and 1024 bytes/sector';
    F3_128Mb_512 : Result := '3.5" floppy, with 128MB and 512 bytes/sector';
    F3_230Mb_512 : Result := '3.5" floppy, with 230MB and 512 bytes/sector';
    F8_256_128 : Result := '8" floppy, with 256KB and 128 bytes/sector';
    F3_200Mb_512 : Result := '3.5" floppy, with 200MB and 512 bytes/sector. (HiFD)';
    F3_240M_512 : Result := '3.5" floppy, with 240MB and 512 bytes/sector. (HiFD)';
    F3_32M_512 : Result := '3.5" floppy, with 32MB and 512 bytes/sector';
  end;
end;

function DeviceTypeToStr(aDeviceType : DEVICE_TYPE):String;
begin
  case aDeviceType of
    FILE_DEVICE_BEEP : Result :='BEEP';
    FILE_DEVICE_CD_ROM : Result :='CD-ROM';
    FILE_DEVICE_CD_ROM_FILE_SYSTEM : Result :='CD-ROM FILE-SYSTEM';
    FILE_DEVICE_CONTROLLER : Result :='CONTROLLER';
    FILE_DEVICE_DATALINK : Result :='DATALINK';
    FILE_DEVICE_DFS : Result :='DFS';
    FILE_DEVICE_DISK : Result :='DISK';
    FILE_DEVICE_DISK_FILE_SYSTEM : Result :='DISK FILE-SYSTEM';
    FILE_DEVICE_FILE_SYSTEM : Result :='FILE-SYSTEM';
    FILE_DEVICE_INPORT_PORT : Result :='INPORT-PORT';
    FILE_DEVICE_KEYBOARD : Result :='KEYBOARD';
    FILE_DEVICE_MAILSLOT : Result :='MAILSLOT';
    FILE_DEVICE_MIDI_IN : Result :='MIDI_IN';
    FILE_DEVICE_MIDI_OUT : Result :='MIDI_OUT';
    FILE_DEVICE_MOUSE : Result :='MOUSE';
    FILE_DEVICE_MULTI_UNC_PROVIDER : Result :='MULTI UNC-PROVIDER';
    FILE_DEVICE_NAMED_PIPE : Result :='NAMED-PIPE';
    FILE_DEVICE_NETWORK : Result :='NETWORK';
    FILE_DEVICE_NETWORK_BROWSER : Result :='NETWORK-BROWSER';
    FILE_DEVICE_NETWORK_FILE_SYSTEM : Result :='NETWORK FILE-SYSTEM';
    FILE_DEVICE_NULL : Result :='NULL';
    FILE_DEVICE_PARALLEL_PORT : Result :='PARALLEL-PORT';
    FILE_DEVICE_PHYSICAL_NETCARD : Result :='PHYSICAL-NETCARD';
    FILE_DEVICE_PRINTER : Result :='PRINTER';
    FILE_DEVICE_SCANNER : Result :='SCANNER';
    FILE_DEVICE_SERIAL_MOUSE_PORT : Result :='SERIAL MOUSE-PORT';
    FILE_DEVICE_SERIAL_PORT : Result :='SERIAL-PORT';
    FILE_DEVICE_SCREEN : Result :='SCREEN';
    FILE_DEVICE_SOUND : Result :='SOUND';
    FILE_DEVICE_STREAMS : Result :='STREAMS';
    FILE_DEVICE_TAPE : Result :='TAPE';
    FILE_DEVICE_TAPE_FILE_SYSTEM : Result :='TAPE FILE-SYSTEM';
    FILE_DEVICE_TRANSPORT : Result :='TRANSPORT';
    FILE_DEVICE_UNKNOWN : Result :='UNKNOWN';
    FILE_DEVICE_VIDEO : Result :='VIDEO';
    FILE_DEVICE_VIRTUAL_DISK : Result :='VIRTUAL-DISK';
    FILE_DEVICE_WAVE_IN : Result :='WAVE_IN';
    FILE_DEVICE_WAVE_OUT : Result :='WAVE_OUT';
    FILE_DEVICE_8042_PORT : Result :='8042-PORT';
    FILE_DEVICE_NETWORK_REDIRECTOR : Result :='NETWORK-REDIRECTOR';
    FILE_DEVICE_BATTERY : Result :='BATTERY';
    FILE_DEVICE_BUS_EXTENDER : Result :='BZS EXTENDER';
    FILE_DEVICE_MODEM : Result :='MODEM';
    FILE_DEVICE_VDM : Result :='VDM';
    FILE_DEVICE_MASS_STORAGE : Result :='MASS-STORAGE';
    FILE_DEVICE_SMB : Result :='SMB';
    FILE_DEVICE_KS : Result :='KS';
    FILE_DEVICE_CHANGER : Result :='CHANGER';
    FILE_DEVICE_SMARTCARD : Result :='SMARTCARD';
    FILE_DEVICE_ACPI : Result :='ACPI';
    FILE_DEVICE_DVD : Result :='DVD';
    FILE_DEVICE_FULLSCREEN_VIDEO : Result :='FULLSCREEN VIDEO';
    FILE_DEVICE_DFS_FILE_SYSTEM : Result :='DFS FILE-SYSTEM';
    FILE_DEVICE_DFS_VOLUME : Result :='DFS-VOLUME';
    FILE_DEVICE_SERENUM : Result :='SERENUM';
    FILE_DEVICE_TERMSRV : Result :='TERMINAL-SERVER';
    FILE_DEVICE_KSEC : Result :='KSEC';
    FILE_DEVICE_FIPS : Result :='FIPS';
    FILE_DEVICE_INFINIBAND : Result :='INFINIBAND';
    else Result := IntToStr(aDeviceType);
  end;
end;

function DeviceExists(aDeviceNumber : DWORD) : Boolean;
var
  hVolume : THandle;
begin
  hVolume := CreateFile(PChar(Format('\\.\PHYSICALDRIVE%d', [aDeviceNumber])),
                        GENERIC_READ,
                        FILE_SHARE_READ or FILE_SHARE_WRITE,
                        nil,
                        OPEN_EXISTING, 0, 0);
  CloseHandle(hVolume);
  Result := hVolume <> INVALID_HANDLE_VALUE;
end;

function GetDeviceNumber(const aDriveLetter : PChar;
                         var aDeviceRec : TDeviceRec) : Boolean; overload;
var
  pVolume : PChar;
  dwSize : DWORD;
  hVolume : THandle;
  SDN : STORAGE_DEVICE_NUMBER;
  DG : DISK_GEOMETRY;
  pDLI : PDRIVE_LAYOUT_INFORMATION;
  lpBytesReturned : DWORD;
  Ret,
  dliSize : Integer;
begin
  Ret := -1;
  dwSize := 6;
  pVolume := StrAlloc(dwSize);
  try
    StrPCopy(pVolume, Format('\\.\%s:', [Copy(aDriveLetter, 1, 1)]));
    hVolume := CreateFile(pVolume,
                          GENERIC_READ,
                          FILE_SHARE_READ or FILE_SHARE_WRITE,
                          nil,
                          OPEN_EXISTING, 0, 0);
    if hVolume <> INVALID_HANDLE_VALUE then
    begin
      if DeviceIOControl(hVolume,
                         IOCTL_STORAGE_GET_DEVICE_NUMBER,
                         nil,
                         0,
                         @SDN,
                         SizeOf(SDN),
                         @lpBytesReturned,
                         nil) then
      begin
        Ret := SDN.DeviceNumber;
        with aDeviceRec do
        begin
          DeviceNr := Ret;
          PartitionNr := SDN.PartitionNumber;
          DeviceTyp := SDN.DeviceType;
          DriveStr := Format('%s:\', [aDriveLetter]);
          case SDN.DeviceType of
            FILE_DEVICE_DISK : DeviceStr := Format('\\.\PHYSICALDRIVE%d', [DeviceNr]);
            else DeviceStr := Format('DEVICE: %s ', [DeviceTypeToStr(DeviceTyp)]);
          end;
          PartitionStr := Format('Partition%d', [PartitionNr]);
        end;
      end;

      if DeviceIOControl(hVolume,
                         IOCTL_DISK_GET_DRIVE_GEOMETRY,
                         nil,
                         0,
                         @DG,
                         SizeOf(DG),
                         @lpBytesReturned,
                         nil) then
        aDeviceRec.DiskGeometry := DG;

      // Für den folgenden Aufruf müssen wir ausreichend Speicher reserviren
      // Falls ich ein Fragt warum 16 Partitionen, sollte sich gesondert informieren;-)
      dliSize := SizeOf(DRIVE_LAYOUT_INFORMATION) + (15 * SizeOf(PARTITION_INFORMATION));
      // Wir nutzen GetMem da DRIVE_LAYOUT_INFORMATION.PartitionCount ein
      // statisches Array ist
      GetMem(pDLI, dliSize);

      if DeviceIOControl(hVolume,
                         IOCTL_DISK_GET_DRIVE_LAYOUT,
                         nil,
                         0,
                         pDLI,
                         dliSize,
                         @lpBytesReturned,
                         nil) then
      begin
        // Hier verkürzen wir das ganze, da wir ja wissen welche Partition
        // wir auslesen wollen
        aDeviceRec.PartitionData := pDLI^.PartitionEntry[aDeviceRec.PartitionNr-1];
        // Und geben den Speicher wieder frei
        FreeMem(pDLI, dliSize);
      end;

      CloseHandle(hVolume);
    end;
  finally
    StrDispose(pVolume);
  end;
  Result := Ret > -1;
end;

function GetDeviceNumber(const aPhysicalDrive : Byte;
                         var aDynDeviceRec : TDynDeviceRec) : Boolean; overload;
var
  pVolume : PChar;
  dwSize : DWORD;
  hVolume : THandle;
  DG : DISK_GEOMETRY;
  pDLI : PDRIVE_LAYOUT_INFORMATION;
  lpBytesReturned : DWORD;
  I, piCount,
  dliSize : Integer;
begin
  Result := false;
  dwSize := Length(Format('\\.\PHYSICALDRIVE%d', [aPhysicalDrive])) + 1;
  pVolume := StrAlloc(dwSize);
  try
    StrPCopy(pVolume, Format('\\.\PHYSICALDRIVE%d', [aPhysicalDrive]));
    hVolume := CreateFile(pVolume,
                          GENERIC_READ,
                          FILE_SHARE_READ or FILE_SHARE_WRITE,
                          nil,
                          OPEN_EXISTING, 0, 0);
    if hVolume <> INVALID_HANDLE_VALUE then
    begin
      aDynDeviceRec.DeviceNr := aPhysicalDrive;
      aDynDeviceRec.DeviceStr:= pVolume;
      if DeviceIOControl(hVolume,
                         IOCTL_DISK_GET_DRIVE_GEOMETRY,
                         nil,
                         0,
                         @DG,
                         SizeOf(DG),
                         @lpBytesReturned,
                         nil) then
        begin
          aDynDeviceRec.DiskGeometry := DG;
          aDynDeviceRec.MediaTypeStr := MediaTypeToStr(DG.MediaType);
        end;
        // Für den folgenden Aufruf müssen wir ausreichend Speicher reserviren
        // Falls sich ein Fragt warum 16 Partitionen, sollte er sich
        // gesondert informieren;-)
        dliSize := SizeOf(DRIVE_LAYOUT_INFORMATION) + (15 * SizeOf(PARTITION_INFORMATION));
        // Wir nutzen GetMem da DRIVE_LAYOUT_INFORMATION.PartitionCount ein
        // statisches Array ist
        GetMem(pDLI, dliSize);

        if DeviceIOControl(hVolume,
                           IOCTL_DISK_GET_DRIVE_LAYOUT,
                           nil,
                           0,
                           pDLI,
                           dliSize,
                           @lpBytesReturned,
                           nil) then
        begin
          // IOCTL_DISK_GET_DRIVE_LAYOUT bringt in
          // DRIVE_LAYOUT_INFORMATION.PartitionCount immer 4 zurück
          // egal ob 1, 2 oder mehr Primärepartitionen vorhanden sind.
          piCount := 0;
          for I := 0 to pDLI^.PartitionCount - 1 do
          begin
           if pDLI^.PartitionEntry[I].StartingOffset.LowPart = 0 then Break;
           Inc(piCount);
          end;
          SetLength(aDynDeviceRec.PartitionData, piCount);
          for I := 0 to piCount-1 do
           aDynDeviceRec.PartitionData[I] := pDLI^.PartitionEntry[I];
          // Speicher wieder freigeben
          FreeMem(pDLI, dliSize);
        end;
        CloseHandle(hVolume);
        Result := true;
    end;
  finally
    StrDispose(pVolume);
  end;
end;


function GetAllLogicalDevices(var aDeviceRecList : TDeviceRecList) : Boolean;
var
  drives : DWORD;
  letter : Char;
  SL : TStringList;
begin
  SL := TStringList.Create;
  try
     for letter := 'Cto 'Zdo
       case GetDriveType(PChar(letter + ':\')) of
        DRIVE_REMOVABLE : SL.Add(letter);
        DRIVE_FIXED : SL.Add(Letter);
       end;
     Result := SL.Count > 0;
     if Result then
     begin
       SetLength(aDeviceRecList, SL.Count);
       for drives := 0 to SL.Count - 1 do
        GetDeviceNumber(PChar(SL.Strings[drives]), aDeviceRecList[drives]);
     end;
  finally
    SL.Free;
  end;
end;

function GetAllPhysicalDevices(var aDynDeviceRecList : TDynDeviceRecList) : Boolean;
var
  I : Byte;
  J : Byte;
  C : Byte;
  DR : TDynDeviceRec;
begin
  C := 0;
  SetLength(aDynDeviceRecList, HIGH(Byte));
  for I := 0 to HIGH(BYTE) do
  begin
    if DeviceExists(I) then
    begin
      if GetDeviceNumber(I, DR) then
      begin
        try
          with aDynDeviceRecList[C] do
          begin
            DeviceNr := DR.DeviceNr;
            DeviceStr := DR.DeviceStr;
            DiskGeometry := DR.DiskGeometry;
            MediaTypeStr := DR.MediaTypeStr;
            SetLength(PartitionData, Length(DR.PartitionData));
            for J := 0 to Length(DR.PartitionData) - 1 do
            PartitionData[J] := DR.PartitionData[J];
          end;
          Inc(C);
        except
        end;
      end;
    end;
  end;
  SetLength(aDynDeviceRecList, C);
  Result := Length(aDynDeviceRecList) > 0;
end;

end.
Und das Formular:
Delphi-Quellcode:
unit frmMain;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ActnList, Menus, ComCtrls, uDevice;

type
  TMainForm = class(TForm)
    ActionList1: TActionList;
    MainMenu1: TMainMenu;
    mnuFile: TMenuItem;
    mnuDevice: TMenuItem;
    _aDummy: TAction;
    aFileExit: TAction;
    aDeviceLogical: TAction;
    aDevicePhysical: TAction;
    mnuFileExit: TMenuItem;
    mnuDeviceLogical: TMenuItem;
    N1: TMenuItem;
    mnuDevicePhysical: TMenuItem;
    tvDevices: TTreeView;
    procedure aFileExitExecute(Sender: TObject);
    procedure aDeviceLogicalExecute(Sender: TObject);
    procedure aDevicePhysicalExecute(Sender: TObject);
  private
    { Private-Deklarationen }
    DRL : TDeviceRecList;
    DDRL: TDynDeviceRecList;

    function FindRootNode(aName : String; aTreeView : TTreeView) : TTreeNode;
    procedure ClearList;
  public
    { Public-Deklarationen }
  end;

var
  MainForm: TMainForm;

implementation

{$R *.dfm}

procedure TMainForm.aDeviceLogicalExecute(Sender: TObject);
var
  I : Integer;
  R : TTreeNode;
begin
  tvDevices.Items.BeginUpdate;
  tvDevices.Items.Clear;
  ClearList;
  if GetAllLogicalDevices(DRL) then
  begin
    for I := 0 to Length(DRL) -1 do
    begin
      R := FindRootNode(DRL[I].DeviceStr, tvDevices);
      if R <> nil then
        tvDevices.Items.AddChild(R, Format('%s (%s)', [DRL[I].PartitionStr, DRL[I].DriveStr]))
      else
      begin
        R := tvDevices.Items.AddChild(nil, DRL[I].DeviceStr);
        tvDevices.Items.AddChild(R, Format('%s (%s)', [DRL[I].PartitionStr, DRL[I].DriveStr]));
      end;
    end;
    tvDevices.Items.AlphaSort();
  end;
  tvDevices.Items.EndUpdate;
end;

procedure TMainForm.aDevicePhysicalExecute(Sender: TObject);
var
  I,
  J : Integer;
  N,
  R : TTreeNode;
begin
  tvDevices.Items.BeginUpdate;
  tvDevices.Items.Clear;
  ClearList;
  if GetAllPhysicalDevices(DDRL) then
  begin
    for I := 0 to Length(DDRL) -1 do
    begin
      R := FindRootNode(DDRL[I].DeviceStr, tvDevices);
      if R <> nil then
        tvDevices.Items.AddChild(R, Format('%s ', [DDRL[I].MediaTypeStr]))
      else
      begin
        R := tvDevices.Items.AddChild(nil, DDRL[I].DeviceStr);
        N := tvDevices.Items.AddChild(R, Format('%s ', [DDRL[I].MediaTypeStr]));
        for J := 0 to Length(DDRL[I].PartitionData) -1 do
         tvDevices.Items.AddChild(N, Format('Partition%d', [J]));
      end;
    end;
    tvDevices.Items.AlphaSort();
  end;
  tvDevices.Items.EndUpdate;
end;

procedure TMainForm.aFileExitExecute(Sender: TObject);
begin
  ClearList;
  Close;
end;

function TMainForm.FindRootNode(aName : String; aTreeView : TTreeView) : TTreeNode;
var
  I : Integer;
begin
  Result := nil;
  aTreeView.Items.BeginUpdate;
  for I := 0 to aTreeView.Items.Count - 1 do
   if aTreeView.Items[I].Text = aName then
   begin
     Result := aTreeView.Items[I];
     Break;
   end;
  aTreeView.Items.EndUpdate;
end;

procedure TMainForm.ClearList;
var
  I : Integer;
begin
  if Length(DDRL) > 0 then
  begin
    for I := Length(DDRL) -1 downto 0 do
      if Length(DDRL[I].PartitionData) > 0 then
        SetLength(DDRL[I].PartitionData, 0);
    SetLength(DDRL, 0);
  end;
  if Length(DRL) > 0 then SetLength(DRL, 0);
end;

end.
Siehe auch Anhang. Ich gebe ja zu ein wenig Optimierung könnte das ganze vertragen

Falls es Fragen gibt, fragen.

Viele Grüße
Angehängte Dateien
Dateityp: zip DeviceInfo.zip (25,9 KB, 63x aufgerufen)

Geändert von Alter Mann ( 8. Sep 2010 um 11:20 Uhr)
  Mit Zitat antworten Zitat