WinSCP Session.SynchronizeDirectories() throws error if one or more files is being used by another process

Viewed 38

I have a local folder D:\test that I want to sync with a remote server folder /home/test. The below code works fine in a normal case. But the problem was when I worked on one or more files from that local folder while Sync running, it throws an error and stop the Syncing.

Stack Trace:

Exception thrown: 'System.IO.IOException' in mscorlib.dll Upload of D:\test\~$New Microsoft PowerPoint Presentation.pptx failed: 
WinSCP.SessionRemoteException: Can't open file 'D:\test\~$New Microsoft PowerPoint Presentation.pptx'.
System Error.  Code: 32.
The process cannot access the file because it is being used by another process
Permissions of  kept with their defaults
Timestamp of  kept with its default (current time)
Exception thrown: 'WinSCP.SessionRemoteException' in WinSCPnet.dll
The thread 0x26c0 has exited with code 0 (0x0).
Error: WinSCP.SessionRemoteException: Can't open file 'D:\test\~$New Microsoft PowerPoint Presentation.pptx'.
System Error.  Code: 32.
The process cannot access the file because it is being used by another process
   at WinSCP.OperationResultBase.Check()```

My Implementation:

try
{
    SessionOptions sessionOptions = new SessionOptions
    {
        Protocol = Protocol.Sftp,
        HostName = "hostName",
        UserName = "root",
        Password = "p@ssword",
        PortNumber = 22,
        SshHostKeyPolicy = SshHostKeyPolicy.GiveUpSecurityAndAcceptAny
    };

    using (Session session = new Session())
    {
        session.Open(sessionOptions);
        session.Timeout = new TimeSpan(1, 0, 0);
        session.FileTransferred += FileTransferred;
        TransferOptions transferOptions = new TransferOptions();
        transferOptions.TransferMode = TransferMode.Automatic;
        transferOptions.AddRawSettings("ReplaceInvalidChars", "0");
        transferOptions.ResumeSupport.State = TransferResumeSupportState.On;

        SynchronizationResult synchronizationResult;
        synchronizationResult = session.SynchronizeDirectories(SynchronizationMode.Remote, @"D:\test\", "/home/test/", true, false, SynchronizationCriteria.Time, transferOptions);
        synchronizationResult.Check();
    }
}
catch (Exception ex)
{
    Console.WriteLine("Error: {0}", ex);
}

private static void FileTransferred(object sender, TransferEventArgs e)
{
    if (e.Error == null)
    {
        Console.WriteLine("Upload of {0} succeeded", e.FileName);
    }
    else
    {
        Console.WriteLine("Upload of {0} failed: {1}", e.FileName, e.Error);
    }

    if (e.Chmod != null)
    {
        if (e.Chmod.Error == null)
        {
            Console.WriteLine("Permissions of {0} set to {1}", e.Chmod.FileName, e.Chmod.FilePermissions);
        }
        else
        {
            Console.WriteLine("Setting permissions of {0} failed: {1}", e.Chmod.FileName, e.Chmod.Error);
        }
    }
    else
    {
        Console.WriteLine("Permissions of {0} kept with their defaults", e.Destination);
    }

    if (e.Touch != null)
    {
        if (e.Touch.Error == null)
        {
            Console.WriteLine("Timestamp of {0} set to {1}", e.Touch.FileName, e.Touch.LastWriteTime);
        }
        else
        {
            Console.WriteLine("Setting timestamp of {0} failed: {1}", e.Touch.FileName, e.Touch.Error);
        }
    }
    else
    {
        Console.WriteLine("Timestamp of {0} kept with its default (current time)", e.Destination);
    }
}

Is there any way to catch the specific error and continue the syncing?

1 Answers

You can make WinSCP .NET assembly skip all files and folders that cannot be opened by handling Session.QueryReceived event:

session.QueryReceived +=
    (sender, args) => {
        Console.WriteLine($"Error: {args.Message}");
        args.Continue();
    };

Code based on article Recursively download directory tree with custom error handling and similar PowerShell question Upload whole drive to a server using WinSCP in PowerShell skipping all files and folders that cannot be read.


Another option is to explicitly exclude the problematic files and folders from the transfer using TransferOptions.FileMask.

Related