Avatar
www.fr-an.de

Ermittlung der Laufwerke

SCSI Pass Through Interface

In "Mit Laufwerksbuchstaben SCSI-Adressen ermitteln und Bezeichnung aus der Registry holen" und "Mit Zähler SCSI-Adressen ermitteln und Bezeichnung aus der Registry holen" wurden die SCSI-Adressen mit Hilfe des SCSI Pass Through Interface ermittelt. Da bietet es sich an, die Bezeichnung der Laufwerke ebenfalls darüber zu ermitteln. In Hinblick auf Windows 7 ist dies sinnvoller als wie in den vorigen Beispielen über die Registry.

Unter Windows 2000 und Windows XP müssten Brennrechte erforderlich sein.

Das sieht beispielsweise so aus:

 
  ...
  var
    nBufferLength : Integer;
    pDriveBuffer  : PAnsiChar;
    pDrive        : PAnsiChar;
    hDevice       : THandle;
    bSCSIBuffer   : array[0..1023] of AnsiChar;
    bInqueryData  : array[0..99] of AnsiChar;
    pSCSIAddr     : PSCSI_ADDRESS;
    pSCSIBuffer   : PSCSI_PASS_THROUGH_DIRECT_WITH_BUFFER;
    nPort         : Integer;
    nTargetId     : Integer;
    nLUnId        : Integer;
    nReturned     : Cardinal;
    pInquery      : PAnsiChar;
    sVendor       : AnsiString;
    sProductId    : AnsiString;
    sRevision     : AnsiString;
    sVendorSpec   : AnsiString;
  begin
    ...
    {
    ******************************************************************************
    *  Liste der optischen Laufwerke erstellen.
    }
    if Win32Platform = VER_PLATFORM_WIN32_NT
    then begin
      {
      *  Init
      }
      lvwDrives.Clear;
      pDriveBuffer := nil;
      pDrive := nil;
      {
      *  Vorhandene Laufwerksbuchstaben einlesen und Pointer übergeben.
      }
      nBufferLength := GetLogicalDriveStringsA(0, nil);
      if nBufferLength > 0
      then try
      GetMem(pDriveBuffer, nBufferLength * SizeOf(AnsiChar));
        if GetLogicalDriveStringsA(nBufferLength, pDriveBuffer) > 0
        then begin
          {
          *  Am Anfang Buffers steht das erste Laufwerk. Deshalb wird der
          *  Pointer des Buffers als Startpointer übergeben.
          }
          pDrive := pDriveBuffer;
          {
          *  Solange es einen Laufwerkseintrag gibt ...
          }
          while Length(pDrive) > 0
          do begin
            {
            *  den Typ des Laufwerks auf CD-ROM prüfen und ggfs eintragen.
            }
            if GetDriveTypeA(pDrive) = DRIVE_CDROM
            then begin
              lvwDrives.Items.Add.Caption := pDrive;
              {
              *  Nun wird das Handle auf das Laufwerk geholt.
              }
              hDevice := CreateFile(PAnsiChar(Format('\\.\%s:', [pDrive[0]])),
                                    GENERIC_READ or GENERIC_WRITE,
                                    FILE_SHARE_READ or FILE_SHARE_WRITE,
                                    nil, OPEN_EXISTING, 0, 0 );
              if hDevice <> INVALID_HANDLE_VALUE
              then begin
                {
                *  Die SCSI Adresse für das Laufwerk ermitteln und eintragen.
                }
                ZeroMemory(@bSCSIBuffer, 1024);
                pscsiAddr := PSCSI_ADDRESS(@bSCSIBuffer);
                pscsiAddr^.Length := SizeOf(TSCSI_ADDRESS);
                if DeviceIoControl(hDevice, IOCTL_SCSI_GET_ADDRESS, nil, 0,
                                   pscsiAddr, SizeOf(TSCSI_ADDRESS), nReturned, nil)
                then begin
                  nPort     := pscsiAddr^.PortNumber;
                  nTargetId := pscsiAddr^.TargetId;
                  nLUnId    := pscsiAddr^.Lun;
                  lvwDrives.Items.Item[lvwDrives.Items.Count - 1].SubItems.Add(
                                  Format('[%d:%d:%d]', [nPort, nTargetId, nLUnId]));
                end;
                {
                *  Laufwerksbezeichnung ermitteln und eintragen.
                }
                ZeroMemory(@bSCSIBuffer, 1024);
                ZeroMemory(@bInqueryData, 100);
                pSCSIBuffer := PSCSI_PASS_THROUGH_DIRECT_WITH_BUFFER(@bSCSIBuffer);
                with pSCSIBuffer^.sptd
                do begin
                  Length             := SizeOf(SCSI_PASS_THROUGH_DIRECT);
                  CdbLength          := 6;
                  SenseInfoLength    := 24;
                  DataIn             := SCSI_IOCTL_DATA_IN;
                  DataTransferLength := Length(bInqueryData);
                  TimeOutValue       := 2;
                  DataBuffer         := @bInqueryData;
                  SenseInfoOffset    := SizeOf(pSCSIBuffer^.sptd) +
                                        SizeOf(pSCSIBuffer^.Filler);
                  Cdb[0]             := $12;
                  Cdb[4]             := Length(bInqueryData);
                end;
                if DeviceIoControl(hDevice, IOCTL_SCSI_PASS_THROUGH_DIRECT,
                           pSCSIBuffer, SizeOf(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER),
                           pSCSIBuffer, SizeOf(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER),
                           nReturned, nil)
                then begin
                  pInquery := @bInqueryData[0];
                  Inc(pInquery, 8);
                  sVendor      := Trim(Copy(pInquery,  1,      8));
                  sProductId   := Trim(Copy(pInquery,  8 + 1, 16));
                  sRevision    := Trim(Copy(pInquery, 24 + 1,  4));
                  sVendorSpec  := Trim(Copy(pInquery, 28 + 1, 20));
                  lvwDrives.Items.Item[lvwDrives.Items.Count - 1].SubItems.Add(
                       Trim(Format('%s %s %s %s',
                                   [sVendor, sProductId, sRevision, sVendorSpec])));
                end;
              end;
              CloseHandle(hDevice);
            end;
            {
            *  Da die Laufwerkstrings mit einer #0 getrennt hintereinander stehen,
            *  wird der Pointer um die Länge des Strings und des Trennzeichens
            *  erhöht.
            }
            inc(pDrive, lStrLenA(pDrive) + 1);
          end;
        end;
      finally
        {
        *  Reservierten Speicher wieder freigeben.
        }
        FreeMem(pDriveBuffer);
        pDriveBuffer := nil;
        pDrive := nil;
      end;
    end;
    ...
  end;
        

Das Ergebnis des Beispiels sieht so aus:

Get Drives 5 (podGetDrives05.7z - 224 kb) MD5 (1 kb). Stand: 2. Januar 2012

 

Korrekturen

02.01.2012 -
Beim Holen des Handles fehlte das FILE_SHARE_WRITE. Das Fehlen bewirkt, das so lange, das Handle geöffnet ist, nicht mit GENERIC_WRITE zugegriffen werden kann.
30.12.2011 -
DataTransferLength und Cdb[4] wurden feste Werte zugewiesen. Sie dürfen nicht größer sein als bInqueryData, deshalb wurde der Wert 100 in Length(bInqueryData) geändert.

Fr_An - Letzte Änderung: 30. Dezember 2011

seit 28. Juli 2010