Show / Hide Table of Contents

MSDN.WhiteKnight - Stack Overflow answers

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

Answer 878471

Link

Образ 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 и создавать образ в том числе системного диска.


Content is retrieved from StackExchange API.

Auto-generated by ruso-archive tools.

Back to top Stack Overflow answers (published from sources in GitHub repository). Copyright (c) 2020, MSDN.WhiteKnight. Content licensed under BSD 3-Clause License.
Generated by DocFX