CopyFileEx wrapper parameters
Asked Answered
R

2

6

I have recently had to change from using File.Copy() to CopyFileEx and I am struggling to find how to use it.

After a lot of googling I found this nice wrapper to use it, but what I need is to get the progress of the copied bytes of the current file, and if possible calculate the progress of copying all files I pass to it.

(I know there are projects that have progress bars linked to CopyFileEx but I'm not experienced enough to pull out the relevant code, and I would like to use this wrapper).

Presumably just by comparing it to the total bytes of the files to be copied, which I would find beforehand, and working out the percentage from that.

My current method of copying is

FileRoutines.CopyFile(new FileInfo("source.txt"), new FileInfo("dest.txt"));

What I am stuck on is how to overload it with the parameter's needed to get the progress info.

public sealed class FileRoutines
{
    public static void CopyFile(FileInfo source, FileInfo destination)
    {
        CopyFile(source, destination, CopyFileOptions.None);
    }

    public static void CopyFile(FileInfo source, FileInfo destination, 
        CopyFileOptions options)
    {
        CopyFile(source, destination, options, null);
    }

    public static void CopyFile(FileInfo source, FileInfo destination, 
        CopyFileOptions options, CopyFileCallback callback)
    {
        CopyFile(source, destination, options, callback, null);
    }

    public static void CopyFile(FileInfo source, FileInfo destination, 
        CopyFileOptions options, CopyFileCallback callback, object state)
    {
        if (source == null) throw new ArgumentNullException("source");
        if (destination == null) 
            throw new ArgumentNullException("destination");
        if ((options & ~CopyFileOptions.All) != 0) 
            throw new ArgumentOutOfRangeException("options");

        new FileIOPermission(
            FileIOPermissionAccess.Read, source.FullName).Demand();
        new FileIOPermission(
            FileIOPermissionAccess.Write, destination.FullName).Demand();

        CopyProgressRoutine cpr = callback == null ? 
            null : new CopyProgressRoutine(new CopyProgressData(
                source, destination, callback, state).CallbackHandler);

        bool cancel = false;
        if (!CopyFileEx(source.FullName, destination.FullName, cpr, 
            IntPtr.Zero, ref cancel, (int)options))
        {
            throw new IOException(new Win32Exception().Message);
        }
    }

    private class CopyProgressData
    {
        private FileInfo _source = null;
        private FileInfo _destination = null;
        private CopyFileCallback _callback = null;
        private object _state = null;

        public CopyProgressData(FileInfo source, FileInfo destination, 
            CopyFileCallback callback, object state)
        {
            _source = source; 
            _destination = destination;
            _callback = callback;
            _state = state;
        }

        public int CallbackHandler(
            long totalFileSize, long totalBytesTransferred, 
            long streamSize, long streamBytesTransferred, 
            int streamNumber, int callbackReason,
            IntPtr sourceFile, IntPtr destinationFile, IntPtr data)
        {
            return (int)_callback(_source, _destination, _state, 
                totalFileSize, totalBytesTransferred);
        }
    }

    private delegate int CopyProgressRoutine(
        long totalFileSize, long TotalBytesTransferred, long streamSize, 
        long streamBytesTransferred, int streamNumber, int callbackReason,
        IntPtr sourceFile, IntPtr destinationFile, IntPtr data);

    [SuppressUnmanagedCodeSecurity]
    [DllImport("Kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)]
    private static extern bool CopyFileEx(
        string lpExistingFileName, string lpNewFileName,
        CopyProgressRoutine lpProgressRoutine,
        IntPtr lpData, ref bool pbCancel, int dwCopyFlags);
}

public delegate CopyFileCallbackAction CopyFileCallback(
    FileInfo source, FileInfo destination, object state, 
    long totalFileSize, long totalBytesTransferred);

public enum CopyFileCallbackAction
{
    Continue = 0,
    Cancel = 1,
    Stop = 2,
    Quiet = 3
}

[Flags]
public enum CopyFileOptions
{
    None = 0x0,
    FailIfDestinationExists = 0x1,
    Restartable = 0x2,
    AllowDecryptedDestination = 0x8,
    All = FailIfDestinationExists | Restartable | AllowDecryptedDestination
}

Any pointers really appreciated.

Radiometeorograph answered 20/12, 2012 at 16:17 Comment(3)
This code looks identical to a MSDN Magazine article's code. If so, you should credit the original. msdn.microsoft.com/en-us/magazine/cc163851.aspxRubin
@ChrisDolan - the link just points to the MSDN Magazine archive - see the chm for MSDN Magazine February 2005 .Net Matters Figure 1 FileRoutines.CopyFile Using the Win32 CopyFileExDespotism
@TheLonelyCoder - Hmm, you're right. I re-found it here: web.archive.org/web/20130304214632/http://msdn.microsoft.com/… but without the CSS so it's a lot harder to readRubin
O
8

The wrapper already has the plumbing needed to handle the progress. Just implement the code to update your progress bar in CallbackHandler before the return. The progressBar1.Maximum defaults to 100, so the code below will calculate the percentage.

Replace your current CopyFile call with this:

CopyFileCallbackAction myCallback(FileInfo source, FileInfo destination, object state, long totalFileSize, long totalBytesTransferred)
{
    double dProgress = (totalBytesTransferred / (double)totalFileSize) * 100.0;
    progressBar1.Value = (int)dProgress;
    return CopyFileCallbackAction.Continue;
}

FileRoutines.CopyFile(new FileInfo("source.txt"), new FileInfo("dest.txt"), myCallback);
Olnek answered 20/12, 2012 at 16:26 Comment(13)
Thanks for your answer. I know the code is already there, I just don't know how to use it :) Could you give me a code example?Radiometeorograph
@BaliC - Code added as requested. :)Olnek
Thanks very much, I will test this tonight and will let you know if it works :)Radiometeorograph
Sorry, how would I call the CallbackHandler method? Or does it get called when I only specify a source file and dest in the calling code?Radiometeorograph
@BaliC - I've added a more specific example for you.Olnek
Thanks for the example. I have implemented the code but it says myCallback needs to return a value?Radiometeorograph
@BaliC - Yes, it is expecting a CopyFileCallbackAction which is setup as an enum in your code. Just return CopyFileCallbackAction.Continue after updating the progress bar.Olnek
That's fantastic, thanks so much, I really appreciate your help. There is just one more thing, I created a blank file and tested it, but as the file was empty it threw an exception because it tried to divide by zero to get the copy progress. Is there any way this can be avoided, and keep the progress bar going?Radiometeorograph
I realize that it can't get the bytes copied if there are none, but how can I still copy the blank file?Radiometeorograph
Add some checks before doing the calculation for the progress bar percentage. Check that 'totalFileSize' is not 0 before doing the update.Olnek
Excellent code. I borrowed it wholesale. BTW, your percent calculation line always returns 0 because (totalBytesTransferred / totalFileSize) is always 0 unless you cast one as a double.Pulitzer
I submitted an edit to cast totalFileSize to double so that dProgress doesn't always return 0Hubie
I am completely new to C#, .NET, DLLs, etc. Can this be achieved in a DLL project?Subaqueous
T
0

If you just want to show a progress bar for a file copy, you can do this. It uses the Windows standard dialog that shows a progress bar and time remaining, and it has a Cancel button. It was just what I needed in basically one line of code.

// The following using directive requires a project reference to Microsoft.VisualBasic. 
using Microsoft.VisualBasic.FileIO;

class FileProgress
{
    static void Main()
    {
        // Specify the path to a folder that you want to copy. If the folder is small,  
        // you won't have time to see the progress dialog box. 
        string sourcePath = @"C:\Windows\symbols\";
        // Choose a destination for the copied files. 
        string destinationPath = @"C:\TestFolder";

        FileSystem.CopyDirectory(sourcePath, destinationPath,
            UIOption.AllDialogs);
    }
}
Turgid answered 1/9, 2016 at 14:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.