AutoHotkey Homepage AutoHotkey Community
Let's help each other out
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Reference implementation for ejecting CD or USB flash drive

 
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions
View previous topic :: View next topic  
Author Message
interiot



Joined: 06 Nov 2005
Posts: 64

PostPosted: Wed Apr 30, 2008 5:54 pm    Post subject: Reference implementation for ejecting CD or USB flash drive Reply with quote

This Microsoft document lays out detailed steps for ejecting a drive. It includes several steps that haven't been mentioned here previously, so I provide this as a complete example implementation.

It's verbose code that can be condensed. Also, you can sometimes leave out the PreventRemovalOfVolume()/AutoEjectVolume() functions, since they apply only to drives that have an internal physical eject mechanism.

Code:
; a direct port of    http://support.microsoft.com/kb/165721


  ; example call
  EjectVolume("E")



; returns true if successful
EjectVolume(cDriveLetter)
{
    if (!(hVolume := OpenVolume(cDriveLetter)))
        return 0

    if (LockVolume(hVolume) && DismountVolume(hVolume)) {
        fRemoveSafely := 1

        ; set PreventRemoval to false, and eject the volume
        if (PreventRemovalOfVolume(hVolume, 0) && AutoEjectVolume(hVolume))
            fAutoEject := 1
    }

    ; close the volume so other processes can use the drive.
    if (! DllCall("CloseHandle", "uint", hVolume))
        return 0

    if (fAutoEject)
        MsgBox,Media in drive %cDriveLetter% has been ejected safely.
    else if (fRemoveSafely)
        MsgBox,Media in drive %cDriveLetter% can be safely removed.
    else {
        MsgBox,Unable to eject drive %cDriveLetter%.  Close all files and try again.
        return 0
    }

    return 1
}


; Open a handle so we can do work on the volume.
; (doesn't check to make sure we have exlusive access; use LockVolume() for that)
OpenVolume(cDriveLetter)
{
    szRootName := cDriveLetter . ":\"
    uDriveType := DllCall("GetDriveType", "str", szRootName)

    if (uDriveType == 2) {      ; DRIVE_REMOVABLE
        dwAccessFlags := 0x80000000 | 0x40000000    ; GENERIC_READ | GENERIC_WRITE
    } else if (uDriveType == 5) {   ; DRIVE_CDROM
        dwAccessFlags := 0x80000000         ; GENERIC_READ
    } else {
        MsgBox, Cannot eject %szRootName%.  Drive type is incorrect.
        return
    }

    szVolumeName := "\\.\" . cDriveLetter . ":"
    hVolume := DllCall("CreateFile"
                , "str",  szVolumeName
                , "uint", dwAccessFlags
                , "uint", 0x1 | 0x2     ; FILE_SHARE_READ | FILE_SHARE_WRITE
                , "uint", 0
                , "uint", OPEN_EXISTING := 3
                , "uint", 0
                , "uint", 0 )
    if (hVolume <= 0)
        return
    return hVolume
}


; Locks a volume. A locked volume can be accessed only through handles to the file object (*hDevice) that locks the volume.
; This operation fails if there are any open files on the volume. Conversely, success of this operation indicates there are no open files.
; This operation is useful for applications that need exclusive access to a volume for a period of time.
; The system flushes all cached data to the volume before locking it.
LockVolume(hVolume)
{
    LOCK_TIMEOUT := 5000       ; 5 Seconds
    LOCK_RETRIES := 20

    dwSleepAmount := LOCK_TIMEOUT / LOCK_RETRIES

    ; loop until the timeout period has expired
    Loop, %LOCK_RETRIES% {
        if (DeviceIoControl(hVolume, FSCTL_LOCK_VOLUME := 0x90018))
            return 1
       
        Sleep,dwSleepAmount
    }
    return 0
}


; unmount
DismountVolume(hVolume)
{
    return DeviceIoControl(hVolume, FSCTL_DISMOUNT_VOLUME := 0x90020)
}


; Enables or disables the mechanism that ejects media, for those devices possessing that locking capability.
; Drivers keep track of the lock count, and thereby allow ANY caller to unlock a drive (not just the one that locked it).  See more at http://msdn.microsoft.com/en-us/library/ms803649.aspx
PreventRemovalOfVolume(hVolume, fPreventRemoval)
{
    ; struct PREVENT_MEDIA_REMOVAL is just a single bool
    PMRBuffer := chr(fPreventRemoval ? 1 : 0)

    return DeviceIoControl(hVolume, IOCTL_STORAGE_MEDIA_REMOVAL := 0x2D4804, PMRBuffer, 1)
}


; Causes the device to eject the media, if the device supports ejection capabilities.
AutoEjectVolume(hVolume)
{
    return DeviceIoControl(hVolume, IOCTL_STORAGE_EJECT_MEDIA := 0x2D4808)
}


; DeviceIoControl() is a general-purpose interface that can send control codes
; to a variety of devices. Each control code represents an operation for the
; driver to perform. For example, a control code can ask a device driver to
; return information about the corresponding device, or direct the driver to
; carry out an action on the device, such as formatting a disk.
;
; For a list of IOCTLs, see
;    http://msdn.microsoft.com/en-us/library/dexter.functioncatall.ioc.aspx
DeviceIoControl(hDevice, dwIoControlCode, lpInBuffer=0, nInBufferSize=0, ByRef lpOutBuffer=0, nOutBufferSize=0, ByRef lpBytesReturned=0, ByRef lpOverlapped=0)
{
    return DllCall("DeviceIoControl"
        , "uint", hDevice
        , "uint", dwIoControlCode
        , (lpInBuffer==0 ? "uint" : "str"), lpInBuffer
        , "uint", nInBufferSize
        , (lpOutBuffer==0 ? "uint" : "str"), lpOutBuffer
        , "uint", nOutBufferSize
        , "uintp", lpBytesReturned
        , (lpOverlapped==0 ? "uint" : "str"), lpOverlapped)
}
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions All times are GMT
Page 1 of 1

 
Jump to:  
You can post new topics in this forum
You can reply to topics in this forum


Powered by phpBB © 2001, 2005 phpBB Group