Enumerates all CD-ROM drives and allows them to be opened and closed at the touch of a button.
I got bored the other day and was looking through the Platform SDK Documentation and came across the DeviceIoControl API function. I came upon the IOCTL_STORAGE_EJECT_MEDIA control code and it got my interest. I decided to make a small application that made use of DeviceIoControl and the IOCTL_STORAGE_EJECT_MEDIA and IOCTL_STORAGE_LOAD_MEDIA control codes.
This project was done with MFC in Microsoft Visual C++ 6.0 and is compatable with Windows NT/2000/XP.
Before getting into the nitty gritty of how to open and close a CD-ROM drive (which isn’t all that hard actually) I’m going to show you how to enumerate all CD-ROM devices on the system and display them. This part is really only applicable to this program or anything that will impliment this in the exact same way (but it’s a good lesson in how to use CListCtrl).
First create a new MFCApp Wizard (exe) project. For the purposes of this project we will be using a a dialog based application without any context based help or any other extra stuff.
Open up the main dialog and add a List Control to the dialog. Right click the control and click ‘Properties’. Click the ‘Styles’ tab and check the select the ‘List’ option in the ‘View’ drop down list and close the ‘Properties’ window. Now right click the List Control again and select ‘Class Wizard’. In the window that appears click the ‘Member Variables’ tab and select the Control ID for the List Control you just created and then click the ‘Add Variable’ button. Enter the name of the variable and click ‘OK’. You have now just created a CListCtrl which is mapped to the List Control you placed in the Dialog.
To add code to enumerate the CD-ROM drives go to the dialog class’ OnInitDialog() function and add the following code before the return TRUE; statement.
CHAR lpszDrive[4];
INT iItem=1;
for (CHAR cDrive='A';cDrive<'Z';cDrive++)
{
sprintf(lpszDrive,"%c:\\",cDrive);
//gets the drive type, if its a CD-ROM insert it into the list
if (GetDriveType(lpszDrive)==DRIVE_CDROM)
{
m_DriveList.InsertItem(iItem,lpszDrive);
iItem++;
}
}
m_DriveList is the name I used for the CListCtrl, replace that with whatever you used (from now on I will just assume you know what to replace with your own variable/class names).
Now we’re going to get into the actual GUI stuff. In the dialog editor change the ID (right click the button and click ‘Properties’ and then enter in the new ID) of the default ‘Cancel’ button (this is because if we use the default ID then we will make it impossible to properly close the program). Double click the ‘OK’ button and the ‘Add Member Function’ and click ‘OK’ to add a function to handle a click event to the ‘OK’ button. In the OnOK() function add the following code (make sure to remove the call to CDialog::OnOK() at the end).
void CCDDriveControlDlg::OnOK()
{
//get the position of the first selected item
POSITION pos=m_DriveList.GetFirstSelectedItemPosition();
//if there is a selected item
if (pos)
{
//while there are selected items
while (pos)
{
//get the list item number of the next selected item
int iItem=m_DriveList.GetNextSelectedItem(pos);
CHAR lpszDrive[4];
//get the drive letter from the selected items text
m_DriveList.GetItemText(iItem,0,lpszDrive,4);
//open the drive
if (!EjectMedia(lpszDrive[0]))
{
MessageBox("Could not open CD Drive.","Error",MB_OK);
}
}
}
}
Now double click the ‘Cancel’ button and click ‘OK’ in the Add Member Function window and add the following code to the new function.
void CCDDriveControlDlg::OnButtonClose()
{
//get the position of the first selected item
POSITION pos=m_DriveList.GetFirstSelectedItemPosition();
//if there is a selected item
if (pos)
{
//while there are selected items
while (pos)
{
//get the list item number of the next selected item
int iItem=m_DriveList.GetNextSelectedItem(pos);
CHAR lpszDrive[4];
//get the drive letter from the selected items text
m_DriveList.GetItemText(iItem,0,lpszDrive,4);
//close the drive
if (!LoadMedia(lpszDrive[0]))
{
MessageBox("Could not close CD Drive.","Error",MB_OK);
}
}
}
}
At this point you can rename the ‘OK’ and ‘Cancel’ buttons if you’d like (I did, but that doesn’t mean you have to).
Now for the meat of the project. The EjectMedia() and LoadMedia() functions.
First the EjectMedia() function.
BOOL EjectMedia(DWORD dwDrive)
{
HANDLE hDevice;
CHAR lpszDeviceName[7];
DWORD dwBytesReturned;
DWORD dwError;
BOOL bResult;
//take the drive letter and put it in the format used in CreateFile
sprintf(lpszDeviceName,"\\\\.\\%c:",dwDrive);
//get a handle to the device, the parameters used here must be used in order for this to work
hDevice=CreateFile(lpszDeviceName,GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
//if for some reason we couldn't get a handle to the device we will try again using slightly different parameters for CreateFile
if (hDevice==INVALID_HANDLE_VALUE)
{
SetLastError(NO_ERROR);
hDevice=CreateFile(lpszDeviceName,GENERIC_READ,FILE_SHARE_READ|FILE_SHARE_WRITE,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
}
dwError=GetLastError();
//if we have a valid handle to a device call the DeviceIoControl API function using the IOCTL_STORAGE_EJECT_MEDIA control code
if ((hDevice!=INVALID_HANDLE_VALUE)&&(dwError==NO_ERROR))
{
bResult=DeviceIoControl(hDevice,IOCTL_STORAGE_EJECT_MEDIA,0,0,0,0,&dwBytesReturned,0);
}
else
{
bResult=FALSE;
}
//close the handle
if (hDevice!=INVALID_HANDLE_VALUE)
{
CloseHandle(hDevice);
}
return bResult;
}
BOOL LoadMedia(DWORD dwDrive)
{
HANDLE hDevice;
CHAR lpszDeviceName[7];
DWORD dwBytesReturned;
DWORD dwError;
BOOL bResult;
//take the drive letter and put it in the format used in CreateFile
sprintf(lpszDeviceName,"\\\\.\\%c:",dwDrive);
//get a handle to the device, the parameters used here must be used in order for this to work
hDevice=CreateFile(lpszDeviceName,GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
//if for some reason we couldn't get a handle to the device we will try again using slightly different parameters for CreateFile
if (hDevice==INVALID_HANDLE_VALUE)
{
SetLastError(NO_ERROR);
hDevice=CreateFile(lpszDeviceName,GENERIC_READ,FILE_SHARE_READ|FILE_SHARE_WRITE,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
}
dwError=GetLastError();
//if we have a valid handle to a device call the DeviceIoControl API function using the IOCTL_STORAGE_LOAD_MEDIA control code
if ((hDevice!=INVALID_HANDLE_VALUE)&&(dwError==NO_ERROR))
{
bResult=DeviceIoControl(hDevice,IOCTL_STORAGE_LOAD_MEDIA,0,0,0,0,&dwBytesReturned,0);
}
else
{
bResult=FALSE;
}
//close the handle
if (hDevice!=INVALID_HANDLE_VALUE)
{
CloseHandle(hDevice);
}
return bResult;
}
Add the above functions to the end of the source file for the dialog and #include stdio.h and winioctl.h in the stdafx.h file.