uDiskName=_T("\\\\.\\C:");
m_hUDisk=CreateFile(LPCTSTR(uDiskName),0,
FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,OPEN_EXISTING,
0,NULL);
bResult = DeviceIoControl(m_hUDisk,
FSCTL_IS_VOLUME_MOUNTED,
NULL, 0,
NULL, 0,
&junk,
(LPOVERLAPPED) NULL);
LastError=GetLastError();
运行后得到的结果:bResult为0,但是LastError却为1.
MSDN中关于返回值是这样说的:
If the volume is currently mounted, DeviceIoControl returns zero.
Otherwise, DeviceIoControl returns an error code.
既然已经判断出c盘已经被mount,那么为什么错误代码却为1(ERROR_FUNCTION)?
11 个解决方案
#1
如果control code为FSCTL_IS_VOLUME_MOUNTED,那么返回结果以及错误代码和上面的一样,按msdn的说法:If the operation succeeds, DeviceIoControl returns a nonzero value.If the operation fails, DeviceIoControl returns zero.也就是说不成功,那么应该如何将一个已经mount的volume dismount?
#2
不好意思,刚才的回复里应该是"如果control code为FSCTL_DISMOUNT_VOLUME".
另外,我试了一下,如果CreateFile时第二个参数为GENERIC_READ或者GENERIC_WRITE时,对U盘执行dismount操作,那么得到的bResult为1,LastError为0,这样的结果好象是对的.而且我在执行时正在往U盘里面写文件,一执行,系统就提示说copy过程被中断,说明的确是已经强行dismount了.但是执行了以后U盘的灯怎么还是亮着?如果这样是对的,那么为什么要加GENERIC_READ或者GENERIC_WRITE?
另外,我试了一下,如果CreateFile时第二个参数为GENERIC_READ或者GENERIC_WRITE时,对U盘执行dismount操作,那么得到的bResult为1,LastError为0,这样的结果好象是对的.而且我在执行时正在往U盘里面写文件,一执行,系统就提示说copy过程被中断,说明的确是已经强行dismount了.但是执行了以后U盘的灯怎么还是亮着?如果这样是对的,那么为什么要加GENERIC_READ或者GENERIC_WRITE?
#3
我觉得msdn关于用DeviceIoControl发送FSCTL_IS_VOLUME_MOUNTED的返回值说明有错,发送其他的FSCTL_XX_XXX请求返回值一律是成功为非0而不成功为0,独独这个FSCTL_IS_VOLUME_MOUNTED的说明相反.事实上,我调试的结果也支持我的看法.
还有个问题令我很困惑:用DeviceIoControl发送FSCTL_IS_VOLUME_MOUNTED请求之前,必须先调用CreateFile得到对应volume的handle,那岂不是说每次检查volume是否被mount一定会显示已经mount(因为如果原来没mount,调用了CreateFile之后,windows的I/O Manager也会mount)?
还有个问题令我很困惑:用DeviceIoControl发送FSCTL_IS_VOLUME_MOUNTED请求之前,必须先调用CreateFile得到对应volume的handle,那岂不是说每次检查volume是否被mount一定会显示已经mount(因为如果原来没mount,调用了CreateFile之后,windows的I/O Manager也会mount)?
#4
确实应该是成功返回非0值,大概是MSDN的版本问题,你到MSDN的网站在查一下就知道成功时返回非0值。
你在调用 CreateFile 时指定了OPEN_EXISTING 就不会自动mount了,如果volume不存在的话,直接返回一个无效的句柄。
你在调用 CreateFile 时指定了OPEN_EXISTING 就不会自动mount了,如果volume不存在的话,直接返回一个无效的句柄。
#5
看了你的回复,这个问题我知道了.谢谢回答.
但是前面还有个问题:CreateFile时为什么要加GENERIC_READ或者GENERIC_WRITE才能正常调用?请指教,分数一定会给.
但是前面还有个问题:CreateFile时为什么要加GENERIC_READ或者GENERIC_WRITE才能正常调用?请指教,分数一定会给.
#6
seaquester:
我觉得你说的"调用 CreateFile 时指定了OPEN_EXISTING 就不会自动mount"好象有问题,我试了一下,我发送dismount请求后立即发送FSCTL_IS_VOLUME_MOUNTED请求,结果返回是0,error code是21(The device is not ready),这是对的,说明已经成功dismount了.
但是我结束本次debug,然后再次开始debug(此时应该没有volume,因为我在两次调试之间没有对U盘进行任何操作),调用 CreateFile (指定了OPEN_EXISTING)的结果是得到了有效handle,而且接着发送FSCTL_IS_VOLUME_MOUNTED请求返回1,说明已经mount.这怎么解释呢?
我觉得你说的"调用 CreateFile 时指定了OPEN_EXISTING 就不会自动mount"好象有问题,我试了一下,我发送dismount请求后立即发送FSCTL_IS_VOLUME_MOUNTED请求,结果返回是0,error code是21(The device is not ready),这是对的,说明已经成功dismount了.
但是我结束本次debug,然后再次开始debug(此时应该没有volume,因为我在两次调试之间没有对U盘进行任何操作),调用 CreateFile (指定了OPEN_EXISTING)的结果是得到了有效handle,而且接着发送FSCTL_IS_VOLUME_MOUNTED请求返回1,说明已经mount.这怎么解释呢?
#7
下面是我的代码,在Win2000下面试过了,没有问题。
#include <windows.h>
#include <stdio.h>
//----------------------------------------------------------------------
//
// PrintWin32Error
//
// Takes the win32 error code and prints the text version.
//
//----------------------------------------------------------------------
void PrintWin32Error( PCHAR Message, DWORD ErrorCode )
{
LPVOID lpMsgBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
ErrorCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(PCHAR) &lpMsgBuf,
0,
NULL
);
fprintf(stderr, "%s: %s\n", Message, lpMsgBuf );
LocalFree( lpMsgBuf );
}
void main()
{
HANDLE hDevice;
BOOL bResult;
DWORD dwBytesReturned;
DWORD dwError;
char s[64];
//
// Getting a handle to a volume.
//
LPCTSTR szDiskName = "\\\\.\\F:";
hDevice = CreateFile(
szDiskName,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL
);
if ((hDevice == NULL) || (hDevice == INVALID_HANDLE_VALUE))
{
sprintf(s, "The volume %s is not mounted!", szDiskName);
PrintWin32Error(s, GetLastError());
return;
}
// Check if The volume is mounted or not.
bResult = DeviceIoControl(
hDevice,
FSCTL_IS_VOLUME_MOUNTED,
NULL,
0,
NULL,
0,
&dwBytesReturned,
NULL
);
dwError = GetLastError();
if (bResult)
{
printf("The volume %s is mounted!\n", szDiskName);
}
else
{
sprintf(s, "The volume %s is not mounted!", szDiskName);
PrintWin32Error(s, GetLastError());
}
CloseHandle(hDevice);
}
#include <windows.h>
#include <stdio.h>
//----------------------------------------------------------------------
//
// PrintWin32Error
//
// Takes the win32 error code and prints the text version.
//
//----------------------------------------------------------------------
void PrintWin32Error( PCHAR Message, DWORD ErrorCode )
{
LPVOID lpMsgBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
ErrorCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(PCHAR) &lpMsgBuf,
0,
NULL
);
fprintf(stderr, "%s: %s\n", Message, lpMsgBuf );
LocalFree( lpMsgBuf );
}
void main()
{
HANDLE hDevice;
BOOL bResult;
DWORD dwBytesReturned;
DWORD dwError;
char s[64];
//
// Getting a handle to a volume.
//
LPCTSTR szDiskName = "\\\\.\\F:";
hDevice = CreateFile(
szDiskName,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL
);
if ((hDevice == NULL) || (hDevice == INVALID_HANDLE_VALUE))
{
sprintf(s, "The volume %s is not mounted!", szDiskName);
PrintWin32Error(s, GetLastError());
return;
}
// Check if The volume is mounted or not.
bResult = DeviceIoControl(
hDevice,
FSCTL_IS_VOLUME_MOUNTED,
NULL,
0,
NULL,
0,
&dwBytesReturned,
NULL
);
dwError = GetLastError();
if (bResult)
{
printf("The volume %s is mounted!\n", szDiskName);
}
else
{
sprintf(s, "The volume %s is not mounted!", szDiskName);
PrintWin32Error(s, GetLastError());
}
CloseHandle(hDevice);
}
#8
我明白你的意思,你是说如果没有插入U盘,或者使用系统提供的"安全删除硬件"程序,则volume肯定是不存在的,CreateFile肯定是返回无效handle,这样的情况下肯定不可能自动mount.
但是我还有个问题,还想麻烦大侠指点一下,我自己在程序里面dismount的结果是U盘的灯还是亮的,而且还是可以在"我的电脑"中看到盘符;而使用系统提供的"安全删除硬件"程序的结果是灯不亮而且盘符已经消失了.这两种方法的区别在哪里?(我想,这应该就是我dismount以后再CreateFile时又自动mount的原因,可能volume并没有真正被删除?)
真想把这个问题搞懂,觉得分数不够可以另开贴给你加.谢谢!
但是我还有个问题,还想麻烦大侠指点一下,我自己在程序里面dismount的结果是U盘的灯还是亮的,而且还是可以在"我的电脑"中看到盘符;而使用系统提供的"安全删除硬件"程序的结果是灯不亮而且盘符已经消失了.这两种方法的区别在哪里?(我想,这应该就是我dismount以后再CreateFile时又自动mount的原因,可能volume并没有真正被删除?)
真想把这个问题搞懂,觉得分数不够可以另开贴给你加.谢谢!
#9
只的dismount达不到安全删除的效果,MSDN里面提供了一种实现方案(我没试过,你自己试一下):
Knowledge Base
HOWTO: Ejecting Removable Media in Windows NT/Windows 2000/Windows XPPSS ID Number:Q165721
Article Last Modified on 01-20-2002
--------------------------------------------------------------------------------
The information in this article applies to:
Microsoft Win32 Application Programming Interface (API)
the operating system: Microsoft Windows NT 3.5, 3.51, 4.0
the operating system: Microsoft Windows 2000
the operating system: Microsoft Windows XP
--------------------------------------------------------------------------------
#include <windows.h>
#include <winioctl.h>
#include <tchar.h>
#include <stdio.h>
// 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);
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
#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;
return DeviceIoControl( hVolume,
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)
printf("Media in Drive %c has been ejected safely.\n",
cDriveLetter);
else {
if (fRemoveSafely)
printf("Media in Drive %c can be safely removed.\n",
cDriveLetter);
}
return TRUE;
}
void Usage()
{
printf("Usage: Eject <drive letter>\n\n");
return ;
}
void main(int argc, char * argv[])
{
if (argc != 2) {
Usage();
return ;
}
if (!EjectVolume(argv[1][0]))
printf("Failure ejecting drive %c.\n", argv[1][0]);
return ;
}
Knowledge Base
HOWTO: Ejecting Removable Media in Windows NT/Windows 2000/Windows XPPSS ID Number:Q165721
Article Last Modified on 01-20-2002
--------------------------------------------------------------------------------
The information in this article applies to:
Microsoft Win32 Application Programming Interface (API)
the operating system: Microsoft Windows NT 3.5, 3.51, 4.0
the operating system: Microsoft Windows 2000
the operating system: Microsoft Windows XP
--------------------------------------------------------------------------------
#include <windows.h>
#include <winioctl.h>
#include <tchar.h>
#include <stdio.h>
// 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);
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
#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;
return DeviceIoControl( hVolume,
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)
printf("Media in Drive %c has been ejected safely.\n",
cDriveLetter);
else {
if (fRemoveSafely)
printf("Media in Drive %c can be safely removed.\n",
cDriveLetter);
}
return TRUE;
}
void Usage()
{
printf("Usage: Eject <drive letter>\n\n");
return ;
}
void main(int argc, char * argv[])
{
if (argc != 2) {
Usage();
return ;
}
if (!EjectVolume(argv[1][0]))
printf("Failure ejecting drive %c.\n", argv[1][0]);
return ;
}
#10
seaquester:
非常感谢你提供的代码!
但是我按照你提供的方法试了一下,发现还是跟原来的结果差不多,即使EjectVolume函数中的fAutoEject为TRUE,最后执行完以后盘符依然存在,而且仍然可以打开里面的文件,还是不能得到如系统"安全删除硬件"的效果(盘符消失).
如果你不介意,我还想向你继续请教.IOCTL_STORAGE_EJECT_MEDIA与FSCTL_DISMOUNT_VOLUME有什么区别?上面所说的现象应如何解释?
非常感谢你提供的代码!
但是我按照你提供的方法试了一下,发现还是跟原来的结果差不多,即使EjectVolume函数中的fAutoEject为TRUE,最后执行完以后盘符依然存在,而且仍然可以打开里面的文件,还是不能得到如系统"安全删除硬件"的效果(盘符消失).
如果你不介意,我还想向你继续请教.IOCTL_STORAGE_EJECT_MEDIA与FSCTL_DISMOUNT_VOLUME有什么区别?上面所说的现象应如何解释?
#11
安全删除U盘代码:
ftp://ftp.heise.de/pub/ct/listings/0316-208.zip
比如E盘是U盘,要删除它,可以这样:
Deveject.exe -EjectDrive:E:
具体怎么作看代码。
ftp://ftp.heise.de/pub/ct/listings/0316-208.zip
比如E盘是U盘,要删除它,可以这样:
Deveject.exe -EjectDrive:E:
具体怎么作看代码。
#1
如果control code为FSCTL_IS_VOLUME_MOUNTED,那么返回结果以及错误代码和上面的一样,按msdn的说法:If the operation succeeds, DeviceIoControl returns a nonzero value.If the operation fails, DeviceIoControl returns zero.也就是说不成功,那么应该如何将一个已经mount的volume dismount?
#2
不好意思,刚才的回复里应该是"如果control code为FSCTL_DISMOUNT_VOLUME".
另外,我试了一下,如果CreateFile时第二个参数为GENERIC_READ或者GENERIC_WRITE时,对U盘执行dismount操作,那么得到的bResult为1,LastError为0,这样的结果好象是对的.而且我在执行时正在往U盘里面写文件,一执行,系统就提示说copy过程被中断,说明的确是已经强行dismount了.但是执行了以后U盘的灯怎么还是亮着?如果这样是对的,那么为什么要加GENERIC_READ或者GENERIC_WRITE?
另外,我试了一下,如果CreateFile时第二个参数为GENERIC_READ或者GENERIC_WRITE时,对U盘执行dismount操作,那么得到的bResult为1,LastError为0,这样的结果好象是对的.而且我在执行时正在往U盘里面写文件,一执行,系统就提示说copy过程被中断,说明的确是已经强行dismount了.但是执行了以后U盘的灯怎么还是亮着?如果这样是对的,那么为什么要加GENERIC_READ或者GENERIC_WRITE?
#3
我觉得msdn关于用DeviceIoControl发送FSCTL_IS_VOLUME_MOUNTED的返回值说明有错,发送其他的FSCTL_XX_XXX请求返回值一律是成功为非0而不成功为0,独独这个FSCTL_IS_VOLUME_MOUNTED的说明相反.事实上,我调试的结果也支持我的看法.
还有个问题令我很困惑:用DeviceIoControl发送FSCTL_IS_VOLUME_MOUNTED请求之前,必须先调用CreateFile得到对应volume的handle,那岂不是说每次检查volume是否被mount一定会显示已经mount(因为如果原来没mount,调用了CreateFile之后,windows的I/O Manager也会mount)?
还有个问题令我很困惑:用DeviceIoControl发送FSCTL_IS_VOLUME_MOUNTED请求之前,必须先调用CreateFile得到对应volume的handle,那岂不是说每次检查volume是否被mount一定会显示已经mount(因为如果原来没mount,调用了CreateFile之后,windows的I/O Manager也会mount)?
#4
确实应该是成功返回非0值,大概是MSDN的版本问题,你到MSDN的网站在查一下就知道成功时返回非0值。
你在调用 CreateFile 时指定了OPEN_EXISTING 就不会自动mount了,如果volume不存在的话,直接返回一个无效的句柄。
你在调用 CreateFile 时指定了OPEN_EXISTING 就不会自动mount了,如果volume不存在的话,直接返回一个无效的句柄。
#5
看了你的回复,这个问题我知道了.谢谢回答.
但是前面还有个问题:CreateFile时为什么要加GENERIC_READ或者GENERIC_WRITE才能正常调用?请指教,分数一定会给.
但是前面还有个问题:CreateFile时为什么要加GENERIC_READ或者GENERIC_WRITE才能正常调用?请指教,分数一定会给.
#6
seaquester:
我觉得你说的"调用 CreateFile 时指定了OPEN_EXISTING 就不会自动mount"好象有问题,我试了一下,我发送dismount请求后立即发送FSCTL_IS_VOLUME_MOUNTED请求,结果返回是0,error code是21(The device is not ready),这是对的,说明已经成功dismount了.
但是我结束本次debug,然后再次开始debug(此时应该没有volume,因为我在两次调试之间没有对U盘进行任何操作),调用 CreateFile (指定了OPEN_EXISTING)的结果是得到了有效handle,而且接着发送FSCTL_IS_VOLUME_MOUNTED请求返回1,说明已经mount.这怎么解释呢?
我觉得你说的"调用 CreateFile 时指定了OPEN_EXISTING 就不会自动mount"好象有问题,我试了一下,我发送dismount请求后立即发送FSCTL_IS_VOLUME_MOUNTED请求,结果返回是0,error code是21(The device is not ready),这是对的,说明已经成功dismount了.
但是我结束本次debug,然后再次开始debug(此时应该没有volume,因为我在两次调试之间没有对U盘进行任何操作),调用 CreateFile (指定了OPEN_EXISTING)的结果是得到了有效handle,而且接着发送FSCTL_IS_VOLUME_MOUNTED请求返回1,说明已经mount.这怎么解释呢?
#7
下面是我的代码,在Win2000下面试过了,没有问题。
#include <windows.h>
#include <stdio.h>
//----------------------------------------------------------------------
//
// PrintWin32Error
//
// Takes the win32 error code and prints the text version.
//
//----------------------------------------------------------------------
void PrintWin32Error( PCHAR Message, DWORD ErrorCode )
{
LPVOID lpMsgBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
ErrorCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(PCHAR) &lpMsgBuf,
0,
NULL
);
fprintf(stderr, "%s: %s\n", Message, lpMsgBuf );
LocalFree( lpMsgBuf );
}
void main()
{
HANDLE hDevice;
BOOL bResult;
DWORD dwBytesReturned;
DWORD dwError;
char s[64];
//
// Getting a handle to a volume.
//
LPCTSTR szDiskName = "\\\\.\\F:";
hDevice = CreateFile(
szDiskName,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL
);
if ((hDevice == NULL) || (hDevice == INVALID_HANDLE_VALUE))
{
sprintf(s, "The volume %s is not mounted!", szDiskName);
PrintWin32Error(s, GetLastError());
return;
}
// Check if The volume is mounted or not.
bResult = DeviceIoControl(
hDevice,
FSCTL_IS_VOLUME_MOUNTED,
NULL,
0,
NULL,
0,
&dwBytesReturned,
NULL
);
dwError = GetLastError();
if (bResult)
{
printf("The volume %s is mounted!\n", szDiskName);
}
else
{
sprintf(s, "The volume %s is not mounted!", szDiskName);
PrintWin32Error(s, GetLastError());
}
CloseHandle(hDevice);
}
#include <windows.h>
#include <stdio.h>
//----------------------------------------------------------------------
//
// PrintWin32Error
//
// Takes the win32 error code and prints the text version.
//
//----------------------------------------------------------------------
void PrintWin32Error( PCHAR Message, DWORD ErrorCode )
{
LPVOID lpMsgBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
ErrorCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(PCHAR) &lpMsgBuf,
0,
NULL
);
fprintf(stderr, "%s: %s\n", Message, lpMsgBuf );
LocalFree( lpMsgBuf );
}
void main()
{
HANDLE hDevice;
BOOL bResult;
DWORD dwBytesReturned;
DWORD dwError;
char s[64];
//
// Getting a handle to a volume.
//
LPCTSTR szDiskName = "\\\\.\\F:";
hDevice = CreateFile(
szDiskName,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL
);
if ((hDevice == NULL) || (hDevice == INVALID_HANDLE_VALUE))
{
sprintf(s, "The volume %s is not mounted!", szDiskName);
PrintWin32Error(s, GetLastError());
return;
}
// Check if The volume is mounted or not.
bResult = DeviceIoControl(
hDevice,
FSCTL_IS_VOLUME_MOUNTED,
NULL,
0,
NULL,
0,
&dwBytesReturned,
NULL
);
dwError = GetLastError();
if (bResult)
{
printf("The volume %s is mounted!\n", szDiskName);
}
else
{
sprintf(s, "The volume %s is not mounted!", szDiskName);
PrintWin32Error(s, GetLastError());
}
CloseHandle(hDevice);
}
#8
我明白你的意思,你是说如果没有插入U盘,或者使用系统提供的"安全删除硬件"程序,则volume肯定是不存在的,CreateFile肯定是返回无效handle,这样的情况下肯定不可能自动mount.
但是我还有个问题,还想麻烦大侠指点一下,我自己在程序里面dismount的结果是U盘的灯还是亮的,而且还是可以在"我的电脑"中看到盘符;而使用系统提供的"安全删除硬件"程序的结果是灯不亮而且盘符已经消失了.这两种方法的区别在哪里?(我想,这应该就是我dismount以后再CreateFile时又自动mount的原因,可能volume并没有真正被删除?)
真想把这个问题搞懂,觉得分数不够可以另开贴给你加.谢谢!
但是我还有个问题,还想麻烦大侠指点一下,我自己在程序里面dismount的结果是U盘的灯还是亮的,而且还是可以在"我的电脑"中看到盘符;而使用系统提供的"安全删除硬件"程序的结果是灯不亮而且盘符已经消失了.这两种方法的区别在哪里?(我想,这应该就是我dismount以后再CreateFile时又自动mount的原因,可能volume并没有真正被删除?)
真想把这个问题搞懂,觉得分数不够可以另开贴给你加.谢谢!
#9
只的dismount达不到安全删除的效果,MSDN里面提供了一种实现方案(我没试过,你自己试一下):
Knowledge Base
HOWTO: Ejecting Removable Media in Windows NT/Windows 2000/Windows XPPSS ID Number:Q165721
Article Last Modified on 01-20-2002
--------------------------------------------------------------------------------
The information in this article applies to:
Microsoft Win32 Application Programming Interface (API)
the operating system: Microsoft Windows NT 3.5, 3.51, 4.0
the operating system: Microsoft Windows 2000
the operating system: Microsoft Windows XP
--------------------------------------------------------------------------------
#include <windows.h>
#include <winioctl.h>
#include <tchar.h>
#include <stdio.h>
// 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);
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
#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;
return DeviceIoControl( hVolume,
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)
printf("Media in Drive %c has been ejected safely.\n",
cDriveLetter);
else {
if (fRemoveSafely)
printf("Media in Drive %c can be safely removed.\n",
cDriveLetter);
}
return TRUE;
}
void Usage()
{
printf("Usage: Eject <drive letter>\n\n");
return ;
}
void main(int argc, char * argv[])
{
if (argc != 2) {
Usage();
return ;
}
if (!EjectVolume(argv[1][0]))
printf("Failure ejecting drive %c.\n", argv[1][0]);
return ;
}
Knowledge Base
HOWTO: Ejecting Removable Media in Windows NT/Windows 2000/Windows XPPSS ID Number:Q165721
Article Last Modified on 01-20-2002
--------------------------------------------------------------------------------
The information in this article applies to:
Microsoft Win32 Application Programming Interface (API)
the operating system: Microsoft Windows NT 3.5, 3.51, 4.0
the operating system: Microsoft Windows 2000
the operating system: Microsoft Windows XP
--------------------------------------------------------------------------------
#include <windows.h>
#include <winioctl.h>
#include <tchar.h>
#include <stdio.h>
// 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);
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
#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;
return DeviceIoControl( hVolume,
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)
printf("Media in Drive %c has been ejected safely.\n",
cDriveLetter);
else {
if (fRemoveSafely)
printf("Media in Drive %c can be safely removed.\n",
cDriveLetter);
}
return TRUE;
}
void Usage()
{
printf("Usage: Eject <drive letter>\n\n");
return ;
}
void main(int argc, char * argv[])
{
if (argc != 2) {
Usage();
return ;
}
if (!EjectVolume(argv[1][0]))
printf("Failure ejecting drive %c.\n", argv[1][0]);
return ;
}
#10
seaquester:
非常感谢你提供的代码!
但是我按照你提供的方法试了一下,发现还是跟原来的结果差不多,即使EjectVolume函数中的fAutoEject为TRUE,最后执行完以后盘符依然存在,而且仍然可以打开里面的文件,还是不能得到如系统"安全删除硬件"的效果(盘符消失).
如果你不介意,我还想向你继续请教.IOCTL_STORAGE_EJECT_MEDIA与FSCTL_DISMOUNT_VOLUME有什么区别?上面所说的现象应如何解释?
非常感谢你提供的代码!
但是我按照你提供的方法试了一下,发现还是跟原来的结果差不多,即使EjectVolume函数中的fAutoEject为TRUE,最后执行完以后盘符依然存在,而且仍然可以打开里面的文件,还是不能得到如系统"安全删除硬件"的效果(盘符消失).
如果你不介意,我还想向你继续请教.IOCTL_STORAGE_EJECT_MEDIA与FSCTL_DISMOUNT_VOLUME有什么区别?上面所说的现象应如何解释?
#11
安全删除U盘代码:
ftp://ftp.heise.de/pub/ct/listings/0316-208.zip
比如E盘是U盘,要删除它,可以这样:
Deveject.exe -EjectDrive:E:
具体怎么作看代码。
ftp://ftp.heise.de/pub/ct/listings/0316-208.zip
比如E盘是U盘,要删除它,可以这样:
Deveject.exe -EjectDrive:E:
具体怎么作看代码。