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

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.