Ref*_*din 32 c# file-io ioexception winforms
How do I wait for the file to be free so that ss.Save() can overwrite it with a new one? If I run this twice close together(ish), I get a generic GDI+ error.
///<summary>
/// Grabs a screen shot of the App and saves it to the C drive in jpg
///</summary>
private static String GetDesktopImage(DevExpress.XtraEditors.XtraForm whichForm)
{
Rectangle bounds = whichForm.Bounds;
// This solves my problem but creates a clutter issue
// var timeStamp = DateTime.Now.ToString("ddd-MMM-dd-yyyy-hh-mm-ss");
// var fileName = "C:\\HelpMe" + timeStamp + ".jpg";
var fileName = "C:\\HelpMe.jpg";
File.Create(fileName);
using (Bitmap ss = new Bitmap(bounds.Width, bounds.Height))
using (Graphics g = Graphics.FromImage(ss))
{
g.CopyFromScreen(whichForm.Location, Point.Empty, bounds.Size);
ss.Save(fileName, ImageFormat.Jpeg);
}
return fileName;
}
Run Code Online (Sandbox Code Playgroud)
Gor*_*son 61
像这样的函数会这样做:
public static bool IsFileReady(string filename)
{
// If the file can be opened for exclusive access it means that the file
// is no longer locked by another process.
try
{
using (FileStream inputStream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.None))
return inputStream.Length > 0;
}
catch (Exception)
{
return false;
}
}
Run Code Online (Sandbox Code Playgroud)
将它粘在一个while循环中,你会有一些东西会阻塞,直到文件可以访问:
public static void WaitForFile(string filename)
{
//This will lock the execution until the file is ready
//TODO: Add some logic to make it async and cancelable
while (!IsFileReady(filename)) { }
}
Run Code Online (Sandbox Code Playgroud)
Alm*_*und 15
如果在写入文件之前检查访问权限,则某些其他进程可能会在您进行写入之前再次获取访问权限.因此我建议使用以下两种方法之一:
得到一个流
private FileStream GetWriteStream(string path, int timeoutMs)
{
var time = Stopwatch.StartNew();
while (time.ElapsedMilliseconds < timeoutMs)
{
try
{
return new FileStream(path, FileMode.Create, FileAccess.Write);
}
catch (IOException e)
{
// access error
if (e.HResult != -2147024864)
throw;
}
}
throw new TimeoutException($"Failed to get a write handle to {path} within {timeoutMs}ms.");
}
Run Code Online (Sandbox Code Playgroud)
然后像这样使用它:
using (var stream = GetWriteStream("path"))
{
using (var writer = new StreamWriter(stream))
writer.Write("test");
}
Run Code Online (Sandbox Code Playgroud)
重试范围
private void WithRetry(Action action, int timeoutMs = 1000)
{
var time = Stopwatch.StartNew();
while(time.ElapsedMilliseconds < timeoutMs)
{
try
{
action();
return;
}
catch (IOException e)
{
// access error
if (e.HResult != -2147024864)
throw;
}
}
throw new Exception("Failed perform action within allotted time.");
}
Run Code Online (Sandbox Code Playgroud)
然后使用WithRetry(()=> File.WriteAllText(Path.Combine(_directory,name),contents));
这是一个对某些用户来说可能有点矫枉过正的解决方案。我创建了一个新的静态类,它有一个仅在文件完成复制时触发的事件。
用户通过调用注册他们想要观看的文件 FileAccessWatcher.RegisterWaitForFileAccess(filePath)。如果该文件尚未被监视,则会启动一个新任务,该任务会反复检查该文件以查看它是否可以打开。每次检查时,它还会读取文件大小。如果文件大小在预定义的时间内(在我的示例中为 5 分钟)没有增加,则退出循环。
当循环从可访问的文件或超时退出时,将FileFinishedCopying触发事件。
public class FileAccessWatcher
{
// this list keeps track of files being watched
private static ConcurrentDictionary<string, FileAccessWatcher> watchedFiles = new ConcurrentDictionary<string, FileAccessWatcher>();
public static void RegisterWaitForFileAccess(string filePath)
{
// if the file is already being watched, don't do anything
if (watchedFiles.ContainsKey(filePath))
{
return;
}
// otherwise, start watching it
FileAccessWatcher accessWatcher = new FileAccessWatcher(filePath);
watchedFiles[filePath] = accessWatcher;
accessWatcher.StartWatching();
}
/// <summary>
/// Event triggered when the file is finished copying or when the file size has not increased in the last 5 minutes.
/// </summary>
public static event FileSystemEventHandler FileFinishedCopying;
private static readonly TimeSpan MaximumIdleTime = TimeSpan.FromMinutes(5);
private readonly FileInfo file;
private long lastFileSize = 0;
private DateTime timeOfLastFileSizeIncrease = DateTime.Now;
private FileAccessWatcher(string filePath)
{
this.file = new FileInfo(filePath);
}
private Task StartWatching()
{
return Task.Factory.StartNew(this.RunLoop);
}
private void RunLoop()
{
while (this.IsFileLocked())
{
long currentFileSize = this.GetFileSize();
if (currentFileSize > this.lastFileSize)
{
this.lastFileSize = currentFileSize;
this.timeOfLastFileSizeIncrease = DateTime.Now;
}
// if the file size has not increased for a pre-defined time limit, cancel
if (DateTime.Now - this.timeOfLastFileSizeIncrease > MaximumIdleTime)
{
break;
}
}
this.RemoveFromWatchedFiles();
this.RaiseFileFinishedCopyingEvent();
}
private void RemoveFromWatchedFiles()
{
FileAccessWatcher accessWatcher;
watchedFiles.TryRemove(this.file.FullName, out accessWatcher);
}
private void RaiseFileFinishedCopyingEvent()
{
FileFinishedCopying?.Invoke(this,
new FileSystemEventArgs(WatcherChangeTypes.Changed, this.file.FullName, this.file.Name));
}
private long GetFileSize()
{
return this.file.Length;
}
private bool IsFileLocked()
{
try
{
using (this.file.Open(FileMode.Open)) { }
}
catch (IOException e)
{
var errorCode = Marshal.GetHRForException(e) & ((1 << 16) - 1);
return errorCode == 32 || errorCode == 33;
}
return false;
}
}
Run Code Online (Sandbox Code Playgroud)
用法示例:
// register the event
FileAccessWatcher.FileFinishedCopying += FileAccessWatcher_FileFinishedCopying;
// start monitoring the file (put this inside the OnChanged event handler of the FileSystemWatcher
FileAccessWatcher.RegisterWaitForFileAccess(fileSystemEventArgs.FullPath);
Run Code Online (Sandbox Code Playgroud)
处理 FileFinishedCopyingEvent:
private void FileAccessWatcher_FileFinishedCopying(object sender, FileSystemEventArgs e)
{
Console.WriteLine("File finished copying: " + e.FullPath);
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
65105 次 |
| 最近记录: |