C# WriteFile () прекращает запись в секторе 242 на USB-накопителях
Я написал ниже код для записи 0xFF чтобы все байты на мои устройства USB storeageбыл. По какой-то причине вызовы WriteFile() начинают ошибаться в секторе 242. Я сделал это на двух отдельных USB-накопителях, а затем рассмотрел устройства в шестнадцатеричном редакторе. Сектор 242, по-видимому, является началом таблицы распределения файлов на формируемом устройстве FAT16 и началом области загрузки на устройстве NTFS. Я уверен, что это не совпадение, что это правда erroring на эти места, я не знаю, как изменить это поведение. HRESULT, который я получаю, когда WriteFile терпит неудачу, -2147024891, который является E_ACCESSDENIED. Кто-нибудь знает, что может быть причиной проблемы?
Примечание: Если вы собираетесь запустить этот код в локальной системе, будьте очень осторожны, так как я жестко закодировал идентификатор физического устройства для моего USB-устройства. Не забудьте обновить переменную deviceId с помощью устройства, на которое вы пытаетесь написать. Вы не хотите разрушать свой жесткий водить.
public enum EMoveMethod : uint
{
Begin = 0,
Current = 1,
End = 2
}
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern uint SetFilePointer([In] SafeFileHandle hFile, [In] long lDistanceToMove, [Out] out int lpDistanceToMoveHigh, [In] EMoveMethod dwMoveMethod);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern SafeFileHandle CreateFile(string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);
[DllImport("kernel32", SetLastError = true)]
internal extern static int ReadFile(SafeFileHandle handle, byte[] bytes, int numBytesToRead, out int numBytesRead, IntPtr overlapped_MustBeZero);
[DllImport("kernel32.dll", SetLastError = true)]
internal extern static int WriteFile(SafeFileHandle handle, byte[] bytes, int numBytesToWrite, out int numBytesWritten, IntPtr overlapped_MustBeZero);
[DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)]
private static extern bool DeviceIoControl(SafeFileHandle hDevice, uint dwIoControlCode, byte[] lpInBuffer, int nInBufferSize, byte[] lpOutBuffer, int nOutBufferSize, out int lpBytesReturned, IntPtr lpOverlapped);
[DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)]
private static extern bool CloseHandle(SafeFileHandle handle);
public void wipeDisk()
{
const uint OPEN_EXISTING = 3;
const uint GENERIC_WRITE = (0x40000000);
const uint FSCTL_LOCK_VOLUME = 0x00090018;
const uint FSCTL_UNLOCK_VOLUME = 0x0009001c;
const uint FSCTL_DISMOUNT_VOLUME = 0x00090020;
bool success = false;
int intOut;
string deviceId = @".PHYSICALDRIVE2";
long DiskSize = 2056320000;
SafeFileHandle diskHandle = CreateFile(deviceId, GENERIC_WRITE, 0, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
if (diskHandle.IsInvalid)
{
Console.WriteLine(deviceId + " open error.");
return;
}
Console.WriteLine(deviceId + " " + Marshal.GetHRForLastWin32Error().ToString() + ": opened.");
success = DeviceIoControl(diskHandle, FSCTL_LOCK_VOLUME, null, 0, null, 0, out intOut, IntPtr.Zero);
if (!success)
{
Console.WriteLine(deviceId + " lock error.");
CloseHandle(diskHandle);
return;
}
Console.WriteLine(deviceId + " " + Marshal.GetHRForLastWin32Error().ToString() + ": locked.");
success = DeviceIoControl(diskHandle, FSCTL_DISMOUNT_VOLUME, null, 0, null, 0, out intOut, IntPtr.Zero);
if (!success)
{
Console.WriteLine(deviceId + " " + Marshal.GetHRForLastWin32Error().ToString() + ": dismount error.");
DeviceIoControl(diskHandle, FSCTL_UNLOCK_VOLUME, null, 0, null, 0, out intOut, IntPtr.Zero);
CloseHandle(diskHandle);
return;
}
Console.WriteLine(deviceId + " " + Marshal.GetHRForLastWin32Error().ToString() + ": unmounted.");
int numBytesPerSector = 512;
long numTotalSectors = DiskSize / 512;
byte[] junkBytes = new byte[512];
for (int x = 0; x < 512; x++)
{
junkBytes[x] = 0xFF;
}
for (long sectorNum = 0; sectorNum < numTotalSectors; sectorNum++)
{
int numBytesWritten = 0;
int moveToHigh;
uint rvalsfp = SetFilePointer(diskHandle, sectorNum * numBytesPerSector, out moveToHigh, EMoveMethod.Begin);
Console.WriteLine("File pointer set " + Marshal.GetHRForLastWin32Error().ToString() + ": " + (sectorNum * numBytesPerSector).ToString());
int rval = WriteFile(diskHandle, junkBytes, junkBytes.Length, out numBytesWritten, IntPtr.Zero);
if (numBytesWritten != junkBytes.Length)
{
Console.WriteLine("Write error on track " + sectorNum.ToString() + " from " + (sectorNum * numBytesPerSector).ToString() + "-" + moveToHigh.ToString() + " " + Marshal.GetHRForLastWin32Error().ToString() + ": Only " + numBytesWritten.ToString() + "/" + junkBytes.Length.ToString() + " bytes written.");
break;
}
else
{
Console.WriteLine("Write success " + Marshal.GetHRForLastWin32Error().ToString() + ": " + numBytesWritten.ToString() + "/" + junkBytes.Length.ToString() + " bytes written.");
}
}
success = DeviceIoControl(diskHandle, FSCTL_UNLOCK_VOLUME, null, 0, null, 0, out intOut, IntPtr.Zero);
if (success)
{
Console.WriteLine(deviceId + " " + Marshal.GetHRForLastWin32Error().ToString() + ": unlocked.");
}
else
{
Console.WriteLine(deviceId + " " + Marshal.GetHRForLastWin32Error().ToString() + ": unlock error: " + Marshal.GetHRForLastWin32Error().ToString());
}
success = CloseHandle(diskHandle);
if (success)
{
Console.WriteLine(deviceId + " " + Marshal.GetHRForLastWin32Error().ToString() + ": handle closed.");
}
else
{
Console.WriteLine(deviceId + " " + Marshal.GetHRForLastWin32Error().ToString() + ": close handle error: " + Marshal.GetHRForLastWin32Error().ToString());
}
}
ИЗМЕНИТЬ/ОБНОВИТЬ
я смог заставить это работать успешно после выполнения низкоуровневой очистки USB-устройства с помощью стороннего инструмента. После того, как диск был полностью обнулен, я смог успешно написать на устройство. Кажется, что windows блокирует устройство, как только оно распознает допустимую файловую систему fat или ntfs и использование
const uint FSCTL_LOCK_VOLUME = 0x00090018;
const uint FSCTL_DISMOUNT_VOLUME = 0x00090020;
в паре с DeviceIoControl, похоже, не переопределяет блокировку windows имеет на устройстве.
кто-нибудь знает, как успешно заблокировать съемное USB-устройство в windows с помощью DeviceIoControl на диске с допустимой файловой системой?
я использовал несколько сторонних инструментов, которые делают то, что я пытаюсь сделать, и они работают успешно. Я знаю, что это возможно, но вся документация MSDN, которую я прочитал, не помогла решить проблему.
ИЗМЕНИТЬ / ОБНОВИТЬ 2
Это взято из http://msdn.microsoft.com/en-us/library/ff551353.aspx
"приложение должно заблокировать том, отключить том, или оба, прежде чем он может выдать dasd I/O. это ново для Windows Vista и было сделано для решения потенциально вредоносных методов.
файловая система заблокирует все операции записи в зарезервированные разделы диска. В этом случае эти зарезервированные разделы включают MBR и две области жира. Для блокирования этих областях, вам нужно заблокировать том, отправив FSCTL_LOCK_VOLUME. Эту структуру необходимо создать на том же дескрипторе Тома, который выполняет фактические операции записи. Этот запрос может завершиться ошибкой, если есть открытые дескрипторы файлов. В этом случае приложение может принудительно отключить файловую систему, выдав FSCTL_DISMOUNT_VOLUME. Однако том фактически не демонтируется до закрытия дескриптора файла. До тех пор приложение может продолжать выдавать ввод-вывод DASD, используя тот же дескриптор файла, который в настоящее время открыто.
существует расширенная область за пределами пространства томов, которая известна файловой системе, где операции записи будут заблокированы. Чтобы разрешить операции записи в эту область, необходимо выполнить fsctl_allow_extended_dasd_io на дескрипторе Тома.
вы можете использовать процедуру Win32 API DeviceIoControl для выпуска всех предыдущих FSCTSs."
Я считаю, что это именно то, что мы реализуем в коде выше, но это кажется, работает неправильно. Мы получаем ручку и блокируем и демонтируем устройство, чтобы мы могли правильно писать в защищенную область?
ИЗМЕНИТЬ / ОБНОВИТЬ 3
Ok это текущий порядок открытия дисков и томов.. Методы блокировки, демонтажа и т. д. работают именно в том порядке, который мы считаем неправильным..
SafeFileHandle volumeHandle = CreateFile(".E:",...);
LockVolume(volumeHandle);
DismountVolume(volumeHandle);
SafeFileHandle diskHandle = CreateFile(".PHYSICALDRIVE1"...);
WriteStuff(diskHandle);
//Fails...
UnlockVolume(volumeHandle);
CloseVolume(volumeHandle);
CloseDisk(diskHandle);
Я все еще получаю те же результаты, он работает только тогда, когда диск разгромлен.
3 ответов
существует путаница между диск и диск здесь.
если вы хотите полный доступ к диск (это ваш случай, когда вы используете \.\PHYSICALDRIVE
), вы должны заблокировать все подключенные Тома, которые в основном все разделы (т. е. диски) вашего физического диска.
вместо FSCTL_LOCK_VOLUME
на дескриптор, возвращенный CreateFile("\.\PHYSICALDRIVE"...)
, получить ручку на каждого установленные объем (который является диском, а не физическим диском) с помощью string.Replace("\\.\{0}:", DriveLetter)
узор.
вы можете получить список смонтированных томов (в конечном счете, вы хотите список букв) для данного физического диска, используя IOCTL_DISK_GET_DRIVE_LAYOUT
.
EDIT:
С MSDN :
запись на дескриптор диска будет успешным, если один из следующих условий:
секторов для записи не попадают в экстенты Тома.
сектора, которые будут записаны, чтобы попасть в монтируемый том, но вы явно заблокировали или демонтировали том с помощью использование FSCTL_LOCK_VOLUME или FSCTL_DISMOUNT_VOLUME.
секторов, написанные в том, что нет смонтированной файловой системы чем сырой.
Итак, в основном, то, что вы должны сделать, это:
- получить дескриптор для каждого из томов
- использовать
FSCTL_LOCK_VOLUME
илиFSCTL_DISMOUNT_VOLUME
на каждом томе. Если в томе не используется файл (т. е. нет открытого дескриптора любым процессом для любого файла),FSCTL_LOCK_VOLUME
достаточно - получить дескриптор на физический диск
- запись на физический диск
- закрыть обе ручки. Закрытие ручки громкости отпустит замок.
также убедитесь, что вы используете приложение с правами администратора (с повышенными процесс).
Я предполагаю, что вы используете Windows Vista
или позже. ОС блокирует любые попытки прямой записи в эти сектора, поэтому сначала вам нужно сделать блокировку. Подробнее об этом здесь:
http://msdn.microsoft.com/en-us/library/ff551353.aspx
также просто проверка, поэтому поднял этот пост:
CreateFile: прямая операция записи на необработанный диск "Доступ запрещен" - Vista, Win7
следственная информация там может быть полезно, HTH...
редактировать
я отредактировал этот ответ, чтобы отразить предложения ken2k.
предложение ken2k действительно исправило проблему, с которой я столкнулся. Я не уверен, почему мои предыдущие попытки использовать этот подход не увенчались успехом, однако я только что пересмотрел/настроил свой код, и этот подход, похоже, работает правильно.
вот шаги, которые я использовал для решения этой проблемы:
- получить дескриптор для физических диск
- получить дескриптор для каждого логического диска на физическом диске
- блокировка каждого диска на физическом диске
- демонтировать каждый диск на физическом диске
- блокировка физического диска (необязательно)
- демонтировать физический диск (необязательно)
- используйте дескриптор физического диска, чтобы обнулить весь физический диск
- разблокировать каждый логический диск
- открыть физический диск (только если вы решите заблокировать диск)
- закройте ручки логического диска
- закройте дескриптор физического диска
Примечание: Если вы хотите выполнять операции "спина к спине" без завершения вашей программы, и вы использовали функциональность FSCTL_DISMOUNT_VOLUME, вам нужно будет "перемонтировать" диск, используя что-то похожее на следующее:
ManagementObjectSearcher searcher = new ManagementObjectSearcher("root\CIMV2", "SELECT * FROM Win32_DiskDrive");
или
System.IO.DriveInfo.GetDrives();
для сопоставления ID логического диска с ID физического диска при попытке для блокировки каждого отдельного логического диска используйте следующий фрагмент кода, чтобы связать метки логического диска с метками физического диска:
List<string> driveLetters = new List<string>();
string deviceId = @"\.\PHYSICALDRIVE1";
string queryString = "ASSOCIATORS OF {Win32_DiskDrive.DeviceID='" + deviceId + "'} WHERE AssocClass = Win32_DiskDriveToDiskPartition";
ManagementObjectSearcher diskSearcher = new ManagementObjectSearcher("root\CIMV2", queryString);
ManagementObjectCollection diskMoc = diskSearcher.Get();
foreach (ManagementObject diskMo in diskMoc)
{
queryString = "ASSOCIATORS OF {Win32_DiskPartition.DeviceID='" + diskMo["DeviceID"] + "'} WHERE AssocClass = Win32_LogicalDiskToPartition";
ManagementObjectSearcher driveSearcher = new ManagementObjectSearcher("root\CIMV2", queryString);
ManagementObjectCollection driveMoc = driveSearcher.Get();
foreach (ManagementObject driveMo in driveMoc)
{
driveLetters.Add("\\.\" + driveMo["DeviceID"].ToString());
}
}
Так, например, если метка физического диска -\.\PHYSICALDRIVE1 и содержит один логический диск с буквой диска "E", приведенный выше код будет отображать \.\E: to \.\PHYSICALDRIVE1.
согласно предложению ken2k это сопоставление также может быть сделано с помощью функции IOCTL_DISK_GET_DRIVE_LAYOUT.
надеюсь, это полезно для кого-то еще!
спасибо всем за помощь в указывая мне в правильном направлении!