Feedback

C# - ISO-Abbild von einer CD/DVD erstellen

Veröffentlicht von am 23.11.2009
(5 Bewertungen)
Mit folgender Klasse, kann man ohne weiteres ein ISO-Abbild einer CD/DVD erstellen. Dabei gibt die Klasse, den Status anhand von geschriebenen Bytes, die Dauer oder den Fortschritt anhand von Prozenten zurück. Außerdem kann man die Methoden erweitern um evtl. mehr Informationen beim erstellen zurück zubekommen.

Folgende Events werden angeboten:
- OnProgress:
Wird ausgelöst, wenn ein Fortschritt stattfindet
- OnMessage:
Wird ausgelöst, wenn eine Meldung beim erstellen auftaucht (Fehler, etc.)
- OnFinish:
Wird ausgelöst, wenn die Erstellung fertig ist

Hinweiß:
Diese Klasse kann nicht genutzt werden um Audio-CDs zu kopieren!

Nutzung:
//Initialisierung
IsoFromMedia iso;
iso = new IsoFromMedia();
iso.OnFinish += new IsoEventHandler(iso_OnFinish);
iso.OnMessage += new IsoEventHandler(iso_OnMessage);
iso.OnProgress += new IsoEventHandler(iso_OnProgress);


//Aufruf
Status status = iso.CreateIsoFromMedia(@"Z:\", @"C:\Daten\test.iso");

if (status != Status.Running)
{
iso.Stop();
//Gebe eine Meldung beim Fehler aus
}


Die Nutzung ist denkbar einfach, aber falls gewünscht, kann ich ein simples Beispielprogramm aufsetzen.

Update 25.11.2009:
Neuen Status "NotReady" implementiert. Damit wird jetzt der Fehler abgefangen, wenn das Gerät noch nicht bereit ist.
Snippet in VB übersetzen
/*
 * IsoFromMedia - Erstellung von einem ISO-Image anhand eines CD/DVD Mediums
 * ----------------------------------------------------------------
 * Copyright © 2009 Konstantin Gross
 * http://www.texturenland.de
 * http://blog.texturenland.de
*/

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;

namespace TL.IsoImage
{
    /// <summary>
    /// Ermöglicht ISO-Images von Medien (CD/DVD) anzulegen
    /// </summary>
    public class IsoFromMedia
    {
        #region Variablen
        /// <summary>
        /// BackgroundWorker für das erstellen der ISO-Datei
        /// </summary>
        BackgroundWorker bgCreator;
        
        /// <summary>
        /// Lesender FileStream
        /// </summary>
        FileStream streamReader;

        /// <summary>
        /// Schreibender FileStream
        /// </summary>
        FileStream streamWriter;
        #endregion

        #region Konstanten
        /// <summary>
        /// 128 KB Blockgröße
        /// </summary>
        const int BUFFER = 0x20000;

        /// <summary>
        /// Maximal 4 GB Größe pro Datei auf FAT32-System
        /// </summary>
        const long LIMIT = 4294967296;
        #endregion

        #region Events
        /// <summary>
        /// Wird ausgelöst, wenn ein Fortschritt stattfindet
        /// </summary>
        public event IsoEventHandler OnProgress;

        /// <summary>
        /// Wird ausgelöst, wenn eine Meldung beim erstellen auftaucht
        /// </summary>
        public event IsoEventHandler OnMessage;

        /// <summary>
        /// Wird ausgelöst, wenn die Erstellung fertig ist
        /// </summary>
        public event IsoEventHandler OnFinish;
        #endregion

        #region Eigenschaften
        /// <summary>
        /// Pfad zu der ISO-Datei
        /// </summary>
        string PathToIso { get; set; }

        /// <summary>
        /// Größe des Mediums
        /// </summary>
        public long MediumSize { get; set; }

        /// <summary>
        /// Handle vom Medium
        /// </summary>
        SafeFileHandle Handle { get; set; }
        #endregion

        #region Konstruktor
        /// <summary>
        /// Konstruktor
        /// </summary>
        public IsoFromMedia()
        {
            bgCreator = new BackgroundWorker();
            bgCreator.WorkerSupportsCancellation = true;
            bgCreator.DoWork += new DoWorkEventHandler(bgCreator_DoWork);
            bgCreator.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgCreator_RunWorkerCompleted);
        }
        #endregion

        #region Methoden
        /// <summary>
        /// Startet den Thread mit dem erstellen der ISO-Datei
        /// </summary>
        void bgCreator_DoWork(object sender, DoWorkEventArgs e)
        {
            Stopwatch stopWatch = new Stopwatch();
            stopWatch.Start();

            try
            {
                streamReader = new FileStream(Handle, FileAccess.Read, BUFFER);
                streamWriter = new FileStream(PathToIso, FileMode.Create, FileAccess.Write, FileShare.None, BUFFER);

                byte[] buffer = new byte[BUFFER];

                //Lese Buffer-Blöcke von der Quelle und schreibe diese in die ISO-Datei
                do
                {
                    if (bgCreator.CancellationPending)
                    {
                        e.Cancel = true;
                        Stop();
                        break;
                    }

                    streamReader.Read(buffer, 0, BUFFER);
                    streamWriter.Write(buffer, 0, BUFFER);

                    if (OnProgress != null)
                    {
                        //Fortschritt in Prozent
                        int percent = Convert.ToInt32((streamWriter.Length * 100) / MediumSize);

                        EventIsoArgs eArgs = new EventIsoArgs(streamWriter.Position, percent);

                        OnProgress(eArgs);
                    }
                } while (streamReader.Position == streamWriter.Position);
            }
            catch (Exception ex)
            {
                if (OnMessage != null)
                {
                    EventIsoArgs eArgs = new EventIsoArgs("Fehler beim erstellen des Images: " + ex.Message);
                    OnMessage(eArgs);
                }
            }
            finally
            {
                if (OnFinish != null)
                {
                    EventIsoArgs eArgs = new EventIsoArgs(stopWatch.Elapsed);
                    OnFinish(eArgs);
                }
            }
        }

        /// <summary>
        /// Wenn die Erstellung abgeschlossen ist
        /// </summary>
        void bgCreator_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            CloseAll();
        }

        /// <summary>
        /// Erstellt ein ISO-Image von einem Medium (CD/DVD)
        /// </summary>
        /// <param name="source">CD/DVD</param>
        /// <param name="destination">Pfad, wohin die ISO-Datei abgelegt werden soll</param>
        /// <returns>
        /// Running = Erstellung läuft
        /// InvalidHandle = Ungültiger Handle
        /// NoDevice = Die Quelle ist kein Medium (CD/DVD)
        /// NotEnoughMemory = Nicht genügend Speicherplatz vorhanden
        /// LimitExceeded = Ziel überschreitet FAT32 Größe von 4 GB (4096 MB)
        /// NotReady = Das Gerät ist nicht bereit
        /// </returns>
        public Status CreateIsoFromMedia(string source, string destination)
        {
            //Ist das Gerät bereit?
            if (!new DriveInfo(source).IsReady)
                return Status.NotReady;

            //Quelle CD/DVD
            if (new DriveInfo(source).DriveType != DriveType.CDRom)
                return Status.NoDevice;

            //Mediumgröße ermitteln
            MediumSize = GetMediumLength(source);

            //Überprüfe Speicherplatz
            long diskSize = new DriveInfo(Path.GetPathRoot(destination)).AvailableFreeSpace;

            if (diskSize <= MediumSize)
                return Status.NotEnoughMemory;

            //Überprüfe Kapazität von > 4096 MB (NTFS)
            if (!CheckNTFS(destination) && MediumSize >= LIMIT)
                return Status.LimitExceeded;

            //Erstelle Handle
            Handle = Win32.CreateFile(source);

            if (!string.IsNullOrEmpty(destination))
                PathToIso = destination;

            //Wenn ungültiger Handle oder geschlossener Handle
            if (Handle.IsInvalid || Handle.IsClosed)
                return Status.InvalidHandle;

            //Erstelle Thread zum erstellen der ISO-Datei
            bgCreator.RunWorkerAsync();

            return Status.Running;            
        }

        /// <summary>
        /// Beendet die Erstellung des Images und löscht zugleich das Image
        /// </summary>
        public void Stop()
        {
            CloseAll();

            if (File.Exists(PathToIso))
                File.Delete(PathToIso);

            if (OnMessage != null)
            {
                EventIsoArgs e = new EventIsoArgs(@"Erstellung des Images abgebrochen");
                OnMessage(e);
            }
        }

        /// <summary>
        /// Schließt alle Streams und Handles und gibt Ressourcen frei
        /// </summary>
        private void CloseAll()
        {
            if (bgCreator != null)
            {
                bgCreator.CancelAsync();
                bgCreator.Dispose();
            }

            if (streamReader != null)
            {
                streamReader.Close();
                streamReader.Dispose();
            }

            if (streamWriter != null)
            {
                streamWriter.Close();
                streamWriter.Dispose();
            }

            if (Handle != null)
            {
                Handle.Close();
                Handle.Dispose();
            }            
        }

        /// <summary>
        /// Größe des Mediums (CD/DVD)
        /// </summary>
        /// <param name="drive">Quell-Laufwerk.</param>
        /// <returns>Größe in Bytes</returns>
        private long GetMediumLength(string drive)
        {
            return new DriveInfo(drive).TotalSize;
        }

        /// <summary>
        /// Zeigt an, ob das Dateisystem NTFS ist
        /// </summary>
        /// <param name="destination">Pfad zu der ISO-Datei</param>
        /// <returns>True, wenn NTFS</returns>
        private bool CheckNTFS(string destination)
        {
            return new DriveInfo(Path.GetPathRoot(destination)).DriveFormat == "NTFS" ? true : false;
        }
        #endregion
    }

    #region Enumeration
    /// <summary>
    /// Gibt den Status der ISO-Image Erstellung aus
    /// </summary>
    public enum Status
    {
        /// <summary>
        /// Erstellung läuft
        /// </summary>
        Running = 1,
        /// <summary>
        /// Ungültiger Handle
        /// </summary>
        InvalidHandle = -1,
        /// <summary>
        /// Die Quelle ist kein Medium (CD/DVD)
        /// </summary>
        NoDevice = -2,
        /// <summary>
        /// Nicht genügend Speicherplatz vorhanden
        /// </summary>
        NotEnoughMemory = -3,
        /// <summary>
        /// Ziel überschreitet FAT32 Größenlimit von 4 GB (4096 MB)
        /// </summary>
        LimitExceeded = -4,
        /// <summary>
        /// Das Gerät ist nicht bereit
        /// </summary>
        NotReady = -5
    }
    #endregion

    #region EventIsoArgs
    public delegate void IsoEventHandler(EventIsoArgs e);

    /// <summary>
    /// Beinhaltet zusätzliche Daten für Event
    /// </summary>
    public class EventIsoArgs : EventArgs
    {
        /// <summary>
        /// Bereits geschriebene Bytes
        /// </summary>
        public long WrittenSize { get; private set; }

        /// <summary>
        /// Fortschritt in Prozenten
        /// </summary>
        public int ProgressPercent { get; private set; }

        /// <summary>
        /// Laufzeit
        /// </summary>
        public TimeSpan ElapsedTime { get; private set; }

        /// <summary>
        /// Nachricht
        /// </summary>
        public string Message { get; private set; }

        public EventIsoArgs(TimeSpan value)
            : base()
        {
            ElapsedTime = value;
        }

        public EventIsoArgs(long value, int percent)
            : base()
        {
            WrittenSize = value;
            ProgressPercent = percent;
        }

        public EventIsoArgs(string value)
            : base()
        {
            Message = value;
        }
    }
    #endregion

    #region Win32
    /// <summary>
    /// Stellt die Funktionalität bereit, mit Windowsmethoden 
    /// </summary>
    internal class Win32
    {
        /// <summary>
        /// Lesezugriff
        /// </summary>
        static uint GENERIC_READ = 0x80000000;

        /// <summary>
        ///  Gibt an, dass nachfolgende öffnen Vorgänge auf das Objekt nur erfolgreich sind, wenn ein Lesezugriff angefordert wird
        /// </summary>
        static uint FILE_SHARE_READ = 0x1;

        /// <summary>
        /// Öffnet die Datei. Wird fehlschlagen, wenn die Datei nicht existiert
        /// </summary>
        static uint OPEN_EXISTING = 0x3;

        /// <summary>
        /// Gibt an, dass die Datei keine anderen Attribute hat, dieses Attribut ist nur gültig wenn es allein verwendet wird
        /// </summary>
        static uint FILE_ATTRIBUTE_NORMAL = 0x00000080;

        /// <summary>
        /// Gibt ein Handle zurück, das genutzt werden kann, um auf eine Datei oder ein Gerät
        /// auf unterschiedlichste Art und Weise zuzugreifen
        /// </summary>
        /// <param name="lpFileName">Der Name der Datei oder des Gerätes, das erstellt oder geöffnet werden soll</param>
        /// <param name="dwDesiredAccess">Der Zugriff auf die angeforderte Datei oder des Gerätes</param>
        /// <param name="dwShareMode">Der angeforderte Austausch-Modus der Datei oder des Geräts</param>
        /// <param name="lpSecurityAttributes">Zeiger der auf ein Sicherheits-Attribut zeigt</param>
        /// <param name="dwCreationDisposition">Eine Aktion, die auf eine Datei oder ein Gerät durchgeführt wird, wenn es vorhanden ist oder nicht vorhanden ist</param>
        /// <param name="dwFlagsAndAttributes">Datei/Gerät-Attribut, am häufigsten wird FILE_ATTRIBUTE_NORMAL genutzt</param>
        /// <param name="hTemplateFile">Ein Handle auf eine Vorlagen-Datei (beim öffnen spielt dieser Parameter keine Rolle)</param>
        /// <returns>Wenn die Methode erfolgreich ausgeführt werden konnte, gibt es einen gültigen Handle auf eine Datei oder ein Gerät zurück</returns>
        [DllImport("kernel32.dll", SetLastError = true)]
        static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);

        /// <summary>
        /// Erstellt das Handle vom Medium
        /// </summary>
        /// <param name="device">Medium (CD/DVD)</param>
        /// <returns>Handle des Mediums</returns>
        public static SafeFileHandle CreateFile(string device)
        {
            //Prüfe wie das Medium angegeben wurde und kürz es demenstprechend
            //Z.B. Z:\ -> Z: ansonsten ändere nichts
            string devName = device.EndsWith(@"\") ? device.Substring(0, device.Length - 1) : device;

            //Erstelle das Handle
            IntPtr devHandle = CreateFile(string.Format(@"\\.\{0}", devName), GENERIC_READ, FILE_SHARE_READ, IntPtr.Zero,
                OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);

            return new SafeFileHandle(devHandle, true);
        }
    }
    #endregion
}

4 Kommentare zum Snippet

Rainer Hilmer schrieb am 23.11.2009:
Cool! #ThumbsUp
Konstantin Gross schrieb am 25.11.2009:
Da ich das Snippet nicht editieren kann, aufgrund eines "Yellow Screen of Death". Möchte ich die Anpassung über diesen Kommentar machen.

Update 25.11.2009:
Neuen Status "NotReady" implementiert. Damit wird jetzt der Fehler abgefangen, wenn das Gerät noch nicht bereit ist.

Dazu muss in die Enumeration "Status", folgender Status hinzugefügt werden:

/// <summary>
/// Das Gerät ist nicht bereit
/// </summary>
NotReady = -5


Und der Methode "CreateIsoFromMedia" muss am Anfang der Methode folgender Code hinzugefügt werden (vor dem Kommentar "Quelle CD/DVD"):

//Ist das Gerät bereit?
if (!new DriveInfo(source).IsReady)
return Status.NotReady;
nex schrieb am 11.09.2016:
Please send me the source code to my email for this your project - ISO-Abbild von einer CD / DVD erstellen. Thanks a lot.

my email is:
scprs@ymail.com
Didi2505 schrieb am 08.03.2017:
Hi, hab versucht den Schnipsel zum laufen zu bekommen. Geht soweit, aber bekomme keine Events zurück. Kann man mir ein Beispielprogramm mit der Klasse senden?

Ich wäre euch sehr dankbar...

MfG Didi2505
Meine E-Mail:
didiw2505@outlook.com
 

Logge dich ein, um hier zu kommentieren!