Nesse artigo vou mostrar como podemos utilizar Threads no c#, vamos criar um sistema de backup de arquivos simples, a figura abaixo mostra como vai funcionar.

Sistema de Backup
Figura 1. Sistema de Backup

Criei essa classe com static para ser acessada sem precisar instaciar a classe, nela existe uma propriedade que tem o caminho do programa winrar que vai ser utilizado para compactar os arquivos.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Thread1
{
    public static class AppConfig
    {
        /// <summary>
        /// Caminho do executavel do programa WinRAR.
        /// </summary>
        public static string PathWinRAR
        {
            get { return @"C:\Arquivos de programas\WinRAR\WinRAR.exe"; }
        }
    }
}
Listagem 1. Criar a classe AppConfig

Classe MessageProcess é utilizada para transferir as mensagens de informação, aviso, início de processo e o fim do processo para a interface do usuário. O enumerador TypeMessage é utilizado para indicar que tipo de mensagem está sendo passada no objeto da classe MessageProcess.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Thread1
{
    public enum TypeMessage
    {
        Information = 1,
        Warning = 2,
        BeginProcess = 3,
        CurrentProcess = 4,
    }

    public class MessageProcess
    {
        public string Message { get; set; }
        public TypeMessage Type { get; set; }
        public object Value { get; set; }

        public MessageProcess(string message, TypeMessage type)
        {
            this.Message = message;
            this.Type = type;
        }

        public MessageProcess(object value, TypeMessage type)
        {
            this.Type = type;
            this.Value = value;
        }
    }
}
Listagem 2. Criar a classe MessageProcess e o enumerador TypeMessage

Classe que tem a função de pesquisar os diretórios e coletar os arquivos para o backup, compactando os lotes de arquivos e movendo para os lotes para o destino informado pelo usuário.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.IO;
using System.Threading;
using System.Diagnostics;

namespace Thread1
{
    public class BackupFiles
    {
        /// <summary>
        /// Construtor
        /// </summary>
        /// <param name="packageLength">Tamanho do pacote.</param>
        /// <param name="pathSource">Caminho da pasta fonte.</param>
        /// <param name="pathDestiny">Caminho da pasta de destino.</param>
        public BackupFiles(long packageLength, string pathSource, string pathDestiny)
        {
            this.packageLength = packageLength;
            this.pathSource = pathSource;
            this.pathDestiny = pathDestiny;
        }
    }
}
Listagem 3. Criar a classe BackupFiles

Criei delegates e eventos para informar o progresso de backup, a classe BackupFiles dispara eventos conforme o progresso do processo.


/// <summary>
/// Erro do processo
/// </summary>
/// <param name="oException">Objeto da classe Exception</param>
public delegate void ErrorHandle(Exception oException);
/// <summary>
/// Mensagem de informação.
/// </summary>
/// <param name="message">Menssagem</param>
public delegate void MessageHandle(string message);
/// <summary>
/// Inicio de processo
/// </summary>
/// <param name="totalPackage">Total de pacotes.</param>
public delegate void BeginProcessHandle(long totalPackage);
/// <summary>
/// Pacote atual.
/// </summary>
/// <param name="currentPackage">Pacote atual</param>
public delegate void CurrentProcessHandle(long currentPackage);
/// <summary>
/// Fim do processo.
/// </summary>
public delegate void EndProcessHandle();

/// <summary>
/// Foi disparado algum erro no processo.
/// </summary>
public event ErrorHandle OnError;
/// <summary>
/// Foi disparado alguma mensagem de informação do processo.
/// </summary>
public event MessageHandle OnInformation;
/// <summary>
/// Foi disparado alguma mensagem de aviso do processo.
/// </summary>
public event MessageHandle OnWarning;
/// <summary>
/// Foi disparado o começo do processo.
/// </summary>
public event BeginProcessHandle OnBeginProcess;
/// <summary>
/// Dispara o pacote atual.
/// </summary>
public event CurrentProcessHandle OnCurrentProcess;
/// <summary>
/// Fim do processo
/// </summary>
public event EndProcessHandle OnEndProcess;
Listagem 4. Delegates e Eventos

São as variáveis que o sistema utiliza para fazer o backup de arquivos, os objetos Queue são classes de fila, o arquivo que entra primeiro é o primeiro que sai da fila.


private Queue<MessageProcess> messageQueue;//Objeto de fila de mensagens.
private Queue<FileInfo> fileInfoQueue;//Objeto de fila de dados dos arquivos.
private Queue<string> packageInfoQueue;//Objeto de fila de pacotes. 
O usuário passa o tamanho que a pasta tem que ter.
private Queue<string> packageZipInfoQueue;//Objeto de fila de pastas zipadas.

private long packageLength;//Tamanho do pacote.
private string pathSource;//Caminho da pasta fonte.
private string pathDestiny;//Caminho da pasta de destino.
private string pathApplication;//Caminho da pasta da aplicação.

private bool isEndCollectFiles;//Indica se o processo de coleta de arquivos acabou.
private bool isEndMoveFiles;//Indica se o processo de mover arquivos acabou.
private bool isEndCompressionDirectory;//Indica se o processo de zipar os arquivos acabou.
private bool isEndMovePackageDestiny;//Indica se o processo de mover os arquivos zipados 
para a pasta de destino acabou.

private bool isExecute;//Indica se o processo de backup está executando.

private long valueCurrentPackage;//Tamanho da pasta de arquivos.

private Thread collectFilesThread;//Processo de coleta de arquivos
private Thread moveFilesThread;//Proceso de mover arquivos.
private Thread compressionDirectoryThread;//Processo de comprimir arquivos.
private Thread movePackageDestinyThread;//Processo de mover a pasta zipada para a pasta de caminho.
private Thread monitoringProcessThread;//Processo de monitoração de processos.
Listagem 5. Criar as variáveis da classe

No método Start é onde vamos iniciar os processos de coletar e mover arquivos, zipar e mover as pastas dos arquivos.


public void Start()
{
    try
    {
        this.isExecute = true;

        this.valueCurrentPackage = 0;

        messageQueue = new Queue<MessageProcess>();
        fileInfoQueue = new Queue<FileInfo>();
        packageInfoQueue = new Queue<string>();
        packageZipInfoQueue = new Queue<string>();

        this.pathApplication = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "BackupFiles");

        if (Directory.Exists(this.pathApplication))
        {
            Directory.Delete(this.pathApplication, true);
        }

        Directory.CreateDirectory(this.pathApplication);

        this.isEndCollectFiles = false;
        this.isEndMoveFiles = false;
        this.isEndCompressionDirectory = false;
        this.isEndMovePackageDestiny = false;

        messageQueue.Enqueue(new MessageProcess("Iniciando processo de coleta de arquivos!", 
        TypeMessage.Information));
        collectFilesThread = new Thread(new ThreadStart(collectFiles));//Criamos a Thread e o 
        método alvo.
        collectFilesThread.IsBackground = true;//Aqui indicamos que se o Processo principal for 
        finalizado essa thread é finalizada.
        collectFilesThread.Start();

        messageQueue.Enqueue(new MessageProcess("Iniciando processo de copiar arquivos!", 
        TypeMessage.Information));
        moveFilesThread = new Thread(new ThreadStart(moveFiles));//Criamos a Thread e o método alvo.
        moveFilesThread.IsBackground = true;
        //Aqui indicamos que se o Processo principal for finalizado essa thread é finalizada.
        moveFilesThread.Start();//Inicia o processo.

        messageQueue.Enqueue(new MessageProcess("Iniciando processo de zipar arquivos!", 
        TypeMessage.Information));
        compressionDirectoryThread = new Thread(new ThreadStart(compressionDirectory));
        //Criamos a Thread e o método alvo.
        compressionDirectoryThread.IsBackground = true;
        //Aqui indicamos que se o Processo principal for finalizado essa thread é finalizada.
        compressionDirectoryThread.Start();//Inicia o processo.

        messageQueue.Enqueue(new MessageProcess("Iniciando processo de mover
        arquivos para midia de backup!", TypeMessage.Information));
        movePackageDestinyThread = new Thread(new ThreadStart(movePackageDestiny));
        //Criamos a Thread e o método alvo.
        movePackageDestinyThread.IsBackground = true;
        //Aqui indicamos que se o Processo principal for finalizado essa thread é finalizada.
        movePackageDestinyThread.Start();//Inicia o processo.

        monitoringProcessThread = new Thread(new ThreadStart(monitoringProcess));
        //Criamos a Thread e o método alvo.
        monitoringProcessThread.IsBackground = true;
        //Aqui indicamos que se o Processo principal for finalizado essa thread é finalizada.
        monitoringProcessThread.Start();//Inicia o processo.
    }
    catch (Exception oException)
    {
        shootEventError(oException);//Disparamos o evento de error.
    }
}

public void Stop()
{
    try
    {
        this.isExecute = false;

        if (collectFilesThread != null)
        {
            collectFilesThread.Abort();//Aborta a Thread.
            collectFilesThread = null;
        }

        if (moveFilesThread != null)
        {
            moveFilesThread.Abort();//Aborta a Thread.
            moveFilesThread = null;
        }

        if (compressionDirectoryThread != null)
        {
            compressionDirectoryThread.Abort();//Aborta a Thread.
            compressionDirectoryThread = null;
        }

        if (movePackageDestinyThread != null)
        {
            movePackageDestinyThread.Abort();//Aborta a Thread.
            movePackageDestinyThread = null;
        }

        if (monitoringProcessThread != null)
        {
            monitoringProcessThread.Abort();//Aborta a Thread.
            monitoringProcessThread = null;
        }              
    }
    catch (Exception oException)
    {
        shootEventError(oException);//Disparamos o evento de error.
    }
}
Listagem 6. Criar os métodos Start e Stop

Essa a primeira tarefa, calcula o total de arquivos e coleta as informações dos arquivos do diretório informado pelo usuário, cada informação coletada é colocada na fila para ser processada pela próxima tarefa [moveFiles()].

collectFiles = Coletando as informações dos arquivos.


private void collectFiles()
{
    try
    {
        long totalPackage = calculateTotalPackage(this.pathSource);//Calcula total de pasta zipadas.

        messageQueue.Enqueue(new MessageProcess(totalPackage, TypeMessage.BeginProcess));

        getDirectories(this.pathSource);
        this.isEndCollectFiles = true;

        messageQueue.Enqueue(new MessageProcess("Fim do processo de coleta de arquivos!", 
        TypeMessage.Information));
    }
    catch (Exception oException)
    {
        if (this.isExecute)
        {
            shootEventError(oException);
            this.Stop();
        }
    }
}

// Coletando os diretórios da pasta fonte.
private void getDirectories(string path)
{
    List<string> directoryCollection = new List<string>(Directory.GetDirectories(path));

    getFiles(path);

    for (int index = 0; index < directoryCollection.Count; index++)
    {
        string subDiretorio = directoryCollection[index];

        directoryCollection.AddRange(Directory.GetDirectories(subDiretorio));

        getFiles(subDiretorio);
    }
}

//Coletando os arquivos.
private void getFiles(string path)
{
    string[] arquivosArray = Directory.GetFiles(path);

    for (int index = 0; index < arquivosArray.Length; index++)
    {
        FileInfo oFileInfo = new FileInfo(arquivosArray[index]);

        object lockObject = new object();
        lock (lockObject)
        {
            fileInfoQueue.Enqueue(oFileInfo);
        }
    }
}

//Calculando as pastas zipadas que serão geradas. 
private int calculateTotalPackage(string path)
{
    long currentPackageLength = 0;
    int numberPackage = 0;
            
    string[] arrayIniFiles = Directory.GetFiles(path);

    for (int iFile = 0; iFile < arrayIniFiles.Length; iFile++)
    {
        FileInfo oFileInfo = new FileInfo(arrayIniFiles[iFile]);

        if (currentPackageLength + oFileInfo.Length > this.packageLength)
        {
            currentPackageLength = 0;
            numberPackage++;
        }

        currentPackageLength += oFileInfo.Length;
    }

    List<string> directoryCollection = new List<string>(Directory.GetDirectories(path));

    for (int index = 0; index < directoryCollection.Count; index++)
    {
        string subDiretorio = directoryCollection[index];

        directoryCollection.AddRange(Directory.GetDirectories(subDiretorio));

        string[] arrayFiles = Directory.GetFiles(subDiretorio);

        for (int iFile = 0; iFile < arrayFiles.Length; iFile++)
        {
            FileInfo oFileInfo = new FileInfo(arrayFiles[iFile]);

            if (currentPackageLength + oFileInfo.Length > this.packageLength)
            {
                currentPackageLength = 0;
                numberPackage++;
            }

            currentPackageLength += oFileInfo.Length;
        }
    }

    if (currentPackageLength > 0)
    {
        numberPackage++;
    }

    return numberPackage;
}
Listagem 7. Calculando total de arquivos e coletando as informações dos arquivos

Essa é a segunda tarefa, move os arquivos da pasta fonte para a pasta temporária. Esse método soma o tamanho dos arquivos e verifica o tamanho da pasta que o usuário digitou, caso o tamanho ultrapassa ou é igual ele cria outra pasta, coloca na fila o nome da pasta para ser processada pela próxima tarefa [compressionDirectory()].


private void moveFiles()
{
try
{
    long currentPackageLength = 0;
    long numberPackage = 1;
            
    string pathPackage = Path.Combine(this.pathApplication, numberPackage.ToString());

    Directory.CreateDirectory(pathPackage);

    while (isEndCollectFiles == false || fileInfoQueue.Count > 0)
    {
        if (fileInfoQueue.Count > 0)
        {
            FileInfo oFileInfo = null;

            object lockObject = new object();
            lock (lockObject)
            {
                oFileInfo = fileInfoQueue.Dequeue();
            }

            if (oFileInfo.Length > this.packageLength)
            {
                messageQueue.Enqueue(new MessageProcess(String.Format("O arquivo {0} é 
                maior que o tamanho do lote de backup[{1}]!", oFileInfo.Name, oFileInfo.Length), 
                TypeMessage.Warning));
                continue;
            }

            if (currentPackageLength + oFileInfo.Length > this.packageLength)
            {
                currentPackageLength = 0;
                numberPackage++;

                lockObject = new object();
                lock (lockObject)
                {
                    packageInfoQueue.Enqueue(pathPackage);
                }

                pathPackage = Path.Combine(this.pathApplication, numberPackage.ToString());

                Directory.CreateDirectory(pathPackage);
            }

            currentPackageLength += oFileInfo.Length;

            string pathDest = oFileInfo.FullName.Replace(oFileInfo.Name, "")
            .Replace(this.pathSource, pathPackage);

            if (!Directory.Exists(pathDest))
            {
                Directory.CreateDirectory(pathDest);
            }

            File.Copy(oFileInfo.FullName, Path.Combine(pathDest, oFileInfo.Name));
        }

        Thread.Sleep(100);
    }

    if (currentPackageLength > 0)
    {
        object lockObject = new object();
        lock (lockObject)
        {
            packageInfoQueue.Enqueue(pathPackage);
        }
    }

    this.isEndMoveFiles = true;

    messageQueue.Enqueue(new MessageProcess("Fim do processo de copiar arquivos!", 
    TypeMessage.Information));
}
catch (Exception oException)
{
    if (this.isExecute)
    {
        shootEventError(oException);
        this.Stop();
    }
}
}
Listagem 8. Movendo os arquivos da pasta fonte para a pasta temporária

Essa é a terceira tarefa, compacta as pastas que foram criadas pela tarefa de mover arquivos, depois de compactar a pasta é colocada na fila para ser processada pela próxima tarefa [movePackageDestiny()].


private void compressionDirectory()
{
    try
    {
        long numberPackage = 1;

        while (isEndMoveFiles == false || packageInfoQueue.Count > 0)
        {
            if (packageInfoQueue.Count > 0)
            {
                string pathPackage = "";
                string pathPackageZip = "";

                object lockObject = new object();
                lock (lockObject)
                {
                    pathPackage = packageInfoQueue.Dequeue();
                }

                pathPackageZip = String.Format("{0}.rar", Path.Combine(this.pathApplication, 
                numberPackage.ToString()));

                ProcessStartInfo oProcessStartInfo = new ProcessStartInfo(AppConfig.PathWinRAR, 
                String.Format("a -ep1 -r \"{0}\" \"{1}\"", pathPackageZip, pathPackage));

                Process oProcess = Process.Start(oProcessStartInfo);

                oProcess.WaitForExit();

                lockObject = new object();
                lock (lockObject)
                {
                    packageZipInfoQueue.Enqueue(pathPackageZip);
                }

                Directory.Delete(pathPackage, true);

                numberPackage++;
            }

            Thread.Sleep(100);
        }

        this.isEndCompressionDirectory = true;

        messageQueue.Enqueue(new MessageProcess("Fim do processo de zipar arquivos!", 
        TypeMessage.Information));
    }
    catch (Exception oException)
    {
        if (this.isExecute)
        {
            shootEventError(oException);
            this.Stop();
        }
    }
}
Listagem 9. Compacta a pasta criadas pela a tarefa anterior [moveFiles()]

Essa é a quarta tarefa, move as pastas compactadas para o pasta de destino, a pasta de destino é digitado pelo usuário na interface.


private void movePackageDestiny()
{
    try
    {
        while (isEndCompressionDirectory == false || packageZipInfoQueue.Count > 0)
        {
            if (packageZipInfoQueue.Count > 0)
            {
                string sourcePathPackageZip = "";

                object lockObject = new object();
                lock (lockObject)
                {
                    sourcePathPackageZip = packageZipInfoQueue.Dequeue();
                }

                FileInfo oFileInfo = new FileInfo(sourcePathPackageZip);

                File.Move(sourcePathPackageZip, Path.Combine(this.pathDestiny, oFileInfo.Name));

                this.valueCurrentPackage++;

                messageQueue.Enqueue(new MessageProcess(this.valueCurrentPackage, 
                TypeMessage.CurrentProcess));
            }

            Thread.Sleep(100);
        }

        this.isEndMovePackageDestiny = true;

        messageQueue.Enqueue(new MessageProcess("Fim do processo de mover arquivos para midia 
        de backup!", TypeMessage.Information));
    }
    catch (Exception oException)
    {
        if (this.isExecute)
        {
            shootEventError(oException);
            this.Stop();
        }
    }
}
Listagem 10. Movendo as pastas compactadas para o local de destino

Essa é a quinta tarefa, verifica se as tarefas anteriores terminaram e coleta as mensagens disparadas pelas tarefas, depois de coletadas as informações, a tarefa de monitoração dispara eventos para interface do usuário, informano progresso do processo.


public void monitoringProcess()
{
    try
    {
        while (this.isEndCollectFiles == false || this.isEndMoveFiles == false || 
        this.isEndCompressionDirectory == false ||
                this.isEndMovePackageDestiny == false || messageQueue.Count > 0)
        {
            if (messageQueue.Count > 0)
            {
                MessageProcess oMessageProcess = null;

                object lockObject = new object();
                lock (lockObject)
                {
                    oMessageProcess = messageQueue.Dequeue();
                }

                if (oMessageProcess.Type == TypeMessage.Information)
                {
                    shootEventInformation(oMessageProcess.Message);
                }
                else if (oMessageProcess.Type == TypeMessage.Information)
                {
                    shootEventWarning(oMessageProcess.Message);
                }
                else if (oMessageProcess.Type == TypeMessage.BeginProcess)
                {
                    shootEventBeginProcess((long)oMessageProcess.Value);
                }
                else if (oMessageProcess.Type == TypeMessage.CurrentProcess)
                {
                    shootEventCurrentProcess((long)oMessageProcess.Value);
                }
            }

            Thread.Sleep(200);
        }

        if (Directory.Exists(this.pathApplication))
        {
            Directory.Delete(this.pathApplication, true);
        }

        shootEventEndProcess();
    }
    catch (Exception oException)
    {
        if (this.isExecute)
        {
            shootEventError(oException);
            this.Stop();
        }
    }
}
Listagem 11. Monitorar os processos

Os métodos para disparar eventos para a interface do usuário, antes de disparar evento a aplicação verifica se o evento foi implementado pela a interface do usuário, caso não seja implentado o evento é null.


private void shootEventError(Exception oException)
{
    if (OnError != null)
    {
        OnError(oException);
    }
}

private void shootEventWarning(string message)
{
    if (OnWarning != null)
    {
        OnWarning(message);
    }
}

private void shootEventInformation(string message)
{
    if (OnInformation != null)
    {
        OnInformation(message);
    }
}

private void shootEventBeginProcess(long totalPackage)
{
    if (OnBeginProcess != null)
    {
        OnBeginProcess(totalPackage);
    }
}

private void shootEventCurrentProcess(long currentPackage)
{
    if (OnCurrentProcess != null)
    {
        OnCurrentProcess(currentPackage);
    }
}

private void shootEventEndProcess()
{
    if (OnEndProcess != null)
    {
        OnEndProcess();
    }
}
Listagem 12. Disparando eventos do processo

Interface do usuário para interagir com o processos de backup.

Tela de backup
Figura 2. Tela de backup

Para atualizar o formulário teremos que verificar se outra thread não está acessando o mesmo componente. Esse é um problema de criar sistemas multitarefas. Para verificar se outra thread está acessando o componente utilizamos a propriedade InvokeRequired, caso essa propriedade true esse método é invocado novamente, utilizamos o método .Invoke().


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Thread1
{
    public partial class MainForm : Form
    {
        private delegate void EnabledDisabledHandle(bool isBegin);

        private event BackupFiles.BeginProcessHandle OnBeginProcess;
        private event BackupFiles.CurrentProcessHandle OnCurrentProcess;
        private event BackupFiles.ErrorHandle OnError;
        private event BackupFiles.MessageHandle OnInformation;
        private event BackupFiles.MessageHandle OnWarnig;
        private event EnabledDisabledHandle OnEnabledDisabled;

        private BackupFiles oBackupFiles;

        private int totalPackageBackup;

        public MainForm()
        {
            InitializeComponent();

            OnBeginProcess += new BackupFiles.BeginProcessHandle(oBackupFiles_OnBeginProcess);
            OnCurrentProcess += new BackupFiles.CurrentProcessHandle(oBackupFiles_OnCurrentProcess);
            OnError += new BackupFiles.ErrorHandle(oBackupFiles_OnError);
            OnInformation += new BackupFiles.MessageHandle(oBackupFiles_OnInformation);
            OnWarnig += new BackupFiles.MessageHandle(oBackupFiles_OnWarning);

            OnEnabledDisabled += new EnabledDisabledHandle(enabledDisabled);
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            enabledDisabled(false);
        }

        void oBackupFiles_OnEndProcess()
        {
            oBackupFiles_OnCurrentProcess(this.totalPackageBackup);
            oBackupFiles_OnInformation("Fim do processo de backup!");
            enabledDisabled(false);
        }

        void oBackupFiles_OnError(Exception oException)
        {
            if (!errorListBox.InvokeRequired)
            {
                errorListBox.Items.Add(oException.Message);
                errorListBox.SelectedIndex = errorListBox.Items.Count - 1;
            }
            else
            {
                errorListBox.Invoke(OnError, new object[1] { oException });
            }
        }

        void oBackupFiles_OnInformation(string message)
        {
            if (!informationListBox.InvokeRequired)
            {
                informationListBox.Items.Add(message);
                informationListBox.SelectedIndex = errorListBox.Items.Count - 1;
            }
            else
            {
                informationListBox.Invoke(OnInformation, new object[1] { message });
            }
        }

        void oBackupFiles_OnWarning(string message)
        {
            if (!warningListBox.InvokeRequired)
            {
                warningListBox.Items.Add(message);
                warningListBox.SelectedIndex = errorListBox.Items.Count - 1;
            }
            else
            {
                warningListBox.Invoke(OnWarnig, new object[1] { message });
            }
        }

        void oBackupFiles_OnCurrentProcess(long currentPackage)
        {
            if (!progressBar1.InvokeRequired)
            {
                if (progressBar1.Maximum >= currentPackage)
                {
                    progressBar1.Value = (int)currentPackage;
                }
            }
            else
            {
                progressBar1.Invoke(OnCurrentProcess, new object[1] { currentPackage });
            }
        }

        void oBackupFiles_OnBeginProcess(long totalPackage)
        {
            if (!progressBar1.InvokeRequired)
            {
                progressBar1.Value = 0;
                progressBar1.Minimum = 0;
                progressBar1.Maximum = (int)totalPackage;

                this.totalPackageBackup = (int)totalPackage;
            }
            else
            {
                progressBar1.Invoke(OnBeginProcess, new object[1] { totalPackage });
            }
        }

        private void startButton_Click(object sender, EventArgs e)
        {
            progressBar1.Value = 0;

            long packageLength = ((long)packageLengthNumericUpDown.Value * 1024) * 1024;

            oBackupFiles = new BackupFiles(packageLength, pathSourceTextBox.Text, 
            pathDestinyTextBox.Text);

            oBackupFiles.OnError += new BackupFiles.ErrorHandle(oBackupFiles_OnError);
            oBackupFiles.OnEndProcess += new BackupFiles.EndProcessHandle(oBackupFiles_OnEndProcess);
            oBackupFiles.OnBeginProcess += new BackupFiles.BeginProcessHandle(
            oBackupFiles_OnBeginProcess);
            oBackupFiles.OnCurrentProcess += new BackupFiles.CurrentProcessHandle(
            oBackupFiles_OnCurrentProcess);
            oBackupFiles.OnWarning += new BackupFiles.MessageHandle(oBackupFiles_OnWarning);
            oBackupFiles.OnInformation += new BackupFiles.MessageHandle(oBackupFiles_OnInformation);

            enabledDisabled(true);

            oBackupFiles.Start();
        }        

        private void stopButton_Click(object sender, EventArgs e)
        {
            oBackupFiles.Stop();

            enabledDisabled(false);
        }

        private void pathSourceButton_Click(object sender, EventArgs e)
        {
            if (folderBrowserDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                pathSourceTextBox.Text = folderBrowserDialog1.SelectedPath;
            }
        }

        private void pathDestinyButton_Click(object sender, EventArgs e)
        {
            if (folderBrowserDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                pathDestinyTextBox.Text = folderBrowserDialog1.SelectedPath;
            }
        }

        private void enabledDisabled(bool isBegin)
        {
            if (!warningListBox.InvokeRequired)
            {
                if (isBegin)
                {
                    stopButton.Enabled = true;
                    packageLengthNumericUpDown.Enabled = false;
                    pathDestinyTextBox.Enabled = false;
                    pathSourceTextBox.Enabled = false;
                    pathDestinyButton.Enabled = false;
                    pathSourceButton.Enabled = false;
                    startButton.Enabled = false;
                    progressBar1.Enabled = true;
                }
                else
                {
                    stopButton.Enabled = false;
                    packageLengthNumericUpDown.Enabled = true;
                    pathDestinyTextBox.Enabled = true;
                    pathSourceTextBox.Enabled = true;
                    pathDestinyButton.Enabled = true;
                    pathSourceButton.Enabled = true;
                    startButton.Enabled = true;

                    progressBar1.Enabled = false;
                }
            }
            else
            {
                warningListBox.Invoke(OnEnabledDisabled, new object[1] { isBegin });
            }
        }
    }
}
Listagem 13. Código do formulário

Esse é um sistema simples de backup, pode ser criadas mais funcionalidades, como restaurar os arquivos.