Home > Software engineering >  How to dispose/release file being use by another process?
How to dispose/release file being use by another process?

Time:06-04

Using filesystemwatcher in the changes event i'm using fileinfo to get the file and then copy the file to a new directory and keep copying the file with overwrite when copy until the file changes end :

private void Watcher_Changes(object sender, FileSystemEventArgs e)
{
    try
    {
        var info = new FileInfo(e.FullPath);
        var newSize = info.Length;
        
        string FileN1 = "File Name : ";
        string FileN2 = info.Name;
        string FileN3 = " Size Changed : From ";
        string FileN5 = "To";
        string FileN6 = newSize.ToString();
        
        Println(FileN1   FileN2   FileN3   FileN5   FileN6);
        
        CopyFileOnChanged(System.IO.Path.GetDirectoryName(e.FullPath), e.FullPath);
    }
    catch (Exception ex)
    {
        PrintErr(ex);
    }
}

And the copy file method :

bool makeonce = false;
string NewFileName = "";
private void CopyFileOnChanged(string Folder, string FileName)
{
    if (makeonce == false)
    {
        string t = "";
        string fn = "";
        string locationToCreateFolder = Folder;
        string folderName;
        string date = DateTime.Now.ToString("ddd MM.dd.yyyy");
        string time = DateTime.Now.ToString("HH.mm tt");
        string format = "Save Game {0} {1}";
        folderName = string.Format(format, date, time);
        Directory.CreateDirectory(locationToCreateFolder   "\\"   folderName);
        t = locationToCreateFolder   "\\"   folderName;
        fn = System.IO.Path.GetFileName(FileName);
        NewFileName = System.IO.Path.Combine(t, fn);
        makeonce = true;
    }
    File.Copy(FileName, NewFileName, true);
}

The problem is when it's making the File.Copy over again it's throwing exception the file is being using by other process.

[ ] File Name : New Text Document (2).txt Size Changed : From To662 At : 6/3/2022 3:56:14 PM [ ] File Name : New Text Document (2).txt Size Changed : From To662 At : 6/3/2022 3:56:14 PM [-] System.IO.IOException: The process cannot access the file 'C:\Program Files (x86)\Win\Save Game Fri 06.03.2022 15.56 PM\New Text Document (2).txt' because it is being used by another process. at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.IO.File.InternalCopy(String sourceFileName, String destFileName, Boolean overwrite, Boolean checkHost) at System.IO.File.Copy(String sourceFileName, String destFileName, Boolean overwrite) at Watcher_WPF.MainWindow.CopyFileOnChanged(String Folder, String FileName) in C:\Users\Chocolade1972\Downloads\Watcher_WPF-master\Watcher_WPF-master\Watcher_WPF\MainWindow.xaml.cs:line 356 at Watcher_WPF.MainWindow.Watcher_Changes(Object sender, FileSystemEventArgs e) in C:\Users\Chocolade1972\Downloads\Watcher_WPF-master\Watcher_WPF-master\Watcher_WPF\MainWindow.xaml.cs:line 258

Line 258 is :

CopyFileOnChanged(System.IO.Path.GetDirectoryName(e.FullPath), e.FullPath);

CodePudding user response:

For brevity I will only outline the solution I created in a professional setting for Invoice processing instead of give you the complete solution (I also cannot, because the code is copyrighted).

So that out of the way, here we go:

What I had first was an "Inbox" Folder, I had a FileSystemWatcher watch. I reacted to new files, but that works quite the same for file changed. For each event, I enqueued an Item:

private ConcurrentQueue<string> _queue = new ();

private void Watcher_Changes(object sender, FileSystemEventArgs e)
{
     _queue.Enqueue(e.FullPath);
}

That's all the EventHandler did. Objective here is to handle events from the FSW as quickly as any possible. Otherwise you may run into exhaustion and the FSW will discard events! (Yes, I learned it the hard way. Through bug reports and a lot of sweat :D)

The actual work was done in a separate thread, that consumed the Queue.

// Just brief display of the concept.
// This function would be used as Thread run every 
// x Time, triggered by a Timer if the Thread is not still running.
private void MyWorkerRun()
{
    // My Input came in mostly in batches, so I ran until the queue was empty.
    // You may need to adapt to maybe only dequeue N Items for each run ... 
    // Whatever does the trick.
    //     while( _queue.Any() ) 
    //
    // Maybe only process the N amount of Items the Queue has at the
    //  start of the current run?
    var itemsToProcess = _queue.Count;
    if( itemsToProcess <= 0 ) return;
    for( int i = 0; i < itemsToProcess; i  )
    {
         string sourcePath = _queue.Dequeue(); // ConcurrentQueue is Thread-Safe
         // No file there anymore? Drop it.
         if(!File.Exists(sourcePath)) continue;
         
         // TODO Construct Target-Path
         string targetPath = GetTargetPath(sourcePath); // Just a dummy for this example...

         // Try to copy, requeue if failed.
         if(!TryCopy(sourcePath, targetPath))
         {
              // Requeue for later
              // It will be picked up in _next_ run,
              // so there should be enough time in between tries.
              _queue.Enqueue(sourcePath);
         }
    }
}

private bool TryCopy(string source, string target){ /* TODO for OP */ }


I have to add that I did this years ago. Today I would probably consider TPL DataFlow to handle the queueing and requeuing for me.


And of course, you can always spice this up. I tried to keep it as simple as possible, while showing the concept clearly.

I later had more requirements: For example, the program should be able to be exited and pick up from where it stopped when started again. It should only retry for X times then write the file into a "deadletterbox", then more processing steps were added, then it should send an email to a certain adress if the queue exceeded N entries ... you get it. You can always make it more complicated if you need to.

CodePudding user response:

t = locationToCreateFolder   "\\"   folderName;

your "locationToCreateFolder" is a directory name and not a path. be cause it comes from here :

CopyFileOnChanged(System.IO.Path.GetDirectoryName(e.FullPath), e.FullPath);

so when you Combine the global path is not valid :

NewFileName = System.IO.Path.Combine(t, fn);
  •  Tags:  
  • c#
  • Related