[SOLVED] Cannot acces the file because it is being used

Issue

I need to sync actions from dir1 to dir2 using FileSystemWatcher. When i create a new file in dir1,the same file must be in dir2,if i delete/rename/move,in dir2 should also.
When i create the file in dir1,it is being created in dir2 also,but when i try to rename the file in dir1,i get this exception : Unhandled exception. System.IO.IOException: The process cannot access the file ‘C:\Users\artio\Desktop\FASassignment\root\dir1\g.txt’ because it is being used by another process. As i googled,this error may appear because the stream isn’t closed,but i dont know if i work with one. Also,when i create a subfolder and a file in it,i get an error that the path isnt found and in dir2 it doesnt create,could you help me please with these two errors? Thanks in advance!
Program:

public class Program2
{
    public static void Main(string[] args)
    {
        string sourcePath = @"C:\Users\artio\Desktop\FASassignment\root\dir1";
        string destinationPath = @"C:\Users\artio\Desktop\FASassignment\root\dir2";

        var source = new DirectoryInfo(sourcePath);
        var destination = new DirectoryInfo(destinationPath);

        using var sourceWatcher = new FileSystemWatcher(sourcePath);

        sourceWatcher.NotifyFilter = NotifyFilters.Attributes
                             | NotifyFilters.CreationTime
                             | NotifyFilters.DirectoryName
                             | NotifyFilters.FileName
                             | NotifyFilters.LastAccess
                             | NotifyFilters.LastWrite
                             | NotifyFilters.Security
                             | NotifyFilters.Size;

        sourceWatcher.Changed += OnChanged;
        sourceWatcher.Created += OnCreated;
        sourceWatcher.Deleted += OnDeleted;
        sourceWatcher.Renamed += OnRenamed;
        sourceWatcher.Error += OnError;

        sourceWatcher.Filter = "*.txt";
        sourceWatcher.IncludeSubdirectories = true;
        sourceWatcher.EnableRaisingEvents = true;

        Console.WriteLine("Press enter to exit.");
        Console.ReadLine();
    }

    private static void OnChanged(object sender, FileSystemEventArgs e)
    {
        if (e.ChangeType != WatcherChangeTypes.Changed)
        {
            return;
        }

        Console.WriteLine($"Changed: {e.FullPath}");
    }

    private static void OnCreated(object sender, FileSystemEventArgs e)
    {
        string value = $"Created: {e.FullPath}";

        string destinationPath = @"C:\Users\artio\Desktop\FASassignment\root\dir2";
        var name = e.Name;
        var fullPath = e.FullPath;
        string destination = Path.Combine(destinationPath, name);
        File.Copy(fullPath, destination,true);

        Console.WriteLine(value);
    }

    private static void OnDeleted(object sender, FileSystemEventArgs e)
    {
        Console.WriteLine($"Deleted: {e.FullPath}");

        var name = e.Name;
        string destinationPath = @"C:\Users\artio\Desktop\FASassignment\root\dir2";
        string destination = Path.Combine(destinationPath, name);
        File.Delete(destination);
    }

    private static void OnRenamed(object sender, RenamedEventArgs e)
    {
        var oldFullPath = e.OldFullPath;
        var fullPath = e?.FullPath;
        Console.WriteLine($"Renamed:");
        Console.WriteLine($"    Old: {e.OldFullPath}");
        Console.WriteLine($"    New: {e.FullPath}");
        string destinationPath = @"C:\Users\artio\Desktop\FASassignment\root\dir2";

        string destination = Path.Combine(destinationPath, fullPath);
        File.Copy(fullPath, destination, true);
        File.Delete(oldFullPath);
    }

    private static void OnError(object sender, ErrorEventArgs e) =>
        PrintException(e.GetException());

    private static void PrintException(Exception? ex)
    {
        if (ex != null)
        {
            Console.WriteLine($"Message: {ex.Message}");
            Console.WriteLine("Stacktrace:");
            Console.WriteLine(ex.StackTrace);
            Console.WriteLine();
            PrintException(ex.InnerException);
        }
    }
}

Solution

Your issue is this line:

string destination = Path.Combine(destinationPath, fullPath);

Debug it and you will see that this does not result in what you think. You are attempting to merge two full paths.

Instead, use this:

private void OnRenamed(object sender, RenamedEventArgs e)
{                       
    Console.WriteLine($"Renamed:");
    Console.WriteLine($"    Old: {e.OldFullPath}");
    Console.WriteLine($"    New: {e.FullPath}");

    var oldFileName = Path.GetFileName(e.OldFullPath);
    var newFileName = Path.GetFileName(e.FullPath);
    var oldDestinationPath = Path.Combine(Destination, oldFileName);
    var newDestinationPath = Path.Combine(Destination, newFileName);
    var info = new FileInfo(oldDestinationPath);            
    info.MoveTo(newDestinationPath);
}

Note I am not using the File class. It’s known to be problematic.

Also, I recommend using a process log of some sort and retries so you can recover. Otherwise, this has the potential to leave a lot of changes behind as files can have holds on them that may result in your operations failing.

UPDATE:

With regards to your other issue concerning additional files, it looks like you are trying to access the file before the process creating it has released the handle on it, so you can try introducing a small delay of a second or something:

private void OnCreated(object sender, FileSystemEventArgs e)
{
    var name = e.Name;
    var fullPath = e.FullPath;
    string destination = Path.Combine(Destination, name);
    Console.WriteLine($"Copy to: {destination}");
    Thread.Sleep(1000); //Intentional delay
    File.Copy(fullPath, destination, true);
    Console.WriteLine("File copied successfully.");
}

Answered By – JuanR

Answer Checked By – David Marino (BugsFixing Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *