Click here to Skip to main content
15,881,381 members
Articles / Desktop Programming / Windows Forms

CopyFileGeneric

Rate me:
Please Sign up or sign in to vote.
4.76/5 (15 votes)
29 Apr 2010CPOL3 min read 76.6K   7.2K   68   28
Copy files with additional functionalities

Introduction

Windows Copy just copies the files while the only functionality it provides is cancelling the copy. However there were a few additional functionalities that I required often; they include pausing a copying process, have the files that are not copied list at the end rather than annoying in between.

CopyFile1.png

CopyFile2.png

This application includes the following features:

  • Pause/Resume a copy process
  • Skip a single file
  • Suppress warnings and errors while copying
  • Show list of un-copied files at the end and option to retry copying them
  • Option to suppress overwrite warnings if a file exists.

I have divided the code in two different segments:

  • Copy: deals with copying the files (Project Backup2)
  • User Interface: deals with the User Interface and synchronization with Copy (Project CopyFilesGeneric)

Using the Code

Project Backup2

This is a Library Project. I have used the Shell Function CopyFileEx() to copy the files. This function is defined as:

C#
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern unsafe bool CopyFileEx(string ExistingFileName, string NewFileName, 
CopyProgressRoutine ProgressRoutine, IntPtr Data, ref Boolean Cancel, 
CopyFileFlags CopyFlags);   

Another function HResultToString() converts an HRESULT value to the corresponding message. For this, I have used a file XPSP2CodesEn.csv which stores the HRESULT and their corresponding string message.

C#
public static string HResultToString(uint result)
{
	System.IO.StreamReader str = 
		new StreamReader(Properties.Resources.ErrorCodeSource);
	LumenWorks.Framework.IO.Csv.CsvReader csv = new CsvReader(str, true);

         csv.SkipEmptyLines = true;
         csv.SupportsMultiline = true;

         foreach (var item in csv)
         {
         	if (uint.Parse(item[0], 
		System.Globalization.CultureInfo.InvariantCulture) == (uint)result)
                 {
                 	return item[2];
                 }
         }

         return "INVALID HRESULT";
} 

For reading the CSV file, I have used CsvReader by Sebastien Lorion (http://www.codeproject.com/KB/database/CsvReader.aspx).

The main function CopyFiles() performs the copying operation. It is to be noted that until the files are not counted, it will be useless to start copying. This is because while counting files, the CountFiles() function marks the files to be copied (by adding it to files).

C#
private void CopyFiles()
{
      if (this.CurrentStatus == CopyStatus.Counting)
      { 
      	    restEvent.Reset();    
      }

      resetEvent.WaitOne();

      this.CurrentStatus = CopyStatus.Copying;
      RaiseCopyStartedEvent();
      RaiseStatusChangedEvent();
            
      foreach (KeyValuePair<string, double> item in files)
      {
      Retry:
      	    bool success = false;
            Boolean b = false;
            previousCopiedSize = 0;
            skip = false;

	    string destinationDirectory = Destination + 
		item.Key.Substring(0, item.Key.LastIndexOf(
                	Path.DirectorySeparatorChar)).Substring(sourceDirectory.Length - 1);
            if (Directory.Exists(item.Key))
            {                    
                  if ((Directory.CreateDirectory(Destination + 
			item.Key.Substring(sourceDirectory.Length - 1)) == null)) 
                        	unCopiedFiles.Add(new UncopiedFilesStructure(item.Key,
			item.Value, UnsafeNativeMethods.GetHResult(
                          	(uint)Marshal.GetLastWin32Error()).ToString()));
                 
                  continue;
            }                

            if (cancelCopy)
            {
                    RaiseCopyCanceledEvent();
                    copyThread.Abort();
            }


            if (destinationDirectory[destinationDirectory.Length - 1] != 
			Path.DirectorySeparatorChar)
               		destinationDirectory += Path.DirectorySeparatorChar;

               this.CurrentFile = item.Key;
               RaiseFileCopyStartEvent(item.Key, Path.Combine
		(destinationDirectory, Path.GetFileName(item.Key)), item.Value);

               unsafe
               {
                     success = UnsafeNativeMethods.CopyFileEx(item.Key,
                        destinationDirectory + Path.GetFileName(item.Key),
                        new CopyProgressRoutine(CopyProgressRoutineHandler), IntPtr.Zero,
                        ref b, this.CopyFlags);
               }
               if (!success)
               {
                   string hres = UnsafeNativeMethods.HResultToString
		(UnsafeNativeMethods.GetHResult((uint)Marshal.GetLastWin32Error()));

                   if (skip) { unCopiedFiles.Add(new UncopiedFilesStructure
			(item.Key, item.Value, "Skipped: " + hres)); continue; }

                   switch (RaiseFileCopyUnsuccessfulEvent
			(item.Key, destinationDirectory + Path.GetFileName(item.Key),
			 item.Value, hres))
                   {
                        case FileHandleProcedure.Skip:
                        case FileHandleProcedure.Cancel:
                            unCopiedFiles.Add(new UncopiedFilesStructure
				(item.Key, item.Value, hres)); break;
                        case FileHandleProcedure.Retry: goto Retry;
                        case FileHandleProcedure.CancelAll: this.cancelCopy = true; break;
                        default: break;
                   }
               }
               else
               {
                    RaiseFileCopyCompletedEvent(item.Key, 
			destinationDirectory + Path.GetFileName(item.Key), item.Value);
               }
        }

        this.CurrentStatus = CopyStatus.CopyCompleted;
        RaiseStatusChangedEvent();
        RaiseCopyCompletedEvent();
} 

Also the user must be notified of a copy failure and dealt with. This task is performed by event FileCopyUnsuccessfull.

Project CopyFile

This project references the Backup2 project and deals with the User Interface and synchronization process.

The most notable function that needs a little explaining is UpdateUI(). This function is defined as:

C#
/// <summary>
/// Synchronize UI with the copying status
/// </summary>
/// <param name="sync">Synchronize Operation To perform</param>
/// <param name="objs">Objects and their values. It must have the following structure:
/// index representation
/// 0. Text; Control whose caption(text) is to be changed
/// 1. the text for control at index 0
/// 2. Status; Text for CopyStatusLabel
/// 3. Status; Text for CurrentFileLabel
/// 4. Progress; the progressbar whose value is to be updated
/// 5. the value of the progressbar specified at index 4
/// 6. UpdateList; Listbox that has to be updated
/// 7. Operation that has to be applied on Listbox at index 6; i.e., ADD, DELETE or CLEAR
/// 8+. objects at index 8 or more are added or deleted from 
/// the listbox at index 6, depending on the operation specified at index 7
/// </param>
private void UpdateUI(SyncronizationOperations sync, object[] objs)
{
    if ((sync & SyncronizationOperations.CheckBox) == SyncronizationOperations.CheckBox)
    {
        CheckBoxStatus();
    }
    if ((sync & SyncronizationOperations.SetText) == SyncronizationOperations.SetText)
    {
        if (objs.Length > 1 && objs[0] != null && objs[1] != null)
        {
            Control content = objs[0] as Control;
            content.Text = objs[1] as string;
        }
    }
    if ((sync & SyncronizationOperations.SetStatus) == SyncronizationOperations.SetStatus)
    {
        if (objs.Length > 2 && objs[2] != null)
        {
            CopyStatusLabel.Text = objs[2] as string;
            this.Text = CopyStatusLabel.Text;
        }
        if (objs.Length > 3 && objs[3] != null)
        {
            CurrentFileLabel.Text = objs[3] as string;
        }
    }
    if ((sync & SyncronizationOperations.SetProgress) == 
			SyncronizationOperations.SetProgress)
    {
        if (objs.Length > 5 && objs[4] != null && objs[5] != null)
        {
            ProgressBar p = objs[4] as ProgressBar;
            p.Value = Convert.ToInt32(objs[5], CultureInfo.InvariantCulture);
            
        }
    }
    if ((sync & SyncronizationOperations.UpdateList) == 
			SyncronizationOperations.UpdateList)
    {
        if (objs.Length > 7 && objs[6] != null && objs[7] != null)
        {
            ListView list = objs[6] as ListView;                    
            switch ((ListboxOperations)objs[7])
            {
                case ListboxOperations.ADD:
                    for (int i = 8; i < objs.Length; i++)
                    {                                
                        list.Items.Add(new ListViewItem
				((objs[i] as string).Split('\t')));
                    }
                    break;
                case ListboxOperations.DELETE:
                    for (int i = 8; i < objs.Length; i++)
                    {
                        list.Items.Remove(new ListViewItem
				((objs[i] as string).Split('\t')));
                    }
                    break;
                case ListboxOperations.CLEAR:
                    list.Items.Clear();
                    break;
                default:
                    break;
            }
        }
    }
}

This code is a really confusing one and needs editing. The thing is I'm new to the concept of threading. This seemed to be the best method I could have came up with. In this method, there are 2 parameters: the first is SyncronizationOperations that defines the operations to be performed; and the second one is an array of objects. The object at index 0 has a control whose text (caption) is to be changed; object at index 1 has the string that has to be assigned to object at index 0. At index 2, we have string that is used to update the overall progress of the copier. Similarly string at index 3 is used to update the name of the current file. Object at index 4 has a progressbar object while that at index 5 has the new value progressbar at index 4. At index 6, we have a listview which is to be updated. The update operations of listview is provide by object (ListboxOperations) and the subsequent elements are the values of listview that are to be operated upon.

History

  • 23 April 2010 - Initial upload
  • 30 April 2010 - Revision
    • Added Support UI to select files to copy
    • Some other minor changes

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Other
India India
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
BugVolumeGuid support ? Pin
bindlishankush29-Nov-11 17:33
bindlishankush29-Nov-11 17:33 
GeneralProcessing stops at 'CountCompleted' [modified] Pin
IanWithycombe20-Oct-10 9:53
IanWithycombe20-Oct-10 9:53 
GeneralThanks for adding the User Interface Pin
Nosa Osayamwen4-May-10 17:53
Nosa Osayamwen4-May-10 17:53 
GeneralRe: Thanks for adding the User Interface Pin
Abhishek Shekhar4-May-10 21:24
Abhishek Shekhar4-May-10 21:24 
GeneralHandle For Paste Pin
Anubhava Dimri4-May-10 0:39
Anubhava Dimri4-May-10 0:39 
GeneralRe: Handle For Paste Pin
Abhishek Shekhar4-May-10 17:16
Abhishek Shekhar4-May-10 17:16 
GeneralRe: Handle For Paste Pin
Anubhava Dimri4-May-10 18:26
Anubhava Dimri4-May-10 18:26 
GeneralRe: Handle For Paste Pin
Abhishek Shekhar4-May-10 21:12
Abhishek Shekhar4-May-10 21:12 
GeneralRe: Handle For Paste Pin
Anubhava Dimri4-May-10 21:19
Anubhava Dimri4-May-10 21:19 
GeneralRe: Handle For Paste Pin
Abhishek Shekhar4-May-10 21:36
Abhishek Shekhar4-May-10 21:36 
GeneralRe: Handle For Paste Pin
Anubhava Dimri4-May-10 21:52
Anubhava Dimri4-May-10 21:52 
GeneralRe: Handle For Paste Pin
Abhishek Shekhar4-May-10 22:28
Abhishek Shekhar4-May-10 22:28 
GeneralGreat job. Pin
Dinesh Mani30-Apr-10 2:30
Dinesh Mani30-Apr-10 2:30 
GeneralRe: Great job. Pin
Abhishek Shekhar30-Apr-10 17:52
Abhishek Shekhar30-Apr-10 17:52 
GeneralAbout The HResultToString Method Pin
Izzet Kerem Kusmezer30-Apr-10 1:37
Izzet Kerem Kusmezer30-Apr-10 1:37 
GeneralRe: About The HResultToString Method Pin
Izzet Kerem Kusmezer30-Apr-10 1:45
Izzet Kerem Kusmezer30-Apr-10 1:45 
GeneralRe: About The HResultToString Method Pin
Abhishek Shekhar30-Apr-10 17:42
Abhishek Shekhar30-Apr-10 17:42 
QuestionI am Confused Pin
Nosa Osayamwen27-Apr-10 12:17
Nosa Osayamwen27-Apr-10 12:17 
AnswerRe: I am Confused Pin
Abhishek Shekhar27-Apr-10 17:26
Abhishek Shekhar27-Apr-10 17:26 
GeneralRe: I am Confused Pin
Nosa Osayamwen28-Apr-10 11:38
Nosa Osayamwen28-Apr-10 11:38 
GeneralGood job! Pin
torial23-Apr-10 6:09
torial23-Apr-10 6:09 
GeneralRe: Good job! Pin
Abhishek Shekhar23-Apr-10 17:23
Abhishek Shekhar23-Apr-10 17:23 
QuestionTeraCopy ? Pin
gpgemini22-Apr-10 21:57
gpgemini22-Apr-10 21:57 
AnswerRe: TeraCopy ? Pin
jlarini23-Apr-10 3:48
jlarini23-Apr-10 3:48 
GeneralRe: TeraCopy ? Pin
gpgemini23-Apr-10 4:13
gpgemini23-Apr-10 4:13 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.