Click here to Skip to main content
Click here to Skip to main content
Go to top

CopyFileGeneric

, 29 Apr 2010
Rate this:
Please Sign up or sign in to vote.
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:

[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.

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).

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:

/// <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)

Share

About the Author

Abhishek Shekhar
Other
India India
No Biography provided

Comments and Discussions

 
BugVolumeGuid support ? Pinmemberbindlishankush29-Nov-11 17:33 
GeneralProcessing stops at 'CountCompleted' [modified] PinmemberIanWithycombe20-Oct-10 9:53 
Thank you for the code. It was just what I was looking for.
 
I have written a program that utilizes the CopyFileGeneric code on a PC running Windows Vista. Everything works fine. But when I move the executables over to a PC running Windows XP, the Copy Window stops with a CopyStatus of 'CountCompleted'. I have to click 'Pause', then 'Resume' for the transfer to start.
Can you think of any reason for this behaviour?
 
Regards, Ian.
 
P.S. I'm using MS Visual C# 2010 Express, with .Net 4
 
23 October 2010
Update: Problem solved by ensuring correct version of .Net.

modified on Saturday, October 23, 2010 9:51 AM

GeneralThanks for adding the User Interface PinmemberNosa Osayamwen4-May-10 17:53 
GeneralRe: Thanks for adding the User Interface PinmemberAbhishek Shekhar4-May-10 21:24 
GeneralHandle For Paste Pinmembereg_Anubhava4-May-10 0:39 
GeneralRe: Handle For Paste PinmemberAbhishek Shekhar4-May-10 17:16 
GeneralRe: Handle For Paste Pinmembereg_Anubhava4-May-10 18:26 
GeneralRe: Handle For Paste PinmemberAbhishek Shekhar4-May-10 21:12 
GeneralRe: Handle For Paste Pinmembereg_Anubhava4-May-10 21:19 
GeneralRe: Handle For Paste PinmemberAbhishek Shekhar4-May-10 21:36 
GeneralRe: Handle For Paste Pinmembereg_Anubhava4-May-10 21:52 
GeneralRe: Handle For Paste PinmemberAbhishek Shekhar4-May-10 22:28 
GeneralGreat job. PinmemberDinesh Mani30-Apr-10 2:30 
GeneralRe: Great job. PinmemberAbhishek Shekhar30-Apr-10 17:52 
GeneralAbout The HResultToString Method PinmemberIzzet Kerem Kusmezer30-Apr-10 1:37 
GeneralRe: About The HResultToString Method PinmemberIzzet Kerem Kusmezer30-Apr-10 1:45 
GeneralRe: About The HResultToString Method PinmemberAbhishek Shekhar30-Apr-10 17:42 
QuestionI am Confused PinmemberNosa Osayamwen27-Apr-10 12:17 
AnswerRe: I am Confused PinmemberAbhishek Shekhar27-Apr-10 17:26 
GeneralRe: I am Confused PinmemberNosa Osayamwen28-Apr-10 11:38 
GeneralGood job! Pinmembertorial23-Apr-10 6:09 
GeneralRe: Good job! PinmemberAbhishek Shekhar23-Apr-10 17:23 
QuestionTeraCopy ? Pinmembergpgemini22-Apr-10 21:57 
AnswerRe: TeraCopy ? Pinmemberjlarini23-Apr-10 3:48 
GeneralRe: TeraCopy ? Pinmembergpgemini23-Apr-10 4:13 

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

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

| Advertise | Privacy | Mobile
Web03 | 2.8.140905.1 | Last Updated 30 Apr 2010
Article Copyright 2010 by Abhishek Shekhar
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid