Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / Win32

How to flush a storage volume's file cache, lock and dismount it, and eject the media

4.78/5 (11 votes)
25 Oct 2011CPOL3 min read 60.3K   1.6K  
Demonstrates how to open a storage volume and do some interesting things with it.

Introduction

Ejecting a media is a well known task for CD/DVD drives. Here we get a physical ejection. But also ZIP/JAZ drives and card readers (drives with a removable media) support ejection and since they are not read only, it is required to dismount their file system before ejection to prevent loss of data.

Background

Ejecting a media is performed by the DeviceIoControl call IOCTL_STORAGE_EJECT_MEDIA. But it is quite reckless, it ejects even when there are open files on the drive! Therefore it's better to flush, lock, and dismount before doing it.

The Sample

This sample is a simplified version of my command-line tool EjectMedia.

It expects the drive letter to eject as a parameter. For non CD/DVD drives, the first step is to flush the file cache by means of the FlushFileBuffers API call. It needs a handle with write access. So, for flushing a whole storage volume, we have to open it with write access, which will fail with ERROR_ACCESS_DENIED for restricted users:

C++
// "\\.\X:" -> to open the volume
char szVolumeAccessPath[] = "\\\\.\\X:";
szVolumeAccessPath[4] = DriveLetter;

HANDLE hVolWrite = CreateFile(szVolumeAccessPath,
                              GENERIC_READ | GENERIC_WRITE,
                              FILE_SHARE_READ | FILE_SHARE_WRITE,
                              NULL, OPEN_EXISTING, 0, NULL);
if ( hVolWrite != INVALID_HANDLE_VALUE ) {
  FlushFileBuffers(hVolWrite);
  CloseHandle(hVolWrite);
}

Then we try to lock and dismount. Here read access is good enough which even restricted users can get. A lock succeeds only if there are no open handles to files on this volume. Dismount succeeds even when lock fails and there are open handles. After dismount, all open file handles on the volume are still open but invalid, any try to use it for read or write actions leads to ERROR_NOT_READY. Therefore it is an option to dismount even when the lock fails, that is the meaning of the 'Force' bool value.

C++
bool Force = false;
HANDLE hVolRead = CreateFile(szVolumeAccessPath,
                  GENERIC_READ,
                  FILE_SHARE_READ | FILE_SHARE_WRITE,
                  NULL, OPEN_EXISTING, 0, NULL);
if ( hVolRead != INVALID_HANDLE_VALUE ) {
  int Locked = DeviceIoControl(hVolRead, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &dwRet, NULL);
  if ( Locked || Force ) {
    res = DeviceIoControl(hVolRead, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, &dwRet, NULL);
  }
}

Finally we perform the ejection:

C++
res = DeviceIoControl(hVolRead, IOCTL_STORAGE_EJECT_MEDIA, NULL, 0, NULL, 0, &dwRet, NULL);

Since we are friendly, we release the lock if we get it:

C++
if ( Locked ) {
  DeviceIoControl(hVolRead, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0, &dwRet, NULL);
}

BTW: The opposite of ejection is loading a media. This really works on CD/DVD drives only:

C++
res = DeviceIoControl(hVolRead, IOCTL_STORAGE_LOAD_MEDIA, NULL, 0, NULL, 0, &dwRet, NULL);

Discussion

Why is there no check for drivetypes DRIVE_CDROM and DRIVE_REMOVABLE?

Windows does not care about the drive type here. The eject request is passed down to the drive's driver which passes it to the hardware. Finally it is the hardware which gives the answer. There are USB hard drives which support the eject. I've seen safe removal failing but ejection succeeding.

What exactly is dismounting?

Dismounting means to detach the file system from the storage volume. This guarantees that the file system is not held in an inconsistent state which is important before mounting it by another driver, e.g., while the system is hibernated and you boot from a bootable CD.

Isn't it the same as removing the drive letter (mountvol X: /D)?

No, absolutely not! The /D does not stand for dismount, it stands for deleting the mount point. And that is all it does. No flushing, no dismounting, no handles are closed or invalidated.

How do we remount a volume?

If the lock is released, then just try to open a file on the volume. Windows automatically mounts the volume then. Handles which have been opened before the dismount was performed stay unusable for read/write, they permanently generate ERROR_NOT_READY even when the volume is mounted again.

Why does locking take a while sometimes?

It seems that locking also flushes the file cache and this may take a little time. Furthermore, applications can register for custom notifications (RegisterDeviceNotification) and then DBT_CUSTOMEVENT comes with GUID_IO_VOLUME_LOCK. FSCTL_LOCK_VOLUME will not return before all applications registered for custom notifications of the volume in question have processed the message.

The Demo Project

The demo project is made with VS6. It requires some headers from a Microsoft Platform SDK. No special LIBs are required.

History

  • 26 Sept 2011 - First release.
  • 25 Oct 2011 - Corrections: The error after dismounting is ERROR_NOT_READY, not ERROR_INVALID_HANDLE, and missing CloseHandle in the demo project.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)