Позволит ли .NET стек создать что-то подобное?
Или нужно или стоит пользоваться другими языками программирования и технологиями для решения поставленной задачи. Подозреваю что что-то вроде плюсов, ибо приходится работать наверняка с чем-то низко системным.
Ответ
Образ 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]
",
(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 и создавать образ в том числе системного диска.
Комментариев нет:
Отправить комментарий