如何 dispose/release 文件被另一个进程使用?
How to dispose/release file being use by another process?
在更改事件中使用 filesystemwatcher 我正在使用 fileinfo 获取文件,然后将文件复制到新目录并在复制时继续复制文件并覆盖,直到文件更改结束:
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);
}
}
和复制文件方法:
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);
}
问题是当它再次 File.Copy 时抛出异常文件正在被其他进程使用。
[+] 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
第 258 行是:
CopyFileOnChanged(System.IO.Path.GetDirectoryName(e.FullPath), e.FullPath);
为简洁起见,我将只概述我在发票处理的专业设置中创建的解决方案,而不是为您提供完整的解决方案(我也不能,因为代码受版权保护)。
让开,我们开始吧:
我首先有一个“收件箱”文件夹,我有一个 FileSystemWatcher 手表。我对新文件做出了反应,但对于已更改的文件,效果完全相同。对于每个事件,我排队了一个项目:
private ConcurrentQueue<string> _queue = new ();
private void Watcher_Changes(object sender, FileSystemEventArgs e)
{
_queue.Enqueue(e.FullPath);
}
这就是 EventHandler 所做的全部工作。 Objective这里是为了尽快处理来自FSW的事件。 否则你可能 运行 筋疲力尽,FSW 将丢弃事件!(是的,我是通过错误报告和大量汗水学到的)
实际 工作是在单独的线程中完成的,该线程消耗了队列。
// 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 */ }
我必须补充一点,我在 年 前做过这个。今天我可能会考虑使用 TPL DataFlow 来为我处理排队和重新排队。
当然,您可以随时为它增添趣味。我尽量保持简单,同时清楚地展示概念。
后来我有更多的要求:比如程序再次启动时要能退出并从停止的地方继续。它应该只重试 X 次,然后将文件写入“死信箱”,然后添加更多处理步骤,如果队列超过 N 个条目,它应该向某个地址发送电子邮件……你明白了。如果需要,您可以随时使其变得更复杂。
t = locationToCreateFolder + "\" + folderName;
您的“locationToCreateFolder”是目录名称而不是路径。因为它来自这里 :
CopyFileOnChanged(System.IO.Path.GetDirectoryName(e.FullPath), e.FullPath);
因此,当您合并全局路径时无效:
NewFileName = System.IO.Path.Combine(t, fn);
在更改事件中使用 filesystemwatcher 我正在使用 fileinfo 获取文件,然后将文件复制到新目录并在复制时继续复制文件并覆盖,直到文件更改结束:
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);
}
}
和复制文件方法:
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);
}
问题是当它再次 File.Copy 时抛出异常文件正在被其他进程使用。
[+] 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
第 258 行是:
CopyFileOnChanged(System.IO.Path.GetDirectoryName(e.FullPath), e.FullPath);
为简洁起见,我将只概述我在发票处理的专业设置中创建的解决方案,而不是为您提供完整的解决方案(我也不能,因为代码受版权保护)。
让开,我们开始吧:
我首先有一个“收件箱”文件夹,我有一个 FileSystemWatcher 手表。我对新文件做出了反应,但对于已更改的文件,效果完全相同。对于每个事件,我排队了一个项目:
private ConcurrentQueue<string> _queue = new ();
private void Watcher_Changes(object sender, FileSystemEventArgs e)
{
_queue.Enqueue(e.FullPath);
}
这就是 EventHandler 所做的全部工作。 Objective这里是为了尽快处理来自FSW的事件。 否则你可能 运行 筋疲力尽,FSW 将丢弃事件!(是的,我是通过错误报告和大量汗水学到的)
实际 工作是在单独的线程中完成的,该线程消耗了队列。
// 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 */ }
我必须补充一点,我在 年 前做过这个。今天我可能会考虑使用 TPL DataFlow 来为我处理排队和重新排队。
当然,您可以随时为它增添趣味。我尽量保持简单,同时清楚地展示概念。
后来我有更多的要求:比如程序再次启动时要能退出并从停止的地方继续。它应该只重试 X 次,然后将文件写入“死信箱”,然后添加更多处理步骤,如果队列超过 N 个条目,它应该向某个地址发送电子邮件……你明白了。如果需要,您可以随时使其变得更复杂。
t = locationToCreateFolder + "\" + folderName;
您的“locationToCreateFolder”是目录名称而不是路径。因为它来自这里 :
CopyFileOnChanged(System.IO.Path.GetDirectoryName(e.FullPath), e.FullPath);
因此,当您合并全局路径时无效:
NewFileName = System.IO.Path.Combine(t, fn);