interiot
Joined: 06 Nov 2005 Posts: 64
|
Posted: Wed Apr 30, 2008 5:54 pm Post subject: Reference implementation for ejecting CD or USB flash drive |
|
|
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)
} |
|
|