Setup-Device-Interface API

Unter dem Begriff “Hass-Liebe” findet man im guten Lexikon mein Foto neben einem Link zur Microsoft Windows Setup Device-Interface API.

Einerseits ist dieses Konstrukt ein schöner abstrakter Mechanismus um praktisches jedes erdenkliche Gerät anzusteuern, auf der anderen Seite zählt die genannte API zum schlimmsten Dokumentations-Chaos, das mir als Entwickler je untergekommen ist.


Wie so oft hängt alles an der Ablaufdokumentation. Jene der formalen API ist eigentlich in Ordnung, doch die notwendige komplexe Zusammensetzung von Aufrufen, die für jede Geräteform anders sein kann, findet man … nirgends.

Tatsächlich ist die Device-Interface API gerade für meine Lieblingsbereiche ein notwendiges Übel um an Pfade von Geräten zu kommen, die man dann öffnen und steuern will. Die Funktion CreateFile() öffnet ja bekanntlich nicht nur Dateien, sondern auch Geräte, vorausgesetzt der korrekte Pfad wird angegeben.

Das komplizierte sind am Anfang die GUIDs, die Geräte und Geräteklassen identifizieren und der undurchsichtige Geräte-Baum, bei dem Bussysteme zu Bussystemen leiten, die wiederum Geräte beinhalten und dann an ganz anderen Ästen unter Umständen “Dienst-Geräte” liegen, die mit einem Gerät verknüpft sind.

Und so kann es passieren, dass man zur Umsetzung einer Programmlogik mehrere Geräte öffnen muss, die aber je nachdem, wo sie angebunden sind (z.B. unterschiedliche USB Ports), dann andere interne Gerätepfade haben.

SetupDiXXXXXX

Wenn etwas mit dem Prefix SetupDi beginnt, handelt es sich um die Setup Device Interface API, mit der Geräte aufgelistet oder deren Eigenschaften ausgegeben werden können.

Jedes angeschlossene reale oder virtuell generierte Gerät des Betriebssystems kann durch diese API aufgelistet werden. Doch dazu brauchen wir stets die richtigen GUIDs.

Geräte sind dann zu allem Übel noch unterschiedlich sortiert. Die häufigste Zuordnung erfolgt über die Geräte-Klassen-GUIDs, und einige davon sind auf den MSDN Seiten dokumentiert. Doch die dort genannten entspringen der Sortierung des Gerätemanagers, es gibt aber noch andere, die auf zahlreiche Header je nach Typ verteilt sind:

  • Ntddstor.h für storage drives
  • Bthdef.h für Bluetooth
  • usw.

Das nervige ist, wenn ein “sichtbares” Gerät unterschiedliche seiner Aspekte als Teil von unterschiedlichen Klassen implementiert. So ist eine Festplatte als Festplattengerät registriert, aber gleichzeitig auch als “Physisches Laufwerk” und auf sein Bits kann man nur über letztere GUID zugreifen.

Hat man aber seine GUID gefunden, geht es zügig weiter.

  • SetupDiGetClassDevs() holt eine Liste der bekannten Geräteinstanzen einer GUID
    • Man erhält hier ein HANDLE, mit dem man alle anderen Daten abholen kann.
  • SetupDiEnumDeviceInterfaces() liefert Infos zu einem Gerät (basierend auf dessen Index in der Liste)
    • Diese API ruft man beginnend beim Index 0 so lange hochzählend auf, bis man den Fehler ERROR_NO_MORE_ITEMS zurück bekommt.
  • SetupDiGetDeviceInterfaceDetail holt uns den Pfad zum Gerät, damit wir es öffnen können.
    • Mit dem Pfad öffnet man dann einen COM-Port, eine Festplatte zum direkten Lesen der Datensektoren oder man verbindet sich so zu einem anderen Peripheriegerät um IOCTL Codes zu ihm zu schicken.

Auch interessant können diverse Einstellungen aus dem Registry-Bereich des Gerätes sein, und dafür nutzt man SetupDiGetDeviceRegistryProperty. Ich nutze häufig das Registry-Property SPDRP_FRIENDLYNAME, mit dem man schnell auf die Bezeichnung des Geräts zugreifen kann, die man auch im Windows Geräte-Manager findet.


Natürlich kann man mit der Setup-API auch noch einiges mehr machen, wie z.B. Treiber INF-Dateien installieren und weitere Infos auslesen wie auch Einstellungen verändern.

Doch meiner Erfahrung nach ist es stets am mühsamsten, den richtigen Pfad und die richtige Klassen-GUID für die gewünschte Anwendung zu finden, bevor man sich denen Details der eigentlichen Aufgabe widmen kann.