tjscience / RoboSharp

RoboSharp is a .NET wrapper for the awesome Robocopy windows application.
MIT License
216 stars 67 forks source link

Only Copying Top Directory #98

Closed RFBomb closed 3 years ago

RFBomb commented 3 years ago

First off, thanks for writing this. I'm working to implement it into my project that requires a ton of syncronizing stuff from a network server to a lot of local PCs. We used RoboCopy in VBA, but to have the additional functionality already wrapped in C# is great. Now if only I could figure out why its not working.

Before going into full detail on the primary issue, I would like to note that Intellisense isn't showing any of the <Summary> tags when hovering any of the options. So throughout writing this, I've been having to refer directly to your source code on the github pages. I am using v1.2.2 according to NuGet.

image

Now, onto the primary issue:

Basically, it is only copying the top-level directory. I can't figure out why, as I've iterated through a bunch of options. The only other issue that I saw that referred to this had closed without ever updating what exactly was going wrong.

What I'm seeing is that the RoboCopy starts when this is hit: await RoboCop.Start();, it will then copy the top level folder into the destination (producing an empty folder), then it ends.

here is how I'm calling the function:

Network.RoboCopy_Simple( Path_Source, Path_Dest, (LogFolderPath != ""), _SyncLog_FileName, LogFolderPath, AppendToLogFile: false);

below is my wrapper function:

     /* Use RoboSharp to wrap RoboCopy
     * https://github.com/tjscience/RoboSharp
     * https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/robocopy
     */

    public static async void RoboCopy_Simple(string SourcePath, string DestPath, bool CreateLogFile = true, string LogFileName = "Default", string LogFolderPath = "Default",
                                        bool PurgeExtrasInDest = true, bool WaitUntilFinished = true, bool DisplayCopyOperationSuccessMessage = false, bool AppendToLogFile = true,
                                        bool CreateProgressBar = false)
    {
        string LogPath;
        if (LogFolderPath == "Default") { LogPath = Vars.PathBackSlash(DestPath); } else { LogPath = Vars.PathBackSlash(LogFolderPath); };
        if (LogFileName == "Default") { LogPath += "RoboCopy_Log.txt"; } else { LogPath += Path.GetFileName(LogFileName); }

        RoboSharp.RoboCommand RoboCop = new RoboCommand();
        // events
        if (CreateProgressBar)
        {
            UserForms.Shared.DualProgressBar PB = new UserForms.Shared.DualProgressBar(); 
            //RoboCop.OnFileProcessed += RoboCopy_UpdateProgressBar(PBForm:PB); // Not Yet Implemented!
        }

        if (DisplayCopyOperationSuccessMessage) RoboCop.OnCommandCompleted += RoboCopy_OnCommandCompleted;

        // copy options
        RoboCop.CopyOptions.Source = SourcePath;
        RoboCop.CopyOptions.Destination = DestPath;
        RoboCop.CopyOptions.Purge = PurgeExtrasInDest;
        RoboCop.CopyOptions.CopySubdirectories = true;
        RoboCop.CopyOptions.UseUnbufferedIo = true; // recomended for large files
        RoboCop.CopyOptions.EnableRestartModeWithBackupFallback = true;
        RoboCop.CopyOptions.MultiThreadedCopiesCount = 3;

        // select options
        RoboCop.SelectionOptions.ExcludeJunctionPointsForDirectories = true;    //Ignore Junctions -> These can be a rabbit hole
        RoboCop.SelectionOptions.ExcludeLonely = false;     //always bring in missing files
        RoboCop.SelectionOptions.IncludeSame = false;        //Avoid copying data if its already identical

        // Logging Options
        switch (true)
        {
            case true when (!CreateLogFile): break; // No Logg file assigned
            case true when AppendToLogFile: RoboCop.LoggingOptions.AppendLogPath = LogPath; break;
            case true when CreateLogFile: RoboCop.LoggingOptions.LogPath = LogPath; break;
        }

        // retry options
        RoboCop.RetryOptions.RetryCount = 1;
        RoboCop.RetryOptions.RetryWaitTime = 2;
        if (WaitUntilFinished) { await RoboCop.Start(); } else { RoboCop.StartAsync(); }

    }

    private static void RoboCopy_UpdateProgressBar(object sender, FileProcessedEventArgs e, UserForms.Shared.DualProgressBar PBForm)
    {
        //Dispatcher.BeginInvoke((Action)(() =>
        //{
        //    //CurrentOperation.Text = e.ProcessedFile.FileClass;
        //    //CurrentFile.Text = e.ProcessedFile.Name;
        //    //CurrentSize.Text = e.ProcessedFile.Size.ToString();
        //}));
    }

    private static void RoboCopy_OnCopyProgressChanged(object sender, RoboCommandCompletedEventArgs e) { }
    private static void RoboCopy_OnCommandCompleted(object sender, RoboCommandCompletedEventArgs e) 
    { 
        //Dispatcher.BeginInvoke((Action)(() => { System.Windows.Forms.MessageBox.Show("Copy Operation Complete!"); } )); 
    }
PCAssistSoftware commented 3 years ago

Hi Robert, bit snowed under with work at the moment so haven't got time for a full response but I use this project for similar purpose - syncing data across multiple servers and IT machines and it works perfectly fine for me. I will try and find some time in the next few days to look through your post in more detail and see if I can find anything that might be causing it to not work.

RFBomb commented 3 years ago

Ok I found 2 problems with RoboSharp, I believe.

Setting this value causes it to only copy the top level directly and continue no further. I tested by using the example backup() in the readme file.

CopyOptions.EnableRestartModeWithBackupFallback = true;

The second issue has to do with my switch statement for logging. As far as I can tell, attempting to log to a txt file kills the process after the top level folder is created. If no log file is specified, it works fine.

RFBomb commented 3 years ago

Ok I got logging to work.

RoboSharp does not currently check for white space within the logging file paths. So any time a user attempts to pass in a path with white space, it will fail.

The solution is to simply check for white space in the path, and wrap it in quotes.

String SanitizedPath(string log) = log.contains(“ “) ? $“\”{log}\”” : log;

since people like myself have already figured out this issue, you probably only want to wrap is like that if the start/end quotes don’t already exist.

PCAssistSoftware commented 3 years ago

That is very odd then as only last week I used RoboSharp to migrate data and user profiles from old server to new server and I always use EnableRestartModeWithBackupFallback (which is the ZB switch when used in command line) and it successfully copied all levels of data.

Other settings I had set when doing that job were:-

CopySubdirectoriesIncludingEmpty CopyAll CopyFlags = "DAT" DirectoryCopyFlags = "DAT"

At the end of the day RoboSharp is only a wrapper for RoboCopy so to test I would look at the "CommandOptions" parameter of in your case RoboCop and then try those commands manually in command line using RoboCopy

RFBomb commented 3 years ago

Once I fixed the logging, i was able to read the message in the log....

ERROR : You do not have the Backup and Restore Files user rights. ***** You need these to perform Backup copies (/B or /ZB).

I've since submitted a pull request to wrap the log filepaths. Seems its a permission issue on my end compared to my network regarding the backup option

PCAssistSoftware commented 3 years ago

Are you running RoboSharp / your app as admin?

I will look at the pull request re log in coming days and approvie the pull request if it works okay - never noticed issue myself as always tend to supply things like paths quoted anyway and for my use of RoboSharp I have created my own GUI which handles the logging side as well

RFBomb commented 3 years ago

We are currently in The process of writing the application, early phases. We are going to use this without admin privileges preferably, copying some network folders down to local or to a USB.

I didn’t supply a quoted string to the dll, since I had assumed it was already handling it. it quotes the source and destination already for example, I would’ve imagined it would do the same to the log file if it’s meant to be act as a standard library.

The ternary input into the pull request is what I’m currently using my code to get around it.

The logging is meant to be hidden from the user, unless something fails. It’s meant more as a way for me and the other dev to look at just in case something goes wrong when someone goes to use the USB, and for example a file is missing

PCAssistSoftware commented 3 years ago

fair point, I am only a collaborator so didn't write original code, but thanks for finding the fault and I or the owner will get it merged soon

PCAssistSoftware commented 3 years ago

@RFBomb - Hi Robert, changes merged for you - thanks for pull request

@tjscience - Hi Terry, I have tested Roberts pull request and it works fine, so have merged it into Dev - perhaps you could update NuGet release for it when you get a moment? Thanks

RFBomb commented 3 years ago

Thanks for the response, Happy to help resolve it. Closing this issue since it was merged and my other issue was my own.

tjscience commented 3 years ago

Yes, I will publish to nuget. Thanks for both of your work on this!

tjscience commented 3 years ago

Nuget package v1.2.3 published and currently being indexed. Thanks again.

PCAssistSoftware commented 3 years ago

My pleasure, thanks Terry