Detect CD tray status

Hi everybody,
I'd need a code to detect if the Cd tray is open or closed. I think this is possible, because a while ago I found the following: http://www.technical-recipes.com/2014/reading-the-status-of-dvd-drives-in-c/
The problem is that I can't make this code to work. I created a new project in Visual Studio 2015, pasted the code there and rewrote a few (many) things. Here's my code:

#include "stdafx.h"
#include <Windows.h>
#include <string>
#include <iostream>
#include <string>


#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

using namespace std;

typedef unsigned short USHORT;
typedef unsigned char UCHAR;
typedef unsigned long ULONG;
typedef void* PVOID;

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;


// Get the drive letter of the first DVD device encountered
string GetDvdDriveLetter()
{
std::string dvdDriveLetter = "";

DWORD drives = GetLogicalDrives();
DWORD dwSize = MAX_PATH;
char szLogicalDrives[MAX_PATH] = { 0 };
DWORD dwResult = GetLogicalDriveStrings(dwSize, (LPTSTR)szLogicalDrives);

// DWORD dwResult = GetLogicalDriveStrings(dwSize, szLogicalDrives);

if (dwResult > 0 && dwResult <= MAX_PATH)
{
char* szSingleDrive = szLogicalDrives;

while (*szSingleDrive)
{
const UINT driveType = GetDriveType((LPTSTR)szSingleDrive);

if (driveType == 5)
{
dvdDriveLetter = szSingleDrive;
dvdDriveLetter = dvdDriveLetter.substr(0, 2);
break;
}

// Get the next drive
szSingleDrive += strlen(szSingleDrive) + 1;
}
}

return dvdDriveLetter;
}

int GetDvdStatus()
{
const std::string dvdDriveLetter = GetDvdDriveLetter();

if (dvdDriveLetter.empty()) return -1;

const std::string strDvdPath = "\\\\.\\"
+ dvdDriveLetter;

HANDLE hDevice; // handle to the drive to be examined
int iResult = -1; // 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.

hDevice = CreateFile((LPTSTR)strDvdPath.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 we cannot access the DVD drive
if (hDevice == INVALID_HANDLE_VALUE)
{
return -1;
}

// A check to see determine if a DVD has been inserted into the drive only when iResult = 1.
// This will do it more quickly than by sending target commands to the SCSI
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);

// Don't request the tray status as we often don't need it
if (iResult == 1) return 2;

hDevice = CreateFile((LPTSTR)strDvdPath.c_str(),
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_READONLY,
NULL);

if (hDevice == INVALID_HANDLE_VALUE)
{
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 - request tray status for drive
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) iResult = 0; // DVD tray closed
else if (DataBuf[5] == 1) iResult = 1; // DVD tray open
else return iResult = 2; // DVD tray closed, media present
}

return iResult;
}

int main()
{
// Uses the following information to obtain the status of a DVD/CD-ROM drive:
// 1. GetLogicalDriveStrings() to list all logical drives
// 2. GetDriveType() to obtain the type of drive
// 3. DeviceIoControl() to obtain the device status
//
switch (GetDvdStatus())
{
case 0:
std::cout << "DVD tray closed, no media" << std::endl;
break;
case 1:
std::cout << "DVD tray open" << std::endl;
break;
case 2:
std::cout << "DVD tray closed, media present" << std::endl;
break;
default:
std::cout << "Drive not ready" << std::endl;
break;
}

std::cout << GetDvdDriveLetter() << std::endl;
getchar();
return 0;
}

Now the project is built, but when I run the app, it don't return anything, saying "Drive not ready". I can't figure out what's happening.
If somebody could find what must be done to get the proper result, according to the state of the CD tray, please tell me.
Thanks in advance
Program ran fine for me.
PS I removed line 1 and 219.

C:\Dev-Cpp\Code>test
DVD tray closed, no media
D:


C:\Dev-Cpp\Code>test
DVD tray open
D:
Hi Samuel,

Thanks for your reply.
I'm surprised it's working well for you. Then maybe there is something wrong on my computer? What could be?
Maybe I should test it onto another computer?

Thanks again very much.
I used a Dell laptop at work and it was great, at home, I'm using a dell laptop and it says closed even when opened.

At work I was using windows 7, at home windows 10.
There is nothing wrong with your computer. Compiler is the main cause for this problem.
I used the same version of Dev-cpp on both my win7 and win10. Why is the compiler the problem ?
Hi SakurasouBusters,

I'm using Windows 10 Pro. For me it says always "Drive not ready". I thought that maybe some piece of software is missing (something like Visual C++ Redistributable or such), but I'm not sure if I'm right and have no idea how to start to find out if I'm right. Could this be the cause?
What you mean by "Compiler is the main cause for this problem"?
Many thanks.
I found the solution. If anyone is interested, here is what to do: in the Resource View, I had to right click the project and click Properties. In the Properties window, go to Configuration Properties -> General and set Character Set to Not set. That's all.
Thanks everyone. Now the issue is fixed.
I'm assuming that's an option in MSVC?
Why does it make any difference? Is it a bug?

IIRC Windows includes wide-character versions of most functions accepting strings as well as legacy ANSI APIs -- but I don't see anything that indicates that might be an issue?
Last edited on
I have no enough experience with C++ to can say why this makes difference, but, with a bit of help, I found the following description on Stack Overflow: http://stackoverflow.com/questions/3924926/cannot-convert-parameter-1-from-char-to-lpcwstr
Last edited on
Topic archived. No new replies allowed.