Thursday, January 22, 2009

Windows Console Program to Eject CD-ROM

I find the *nix command "eject" very convienent to eject cd/dvd. There is not builtin command in Windows but microsoft has a sample code here http://support.microsoft.com/kb/q165721/ that does just that. Here is the code with few modification i made. The syntax of the command is eject.exe {driver letter} [/c]. The /c switch is to close the drive bay.

/**
http://support.microsoft.com/kb/q165721/
*/
#include
#include
#include
#include
// Prototypes
BOOL EjectVolume(TCHAR cDriveLetter);
HANDLE OpenVolume(TCHAR cDriveLetter);
BOOL LockVolume(HANDLE hVolume);
BOOL DismountVolume(HANDLE hVolume);
BOOL PreventRemovalOfVolume(HANDLE hVolume, BOOL fPrevent);
BOOL AutoEjectVolume(HANDLE hVolume);
BOOL CloseVolume(HANDLE hVolume);
BOOL EJECT = TRUE;
//default is to eject device;
but a command switch can tell program to close cdrom
LPTSTR szVolumeFormat = TEXT("\\\\.\\%c:");
LPTSTR szRootFormat = TEXT("%c:\\");
LPTSTR szErrorFormat = TEXT("Error %d: %s\n");
void ReportError(LPTSTR szMsg)
{
    _tprintf(szErrorFormat, GetLastError(), szMsg);
}
HANDLE OpenVolume(TCHAR cDriveLetter)
{
    HANDLE hVolume;
    UINT uDriveType;
    TCHAR szVolumeName[8];
    TCHAR szRootName[5];
    DWORD dwAccessFlags;
    wsprintf(szRootName, szRootFormat, cDriveLetter);
    uDriveType = GetDriveType(szRootName);
    switch(uDriveType)
    {
        case DRIVE_REMOVABLE:
        dwAccessFlags = GENERIC_READ | GENERIC_WRITE;
        break;
        case DRIVE_CDROM:
        dwAccessFlags = GENERIC_READ;
        break;
        default:
        _tprintf(TEXT("Cannot eject.  Drive type is incorrect.\n"));
        return INVALID_HANDLE_VALUE;
    }
    wsprintf(szVolumeName, szVolumeFormat, cDriveLetter);
    hVolume = CreateFile(   szVolumeName,
    dwAccessFlags,
    FILE_SHARE_READ | FILE_SHARE_WRITE,
    NULL,
    OPEN_EXISTING,
    0,
    NULL );
    if (hVolume == INVALID_HANDLE_VALUE)
    ReportError(TEXT("CreateFile"));
    return hVolume;
}
BOOL CloseVolume(HANDLE hVolume)
{
    return CloseHandle(hVolume);
}
#define LOCK_TIMEOUT        10000       // 10 Seconds (1 s == 1000 ms)
#define LOCK_RETRIES        20
BOOL LockVolume(HANDLE hVolume)
{
    DWORD dwBytesReturned;
    DWORD dwSleepAmount;
    int nTryCount;
    dwSleepAmount = LOCK_TIMEOUT / LOCK_RETRIES;
    // Do this in a loop until a timeout period has expired
    for (nTryCount = 0;
    nTryCount <
    LOCK_RETRIES;
    nTryCount++)
    {
        if (DeviceIoControl(hVolume,
        FSCTL_LOCK_VOLUME,
        NULL, 0,
        NULL, 0,
        &dwBytesReturned,
        NULL))
        return TRUE;
        Sleep(dwSleepAmount);
    }
    return FALSE;
}
BOOL DismountVolume(HANDLE hVolume)
{
    DWORD dwBytesReturned;
    return DeviceIoControl( hVolume,
    FSCTL_DISMOUNT_VOLUME,
    NULL, 0,
    NULL, 0,
    &dwBytesReturned,
    NULL);
}
BOOL PreventRemovalOfVolume(HANDLE hVolume, BOOL fPreventRemoval)
{
    DWORD dwBytesReturned;
    PREVENT_MEDIA_REMOVAL PMRBuffer;
    PMRBuffer.PreventMediaRemoval = fPreventRemoval;
    return DeviceIoControl( hVolume,
    IOCTL_STORAGE_MEDIA_REMOVAL,
    &PMRBuffer, sizeof(PREVENT_MEDIA_REMOVAL),
    NULL, 0,
    &dwBytesReturned,
    NULL);
}
AutoEjectVolume(HANDLE hVolume)
{
    DWORD dwBytesReturned;
    if(EJECT)
    return DeviceIoControl( hVolume,
    //        IOCTL_STORAGE_LOAD_MEDIA ,
    IOCTL_STORAGE_EJECT_MEDIA ,
    NULL, 0,
    NULL, 0,
    &dwBytesReturned,
    NULL);
    return DeviceIoControl( hVolume,
    IOCTL_STORAGE_LOAD_MEDIA ,
    //        IOCTL_STORAGE_EJECT_MEDIA ,
    NULL, 0,
    NULL, 0,
    &dwBytesReturned,
    NULL);
}
BOOL EjectVolume(TCHAR cDriveLetter)
{
    HANDLE hVolume;
    BOOL fRemoveSafely = FALSE;
    BOOL fAutoEject = FALSE;
    // Open the volume.
    hVolume = OpenVolume(cDriveLetter);
    if (hVolume == INVALID_HANDLE_VALUE)
    return FALSE;
    // Lock and dismount the volume.
    if (LockVolume(hVolume) && DismountVolume(hVolume))
    {
        fRemoveSafely = TRUE;
        // Set prevent removal to false and eject the volume.
        if (PreventRemovalOfVolume(hVolume, FALSE) &&
        AutoEjectVolume(hVolume))
        fAutoEject = TRUE;
    }
    // Close the volume so other processes can use the drive.
    if (!CloseVolume(hVolume))
    return FALSE;
    if (fAutoEject)
    if(EJECT)
    printf("Media in Drive %c has been ejected safely.\n",
    cDriveLetter);
    else
    {
        if (fRemoveSafely)
        {
            if(EJECT)
            printf("Media in Drive %c can be safely removed.\n",
            cDriveLetter);
        }
    }
    return TRUE;
}
void Usage()
{
    printf("Usage: Eject [/C] \n");
    printf("When /c is supplied, the program will close CD-ROM bay\n\n");
    return ;
}
void main(int argc, char * argv[])
{
    if (argc <
    2)
    {
        Usage();
        return ;
    }
    if(argc == 3)
    {
        if(strlen(argv[2])>
        1 && argv[2][1] == 'c' || argv[2][1]== 'C')
        {
            EJECT = FALSE;
        }
    }
    if (!EjectVolume(argv[1][0]))
    {
        printf("Failure ejecting drive %c.\n\n", argv[1][0]);
        Usage();
    }
    return ;
}

No comments:

Post a Comment