Страницы

Поиск по вопросам

среда, 25 декабря 2019 г.

Склонировать диск (в т.ч системный) в ISO

#net #windows #winapi #любой_язык #iso_образ


Позволит ли .NET стек создать что-то подобное?

Или нужно или стоит пользоваться другими языками программирования и технологиями
для решения поставленной задачи. Подозреваю что что-то вроде плюсов, ибо приходится
работать наверняка с чем-то низко системным.
    


Ответы

Ответ 1



Образ ISO - это простая посекторная копия диска (точнее, раздела, или "дорожки", применительно к CD), без сжатия и каких-либо служебных заголовков. Название произошло от файловой системы ISO-9660, которая была одной из первых файловых систем для CD, но фактически образ ISO может содержать любую файловую систему в зависимости от типа носителя: UDF для DVD, FAT для дискет, FAT32/NTFS для флешек и т.п. Чтобы создать такой образ, достаточно открыть диск на чтение и переписать все содержимое в файл, поэтому потребуется лишь несколько функций для работы с файлами из WinAPI. Да, конечно, это можно сделать в .NET. Создать образ CD-ROM или другого подобного незаписываемого носителя довольно просто: using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; namespace ConsoleApplication1 { class Program { [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] static extern bool GetDiskFreeSpace(string lpRootPathName, out uint lpSectorsPerCluster, out uint lpBytesPerSector, out uint lpNumberOfFreeClusters, out uint lpTotalNumberOfClusters); const uint GENERIC_READ = 0x80000000; const uint FILE_SHARE_READ = 0x00000001; const uint FILE_SHARE_WRITE = 0x00000002; const uint OPEN_EXISTING = 3; const uint FILE_ATTRIBUTE_NORMAL = 0x00000080; [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] public static extern IntPtr CreateFile( [MarshalAs(UnmanagedType.LPTStr)] string filename, [MarshalAs(UnmanagedType.U4)] uint access, [MarshalAs(UnmanagedType.U4)] uint share, IntPtr securityAttributes, [MarshalAs(UnmanagedType.U4)] uint creationDisposition, [MarshalAs(UnmanagedType.U4)] uint flagsAndAttributes, IntPtr templateFile); [DllImport("kernel32.dll", SetLastError = true)] static extern bool ReadFile(IntPtr hFile, [Out] byte[] lpBuffer, uint nNumberOfBytesToRead, out uint lpNumberOfBytesRead, IntPtr lpOverlapped); [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] static extern bool CloseHandle(IntPtr hObject); static void CreateISO(string letter, string outfile) { byte[] buf; uint dwRead = 0; ulong count = 0; uint dwSectorsPerCluster, dwBytesPerSector, dwNumberOfFreeClusters, dwTotalNumberOfClusters; uint blocksize; ulong totalsize; const uint SECTOR_SIZE = 2048; //размер сектора для CD string fmt = "\\\\.\\{0}:"; //формат имени файла для открытия тома //найдем размер сектора и объем данных на диске bool res = GetDiskFreeSpace(letter + ":\\", out dwSectorsPerCluster, out dwBytesPerSector, out dwNumberOfFreeClusters, out dwTotalNumberOfClusters); //вычислим размер блока, кратный размеру сектора, и общий объем данных в байтах if (res) { blocksize = dwBytesPerSector * 20; totalsize = dwSectorsPerCluster * dwBytesPerSector * (ulong)dwTotalNumberOfClusters; } else { Console.WriteLine("GetDiskFreeSpace error {0}", Marshal.GetLastWin32Error()); blocksize = SECTOR_SIZE * 20; totalsize = 4700307456; //DVD } buf = new byte[blocksize]; string volume = String.Format(fmt, letter); //открываем том для прямого доступа на чтение var handle = CreateFile(volume, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero); if (handle.ToInt64() == -1) throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); //открываем файл для записи var target = new System.IO.FileStream(outfile, System.IO.FileMode.Create); try { while (true) { dwRead = 0; Array.Clear(buf, 0, buf.Length); //считаем блок данных res = ReadFile(handle, buf, blocksize, out dwRead, IntPtr.Zero); if (res == false) { Console.WriteLine(); Console.WriteLine("ReadFile error {0}", Marshal.GetLastWin32Error()); break; } if (dwRead == 0) break; //конец считывания count += dwRead; Console.Write(" Creating image: {0}% [{1} KB / {2} KB]\r", (count * 100.0f / totalsize).ToString("F1").PadLeft(5), (count / 1024.0f).ToString("F2").PadLeft(10), (totalsize / 1024.0f).ToString("F2").PadLeft(10)); //запишем блок в файл target.Write(buf, 0, (int)dwRead); } } finally { CloseHandle(handle); target.Close(); } Console.WriteLine(); } static void Main(string[] args) { CreateISO("E", "c:\\distr\\image.iso"); Console.ReadKey(); } } } Для создания образа записываемого носителя (флешки и т.п.), необходимо сначала попросить пользователя закрыть все программы, которые его используют, и открывать диск монопольно (0 вместо FILE_SHARE_READ | FILE_SHARE_WRITE). Если этого не сделать, во время создания образа часть информации на диске может быть параллельно изменена другой программой, и образ получится в итоге в некорректном состоянии. Альтернативное решение этой проблемы - использование Volume Shadow-Copy Services (VSS). Что касается жестких дисков, то создавать ISO-образ таким методом для них не выглядит хорошей идеей. Во-первых, открыть жесткий диск эксклюзивно не всегда получится (а системный и вовсе никогда). Во-вторых, формат ISO для них просто не предназначен, и многие утилиты вообще не смогут обработать ISO-образ для жесткого диска. Вместо этого лучше использовать специальный формат для образов жестких дисков, например VHD. Он поддерживается как средствами Windows, так и сторонними продуктами типа Акрониса. Опять же, для программного создания таких образов можно не изобретать что-то свое, а воспользоваться стандартной утилитой командной строки wbadmin, которая автоматически умеет использовать VSS и создавать образ в том числе системного диска.

Комментариев нет:

Отправить комментарий