CD Drive Control

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.

Nathan Pakovskie is an esteemed senior developer and educator in the tech community, best known for his contributions to Geekpedia.com. With a passion for coding and a knack for simplifying complex tech concepts, Nathan has authored several popular tutorials on C# programming, ranging from basic operations to advanced coding techniques. His articles, often characterized by clarity and precision, serve as invaluable resources for both novice and experienced programmers. Beyond his technical expertise, Nathan is an advocate for continuous learning and enjoys exploring emerging technologies in AI and software development. When he’s not coding or writing, Nathan engages in mentoring upcoming developers, emphasizing the importance of both technical skills and creative problem-solving in the ever-evolving world of technology. Specialties: C# Programming, Technical Writing, Software Development, AI Technologies, Educational Outreach

Leave a Reply

Your email address will not be published. Required fields are marked *

Back To Top