Sometimes you would like to know if the tray of your CD/DVD/Bluray drive is open, closed and if a media is inserted. The following example shows how to archive that with C++. The return codes of the function GetDriveStatus() are:
1 2 3 4 |
-1 DRIVE_NOT_READY 0 DRIVE_CLOSED_NO_MEDIA 1 DRIVE_OPEN 2 DRIVE_CLOSED_MEDIA_PRESENT |
And now the example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
//** Defines taken from ntddscsi.h in MS Windows DDK CD #define SCSI_IOCTL_DATA_OUT 0 //Give data to SCSI device (e.g. for writing) #define SCSI_IOCTL_DATA_IN 1 //Get data from SCSI device (e.g. for reading) #define SCSI_IOCTL_DATA_UNSPECIFIED 2 //No data (e.g. for ejecting) #define MAX_SENSE_LEN 18 //Sense data max length #define IOCTL_SCSI_PASS_THROUGH_DIRECT 0x4D014 typedef struct _SCSI_PASS_THROUGH_DIRECT { USHORT Length; UCHAR ScsiStatus; UCHAR PathId; UCHAR TargetId; UCHAR Lun; UCHAR CdbLength; UCHAR SenseInfoLength; UCHAR DataIn; ULONG DataTransferLength; ULONG TimeOutValue; PVOID DataBuffer; ULONG SenseInfoOffset; UCHAR Cdb[16]; }SCSI_PASS_THROUGH_DIRECT, *PSCSI_PASS_THROUGH_DIRECT; typedef struct _SCSI_PASS_THROUGH_DIRECT_AND_SENSE_BUFFER { SCSI_PASS_THROUGH_DIRECT sptd; UCHAR SenseBuf[MAX_SENSE_LEN]; }T_SPDT_SBUF; int GetDriveStatus(const CStdString &strPath, bool bStatusEx) { HANDLE hDevice; // handle to the drive to be examined int iResult; // results flag ULONG ulChanges=0; DWORD dwBytesReturned; T_SPDT_SBUF sptd_sb; //SCSI Pass Through Direct variable. byte DataBuf[8]; //Buffer for holding data to/from drive. CLog::Log(LOGDEBUG, __FUNCTION__": Requesting status for drive %s.", strPath.c_str()); hDevice = CreateFile( strPath.c_str(), // drive 0, // no access to the drive FILE_SHARE_READ, // share mode NULL, // default security attributes OPEN_EXISTING, // disposition FILE_ATTRIBUTE_READONLY, // file attributes NULL); if (hDevice == INVALID_HANDLE_VALUE) // cannot open the drive { CLog::Log(LOGERROR, __FUNCTION__": Failed to CreateFile for %s.", strPath.c_str()); return -1; } CLog::Log(LOGDEBUG, __FUNCTION__": Requesting media status for drive %s.", strPath.c_str()); iResult = DeviceIoControl((HANDLE) hDevice, // handle to device IOCTL_STORAGE_CHECK_VERIFY2, // dwIoControlCode NULL, // lpInBuffer 0, // nInBufferSize &ulChanges, // lpOutBuffer sizeof(ULONG), // nOutBufferSize &dwBytesReturned , // number of bytes returned NULL ); // OVERLAPPED structure CloseHandle(hDevice); if(iResult == 1) return 2; // don't request the tray status as we often doesn't need it if(!bStatusEx) return 0; hDevice = CreateFile( strPath.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); if (hDevice == INVALID_HANDLE_VALUE) { CLog::Log(LOGERROR, __FUNCTION__": Failed to CreateFile2 for %s.", strPath.c_str()); return -1; } sptd_sb.sptd.Length=sizeof(SCSI_PASS_THROUGH_DIRECT); sptd_sb.sptd.PathId=0; sptd_sb.sptd.TargetId=0; sptd_sb.sptd.Lun=0; sptd_sb.sptd.CdbLength=10; sptd_sb.sptd.SenseInfoLength=MAX_SENSE_LEN; sptd_sb.sptd.DataIn=SCSI_IOCTL_DATA_IN; sptd_sb.sptd.DataTransferLength=sizeof(DataBuf); sptd_sb.sptd.TimeOutValue=2; sptd_sb.sptd.DataBuffer=(PVOID)&(DataBuf); sptd_sb.sptd.SenseInfoOffset=sizeof(SCSI_PASS_THROUGH_DIRECT); sptd_sb.sptd.Cdb[0]=0x4a; sptd_sb.sptd.Cdb[1]=1; sptd_sb.sptd.Cdb[2]=0; sptd_sb.sptd.Cdb[3]=0; sptd_sb.sptd.Cdb[4]=0x10; sptd_sb.sptd.Cdb[5]=0; sptd_sb.sptd.Cdb[6]=0; sptd_sb.sptd.Cdb[7]=0; sptd_sb.sptd.Cdb[8]=8; sptd_sb.sptd.Cdb[9]=0; sptd_sb.sptd.Cdb[10]=0; sptd_sb.sptd.Cdb[11]=0; sptd_sb.sptd.Cdb[12]=0; sptd_sb.sptd.Cdb[13]=0; sptd_sb.sptd.Cdb[14]=0; sptd_sb.sptd.Cdb[15]=0; ZeroMemory(DataBuf, 8); ZeroMemory(sptd_sb.SenseBuf, MAX_SENSE_LEN); //Send the command to drive CLog::Log(LOGDEBUG, __FUNCTION__": Requesting tray status for drive %s.", strPath.c_str()); iResult = DeviceIoControl((HANDLE) hDevice, IOCTL_SCSI_PASS_THROUGH_DIRECT, (PVOID)&sptd_sb, (DWORD)sizeof(sptd_sb), (PVOID)&sptd_sb, (DWORD)sizeof(sptd_sb), &dwBytesReturned, NULL); CloseHandle(hDevice); if(iResult) { if(DataBuf[5] == 0) // tray close return 0; else if(DataBuf[5] == 1) // tray open return 1; else return 2; // tray closed, media present } CLog::Log(LOGERROR, __FUNCTION__": Could not determine tray status %d", GetLastError()); return -1; } |
We first check in line 57 with IOCTL_STORAGE_CHECK_VERIFY2 if we have a media in our drive. If this returns 1 we have a medium loaded and we can early return and save another call to DeviceIoControl().
In line 123 we finally request the tray status with a IOCTL_SCSI_PASS_THROUGH_DIRECT call.