C# Извлечь USB - код для LockVolume, DismountVolume и PrepareRemovalOfVolume
Я изо всех сил пытаюсь создать приложение, способное извлечь любое запоминающее устройство USB. После многих попыток я наконец-то использую код, найденный в codeproject https://www.codeproject.com/Articles/375916/How-to-Prepare-a-USB-Drive-for-Safe-Removal НО с некоторыми USB-устройствами, которые я получить сообщение об ошибке "Это устройство в настоящее время используется. Закройте все программы или окна, которые могут использовать это устройство, а затем повторите попытку". Я не очень понимаю, почему это происходит для некоторых устройств, а не для других...
Отображается окно ошибки, и в основном метод RemovalDrive() возвращает false, USB не извлекается.
public static bool RemoveDrive( string driveCharWithColon )
{
// open the storage volume
IntPtr hVolume = CreateFile( @"\\.\" + driveCharWithColon, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero );
if ( hVolume.ToInt32( ) == -1 ) return false;
// get the volume's device number
long DeviceNumber = GetDeviceNumber( hVolume );
if ( DeviceNumber == -1 ) return false;
// get the drive type which is required to match the device numbers correctely
string rootPath = driveCharWithColon + "\\";
DriveType driveType = GetDriveType( rootPath );
// get the dos device name (like \device\floppy0) to decide if it's a floppy or not - who knows a better way?
StringBuilder pathInformation = new StringBuilder( 250 );
uint res = QueryDosDevice( driveCharWithColon, pathInformation, 250 );
if ( res == 0 ) return false;
// get the device instance handle of the storage volume by means of a SetupDi enum and matching the device number
long DevInst = GetDrivesDevInstByDeviceNumber( DeviceNumber, driveType, pathInformation.ToString( ) );
if ( DevInst == 0 ) return false;
// get drives's parent, e.g. the USB bridge, the SATA port, an IDE channel with two drives!
int DevInstParent = 0;
CM_Get_Parent( ref DevInstParent, ( int ) DevInst, 0 );
for ( int tries=1; tries <= 3; tries++ ) // sometimes we need some tries...
{
int r = CM_Request_Device_Eject_NoUi( DevInstParent, IntPtr.Zero, null, 0, 0 );
if ( r == 0 ) return true;
Thread.Sleep( 500 );
}
return false;
}
Читая больше ответов, я нашел эту карту памяти Eject Memory Card Card Reader C#
Где упоминается, что перед вызовом метода CM_Request_Device_Eject_NoUi я должен вызвать "LockVolume, DismountVolume и PrepareRemovalOfVolume", используя hVolume, возвращаемый из CreateFile.
К сожалению, эти функции предоставляются только Microsoft в C++. как к вынув-съемную медиа-в-окно
Я хотел попробовать это решение, но я действительно не знаю, как реализовать эти методы с помощью C#.
Кто-нибудь может дать мне представление о том, что может происходить? Я пытался просто закрыть обработчик файла
CloseHandle(hVolume);
перед вызовом метода CM_Request_Device_Eject_NoUi, но он создал другое исключение SEHException (внешний компонент выдал исключение).
1 ответ
Мне удалось реализовать в C# 3 метода, но методы LockVolume и DismountVolume всегда возвращают ложное значение.
const int LOCK_TIMEOUT = 10000; // 10 Seconds
const int LOCK_RETRIES = 20;
public static bool LockVolume(IntPtr hVolume)
{
int dwBytesReturned;
int 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,
IntPtr.Zero, 0,
IntPtr.Zero, 0,
out dwBytesReturned,
IntPtr.Zero))
return true;
Thread.Sleep(dwSleepAmount);
}
return false;
}
public static bool PreventRemovalOfVolume(IntPtr hVolume, bool fPreventRemoval)
{
int retVal;
IntPtr buffer = new IntPtr((fPreventRemoval) ? 1 : 0);
return DeviceIoControl(hVolume, IOCTL_STORAGE_MEDIA_REMOVAL, buffer, 1, IntPtr.Zero, 0, out retVal, IntPtr.Zero);
}
public static bool DismountVolume(IntPtr hVolume)
{
int dwBytesReturned;
return DeviceIoControl(hVolume,
FSCTL_DISMOUNT_VOLUME,
IntPtr.Zero, 0,
IntPtr.Zero, 0,
out dwBytesReturned,
IntPtr.Zero);
}
public static bool AutoEjectVolume(IntPtr hVolume)
{
int dwBytesReturned;
return DeviceIoControl(hVolume,
IOCTL_STORAGE_EJECT_MEDIA,
IntPtr.Zero, 0,
IntPtr.Zero, 0,
out dwBytesReturned,
IntPtr.Zero);
}
public static bool CloseVolume(IntPtr hVolume)
{
return CloseHandle(hVolume);
}
ОБНОВЛЕНИЕ: я зафиксировал ошибку, вызванную вызовом DeviceIoControl, используя код
int error = Marshal.GetLastWin32Error();
и я получил ошибку 6, что означает ERROR_INVALID_HANDLE.:O
Теперь я еще больше запутался, так как этот обработчик отлично работает в остальной части метода RemoveDrive, включая вызов CloseVolume(обработчик).
public static bool RemoveDrive(string driveCharWithColon)
{
// open the storage volume
IntPtr hVolume = CreateFile(@"\\.\" + driveCharWithColon, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
if (hVolume.ToInt32() == -1) return false;
// get the volume's device number
long DeviceNumber = GetDeviceNumber(hVolume);
if (DeviceNumber == -1) return false;
// get the drive type which is required to match the device numbers correctely
string rootPath = driveCharWithColon + "\\";
DriveType driveType = GetDriveType(rootPath);
// get the dos device name (like \device\floppy0) to decide if it's a floppy or not - who knows a better way?
StringBuilder pathInformation = new StringBuilder(250);
uint res = QueryDosDevice(driveCharWithColon, pathInformation, 250);
if (res == 0) return false;
// get the device instance handle of the storage volume by means of a SetupDi enum and matching the device number
long DevInst = GetDrivesDevInstByDeviceNumber(DeviceNumber, driveType, pathInformation.ToString());
if (DevInst == 0) return false;
// get drives's parent, e.g. the USB bridge, the SATA port, an IDE channel with two drives!
int DevInstParent = 0;
CM_Get_Parent(ref DevInstParent, (int)DevInst, 0);
// Lock and dismount the volume.
bool e = LockVolume(hVolume);
int error = Marshal.GetLastWin32Error();
bool f = DismountVolume(hVolume);
int error2 = Marshal.GetLastWin32Error();
// Close the volume so other processes can use the drive.
if (!CloseVolume(hVolume))
return false;
for (int tries = 1; tries <= 3; tries++) // sometimes we need some tries...
{
int r = CM_Request_Device_Eject_NoUi(DevInstParent, IntPtr.Zero, null, 0, 0);
if (r == 0) return true;
Thread.Sleep(500);
}
return false;
}