Home | Kontakt | Sitemap

Start

Über mich

Kontakt

Sitemap

Lizenz

Anleitungen

DVD, miniDVD

SVCD

Audio, Audio-CD (CD-DA)

AVI

Software

Von Freunden und Bekannten

Eigene Programme

Programmierung

Delphi

Lazarus

Delphi/Lazarus

Projekte

MPEG-1/2 Video

Optische Laufwerke

Audio-CD (CDDA)

Raspberry Pi Dashcam

Verschiedenes

MPEG 2 Schnitt

Project X

VCD Easy

Hardlinks

Windows

Links

Software

Programmierung Lazarus - USB-Laufwerk?

USB-Laufwerk?

  • Beschreibung
  • Ermittlung der Laufwerke
  • Funktion
  • Input-Struktur
  • Output-Struktur
  • Abfrage
  • Aufruf und Auswertung
  • Download
  • Links
  • Änderungen
  • Beschreibung

    Manchmal ist es von Interesse, zu wissen, welche Laufwerke über USB am System angeschlossen sind. Hier werden unter Windows (ab XP) die logischen Laufwerke sowie das Bussystem ermittelt, mit welchem sie angeschlossen sind.

    Ermittlung der Laufwerke

    Die Laufwerke bzw. die Buchstaben, mit denen sie gekennzeichnet sind, werden mit der Funktion GetLogicalDriveStringsA wie im Projekt Optische Laufwerke im Kapitel Laufwerke - Buchstaben dargestellt, ermittelt.

    Funktion

    Für die Ermittlung des Bustypes wird die Funktion DeviceIoControl mit dem I/O Control Code IOCTL_STORAGE_QUERY_PROPERTY verwendet. Dieser Code ist im Windows Dev Center so definiert:

    BOOL
    WINAPI
    DeviceIoControl(
         _In_        (HANDLE)       hDevice,               // handle to a partition
         _In_        (DWORD) IOCTL_STORAGE_QUERY_PROPERTY, // dwIoControlCode
         _In_        (LPVOID)       lpInBuffer,            // input buffer - STORAGE_PROPERTY_QUERY structure
         _In_        (DWORD)        nInBufferSize,         // size of input buffer
         _Out_opt_   (LPVOID)       lpOutBuffer,           // output buffer - see Remarks
         _In_        (DWORD)        nOutBufferSize,        // size of output buffer
         _Out_opt_   (LPDWORD)      lpBytesReturned,       // number of bytes returned
         _Inout_opt_ (LPOVERLAPPED) lpOverlapped );        // OVERLAPPED structure
    

    Den Wert für IOCTL_STORAGE_QUERY_PROPERTY findet man in der ntddstor.h:

    #define IOCTL_STORAGE_QUERY_PROPERTY
                    CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS)
    

    Wie in I/O Control Code dargestellt, ergibt sich daraus:

    const
      FILE_DEVICE_MASS_STORAGE = 45;
      IOCTL_STORAGE_BASE       = FILE_DEVICE_MASS_STORAGE;
    
      FILE_ANY_ACCESS = 0;
    
      METHOD_BUFFERED = 0;
    
      IOCTL_STORAGE_QUERY_PROPERTY = (IOCTL_STORAGE_BASE shl 16) or
                                     (FILE_ANY_ACCESS shl 14) or
                                     ($0500 shl 2) or
                                     (METHOD_BUFFERED);
    

    Input-Struktur

    Dafür ist die STORAGE_PROPERTY_QUERY Struktur angegeben:

     typedef struct _STORAGE_PROPERTY_QUERY {
      STORAGE_PROPERTY_ID PropertyId;
      STORAGE_QUERY_TYPE  QueryType;
      BYTE                AdditionalParameters[1];
    } STORAGE_PROPERTY_QUERY, *PSTORAGE_PROPERTY_QUERY;
    

    STORAGE_PROPERTY_ID und STORAGE_QUERY_TYPE sind Aufzählungen. Die AdditionalParameters sind ein Array aus Bytewerten. In Lazarus ergibt sich daraus:

    type
      _STORAGE_PROPERTY_ID = (
        StorageDeviceProperty = 0,
        StorageAdapterProperty,
        StorageDeviceIdProperty,
        StorageDeviceUniqueIdProperty,
        StorageDeviceWriteCacheProperty,
        StorageMiniportProperty,
        StorageAccessAlignmentProperty,
        StorageDeviceSeekPenaltyProperty,
        StorageDeviceTrimProperty,
        StorageDeviceWriteAggregationProperty,
        StorageDeviceDeviceTelemetryProperty,
        StorageDeviceLBProvisioningProperty,
        StorageDevicePowerProperty,
        StorageDeviceCopyOffloadProperty,
        StorageDeviceResiliencyProperty,
        StorageDeviceMediumProductType,
        StorageAdapterRpmbProperty,
        StorageAdapterCryptoProperty,
        StorageDeviceTieringProperty,
        StorageDeviceFaultDomainProperty,
        StorageDeviceClusportProperty,
        StorageDeviceDependantDevicesProperty,
        StorageDeviceIoCapabilityProperty,
        StorageAdapterProtocolSpecificProperty,
        StorageDeviceProtocolSpecificProperty,
        StorageAdapterTemperatureProperty,
        StorageDeviceTemperatureProperty,
        StorageAdapterPhysicalTopologyProperty,
        StorageDevicePhysicalTopologyProperty,
        StorageDeviceAttributesProperty,
        StorageDeviceManagementStatus,
        StorageAdapterSerialNumberProperty,
        StorageDeviceLocationProperty,
        StorageDeviceNumaProperty,
        StorageDeviceZonedDeviceProperty,
        StorageDeviceUnsafeShutdownCount,
        StorageDeviceEnduranceProperty);
      TSTORAGE_PROPERTY_ID = _STORAGE_PROPERTY_ID;
      PSTORAGE_PROPERTY_ID = ^_STORAGE_PROPERTY_ID;
     
      _STORAGE_QUERY_TYPE = (
        PropertyStandardQuery = 0,
        PropertyExistsQuery,
        PropertyMaskQuery,
        PropertyQueryMaxDefined);
      TSTORAGE_QUERY_TYPE = _STORAGE_QUERY_TYPE;
      PSTORAGE_QUERY_TYPE = ^_STORAGE_QUERY_TYPE;
     
      _STORAGE_PROPERTY_QUERY = record
        PropertyId           : TSTORAGE_PROPERTY_ID;
        QueryType            : TSTORAGE_QUERY_TYPE;
        AdditionalParameters : array[0..9] of Byte;
      end;
      TSTORAGE_PROPERTY_QUERY = _STORAGE_PROPERTY_QUERY;
      PSTORAGE_PROPERTY_QUERY = ^_STORAGE_PROPERTY_QUERY;
    

    Damit es bei der Ausführung keinen Fehlerabbruch gibt, muss das Array für die AdditionalParameters mindestens 10 Byte lang sein.

    Output-Struktur

    Dafür ist der STORAGE_DESCRIPTOR_HEADER angegeben. Zu der Abfrage bekommt man eine Antwort mit der STORAGE_DEVICE_DESCRIPTOR structure:

    typedef struct _STORAGE_DEVICE_DESCRIPTOR {
      DWORD            Version;
      DWORD            Size;
      BYTE             DeviceType;
      BYTE             DeviceTypeModifier;
      BOOLEAN          RemovableMedia;
      BOOLEAN          CommandQueueing;
      DWORD            VendorIdOffset;
      DWORD            ProductIdOffset;
      DWORD            ProductRevisionOffset;
      DWORD            SerialNumberOffset;
      STORAGE_BUS_TYPE BusType;
      DWORD            RawPropertiesLength;
      BYTE             RawDeviceProperties[1];
    } STORAGE_DEVICE_DESCRIPTOR, *PSTORAGE_DEVICE_DESCRIPTOR;
    

    Der STORAGE_BUS_TYPE ist eine Aufzählung und RawDeviceProperties ein Bytearray, welches bei den meinen SATA-Festplatten leer blieb und bei der USB-Platte vier Werte enthielt. Es ergibt sich für Lazarus:

    type
      _STORAGE_BUS_TYPE = (
        BusTypeUnknown = 0,
        BusTypeScsi,
        BusTypeAtapi,
        BusTypeAta,
        BusType1394,
        BusTypeSsa,
        BusTypeFibre,
        BusTypeUsb,
        BusTypeRAID,
        BusTypeiScsi,
        BusTypeSas,
        BusTypeSata,
        BusTypeSd,
        BusTypeMmc,
        BusTypeVirtual,
        BusTypeFileBackedVirtual,
        BusTypeSpaces,
        BusTypeNvme,
        BusTypeSCM,
        BusTypeUfs,
        BusTypeMax,
        BusTypeMaxReserved);
      TSTORAGE_BUS_TYPE = _STORAGE_BUS_TYPE;
      PSTORAGE_BUS_TYPE = ^_STORAGE_BUS_TYPE;
     
      _STORAGE_DEVICE_DESCRIPTOR = record
        Version               : Cardinal;
        Size                  : Cardinal;
        DeviceType            : Byte;
        DeviceTypeModifier    : Byte;
        RemovableMedia        : Boolean;
        CommandQueueing       : Boolean;
        VendorIdOffset        : Cardinal;
        ProductIdOffset       : Cardinal;
        ProductRevisionOffset : Cardinal;
        SerialNumberOffset    : Cardinal;
        BusType               : TSTORAGE_BUS_TYPE;
        RawPropertiesLength   : Cardinal;
        RawDeviceProperties   : array[0..3] of Byte;
      end;
      TSTORAGE_DEVICE_DESCRIPTOR = _STORAGE_DEVICE_DESCRIPTOR;
      PSTORAGE_DEVICE_DESCRIPTOR = ^_STORAGE_DEVICE_DESCRIPTOR;
    

    Abfrage

    Die Function GetDeviceProperty enthält lediglich den Aufruf der Funktion DeviceIoControl. Dazu werden ihr das Handle auf das Laufwerk als Konstante und die Antwortstruktur als Variable übergeben. Das Ergebnis der Funktion ist das Ergebnis der Abfrage.

    function GetDeviceProperty(const hDevice: THandle;
                               var DevDesc: TSTORAGE_DEVICE_DESCRIPTOR): Boolean;
    var
      Query           : TSTORAGE_PROPERTY_QUERY;
      dwBytesReturned : Cardinal;
    begin
      // Input-Struktur
      FillChar(Query, SizeOf(TSTORAGE_PROPERTY_QUERY), 0);
      Query.PropertyId := StorageDeviceProperty;
      Query.QueryType  := PropertyStandardQuery;
      // Output-Struktur
      FillChar(DevDesc, SizeOf(TSTORAGE_DEVICE_DESCRIPTOR), 0);
      DevDesc.Size := SizeOf(TSTORAGE_DEVICE_DESCRIPTOR);
      // Ausführung
      dwBytesReturned := 0;
      Result := DeviceIoControl(hDevice,
                                IOCTL_STORAGE_QUERY_PROPERTY,
                                @Query, SizeOf(TSTORAGE_PROPERTY_QUERY),
                                @DevDesc, SizeOf(TSTORAGE_DEVICE_DESCRIPTOR),
                                dwBytesReturned,
                                nil);
      if not(Result)
      then ShowMessage(SysErrorMessage(GetLastError));
    end;
    

    Aufruf und Auswertung

    Vor dem Aufruf der Function GetDeviceProperty wird das Handle zu dem Laufwerk geholt. Hat das Handle einen gültigen Wert wird die Funktion aufgerufen und die Antwortstruktur ausgewertet.

    function IsUSBDrive(const DriveChar: Char): Boolean;
    var
      hDevice : THandle;
      DevDesc : TSTORAGE_DEVICE_DESCRIPTOR;
    begin
      // Handle holen
      hDevice := CreateFile(PChar(Format('\\.\%s:', [DriveChar])),
                            0,
                            FILE_SHARE_READ or FILE_SHARE_WRITE,
                            nil,
                            OPEN_EXISTING,
                            0, 0);
      // Handle prüfen, Abfrage und Auswertung
      Result := (hDevice <> INVALID_HANDLE_VALUE) and
                (GetDeviceProperty(hDevice, DevDesc)) and
                (DevDesc.BusType = (BusTypeUsb));
      // Handle schließen
      CloseHandle(hDevice);
    end;
    

    Download

    Eine Demo, welche GetDeviceProperty verwendet. Die Verwendung erfolgt ein wenig anders. Erstellt und compiliert mit Lazarus 2.0.6 und FPC 3.0.4.

    isusb_exe.7z (698 kb) - MD5
    isusb_src.7z (61 kb) - MD5
    Stand: 8. März 2020

    Änderungen

    08.03.2020Erstellung der Seite.