MS EnumCD example converted to PureBasic
Posted: Thu Mar 10, 2016 9:05 pm
Just in case it helps someone, I've converted the EnumCD example from Microsoft to PureBasic (the original can be found here: https://support.microsoft.com/en-us/kb/305184 )
Code: Select all
EnableExplicit
; Purebasic Conversion of EnumCD from Microsoft - https://support.microsoft.com/en-us/kb/305184
; ###################################################################
;- ~ Prototypes
; ###################################################################
Prototype ProtoSetupDiGetClassDevs(*ClassGuid, *Enumerator, hwndParent.i, Flags.l)
If OpenLibrary(0, "setupapi.dll")
CompilerIf #PB_Compiler_Unicode=0
Global SetupDiGetClassDevs.ProtoSetupDiGetClassDevs = GetFunction(0, "SetupDiGetClassDevsA")
CompilerElse
Global SetupDiGetClassDevs.ProtoSetupDiGetClassDevs = GetFunction(0, "SetupDiGetClassDevsW")
CompilerEndIf
CloseLibrary(0)
EndIf
; ###################################################################
;- ~ Constants
; ###################################################################
#SPDRP_HARDWAREID = 1
DataSection
GUID_DEVCLASS_CDROM:
Data.l $4D36E965 : Data.w $E325, $11CE : Data.b $BF, $C1, $08, $00, $2B, $E1, $03, $18
GUID_DEVINTERFACE_CDROM:
Data.l $53f56308 : Data.w $b6bf, $11d0 : Data.b $94, $f2, $00, $a0, $c9, $1e, $fb, $8b
EndDataSection
#StorageAdapterProperty = 1 ; https://msdn.microsoft.com/en-us/library/windows/desktop/ff800840%28v=vs.85%29.aspx
#StorageDeviceProperty = 0 ; https://msdn.microsoft.com/en-us/library/windows/desktop/ff800840%28v=vs.85%29.aspx
#PropertyStandardQuery = 0 ; https://msdn.microsoft.com/en-us/library/windows/desktop/ff800840%28v=vs.85%29.aspx
#FILE_DEVICE_CD_ROM = 02
#FILE_DEVICE_DVD = 51
#SCSI_MAX_CDB_LEN=16
#SCSI_MAX_SENSE_LEN=24; 64
#SCSI_MAX_INDIRECT_DATA=$FFFF ; 16384
#IOCTL_SCSI_PASS_THROUGH = $0004D004
#IOCTL_STORAGE_QUERY_PROPERTY = $002D1400
#IOCTL_STORAGE_GET_MEDIA_TYPES_EX = $002D0C04
#SCSI_IOCTL_DATA_IN = 1
; from EnumCD.h file:
; Command Descriptor Block constants.
#CDB6GENERIC_LENGTH =6
; SCSI CDB operation codes
#SCSIOP_INQUIRY =$12
#SCSIOP_MODE_SENSE =$1A
#MODE_PAGE_CAPABILITIES=$2A
#DebugLevel = 1
; 0 = Suppress All Messages
; 1 = Display & Fatal Error Message
; 2 = Warning & Debug Messages
; 3 = Informational Messages
; ###################################################################
;- ~ Structures
; ###################################################################
Structure SP_DEVINFO_DATA Align #PB_Structure_AlignC
cbSize.l
ClassGuid.GUID
DevInst.l; // DEVINST handle
Reserved.l;
EndStructure
Structure SP_DEVICE_INTERFACE_DETAIL_DATA Align #PB_Structure_AlignC
cbSize.l ; DWORD cbSize
DevicePath.s{1024} ; TCHAR DevicePath[ANYSIZE_ARRAY]
EndStructure
Structure STORAGE_PROPERTY_QUERY Align #PB_Structure_AlignC
PropertyId.l;STORAGE_PROPERTY_ID
QueryType.l;STORAGE_QUERY_TYPE
AdditionalParameters.l
EndStructure
Structure STORAGE_ADAPTER_DESCRIPTOR Align #PB_Structure_AlignC
Version.l ; DWORD Version;
Size.l ; DWORD Size;
MaximumTransferLength.l ; DWORD MaximumTransferLength;
MaximumPhysicalPages.l ; DWORD MaximumPhysicalPages;
AlignmentMask.l ; DWORD AlignmentMask;
AdapterUsesPio.a ; BOOLEAN AdapterUsesPio;
AdapterScansDown.a ; BOOLEAN AdapterScansDown;
CommandQueueing.a ; BOOLEAN CommandQueueing;
AcceleratedTransfer.a ; BOOLEAN AcceleratedTransfer;
BusType.a ; BYTE BusType;
BusMajorVersion.u ; WORD BusMajorVersion;
BusMinorVersion.u ; WORD BusMinorVersion;
EndStructure
; https://msdn.microsoft.com/en-us/library/windows/desktop/ff800835%28v=vs.85%29.aspx
Structure STORAGE_DEVICE_DESCRIPTOR Align #PB_Structure_AlignC
Version.l ; DWORD Version
Size.l ; DWORD Size
DeviceType.a ; BYTE DeviceType
DeviceTypeModifier.a ; BYTE DeviceTypeModifier
RemovableMedia.a ; BOOLEAN RemovableMedia
CommandQueueing.a ; BOOLEAN CommandQueueing
VendorIdOffset.l ; DWORD VendorIdOffset
ProductIdOffset.l ; DWORD ProductIdOffset
ProductRevisionOffset.l ; DWORD ProductRevisionOffset
SerialNumberOffset.l ; DWORD SerialNumberOffset
BusType.u ; STORAGE_BUS_TYPE BusType >> Wert aus ENUM STORAGE_BUS_TYPE
RawPropertiesLength.l ; DWORD RawPropertiesLength
RawDeviceProperties.a ; BYTE RawDeviceProperties[1] >> Contains an array of length one that serves as a place holder for the first byte of the bus specific property data
EndStructure
Structure GET_MEDIA_TYPES Align #PB_Structure_AlignC
DeviceType.l ; DWORD DeviceType;
MediaInfoCount.l ; DWORD MediaInfoCount;
MediaInfo.i ; DEVICE_MEDIA_INFO MediaInfo[1];
EndStructure
; https://msdn.microsoft.com/en-us/library/windows/hardware/ff565345%28v=vs.85%29.aspx
Structure SCSI_PASS_THROUGH Align #PB_Structure_AlignC
Length.u ; USHORT Length
ScsiStatus.a ; UCHAR ScsiStatus
PathId.a ; UCHAR PathId
TargetId.a ; UCHAR TargetId
Lun.a ; UCHAR Lun
CdbLength.a ; UCHAR CdbLength
SenseInfoLength.a ; UCHAR SenseInfoLength
DataIn.a ; UCHAR DataIn
DataTransferLength.l ; ULONG DataTransferLength
TimeOutValue.l ; ULONG TimeOutValue
DataBufferOffset.l ; ULONG_PTR DataBufferOffset
SenseInfoOffset.l ; ULONG SenseInfoOffset
Cdb.a[#SCSI_MAX_CDB_LEN] ; UCHAR Cdb [SCSI_MAX_CDB_LEN]
EndStructure
Structure SCSI_PASS_THROUGH_WITH_BUFFERS Align #PB_Structure_AlignC
spt.SCSI_PASS_THROUGH
; Filler.l ; ULONG Filler
ucSenseBuf.a[#SCSI_MAX_SENSE_LEN] ; UCHAR ucSenseBuf [SCSI_MAX_SENSE_LEN]
ucDataBuf.a[#SCSI_MAX_INDIRECT_DATA]; UCHAR ucDataBuf [SCSI_MAX_INDIRECT_DATA]
EndStructure
; ###################################################################
;- ~ Procedures
; ###################################################################
Procedure.s HexL(Val.i, Len.a)
ProcedureReturn RSet(Hex(Val), Len, "0")
EndProcedure
Procedure.s StrL(Val.i, Len.a)
ProcedureReturn RSet(Str(Val), Len, "0")
EndProcedure
Procedure.s BusType(Type.u)
; Bus Type
Select Type
Case $00 : ProcedureReturn "UNKNOWN"
Case $01 : ProcedureReturn "SCSI"
Case $02 : ProcedureReturn "ATAPI"
Case $03 : ProcedureReturn "ATA"
Case $04 : ProcedureReturn "IEEE 1394"
Case $05 : ProcedureReturn "SSA"
Case $06 : ProcedureReturn "FIBRE"
Case $07 : ProcedureReturn "USB"
Case $08 : ProcedureReturn "RAID"
; additional types not available in original MS EnumCD:
Case $09 : ProcedureReturn "iScsi"
Case $0A : ProcedureReturn "SAS"
Case $0B : ProcedureReturn "SATA"
Case $0C : ProcedureReturn "SD"
Case $0D : ProcedureReturn "MMC"
Case $0E : ProcedureReturn "Virtual"
Case $0F : ProcedureReturn "FileBackedVirtual"
Default : ProcedureReturn "Bustype=" + Str(Type)
EndSelect
EndProcedure
Procedure.s DeviceType(Type.i)
; SCSI Device Type
Select Type
Case $00 : ProcedureReturn "Direct Access Device"
Case $01 : ProcedureReturn "Tape Device"
Case $02 : ProcedureReturn "Printer Device"
Case $03 : ProcedureReturn "Processor Device"
Case $04 : ProcedureReturn "WORM Device"
Case $05 : ProcedureReturn "CDROM Device"
Case $06 : ProcedureReturn "Scanner Device"
Case $07 : ProcedureReturn "Optical Disk"
Case $08 : ProcedureReturn "Media Changer"
Case $09 : ProcedureReturn "Comm. Device"
Case $0A : ProcedureReturn "ASCIT8"
Case $0B : ProcedureReturn "ASCIT8"
Case $0C : ProcedureReturn "Array Device"
Case $0D : ProcedureReturn "Enclosure Device"
Case $0E : ProcedureReturn "RBC Device"
Default : ProcedureReturn "Unknown Device "+ Str(Type)
EndSelect
EndProcedure
Procedure.s YesNo(Bool.a)
Select Bool
Case #False : ProcedureReturn "No"
Default : ProcedureReturn "Yes"
EndSelect
EndProcedure
Procedure DebugPrint( aDebugPrintLevel.a, sDebugMessage.s)
; Routine Description: This routine print the given string, If given Debug level is <= To the current Debug level.
; Arguments: DebugPrintLevel - Debug level of the given message
; DebugMessage - Message To be printed
; Return Value: None
Protected iCnt.i
If aDebugPrintLevel<=#DebugLevel
Debug sDebugMessage
Print(sDebugMessage)
EndIf
EndProcedure
Procedure GetRegistryProperty( iDevInfo.i, iIndex.i )
; Routine Description: This routine enumerates the CD devices using the Setup class Interface
; GUID GUID_DEVCLASS_CDROM. Gets the Device ID from the Registry
; property.
; Arguments: DevInfo - Handles To the device information List
; Index - Device member
; Return Value: TRUE / FALSE. This decides whether To Continue Or Not
Protected deviceInfoData.SP_DEVINFO_DATA
Protected iStatus.i
Protected iErrorCode.i
Protected dataType.l
Protected *Buffer=#Null
Protected iBufferSize.i=0
deviceInfoData\cbSize = SizeOf(SP_DEVINFO_DATA)
iStatus = SetupDiEnumDeviceInfo_(iDevInfo, iIndex, @deviceInfoData)
If iStatus=#False
iErrorCode=GetLastError_()
If iErrorCode=#ERROR_NO_MORE_ITEMS
DebugPrint( 2, ~"\nNo more devices.\n")
Else
DebugPrint( 1, "SetupDiEnumDeviceInfo failed with error: "+Str(iErrorCode)+~"\n")
EndIf
ProcedureReturn #False
EndIf
; We won't know the size of the HardwareID buffer until we call
; this function. So call it with a null To begin with, and then
; use the required buffer size to alloc the necessary space.
; Keep calling we have success Or an unknown failure.
iStatus=SetupDiGetDeviceRegistryProperty_(iDevInfo, @deviceInfoData, #SPDRP_HARDWAREID, @dataType, *Buffer, iBufferSize, @iBufferSize)
If iStatus=#False
iErrorCode=GetLastError_()
If iErrorCode<>#ERROR_INSUFFICIENT_BUFFER
If iErrorCode=#ERROR_INVALID_DATA
; May be a Legacy Device With no HardwareID. Continue.
ProcedureReturn #True
Else
DebugPrint( 1, "SetupDiGetDeviceInterfaceDetail failed with error: "+Str(iErrorCode)+~"\n")
ProcedureReturn #False
EndIf
EndIf
EndIf
; We need To change the buffer size.
*Buffer=AllocateMemory(iBufferSize)
iStatus=SetupDiGetDeviceRegistryProperty_(iDevInfo, @deviceInfoData, #SPDRP_HARDWAREID, @dataType, *Buffer, iBufferSize, @iBufferSize)
If iStatus=#False
iErrorCode=GetLastError_()
If iErrorCode=#ERROR_INVALID_DATA
; May be a Legacy Device With no HardwareID. Continue.
ProcedureReturn #True
Else
DebugPrint( 1, "SetupDiGetDeviceInterfaceDetail failed with error: "+Str(iErrorCode)+~"\n")
ProcedureReturn #False
EndIf
EndIf
DebugPrint( 1, ~"\n\nDevice ID: "+PeekS(*Buffer)+~"\n")
If *Buffer
FreeMemory(*Buffer)
EndIf
ProcedureReturn #True
EndProcedure
Procedure GetMediaType(hDevice.i, *cdTypeString.STRING)
Protected iStatus.i
Protected Dim buffer.a(2048) ; // Must be big enough hold DEVICE_MEDIA_INFO
Protected returned.i
Protected *mediaTypes.GET_MEDIA_TYPES
Protected iTmp.i
; Get the Media type.
iStatus=DeviceIoControl_(hDevice,
#IOCTL_STORAGE_GET_MEDIA_TYPES_EX,
#Null,
0,
@buffer(),
ArraySize(buffer()),
@returned,
#False)
If Not iStatus
iTmp=GetLastError_()
DebugPrint( 2, "IOCTL_STORAGE_GET_MEDIA_TYPES_EX failed with error code "+Str(iTmp)+~"\n\n")
ProcedureReturn 0
EndIf
*mediaTypes=@buffer()
Select *mediaTypes\DeviceType
Case #FILE_DEVICE_CD_ROM
*cdTypeString\s="CD-ROM"
Case #FILE_DEVICE_DVD
*cdTypeString\s="DVD"
Default
*cdTypeString\s="Unknown"
EndSelect
ProcedureReturn *mediaTypes\DeviceType
EndProcedure
Procedure PrintError(ErrorCode.i)
; Routine Description: Prints formated error message
; Arguments: ErrorCode - Error code To print
; Return Value: None
Protected count.i
Protected Dim errorBuffer.a(80)
Protected iTmp.i
count=FormatMessage_(#FORMAT_MESSAGE_FROM_SYSTEM, #Null, ErrorCode, 0, @errorBuffer(0), 80, #Null)
If count <> 0
DebugPrint( 1, PeekS(@errorBuffer(0))+~"\n")
Else
iTmp=GetLastError_()
DebugPrint( 1, "Format message failed. Error: "+Str(iTmp)+~"\n")
EndIf
EndProcedure
Procedure PrintSenseInfo(*sptwb.SCSI_PASS_THROUGH_WITH_BUFFERS)
; Routine Description: Prints the SCSI status And Sense Info from the device
; Arguments: sptwb - Pass Through buffer that contains the Sense info
; Return Value: None
Protected iCnt.i
Protected sTmp.s
DebugPrint( 1, "Scsi status: "+HexL(*sptwb\spt\ScsiStatus, 2)+~"h\n\n")
If *sptwb\spt\SenseInfoLength=0
ProcedureReturn
EndIf
DebugPrint( 3, ~"Sense Info -- consult SCSI spec for details\n")
DebugPrint( 3, ~"-------------------------------------------------------------\n")
For iCnt=0 To *sptwb\spt\SenseInfoLength-1
DebugPrint( 3, HexL(*sptwb\ucSenseBuf[iCnt], 2)+" ")
Next
DebugPrint( 1, ~"\n\n")
EndProcedure
Procedure PrintDataBuffer(*DataBuffer, iBufferLength.i)
; Routine Description: Prints the formated Data buffer
; Arguments: DataBuffer - Buffer To be printed
; BufferLength - Length of the buffer
; Return Value: None
Protected iCnt.i
Protected sTmp.s
DebugPrint( 3, ~" 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\n")
DebugPrint( 3, ~" ---------------------------------------------------------------\n")
sTmp=""
For iCnt=0 To iBufferLength-1
If iCnt % 16 = 0
DebugPrint( 3, " "+HexL(iCnt, 3)+" ")
EndIf
DebugPrint( 3, HexL(PeekA(*DataBuffer+iCnt), 2)+" ")
If ((iCnt+1) % 8 = 0)
DebugPrint( 3, " ")
EndIf
If ((iCnt+1) % 16 = 0)
DebugPrint( 3, ~"\n")
sTmp=""
EndIf
Next
DebugPrint( 3, ~"\n\n")
EndProcedure
Procedure PrintStatusResults(iStatus.i, iReturned.i, *sptwb.SCSI_PASS_THROUGH_WITH_BUFFERS)
; Routine Description: Prints the SCSI Inquiry Data from the device
; Arguments: Status - Status of the DeviceIOControl
; Returned - Number of bytes returned
; Psptwb - SCSI pass through Structure
; Return Value: None
Protected iTmp.i
Protected iCnt.i
Protected errorCode.i
Protected devType.u
DebugPrint( 1, ~"\nInquiry Data from Pass Through\n")
DebugPrint( 1, ~"------------------------------\n")
If Not iStatus
iTmp=GetLastError_()
DebugPrint( 1, "Error: "+Str(iTmp))
PrintError(errorCode)
ProcedureReturn
EndIf
If *sptwb\spt\ScsiStatus
PrintSenseInfo(*sptwb)
ProcedureReturn
Else
devType=*sptwb\ucDataBuf[0] & $1f
; Our Device Table can handle only 16 devices.
DebugPrint( 1, "Device Type: "+DeviceType(devType)+" (0x"+Hex(devType)+~")\n")
DebugPrint( 1, "Vendor ID : ")
For iCnt=8 To 15
DebugPrint( 1, Chr(*sptwb\ucDataBuf[iCnt]))
Next
DebugPrint( 1, ~"\nProduct ID : ")
For iCnt=16 To 31
DebugPrint( 1, Chr(*sptwb\ucDataBuf[iCnt]))
Next
DebugPrint( 1, ~"\nProduct Rev: ")
For iCnt=32 To 35
DebugPrint( 1, Chr(*sptwb\ucDataBuf[iCnt]))
Next
DebugPrint( 1, ~"\nVendor Str : ")
For iCnt=36 To 55
DebugPrint( 1, Chr(*sptwb\ucDataBuf[iCnt]))
Next
DebugPrint( 1, ~"\n\n")
DebugPrint( 3, "Scsi status: "+HexL(*sptwb\spt\ScsiStatus, 2)+"h , Bytes returned: "+Hex(iReturned) + "h, ")
DebugPrint( 3, "Data buffer length: "+Hex(*sptwb\spt\DataTransferLength)+~"h\n\n\n")
PrintDataBuffer(@*sptwb\ucDataBuf[0], 192)
EndIf
EndProcedure
Procedure PrintCapResults(iStatus.i, iReturned.i, *sptwb.SCSI_PASS_THROUGH_WITH_BUFFERS)
; Routine Description: Prints the CD Drive Capabilities
; Arguments: Status - Status of the DeviceIOControl
; Returned - Number of bytes returned
; Psptwb - SCSI pass through Structure
; Return Value: None
Protected iTmp.i
Protected CDReader.a
Protected CDwriter.a
Protected DVDReader.a
Protected DVDwriter.a
DebugPrint( 1, ~"\nCD Drive Capabilities from Pass Through\n")
DebugPrint( 1, ~"---------------------------------------\n")
If Not iStatus
iTmp=GetLastError_()
DebugPrint( 1, "Error: "+Str(iTmp))
PrintError(iTmp)
ProcedureReturn
EndIf
If (*sptwb\spt\ScsiStatus)
PrintSenseInfo(*sptwb)
ProcedureReturn
Else
; Notes:
; 1. The header of 6-byte MODE commands is 4 bytes long.
; 2. No Block Descriptors returned before parameter page As was specified when building the Mode command.
; 3. First two bytes of a parameter page are the Page Code And Page Length bytes.
; Therefore, our useful Data starts at the 7th byte in the Data buffer.
CDReader=( *sptwb\ucDataBuf[6] & $01) | (*sptwb\ucDataBuf[6] & $02)
If (CDReader)
DebugPrint( 1, ~"CD Reader : Yes\n")
DebugPrint( 1, " CD-R disc : "+YesNo(*sptwb\ucDataBuf[6] & $01)+~"\n")
DebugPrint( 1, " CD-RW disc: "+YesNo(*sptwb\ucDataBuf[6] & $02)+~"\n")
Else
DebugPrint( 1, ~"CD Reader : No\n")
EndIf
CDwriter=(*sptwb\ucDataBuf[7] & $01) | (*sptwb\ucDataBuf[7] & $02)
If (CDwriter)
DebugPrint( 1, ~"\nCD writer : Yes\n")
DebugPrint( 1, " CD-R disc : "+YesNo(*sptwb\ucDataBuf[7] & $01)+~"\n")
DebugPrint( 1, " CD-RW disc: "+YesNo(*sptwb\ucDataBuf[7] & $02)+~"\n")
Else
DebugPrint( 1, ~"\nCD writer : No\n")
EndIf
DVDReader=( *sptwb\ucDataBuf[6] & $08) | ( *sptwb\ucDataBuf[6] & $10) | (*sptwb\ucDataBuf[6] & $20)
If (DVDReader)
DebugPrint( 1, ~"\nDVD Reader : Yes\n")
DebugPrint( 1, " DVD-ROM disc: "+YesNo(*sptwb\ucDataBuf[6] & $08)+~"\n")
DebugPrint( 1, " DVD-R disc : "+YesNo(*sptwb\ucDataBuf[6] & $10)+~"\n")
DebugPrint( 1, " DVD-RAM disc: "+YesNo(*sptwb\ucDataBuf[6] & $20)+~"\n")
Else
DebugPrint( 1, ~"\nDVD Reader : No\n")
EndIf
DVDwriter=( *sptwb\ucDataBuf[7] & $10) | ( *sptwb\ucDataBuf[7] & $20)
If (DVDwriter)
DebugPrint( 1, ~"\nDVD writer : Yes\n")
DebugPrint( 1, " DVD-R disc : "+YesNo(*sptwb\ucDataBuf[7] & $10)+~"\n")
DebugPrint( 1, " DVD-RAM disc: "+YesNo(*sptwb\ucDataBuf[7] & $20)+~"\n")
Else
DebugPrint( 1, ~"\nDVD writer : No\n")
EndIf
DebugPrint( 1, ~"\n\n")
DebugPrint( 3, "Scsi status: "+HexL(*sptwb\spt\ScsiStatus, 2)+"h, Bytes returned: "+Hex(iReturned)+"h, ")
DebugPrint( 3, "Data buffer length: "+Hex(*sptwb\spt\DataTransferLength)+~"h\n\n\n")
PrintDataBuffer(@*sptwb\ucDataBuf[0], 192)
DebugPrint( 1, ~"\n\n")
EndIf
EndProcedure
Procedure GetDeviceProperty(iIntDevInfo.i, iIndex.i)
; Routine Description: This routine enumerates the CD devices using the Device Interface
; GUID CdRomClassGuid. Gets the Adapter & Device property from the port
; driver. Then sends IOCTL through SPTI To get the device Inquiry Data.
; Arguments: IntDevInfo - Handles To the Interface device information List
; Index - Device member
; Return Value: TRUE / FALSE. This decides whether To Continue Or Not
Protected interfaceData.SP_DEVICE_INTERFACE_DATA
Protected iStatus.i
Protected iErrorCode.i
Protected iReqSize.i
Protected iInterfaceDetailDataSize.i
Protected *InterfaceDetailData.SP_DEVICE_INTERFACE_DETAIL_DATA=#Null
Protected hDevice.i
Protected query.STORAGE_PROPERTY_QUERY
Protected *adpDesc.STORAGE_ADAPTER_DESCRIPTOR
Protected Dim outBuf.a(512)
Protected iReturnedLength.i
Protected iCdType.i=0
Protected CdTypeString.STRING
Protected *devDesc.STORAGE_DEVICE_DESCRIPTOR
Protected iCnt.i
Protected iTmp.i
Protected sptwb.SCSI_PASS_THROUGH_WITH_BUFFERS
Protected iLength.i
Protected iReturned.i
interfaceData\cbSize=SizeOf(SP_DEVICE_INTERFACE_DATA)
iStatus=SetupDiEnumDeviceInterfaces_(iIntDevInfo, ; Interface Device Info handle
0, ; Device Info Data
?GUID_DEVINTERFACE_CDROM, ; Interface registered by driver
iIndex, ; Member
@interfaceData) ; Device Interface Data
If iStatus=#False
iErrorCode=GetLastError_()
If iErrorCode=#ERROR_NO_MORE_ITEMS
DebugPrint( 2, ~"\nNo more interfaces\n")
Else
DebugPrint( 1, "SetupDiEnumDeviceInterfaces failed with error: "+Str(iErrorCode)+~"\n")
EndIf
ProcedureReturn #False
EndIf
; Find out required buffer size, so pass NULL
iStatus=SetupDiGetDeviceInterfaceDetail_(iIntDevInfo, ; Interface Device info handle
@interfaceData, ; Interface Data for the event class
#Null, ; Checking for buffer size
0, ; Checking for buffer size
@iReqSize, ; Buffer size required to get the detail Data
#Null) ; Checking for buffer size
; This call returns ERROR_INSUFFICIENT_BUFFER With reqSize
; set To the required buffer size. Ignore the above error And
; pass a bigger buffer To get the detail Data
If iStatus=#False
iErrorCode=GetLastError_()
If iErrorCode<>#ERROR_INSUFFICIENT_BUFFER
DebugPrint( 1, "SetupDiGetDeviceInterfaceDetail failed with error: "+Str(iErrorCode)+~"\n")
ProcedureReturn #False
EndIf
EndIf
; Allocate memory To get the Interface detail Data
; This contains the devicepath we need To open the device
iInterfaceDetailDataSize=iReqSize
*InterfaceDetailData=AllocateMemory(iInterfaceDetailDataSize)
If Not *InterfaceDetailData
DebugPrint( 1, ~"Unable to allocate memory to get the interface detail data.\n")
ProcedureReturn #False
EndIf
*InterfaceDetailData\cbSize=4+SizeOf(CHARACTER) ; SizeOf(SP_DEVICE_INTERFACE_DETAIL_DATA) >> 4Byte Long + Size of 1 Char
iStatus=SetupDiGetDeviceInterfaceDetail_(iIntDevInfo, ; Interface Device info handle
@interfaceData, ; Interface Data For the event class
*InterfaceDetailData, ; Interface detail Data
iInterfaceDetailDataSize, ; Interface detail Data size
@iReqSize, ; Buffer size required To get the detail Data
#Null) ; Interface device info
If iStatus=#False
iTmp=GetLastError_()
DebugPrint( 1, "Error in SetupDiGetDeviceInterfaceDetail failed with error: "+Str(iTmp)+~"\n")
ProcedureReturn #False
EndIf
; Now we have the device path. Open the device Interface
; To send Pass Through command
DebugPrint( 2, "Interface: "+ *InterfaceDetailData\DevicePath+~"\n")
hDevice=CreateFile_(*InterfaceDetailData\DevicePath, ; device Interface name
#GENERIC_READ | #GENERIC_WRITE, ; dwDesiredAccess
#FILE_SHARE_READ | #FILE_SHARE_WRITE, ; dwShareMode
#Null, ; lpSecurityAttributes
#OPEN_EXISTING, ; dwCreationDistribution
0, ; dwFlagsAndAttributes
#Null) ; hTemplateFile
; We have the handle To talk To the device.
; So we can release the interfaceDetailData buffer
FreeMemory(*InterfaceDetailData)
If hDevice=#INVALID_HANDLE_VALUE
iTmp=GetLastError_()
DebugPrint( 1, "CreateFile failed with error: "+Str(iTmp)+~"\n")
ProcedureReturn #True
EndIf
query\PropertyId=#StorageAdapterProperty
query\QueryType= #PropertyStandardQuery
iStatus=DeviceIoControl_(hDevice,
#IOCTL_STORAGE_QUERY_PROPERTY,
@query,
SizeOf(STORAGE_PROPERTY_QUERY),
@outBuf(),
512,
@iReturnedLength,
#Null)
If Not iStatus
iTmp=GetLastError_()
DebugPrint( 1, "IOCTL failed with error code "+Str(iTmp)+~"\n\n")
Else
*adpDesc=@outBuf()
DebugPrint( 1, ~"\nAdapter Properties\n")
DebugPrint( 1, ~"------------------\n")
DebugPrint( 1, "Bus Type : "+BusType(*adpDesc\BusType)+~"\n")
DebugPrint( 1, "Max. Tr. Length: 0x"+Hex(*adpDesc\MaximumTransferLength)+~"\n")
DebugPrint( 1, "Max. Phy. Pages: 0x"+Hex(*adpDesc\MaximumPhysicalPages)+~"\n")
DebugPrint( 1, "Alignment Mask : 0x"+Hex(*adpDesc\AlignmentMask)+~"\n")
iCdType=GetMediaType(hDevice, @CdTypeString)
query\PropertyId=#StorageDeviceProperty
query\QueryType=#PropertyStandardQuery
iStatus=DeviceIoControl_(hDevice,
#IOCTL_STORAGE_QUERY_PROPERTY,
@query,
SizeOf( STORAGE_PROPERTY_QUERY ),
@outBuf(),
512,
@iReturnedLength,
#Null)
If Not iStatus
iTmp=GetLastError_()
DebugPrint( 1, "IOCTL failed with error code "+Str(iTmp)+~"\n\n")
Else
DebugPrint( 1, ~"\nDevice Properties\n")
DebugPrint( 1, ~"-----------------\n")
*devDesc=@outBuf()
; Our device table can handle only 16 devices.
DebugPrint( 1, "SCSI Device Type: "+DeviceType(*devDesc\DeviceType)+" (0x"+Hex(*devDesc\DeviceType)+~")\n")
If iCdType
DebugPrint( 1, "Media Type : "+CdTypeString\s+" (0x"+Hex(iCdType)+~")\n")
EndIf
If *devDesc\DeviceTypeModifier
DebugPrint( 1, "Device Modifier : 0x"+Hex(*devDesc\DeviceTypeModifier)+~"\n")
EndIf
DebugPrint( 1, "Removable Media : "+YesNo(*devDesc\RemovableMedia)+~"\n")
If *devDesc\VendorIdOffset And PeekA(@outBuf()+*devDesc\VendorIdOffset)
DebugPrint( 1, "Vendor ID : " )
iCnt=*devDesc\VendorIdOffset
While PeekA(@outBuf()+iCnt)<>#Null And iCnt<iReturnedLength
DebugPrint( 1, Chr(PeekA(@outBuf()+iCnt)))
iCnt+1
Wend
DebugPrint( 1, ~"\n")
EndIf
If *devDesc\ProductIdOffset And PeekA(@outBuf()+*devDesc\ProductIdOffset)
DebugPrint( 1, "Product ID : " )
iCnt=*devDesc\ProductIdOffset
While PeekA(@outBuf()+iCnt)<>#Null And iCnt<iReturnedLength
DebugPrint( 1, Chr(PeekA(@outBuf()+iCnt)))
iCnt+1
Wend
DebugPrint( 1, ~"\n")
EndIf
If *devDesc\ProductRevisionOffset And PeekA(@outBuf()+*devDesc\ProductRevisionOffset)
DebugPrint( 1, "Product Revision: " )
iCnt=*devDesc\ProductRevisionOffset
While PeekA(@outBuf()+iCnt)<>#Null And iCnt<iReturnedLength
DebugPrint( 1, Chr(PeekA(@outBuf()+iCnt)))
iCnt+1
Wend
DebugPrint( 1, ~"\n")
EndIf
If *devDesc\SerialNumberOffset And PeekA(@outBuf()+*devDesc\SerialNumberOffset)
DebugPrint( 1, "Serial Number : " )
iCnt=*devDesc\SerialNumberOffset
While PeekA(@outBuf()+iCnt)<>#Null And iCnt<iReturnedLength
DebugPrint( 1, Chr(PeekA(@outBuf()+iCnt)))
iCnt+1
Wend
DebugPrint( 1, ~"\n")
EndIf
EndIf
EndIf
FillMemory(@sptwb, SizeOf(SCSI_PASS_THROUGH_WITH_BUFFERS))
sptwb\spt\Length=SizeOf(SCSI_PASS_THROUGH)
sptwb\spt\PathId=0
sptwb\spt\TargetId=1
sptwb\spt\Lun=0
sptwb\spt\CdbLength=#CDB6GENERIC_LENGTH
sptwb\spt\SenseInfoLength=24
sptwb\spt\DataIn=#SCSI_IOCTL_DATA_IN
sptwb\spt\DataTransferLength=192
sptwb\spt\TimeOutValue=2
sptwb\spt\DataBufferOffset=OffsetOf(SCSI_PASS_THROUGH_WITH_BUFFERS\ucDataBuf)
sptwb\spt\SenseInfoOffset=OffsetOf(SCSI_PASS_THROUGH_WITH_BUFFERS\ucSenseBuf)
sptwb\spt\Cdb[0]=#SCSIOP_INQUIRY
sptwb\spt\Cdb[4]=192
iLength=OffsetOf(SCSI_PASS_THROUGH_WITH_BUFFERS\ucDataBuf) + sptwb\spt\DataTransferLength
iStatus=DeviceIoControl_(hDevice,
#IOCTL_SCSI_PASS_THROUGH,
@sptwb,
SizeOf(SCSI_PASS_THROUGH),
@sptwb,
iLength,
@iReturned,
#False)
PrintStatusResults(iStatus, iReturned, @sptwb)
; If device supports SCSI-3, then we can get the CD drive capabilities, i.e. ability To
; Read/write To CD-ROM/R/RW Or/And Read/write To DVD-ROM/R/RW.
; Use the previous spti Structure, only modify the command To "mode sense"
sptwb\spt\Cdb[0]=#SCSIOP_MODE_SENSE
sptwb\spt\Cdb[1]=$08 ; target shall not return any block descriptors
sptwb\spt\Cdb[2]=#MODE_PAGE_CAPABILITIES
iStatus=DeviceIoControl_(hDevice,
#IOCTL_SCSI_PASS_THROUGH,
@sptwb,
SizeOf(SCSI_PASS_THROUGH),
@sptwb,
iLength,
@iReturned,
#False)
PrintCapResults(iStatus, iReturned, @sptwb)
; Close handle the driver
If Not CloseHandle_(hDevice)
DebugPrint( 2, ~"Failed to close device.\n")
EndIf
ProcedureReturn #True
EndProcedure
;- ~ Main
; Routine Description: This is the main function. It takes no arguments from the user.
; Arguments: None
; Return Value: Status
Define hDevInfo.i
Define hIntDevInfo.i
Define iIndex.i
Define iStatus.i
Define iTmp.i
If Not OpenConsole("EnumCD Purebasic")
End(1)
EndIf
hDevInfo = SetupDiGetClassDevs_( ?GUID_DEVCLASS_CDROM, #Null, #Null, #DIGCF_PRESENT ) ; All devices present on system
If hDevInfo = #INVALID_HANDLE_VALUE
iTmp=GetLastError_()
DebugPrint( 1, "SetupDiGetClassDevs failed with error: "+Str(iTmp)+~"\n")
End(1)
EndIf
; Open the device using device Interface registered by the driver
; Get the Interface device information set that contains all devices of event class.
hIntDevInfo = SetupDiGetClassDevs_( ?GUID_DEVINTERFACE_CDROM, ; https://msdn.microsoft.com/en-us/library/windows/hardware/ff537883%28v=vs.85%29.aspx
#Null, ; Enumerator
#Null, ; Parent Window
(#DIGCF_PRESENT | #DIGCF_DEVICEINTERFACE )); Only Devices present & Interface class
If hDevInfo=#INVALID_HANDLE_VALUE
iTmp=GetLastError_()
DebugPrint( 1, "SetupDiGetClassDevs failed with error: "+Str(iTmp)+~"\n")
End(1)
EndIf
; Enumerate all the CD devices
iIndex=0
While (#True)
DebugPrint( 1, "Properties for Device "+Str(iIndex+1))
iStatus=GetRegistryProperty( hDevInfo, iIndex )
If iStatus=#False
Break
EndIf
iStatus=GetDeviceProperty( hIntDevInfo, iIndex )
If iStatus=#False
Break
EndIf
iIndex+1
Wend
DebugPrint( 1, ~"\r *** End of Device List *** \n")
SetupDiDestroyDeviceInfoList_(hDevInfo)
SetupDiDestroyDeviceInfoList_(hIntDevInfo)
CompilerIf Not #PB_Editor_CreateExecutable
PrintN("Press return to exit")
Input()
CompilerEndIf
CloseConsole()
End(0)