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: Treiber: Hardware direkt: 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.
Anzeige
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. |
sevGraph (VB/VBA) Grafische Auswertungen Präsentieren Sie Ihre Daten mit wenig Aufwand in grafischer Form. sevGraph unterstützt hierbei Balken-, Linien- und Stapel-Diagramme (Stacked Bars), sowie 2D- und 3D-Tortendiagramme und arbeitet vollständig datenbankunabhängig! Tipp des Monats Dezemeber 2024 Roland Wutzke MultiSort im ListView-Control Dieses Beispiel zeigt, wie sich verschiedene Sortierfunktionen für ein ListView Control realisieren lassen. Access-Tools Vol.1 Über 400 MByte Inhalt Mehr als 250 Access-Beispiele, 25 Add-Ins und ActiveX-Komponenten, 16 VB-Projekt inkl. Source, mehr als 320 Tipps & Tricks für Access und VB |
|||||||||||||
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. |