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.
надеюсь, это полезно для кого-то еще!
спасибо всем за помощь в указывая мне в правильном направлении!