Setup-Device-Interface API
« | 16 Mar 2019 | »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 drivesBthdef.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.
- Diese API ruft man beginnend beim Index 0 so lange hochzählend auf,
bis man den Fehler
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.