vb@rchiv
VB Classic
VB.NET
ADO.NET
VBA
C#
Erstellen von dynamischen Kontextmen?s - wann immer Sie sie brauchen!  
 vb@rchiv Quick-Search: Suche startenErweiterte Suche starten   Impressum  | Datenschutz  | vb@rchiv CD Vol.6  | Shop Copyright ©2000-2024
 
zurück
Rubrik: Verschiedenes   |   VB-Versionen: VB5, VB601.03.05
CDROM-Laufwerksinfo auslesen

Hallo und herzlich Willkommen zum CD-ROM-Workshop. Was sich so unscheinbar anhört, wird Sie auf den folgenden Seiten sicher noch überraschen. Wir werden Ihnen zeigen, wie Sie mittels weniger Aufrufe alles über Ihre Laufwerke erfahren können. Dazu werden wir zum Teil die Registry, die CD-ROM-Treiber oder die Hardware direkt ansprechen. Nachstehend wollen wir Ihnen die Methoden und Ihre Möglichkeiten vorstellen.

Autor:  Matthias VolkBewertung:     [ Jetzt bewerten ]Views:  18.817 

Hallo und herzlich Willkommen zum CD-ROM-Workshop. Was sich so unscheinbar anhört, wird Sie auf den folgenden Seiten sicher noch überraschen. Wir werden Ihnen zeigen, wie Sie mittels weniger Aufrufe alles über Ihre Laufwerke erfahren können. Dazu werden wir zum Teil die Registry, die CD-ROM-Treiber oder die Hardware direkt ansprechen. Nachstehend wollen wir Ihnen die Methoden und Ihre Möglichkeiten vorstellen.

Registry:
Wenn wir sagen "Registry" meinen wir die "SetupDI"-API's. Diese Api-Funktionen sind auf den Systemen Windows 98, ME, 2000 und XP vorhanden und verwendbar. Diese API's sind nicht speziell für CD-Laufwerke, sondern eher für alle vom System erkannten Hardwaregeräte. Mit diesen Funktionen kann man jegliche installierte Hardware im System ermitteln. Außerdem kann man erfahren, welcher Hardwaregruppe eine Hardware angehört, welchen Treiber sie verwendet, etc. Was besonders interessant ist: das System sendet auf Wunsch Benachrichtigungen über den Statuswechsel einer Hardware (z.B. Neue Hardware wurde installiert, Medium wurde eingelegt / entfernt).

Treiber:
Den Treiber direkt anzusprechen bietet viele Vorteile, denn nicht alle Informationen werden in der Registry abgelegt. Über die "CreateFile"-API können wir direkt einen Zugriff auf die Gerätetreiber bekommen. Dies gilt - wie bei der Registry - natürlich nicht nur für CD-Laufwerke. Vielmehr kann man auf fast jede Hardware zugreifen z. B. Festplatten, Optische Laufwerke, USB-Sticks etc. Nutzt man diese Methode, so kann man nicht nur Informationen über die Hardware ermitteln (Hersteller, Produkt, Status), sondern auch über das Medium (Diskette, CD, DVD, Partition). Auch kann man mit dieser Methode das betreffende Gerät in gewisser Weise steuern. So kann man z. B. Audio-CDs abspielen (sicher gibt es einfachere Wege), bestimmte Daten vom Datenträger lesen / schreiben (z. B. den TOC einer CD/DVD) oder ähnliches.

Hardware direkt:
Ein Treiber kann zwar viel, aber nicht alles. So werden in vielen Fällen vom System "Standardtreiber" verwendet. Da diese Treiber sich nicht mit jeder Hardware auskennen, muss man manche Informationen direkt vom Gerät ermitteln. Dieses geschieht ebenfalls über die "CreateFile"-Api. Hat man erst mal ein Handle zu dem betreffenden Gerät, kann man direkt per ATAPI oder SCSI an die Geräteinformationen kommen. Leider ist dies auch nicht die einfachste Methode. Informationen über die verwendeten Protokolle und Spezifikationen sind sehr unverständlich und Beispiele rar gesät. Dennoch können wir Ihnen einige interessante Features demonstrieren, was Sie auf den folgenden Seiten feststellen werden.

Registry:

Zu Beginn hatten wir Sie ja schon auf die Setup-Apis vorbereitet, nun wollen wir Ihnen auch gleich ein paar vorstellen. Im Grunde ist das Vorgehen recht einfach, vorab wollen wir Ihnen das Schema etwas näher bringen.

Bestimme Hardwaregeräte gehören einer Geräteklasse an. Diese Geräteklassen (z.B. "Standard CD-Rom-Laufwerke" oder "Festplatten-Laufwerke") besitzen in der Registry eine GUID (Global Unique ID, globale eindeutige ID). Dabei unterscheidet man zwischen den Geräteklassen-ID's und Geräteschittstellen-IDs. Eine Geräteschittstelle wird vom Hardwaretreiber bereitgestellt und gehört immer einer Geräteklasse an. So können auch mehrere Geräte einer Klasse angehören (z. B. könnten Ihr "Plextor DVD-ROM Laufwerk" und Ihr "Teac CDRW-Brenner" der CD-ROM-Klasse angehören. Nun kann man sich schon vorstellen, das es sehr einfach ist z. B. alle CD-Laufwerke zu ermitteln, wie einfach wollen wir Ihnen nun auch gleich zeigen.

Die folgenden API-Deklarationen und ihre Anwendung wollen wir Ihnen etwas näher beschreiben:

' Benötigte API-Deklarationen
Public Declare Function SetupDiGetClassDevs Lib "setupapi.dll" _
  Alias "SetupDiGetClassDevsA" ( _
  ByRef ClassGuid As GUID, _
  ByVal Enumerator As String, _
  ByVal hwndParent As Long, _
  ByVal flags As DEVICEFLAGS) As Long
 
Public Declare Function SetupDiDestroyDeviceInfoList Lib "setupapi.dll" ( _
  ByVal DeviceInfoSet As Long) As Long
 
Public Declare Function SetupDiGetDeviceRegistryProperty Lib "setupapi.dll" _
  Alias "SetupDiGetDeviceRegistryPropertyA" ( _
  ByVal DeviceInfoSet As Long, _
  ByRef DeviceInfoData As SP_DEVINFO_DATA, _
  ByVal Property As DEVICEPROPERTYINDEX, _
  ByRef PropertyRegDataType As REGPROPERTYTYPES, _
  ByVal PropertyBuffer As String, _
  ByVal PropertyBufferSize As Long, _
  ByRef RequiredSize As Long) As Long
 
Public Declare Function SetupDiEnumDeviceInfo Lib "setupapi.dll" ( _
  ByVal DeviceInfoSet As Long, _
  ByVal MemberIndex As Long, _
  ByRef DeviceInfoData As SP_DEVINFO_DATA) As Long
 
Public Declare Function SetupDiEnumDeviceInterfaces Lib "setupapi.dll" ( _
  ByVal DeviceInfoSet As Long, _
  ByRef DeviceInfoData As Any, _
  ByRef InterfaceClassGuid As GUID, _
  ByVal MemberIndex As Long, _
  ByRef DeviceInterfaceData As SP_DEVICE_INTERFACE_DATA) As Long
 
Public Declare Function SetupDiGetDeviceInterfaceDetail Lib "setupapi.dll" _
  Alias "SetupDiGetDeviceInterfaceDetailA" ( _
  ByVal DeviceInfoSet As Long, _
  ByRef DeviceInterfaceData As Any, _
  ByRef DeviceInterfaceDetailData As Any, _
  ByVal DeviceInterfaceDetailDataSize As Long, _
  ByRef RequiredSize As Long, _
  ByRef DeviceInfoData As Any) As Long
 
Public Type GUID
  Data1 As Long
  Data2 As Integer
  Data3 As Integer
  Data4(0 To 7) As Byte
End Type
 
Public Enum DEVICEFLAGS
  DIGCF_ALLCLASSES = &H4&
  DIGCF_DEVICEINTERFACE = &H10
  DIGCF_PRESENT = &H2
  DIGCF_PROFILE = &H8
End Enum
 
Public Type SP_DEVINFO_DATA
  cbSize As Long
  ClassGuid As GUID
  DevInst As Long
  Reserved As Long
End Type
 
Public Enum DEVICEPROPERTYINDEX
  SPDRP_ADDRESS = (&H1C)
  SPDRP_BUSNUMBER = (&H15)
  SPDRP_BUSTYPEGUID = (&H13)
  SPDRP_CAPABILITIES = (&HF)
  SPDRP_CHARACTERISTICS = (&H1B)
  SPDRP_CLASS = (&H7)
  SPDRP_CLASSGUID = (&H8)
  SPDRP_COMPATIBLEIDS = (&H2)
  SPDRP_CONFIGFLAGS = (&HA)
  SPDRP_DEVICEDESC = &H0
  SPDRP_DEVTYPE = (&H19)
  SPDRP_DRIVER = (&H9)
  SPDRP_ENUMERATOR_NAME = (&H16)
  SPDRP_EXCLUSIVE = (&H1A)
  SPDRP_FRIENDLYNAME = (&HC)
  SPDRP_HARDWAREID = (&H1)
  SPDRP_LEGACYBUSTYPE = (&H14)
  SPDRP_LOCATION_INFORMATION = (&HD)
  SPDRP_LOWERFILTERS = (&H12)
  SPDRP_MFG = (&HB)
  SPDRP_PHYSICAL_DEVICE_OBJECT_NAME = (&HE)
  SPDRP_SECURITY = (&H17)
  SPDRP_SECURITY_SDS = (&H18)
  SPDRP_SERVICE = (&H4)
  SPDRP_UI_NUMBER = (&H10)
  SPDRP_UPPERFILTERS = (&H11)
End Enum
 
Public Enum REGPROPERTYTYPES
  REG_BINARY = 3
  REG_DWORD = 4
  REG_DWORD_BIG_ENDIAN = 5
  REG_DWORD_LITTLE_ENDIAN = 4
  REG_EXPAND_SZ = 2
  REG_MULTI_SZ = 7
  REG_SZ = 1
End Enum
 
Public Type SP_DEVICE_INTERFACE_DATA
  cbSize As Long
  InterfaceClassGuid As GUID
  DevInst As Long
  Reserved As Long
End Type
 
Public Enum SetupErrors
  ERROR_INSUFFICIENT_BUFFER = 122
  ERROR_INVALID_DATA = 13&
  ERROR_NO_MORE_ITEMS = 259&
End Enum

Um auf eine Geräteklasse oder Geräteschnittstelle zugreifen zu können, müssen wir die SetupDiGetClassDevs-Funktion aufrufen, dieser Funktion müssen wir eine bereits bekannte GUID übergeben. Um eine GUID zu erhalten, kann man entweder alle Geräteklassen enumerieren, oder eine GUID (weil auf jedem System identisch) einfach so übergeben. Geräteschnittstellen kann man nicht so einfach enumerieren, da sie vom Gerätetreiber installiert werden. Es ist nicht möglich alle Geräteschnittstellen auf einem System zu ermitteln, viel mehr muss die GUID einer Geräteschnittstelle bereits bekannt sein. Die GUIDs der Geräteschnittstellen sind auch auf jedem System identisch und müssen manuell übergeben werden. Hat man so einen Handle erstellt, muss man nach getaner Arbeit die Ressourcen wieder dem System rückführen, dies geschieht mittels der SetupDiDestroyDeviceInfoList-Funktion.

Nehmen wir nun z. B. die GUID einer CD-ROM-Klasse und enumerieren erst mal alle CD-Rom-Laufwerke, die dieser Klasse angehören (SetupDiEnumDeviceInfo-API). Mit den Ergebnissen dieser Enumeration können dann die Registryeinstellungen des Gerätes ermittelt (SetupDiGetDeviceRegistryProperty-Funktion) und ggf. ausgegeben werden.

Da uns die Geräteschnittstellen noch Informationen vorenthalten, sollten wir auch diese Informationen ermitteln. Dazu nehmen wir unsere GUID der CD-Rom-Schnittstelle und enumerieren alle Geräte, die dieser Schnittstelle angehören. Was daran wirklich interessant ist, ist die Möglichkeit, den Pfad der Geräteschnittstelle (des Gerätetreibers) zu ermitteln. Dies gelingt uns durch die DeviceInterfaceDetailData-Funktion. Den Pfad des Gerätes können wir dann später weiter verwenden, um eine direkte Kommunikation zum Gerätetreiber aufzunehmen. Nun haben wir Ihnen viel erzählt und wollen Ihnen auch mal ein kleines Beispiel zeigen.

Beispiel:

Public Sub DevPrintRegSetting()
  Dim I As Integer
  Dim Retval As Long
  Dim SE As Long
  Dim DIDC As SP_DEVINFO_DATA
  Dim DIDI As SP_DEVICE_INTERFACE_DATA
  Dim GUID_DEVCLASS_CDROM As GUID
  Dim GUID_DEVINTERFACE_CDROM As GUID
  Dim hDevInfoC As Long
  Dim hDevInfoI As Long
  Dim Buffer() As Byte
  Dim BufferLen As Long
 
  With GUID_DEVCLASS_CDROM ' CD-ROM Geräteklassen GUID
    .Data1 = &H4D36E965
    .Data2 = &HE325
    .Data3 = &H11CE
    .Data4(0) = &HBF
    .Data4(1) = &HC1
    .Data4(2) = &H8
    .Data4(3) = &H0
    .Data4(4) = &H2B
    .Data4(5) = &HE1
    .Data4(6) = &H3
    .Data4(7) = &H18
  End With
  hDevInfoC = SetupDiGetClassDevs(GUID_DEVCLASS_CDROM, vbNullString, 0, DIGCF_PRESENT)
 
  With GUID_DEVINTERFACE_CDROM ' CD-ROM Geräteschnittstellen GUID
    .Data1 = &H53F56308
    .Data2 = &HB6BF
    .Data3 = &H11D0
    .Data4(0) = &H94
    .Data4(1) = &HF2
    .Data4(2) = &H0
    .Data4(3) = &HA0
    .Data4(4) = &HC9
    .Data4(5) = &H1E
    .Data4(6) = &HFB
    .Data4(7) = &H8B
  End With
  hDevInfoI = SetupDiGetClassDevs(GUID_DEVINTERFACE_CDROM, vbNullString, _
    0, DIGCF_PRESENT Or DIGCF_DEVICEINTERFACE)
 
  DIDC.cbSize = Len(DIDC)
  DIDI.cbSize = Len(DIDI)
  Do
    Retval = SetupDiEnumDeviceInfo(hDevInfoC, I, DIDC)
    If Retval = 0 Then
      SE = Err.LastDllError
      If SE = ERROR_NO_MORE_ITEMS Then
        Exit Do
      Else
        MsgBox "Error, can't enumerate Items"
        Exit Sub
      End If
    End If
    Debug.Print "Name: " & GetSetupRegSetting(hDevInfoC, DIDC, SPDRP_FRIENDLYNAME)
 
    Retval = SetupDiEnumDeviceInterfaces(hDevInfoI, ByVal 0&, GUID_DEVINTERFACE_CDROM, I, DIDI)
    If Retval = 0 Then
      SE = Err.LastDllError
      If SE = ERROR_NO_MORE_ITEMS Then
        Exit Do
      Else
        MsgBox "Error, can't enumerate Items"
        Exit Sub
      End If
    End If
 
    Retval = SetupDiGetDeviceInterfaceDetail(hDevInfoI, DIDI, ByVal 0&, 0&, BufferLen, ByVal 0&)
    If Retval = 0 Then
      SE = Err.LastDllError
      If SE = ERROR_INSUFFICIENT_BUFFER Then
        ReDim Buffer(BufferLen - 1)
        Buffer(0) = 5
 
        Retval = SetupDiGetDeviceInterfaceDetail(hDevInfoI, DIDI, Buffer(0), BufferLen, _
          BufferLen, ByVal 0&)
      End If
    End If
    Debug.Print "Gerätetreiberpfad: " & Mid$(StrConv(Buffer, vbUnicode), 5, BufferLen - 5)
 
    I = I + 1
  Loop
 
  Call SetupDiDestroyDeviceInfoList(hDevInfoC)
  Call SetupDiDestroyDeviceInfoList(hDevInfoI)
End Sub
Public Function GetSetupRegSetting(ByVal hDevInfo As Long, _
  DID As SP_DEVINFO_DATA, ByVal RegSetting As DEVICEPROPERTYINDEX) As String
 
  Dim BuffStr As String
  Dim BuffLng As Long
  Dim BufferLen As Long
  Dim RegType As REGPROPERTYTYPES
  Dim SE As SetupErrors
  Dim Retval As Long
 
   Retval = SetupDiGetDeviceRegistryProperty(hDevInfo, DID, RegSetting, RegType, "", 0&, BufferLen)
   If Retval = 0 Then
     SE = Err.LastDllError
 
     If SE = ERROR_INSUFFICIENT_BUFFER Then
       Select Case RegType
         Case REG_SZ, REG_MULTI_SZ, REG_EXPAND_SZ, REG_BINARY
           BuffStr = Space$(BufferLen)
           Retval = SetupDiGetDeviceRegistryProperty(hDevInfo, DID, RegSetting, _
             RegType, BuffStr, Len(BuffStr), BufferLen)
 
         Case REG_DWORD, REG_DWORD_BIG_ENDIAN, REG_DWORD_LITTLE_ENDIAN
           Retval = SetupDiGetDeviceRegistryProperty(hDevInfo, DID, RegSetting, RegType, _
             BuffLng, Len(BuffLng), BufferLen)
           BuffStr = CStr(BuffLng)
           BuffLng = BuffLng + 1
       End Select
 
       If Retval <> 0 Then
         GetSetupRegSetting = Left$(BuffStr, BufferLen - 1)
       Else
         MsgBox "Error while dermitting Regestryproperty"
       End If
 
    ElseIf SE <> ERROR_INVALID_DATA Then
      MsgBox "Error, can't get Regestryproperty"
    End If
  End If
End Function

Natürlich bieten die Setup-API's noch weitere Features, die wir Ihnen leider nicht alle vorstellen können. Denkbar wäre aber z. B. ein eigener Gerätemanager oder eine Hardwarediagnoseanwendung. Weitere Informationen zu diesem Thema finden sie in der Microsoft MSDN-Library.
 

Treiber:

Auf der vorangegangenen Seite haben wir Ihnen gezeigt, wie sie zu einem CD-Laufwerk den Pfad des Gerätetreibers ermitteln können. Diesen Treiberpfad wollen wir nun weiter verwenden, um eine direkte Kommunikation mit diesem Treiber herzustellen, was im übrigen nur unter Windows NT4, 2000 und XP oder neuer möglich ist (unter Windows 9x/ME unterscheidet sich das Verfahren dadurch, dass nur die Hardware direkt angesprochen werden kann). Dazu nutzen wir die "CreateFile"-API, der wir den Pfad des Gerätetreibers anstatt eines Dateinamens übergeben können. Nachdem wir ein Handle zu dem CD-Laufwerk erhalten haben, können wir direkt mittels der "DeviceIOControl"-Funktion dem Treiber unsere Anweisungen senden. Microsoft bietet zu den verschiedenen Hardwaregruppen bestimmte Anweisungen. Alle Geräte, die ein Medium unterstützen (CDROM, USB-Stick, Festplatten) auf das man zugreifen kann, bieten zusätzlich noch eine gemeinsame Ansprechmöglichkeit. Zum Schluss muss man, wie beim Öffnen einer Datei, das Handle wieder schließen. Dies geschieht wie bei normalen Dateien über die "CloseHandle"-Funktion. Nun wollen wir Ihnen das Ganze auch mal etwas konkreter darstellen...

' Benötigte API-Deklarationen
Public Declare Function CreateFile Lib "kernel32.dll" _
  Alias "CreateFileA" ( _
  ByVal lpFilename As String, _
  ByVal dwDesiredAccess As DESIREDFLAGS, _
  ByVal dwShareMode As SHAREFLAGS, _
  ByRef lpSecurityAttributes As Any, _
  ByVal dwCreationDisposition As DISPOSITIONFLAGS, _
  ByVal dwFlagsAndAttributes As Long, _
  ByVal hTemplateFile As Long) As Long
 
Public Declare Function CloseHandle Lib "kernel32.dll" ( _
  ByVal hObject As Long) As Long
 
Public Declare Function DeviceIoControl Lib "kernel32.dll" ( _
  ByVal hDevice As Long, _
  ByVal dwIoControlCode As IOCOMMANDS, _
  ByRef lpInBuffer As Any, _
  ByVal nInBufferSize As Long, _
  ByRef lpOutBuffer As Any, _
  ByVal nOutBufferSize As Long, _
  ByRef lpBytesReturned As Long, _
  ByRef lpOverlapped As Any) As Long
 
Public Enum DESIREDFLAGS
  GENERIC_READ = &H80000000
  GENERIC_WRITE = &H40000000
End Enum
 
Public Enum SHAREFLAGS
  FILE_SHARE_READ = &H1
  FILE_SHARE_WRITE = &H2
End Enum
 
Public Enum DISPOSITIONFLAGS
  OPEN_EXISTING = 3
End Enum
 
Public Enum IOCOMMANDS
  IOCTL_STORAGE_CHECK_VERIFY = &H2D4800
  IOCTL_STORAGE_CHECK_VERIFY2 = &H2D0800
  IOCTL_STORAGE_MEDIA_REMOVAL = &H2D4804
  IOCTL_STORAGE_EJECT_MEDIA = &H2D4808
  IOCTL_STORAGE_LOAD_MEDIA = &H2D480C
  IOCTL_STORAGE_LOAD_MEDIA2 = &H2D080C
  IOCTL_STORAGE_RESERVE = &H2D4810
  IOCTL_STORAGE_RELEASE = &H2D4814
  IOCTL_STORAGE_FIND_NEW_DEVICES = &H2D4818
  IOCTL_STORAGE_EJECTION_CONTROL = &H2D0940
  IOCTL_STORAGE_MCN_CONTROL = &H2D0944
  IOCTL_STORAGE_GET_MEDIA_TYPES = &H2D0C00
  IOCTL_STORAGE_GET_MEDIA_TYPES_EX = &H2D0C04
  IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER = &H2D0C10
  IOCTL_STORAGE_GET_HOTPLUG_INFO = &H2D0C14
  IOCTL_STORAGE_SET_HOTPLUG_INFO = &H2DCC18
  IOCTL_STORAGE_RESET_BUS = &H2D5000
  IOCTL_STORAGE_RESET_DEVICE = &H2D5004
  IOCTL_STORAGE_BREAK_RESERVATION = &H2D5014
  IOCTL_STORAGE_GET_DEVICE_NUMBER = &H2D1080
  IOCTL_STORAGE_PREDICT_FAILURE = &H2D1100
  IOCTL_STORAGE_QUERY_PROPERTY = &H2D1400
  IOCTL_SCSI_PASS_THROUGH = &H4D004
  IOCTL_SCSI_MINIPORT = &H4D008
  IOCTL_SCSI_GET_INQUIRY_DATA = &H4100C
  IOCTL_SCSI_GET_CAPABILITIES = &H41010
  IOCTL_SCSI_PASS_THROUGH_DIRECT = &H4D014
  IOCTL_SCSI_GET_ADDRESS = &H41018
  IOCTL_SCSI_RESCAN_BUS = &H4101C
  IOCTL_SCSI_GET_DUMP_POINTERS = &H41020
  IOCTL_SCSI_FREE_DUMP_POINTERS = &H41024
  IOCTL_IDE_PASS_THROUGH = &H4D028
End Enum

Die Vorgehensweise ist denkbar einfach. Erst erstellt man ein Handle zum Gerätetreiber mittels der "CreateFile"-Funktion. Mit diesem Handle ist es dann möglich, die in der MSDN beschriebenen Kommandos an den Gerätetreiber zu senden. Und wie gesagt, das Handle sollte nach getaner Arbeit wieder aufgelöst werden, da andere Anwendungen, die unter Umständen einen exklusiven Zugriff auf den Gerätetreiber benötigen, sonst einen Fehler melden könnten.

Um eine Verbindung zum Gerätetreiber aufzunehmen, benötigt man nicht unbedingt diesen "Gerätetreiberpfad", den Sie ja schon im letzten Abschnitt kennen gelernt haben. Man kann, wenn die Hardware oder das Medium näher bekannt ist, auch das Laufwerk oder die Partition direkt an die "CreateFile"-Funktion weitergeben. So kann man z. B. eine Kommunikation zum Gerätetreiber des CD-ROM-Laufwerks von Laufwerk "D:" ganz einfach herstellen, in dem man der "CreateFile"-Funktion den Pfad "\\.\X:" (X symbolisiert den Laufwerksbuchstaben) übergibt. Auch ein Zugriff anhand des Laufwerksindexes ist möglich. Über "\\.\PHYSICALDRIVEX" (X symbolisiert den 0-basierenden Index des Laufwerks) erhalten Sie ebenfalls das Handle zum Gerätetreiber des Laufwerks (beide Verfahren funktionieren nur auf Windows NT, 2000, XP und höher).

Nun wollen wir Ihnen mal ein kleines Anwendungsbeispiel zeigen, in dem man z. B. die CD-ROM-Auswurfsteuerung bereitstellen kann. Dies ist vielleicht nichts Neues, da es an Beispielen nur so wimmelt wo gezeigt wird, wie die CD-Schublade ausgeworfen wird. Aber wussten Sie, dass Sie die Laufwerksklappe auch verriegeln können, sogar gegen den manuellen Auswurf ?

Beispiel:

Public Enum DeviceAction
  StorageEject = 0
  StorageLoad = 1
  StorageLock = 2
  StorageUnlock = 3
  StorageAccessible = 4
End Enum
 
Private DriveLocked As Boolean
 
' Hier den CD-Laufwerksbuchstaben eintragen!
Private Const TmpDevPath1 = "\\.\D:"
Private Function SendDevicedCommand(ByVal DevPath As String, _
  ByVal DAction As DeviceAction) As Boolean
 
  Dim hTmpDevice As Long
  Dim BReturned As Long
  Dim Retval As Long
 
   hTmpDevice = CreateFile(DevPath, GENERIC_READ Or GENERIC_WRITE, FILE_SHARE_READ Or _
    FILE_SHARE_WRITE, ByVal 0&, OPEN_EXISTING, 0, ByVal 0&)
  If hTmpDevice = -1 Then
    MsgBox "ERROR, can't create Devicehandle"
    Exit Function
  End If
 
  Select Case DAction
    Case StorageEject
      Retval = DeviceIoControl(hTmpDevice, IOCTL_STORAGE_EJECT_MEDIA, _
        ByVal 0&, 0&, ByVal 0&, 0&, BReturned, ByVal 0&)
 
    Case StorageLoad
      Retval = DeviceIoControl(hTmpDevice, IOCTL_STORAGE_LOAD_MEDIA, _
        ByVal 0&, 0&, ByVal 0&, 0&, BReturned, ByVal 0&)
 
    Case StorageLock
      Retval = DeviceIoControl(hTmpDevice, IOCTL_STORAGE_MEDIA_REMOVAL, _
        CByte(1), 1&, ByVal 0&, 0&, BReturned, ByVal 0&)
 
    Case StorageUnlock
      Retval = DeviceIoControl(hTmpDevice, IOCTL_STORAGE_MEDIA_REMOVAL, _
        CByte(0), 1&, ByVal 0&, 0&, BReturned, ByVal 0&)
 
    Case StorageAccessible
      Retval = DeviceIoControl(hTmpDevice, IOCTL_STORAGE_CHECK_VERIFY, _
        ByVal 0&, 0, ByVal 0&, 0&, BReturned, ByVal 0&)
      SendDevicedCommand = Retval
      Exit Function
  End Select
 
  If Retval = 0 And Err.LastDllError <> 1 Then
    MsgBox "Error, can't set Deviceaction"
  Else
    SendDevicedCommand = True
  End If
 
  Call CloseHandle(hTmpDevice)
End Function
Public Sub LockDrive(ByVal bLock As Boolean)
  If bLock Then
    Call SendDevicedCommand(TmpDevPath, StorageLock)
  Else
    Call SendDevicedCommand(TmpDevPath, StorageUnlock)
  End If
  DriveLocked = bLock
End Sub
Public Sub EjectDrive(ByVal bEject As Boolean)
  If bEject Then
    Call SendDevicedCommand(TmpDevPath, StorageEject)
  Else
    Call SendDevicedCommand(TmpDevPath, StorageLoad)
  End If
End Sub
Public Property Get DiscInserted() As Boolean
  DiscInserted = SendDevicedCommand(TmpDevPath, StorageAccessible)
End Property

Über dieses Verfahren ist natürlich noch viel mehr möglich, leider können wir Ihnen hier nicht alles vorstellen. Denkbar wäre z. B. ein CD-Player, CD-Ripper oder DVD-Player. Wie gesagt, unterstützen Gerätetreiber nur Standardmethoden der Hardware. Will man spezielle Hardwareeigenschaften nutzen, wie z. B. das CD-Brennen oder ähnliches, muss man die Hardware direkt ansprechen. Dies wollen wir Ihnen auf der nächsten Seite dieses Workshops zeigen.
 

Hardware:

Die Hardware direkt anzusprechen ist ähnlich wie das Ansprechen des Gerätetreibers. Hier wird wieder das Handle zum Gerätetreiber benötigt. Dieses wird wie zuvor beschrieben mittels der "CreateFile"-Funktion erstellt. Anschließend nutzt man wieder die "DeviceIOControl"-Funktion mit einem bestimmten Kommando, um die Hardware anzusprechen. Dazu kann man entweder die ATAPI, oder wie hier beschrieben, die SCSI Schnittstelle verwenden. Leider sucht man in der MSDN nach ATAPI- oder SCSI-Befehlen vergebens, lediglich RAW-Kommandos, die nicht nur für Windows sondern vielmehr für die generelle Hardwaresteuerung beschrieben ist, sind im Internet auffindbar (Suchbegriff z. B. "SCSI Mulitmedia Commands SMC). Demnach ist es nicht so einfach, die benötigten Befehle und Rückgaben der Hardware umzusetzen. Ein wenig an Informationen konnten wir dem Internet dennoch entlocken, und diese wollen wir Ihnen hier vorstellen.

Für die Übermittlung von SCSI-Befehlen an ein Hardwaregerät verwendet man die "DeviceIOControl"-Funktion und übergibt ihr den Befehl "IOCTL_SCSI_PASS_THROUGH" (es sind mehrere Abwandlungen vorhanden, z. B. mit / ohne Puffer etc.) und die betreffende SCSI-Struktur. Die SCSI-Struktur wird zu Beginn mit dem auszuführenden Befehl und den evtl. erforderlichen Daten zur Ausführung des Befehls gefüllt. Nach dem erfolgreichen Aufruf der Funktion enthält diese Struktur dann ggf. eine Fehlernummer oder Daten, falls diese angefordert wurden. In dem nun folgenden Beispiel wollen wir Ihnen zeigen, wie sie per SCSI diverse Einstellungen eines CD-Brenners ermitteln können, darunter sind die unterstützten Lese / Schreibmedien, Lese / Schreibgeschwindigkeiten und die Größe des Puffers.

' Benötigte API-Deklarationen
Public Declare Sub CopyMemory Lib "kernel32.dll" _
  Alias "RtlMoveMemory" ( _
  ByRef Destination As Any, _
  ByRef Source As Any, _
  ByVal Length As Long)
 
Public Type SCSI_PASS_THROUGH
  Length As Integer
  ScsiStatus As Byte
  PathId As Byte
  TargetId As Byte
  Lun As Byte
  CdbLength As Byte
  SenseInfoLength As Byte
  DataIn As Byte
  DataTransferLength As Long
  TimeOutValue As Long
  DataBufferOffset As Long
  SenseInfoOffset As Long
  Cdb(15) As Byte
End Type
 
Public Type SCSI_PASS_THROUGH_WITH_BUFFERS
  Spt As SCSI_PASS_THROUGH
  SenseBuf(31) As Byte
  DataBuf(511) As Byte
End Type
 
Public Enum SCSI_PASS_THROUGH_CDBMODE
  MODE_PAGE_ERROR_RECOVERY = &H1
  MODE_PAGE_DISCONNECT = &H2
  MODE_PAGE_FORMAT_DEVICE = &H3
  MODE_PAGE_RIGID_GEOMETRY = &H4
  MODE_PAGE_FLEXIBILE = &H5
  MODE_PAGE_VERIFY_ERROR = &H7
  MODE_PAGE_CACHING = &H8
  MODE_PAGE_PERIPHERAL = &H9
  MODE_PAGE_CONTROL = &HA
  MODE_PAGE_MEDIUM_TYPES = &HB
  MODE_PAGE_NOTCH_PARTITION = &HC
  MODE_SENSE_RETURN_ALL = &H3F
  MODE_SENSE_CURRENT_VALUES = &H0
  MODE_SENSE_CHANGEABLE_VALUES = &H40
  MODE_SENSE_DEFAULT_VAULES = &H80
  MODE_SENSE_SAVED_VALUES = &HC0
  MODE_PAGE_DEVICE_CONFIG = &H10
  MODE_PAGE_MEDIUM_PARTITION = &H11
  MODE_PAGE_DATA_COMPRESS = &HF
  MODE_PAGE_CAPABILITIES = &H2A
End Enum
 
Public Enum SCSI_PASS_THROUGH_CDBLENGTH
  CDB6GENERIC_LENGTH = 6
  CDB10GENERIC_LENGTH = 10
  CDB12GENERIC_LENGTH = 12
End Enum
 
Public Enum SCSI_PASS_THROUGH_DATAIN
  SCSI_IOCTL_DATA_OUT = 0
  SCSI_IOCTL_DATA_IN = 1
  SCSI_IOCTL_DATA_UNSPECIFIED = 2
End Enum
 
Public Enum SCSI_PASS_THROUGH_OPERATION
  SCSIOP_TEST_UNIT_READY = &H0
  SCSIOP_REZERO_UNIT = &H1
  SCSIOP_REWIND = &H1
  SCSIOP_REQUEST_BLOCK_ADDR = &H2
  SCSIOP_REQUEST_SENSE = &H3
  SCSIOP_FORMAT_UNIT = &H4
  SCSIOP_READ_BLOCK_LIMITS = &H5
  SCSIOP_REASSIGN_BLOCKS = &H7
  SCSIOP_READ6 = &H8
  SCSIOP_RECEIVE = &H8
  SCSIOP_WRITE6 = &HA
  SCSIOP_PRINT = &HA
  SCSIOP_SEND = &HA
  SCSIOP_SEEK6 = &HB
  SCSIOP_TRACK_SELECT = &HB
  SCSIOP_SLEW_PRINT = &HB
  SCSIOP_SEEK_BLOCK = &HC
  SCSIOP_PARTITION = &HD
  SCSIOP_READ_REVERSE = &HF
  SCSIOP_WRITE_FILEMARKS = &H10
  SCSIOP_FLUSH_BUFFER = &H10
  SCSIOP_SPACE = &H11
  SCSIOP_INQUIRY = &H12
  SCSIOP_VERIFY6 = &H13
  SCSIOP_RECOVER_BUF_DATA = &H14
  SCSIOP_MODE_SELECT = &H15
  SCSIOP_RESERVE_UNIT = &H16
  SCSIOP_RELEASE_UNIT = &H17
  SCSIOP_COPY = &H18
  SCSIOP_ERASE = &H19
  SCSIOP_MODE_SENSE = &H1A
  SCSIOP_START_STOP_UNIT = &H1B
  SCSIOP_STOP_PRINT = &H1B
  SCSIOP_LOAD_UNLOAD = &H1B
  SCSIOP_RECEIVE_DIAGNOSTIC = &H1C
  SCSIOP_SEND_DIAGNOSTIC = &H1D
  SCSIOP_MEDIUM_REMOVAL = &H1E
  SCSIOP_READ_CAPACITY = &H25
  SCSIOP_READ = &H28
  SCSIOP_WRITE = &H2A
  SCSIOP_SEEK = &H2B
  SCSIOP_LOCATE = &H2B
  SCSIOP_WRITE_VERIFY = &H2E
  SCSIOP_VERIFY = &H2F
  SCSIOP_SEARCH_DATA_HIGH = &H30
  SCSIOP_SEARCH_DATA_EQUAL = &H31
  SCSIOP_SEARCH_DATA_LOW = &H32
  SCSIOP_SET_LIMITS = &H33
  SCSIOP_READ_POSITION = &H34
  SCSIOP_SYNCHRONIZE_CACHE = &H35
  SCSIOP_COMPARE = &H39
  SCSIOP_COPY_COMPARE = &H3A
  SCSIOP_WRITE_DATA_BUFF = &H3B
  SCSIOP_READ_DATA_BUFF = &H3C
  SCSIOP_CHANGE_DEFINITION = &H40
  SCSIOP_READ_SUB_CHANNEL = &H42
  SCSIOP_READ_TOC = &H43
  SCSIOP_READ_HEADER = &H44
  SCSIOP_PLAY_AUDIO = &H45
  SCSIOP_PLAY_AUDIO_MSF = &H47
  SCSIOP_PLAY_TRACK_INDEX = &H48
  SCSIOP_PLAY_TRACK_RELATIVE = &H49
  SCSIOP_PAUSE_RESUME = &H4B
  SCSIOP_LOG_SELECT = &H4C
  SCSIOP_LOG_SENSE = &H4D
End Enum
' Hier wieder den CD-Laufwerksbuchstaben eintragen!
Private Const DevPath = "\\.\D:"
Public Sub DebugSCSIInfo()
  Dim hDevice As Long
  Dim Retval As Long
  Dim TmpLen As Long
  Dim SCSIPass As SCSI_PASS_THROUGH_WITH_BUFFERS
  Dim TmpStr As String
  Dim TmpInt As Integer
 
  hDevice = CreateFile(DevPath, GENERIC_READ Or GENERIC_WRITE, FILE_SHARE_READ Or _
    FILE_SHARE_WRITE, ByVal 0&, OPEN_EXISTING, 0, ByVal 0&)
  If hDevice = -1 Then
    MsgBox "ERROR, can't create Devicehandle"
    Exit Sub
  End If
 
  With SCSIPass.Spt
    .Length = Len(SCSIPass.Spt) + 3
    .PathId = 0
    .TargetId = 1
    .Lun = 0
    .CdbLength = SCSI_PASS_THROUGH_CDBLENGTH.CDB6GENERIC_LENGTH
    .SenseInfoLength = 24
    .DataIn = SCSI_PASS_THROUGH_DATAIN.SCSI_IOCTL_DATA_IN
    .DataTransferLength = 192
    .TimeOutValue = 2
    .DataBufferOffset = Len(SCSIPass.Spt) + 35
    .SenseInfoOffset = Len(SCSIPass.Spt) + 3
 
    .Cdb(0) = SCSI_PASS_THROUGH_OPERATION.SCSIOP_INQUIRY
    .Cdb(4) = .DataTransferLength
  End With
 
  TmpLen = 0
  Retval = DeviceIoControl(hDevice, IOCTL_SCSI_PASS_THROUGH, SCSIPass, _
    Len(SCSIPass), SCSIPass, Len(SCSIPass), TmpLen, ByVal 0&)
  If TmpLen > 0 Then
    TmpStr = Space$(8)
    CopyMemory ByVal TmpStr, SCSIPass.DataBuf(8), Len(TmpStr)
    Debug.Print "Hersteller: " & Left$(TmpStr, InStr(1, TmpStr & _
      vbNullChar, vbNullChar) - 1)
 
    TmpStr = Space$(16)
    CopyMemory ByVal TmpStr, SCSIPass.DataBuf(16), Len(TmpStr)
    Debug.Print "Produkt-ID: " & Left$(TmpStr, InStr(1, TmpStr & _
      vbNullChar, vbNullChar) - 1)
 
    TmpStr = Space$(4)
    CopyMemory ByVal TmpStr, SCSIPass.DataBuf(32), Len(TmpStr)
    Debug.Print "Produkt-Revision: " & Left$(TmpStr, InStr(1, TmpStr & _
      vbNullChar, vbNullChar) - 1)
 
    TmpStr = Space$(18)
    CopyMemory ByVal TmpStr, SCSIPass.DataBuf(38), Len(TmpStr)
    Debug.Print "Seriennummer: " & Left$(TmpStr, InStr(1, TmpStr & _
      vbNullChar, vbNullChar) - 1)
  End If
 
  With SCSIPass.Spt
    .Cdb(0) = SCSI_PASS_THROUGH_OPERATION.SCSIOP_MODE_SENSE
    .Cdb(1) = &H8
    .Cdb(2) = SCSI_PASS_THROUGH_CDBMODE.MODE_PAGE_CAPABILITIES
    .Cdb(4) = .DataTransferLength
  End With
 
  TmpLen = 0
  Retval = DeviceIoControl(hDevice, IOCTL_SCSI_PASS_THROUGH, SCSIPass, _
    Len(SCSIPass), SCSIPass, Len(SCSIPass), TmpLen, ByVal 0&)
  If TmpLen > 0 Then
    CopyMemory TmpInt, SCSIPass.DataBuf(15), 2
    Debug.Print "Puffergröße: " & TmpInt & " KB"
 
    CopyMemory TmpInt, SCSIPass.DataBuf(11), 2
    Debug.Print "Max. lesen: " & TmpInt & " KB"
 
    CopyMemory TmpInt, SCSIPass.DataBuf(21), 2
    Debug.Print "Max. schreiben: " & TmpInt & " KB"
 
    With SCSIPass
      TmpStr = ""
      If (.DataBuf(6) And &H1) Or (.DataBuf(6) And &H2) Then
        TmpStr = "CD-ROM,"
 
        If .DataBuf(6) And &H1 Then
          TmpStr = TmpStr & "CD-R,"
        End If
        If .DataBuf(6) And &H2 Then
          TmpStr = TmpStr & "CD-RW,"
        End If
      End If
      If (.DataBuf(6) And &H8) Or (.DataBuf(6) And &H10) Or (.DataBuf(6) And &H20) Then
        If .DataBuf(6) And &H8 Then
          TmpStr = TmpStr & "DVD-ROM,"
        End If
        If .DataBuf(6) And &H10 Then
          TmpStr = TmpStr & "DVD-R,DVD-RW,"
        End If
        If .DataBuf(6) And &H20 Then
          TmpStr = TmpStr & "DVD-RAM,"
        End If
      End If
      TmpStr = Left$(TmpStr, Len(TmpStr) - 1)
      Debug.Print "Medien lesen: " & TmpStr
 
      TmpStr = ""
      If (.DataBuf(7) And &H1) Or (.DataBuf(7) And &H2) Then
        If .DataBuf(7) And &H1 Then
          TmpStr = "CD-R,"
        End If
        If .DataBuf(7) And &H2 Then
          TmpStr = TmpStr & "CD-RW,"
        End If
      End If
      If (.DataBuf(7) And &H10) Or (.DataBuf(7) And &H20) Then
        If .DataBuf(7) And &H10 Then
          TmpStr = TmpStr & "DVD-R,DVD-RW,"
        End If
        If .DataBuf(7) And &H20 Then
          TmpStr = TmpStr & "DVD-RAM,"
        End If
      End If
      TmpStr = Left$(TmpStr, Len(TmpStr) - 1)
      Debug.Print "Medien schreiben: " & TmpStr
 
      ' Irgendwo in diesem Puffer befinden sich auch weitere Flags,
      ' z. B. ob der Brenner die "BufferUnderrun"-Technologie oder ob
      ' Double-Layer Medien unterstützt werden.
      ' Für Informationen dies betreffend wäre ich sehr dankbar :-)
 
    End With
  End If
 
  Call CloseHandle(hDevice)
End Sub

Natürlich ist viel mehr aus dem SCSI-Protokoll heraus zu holen, leider können wir Ihnen hier nicht alle Möglichkeiten vorstellen. Denkbar wäre es allerdings, mit dieser Schnittstelle eine CD-Ripping-, CD-Brenn- oder sogar eine Festplatten Defragmentierungsanwendung zu erstellen. Ich hoffe wir konnten Sie ein wenig begeistern, tiefer in die Hardwareprogrammierung einzusteigen und dass sie viele neue interessante Informationen sammeln konnten. Schauen Sie sich vielleicht auch mal das Beispielprojekt an (Lauffähig unter Windows 2000/XP), welches noch ein paar Informationen mehr ausgibt. In diesem Sinne frohes Schaffen.
 

Dieser Workshop wurde bereits 18.817 mal aufgerufen.

Über diesen Workshop im Forum diskutieren
Haben Sie Fragen oder Anregungen zu diesem Workshop, können Sie gerne mit anderen darüber in unserem Forum diskutieren.

Neue Diskussion eröffnen

nach obenzurück


Anzeige

Kauftipp Unser Dauerbrenner!Diesen und auch alle anderen Workshops finden Sie auch auf unserer aktuellen vb@rchiv  Vol.6
(einschl. Beispielprojekt!)

Ein absolutes Muss - Geballtes Wissen aus mehr als 8 Jahren vb@rchiv!
- nahezu alle Tipps & Tricks und Workshops mit Beispielprojekten
- Symbol-Galerie mit mehr als 3.200 Icons im modernen Look
Weitere Infos - 4 Entwickler-Vollversionen (u.a. sevFTP für .NET), Online-Update-Funktion u.v.m.
 
   

Druckansicht Druckansicht Copyright ©2000-2024 vb@rchiv Dieter Otter
Alle Rechte vorbehalten.
Microsoft, Windows und Visual Basic sind entweder eingetragene Marken oder Marken der Microsoft Corporation in den USA und/oder anderen Ländern. Weitere auf dieser Homepage aufgeführten Produkt- und Firmennamen können geschützte Marken ihrer jeweiligen Inhaber sein.

Diese Seiten wurden optimiert für eine Bildschirmauflösung von mind. 1280x1024 Pixel