前言:我对编码非常陌生,而且大部分是自学的。
我正在编写一个小应用程序,它会自动将我的游戏保存文件复制到我的备份目录。它使用 FileSystemWatcher 类来监视游戏的保存目录并在我玩游戏时查找更改,并在调整文件大小或写入文件时调用 OnChanged。
我遇到的问题是,当我的程序复制文件时,有时会导致游戏崩溃(在本例中为泰拉瑞亚)。我猜这与访问限制有关,我对此没有太多经验。我很惊讶 File.Copy() 会导致问题,因为它只是读取,但它与泰拉瑞亚一致崩溃。
单步执行 OnChanged 调用表明泰拉瑞亚会写入临时文件,然后将它们复制到实际的保存文件中,并删除临时文件。很常见。
所以这是我的代码以及我所得到的笨拙的解决方法。它使用两个计时器:_delayTimer 在第一次调用 OnChanged 时启动,_canBackupTimer 在 _delayTimer 过去时启动。我在测试过的游戏中没有遇到任何问题。
对于每个Timer,间隔时间为5秒,并且AutoReset = True,所以经过一次后就会停止。
这是我在监控游戏时避免 IO 异常的唯一方法吗?必须有更好的方法,但我不知道该去哪里寻找。我很惊讶 File.Copy 会限制对游戏保存过程的访问。
我应该查看文件访问权限吗?
private static void _delayTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) {
_canBackupTimer.Enabled = true;
_canBackupTimer.Start();
_delayTimer.Enabled = false;
}
private static void _canBackupTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) {
_lastAutoBackupTime = DateTime.Now;
_canBackupTimer.Enabled = false;
}
private static void OnChanged(object source, FileSystemEventArgs e) {
while (true) {
if (!_canBackupTimer.Enabled && !_delayTimer.Enabled && (DateTime.Now - _lastAutoBackupTime).Seconds > 10) {
//If neither timer is running and 10 seconds
//have elapsed since canBackupTimer stopped
_delayTimer.Enabled = true;
_delayTimer.Start();
continue;
}
if (_canBackupTimer.Enabled) {
//if canBackupTimer is running, do autobackup
Game autoBackupGame = null;
//_gamesToAutoBackup is a List<Game> for the program to watch.
//Check to identify which Game is being backed up
//g.RootFolder is the game's base directory, e.g. "...\Terraria\"
foreach (var g in _gamesToAutoBackup) {
if (e.FullPath.Contains(g.Name) || e.FullPath.Contains(g.RootFolder))
autoBackupGame = g;
}
if (autoBackupGame.RootFolder == null) {
var dir = new DirectoryInfo(autoBackupGame.Path);
autoBackupGame.RootFolder = dir.Name;
}
//Find the base directory of the file being changed and trim off
//the unneeded pieces, e.g. "C:\Users\Rob\..."
var indexOfGamePart = e.FullPath.IndexOf(autoBackupGame.RootFolder);
var friendlyPath = e.FullPath.Substring(0, indexOfGamePart);
var newPath = e.FullPath.Replace(friendlyPath, "\\");
if (Directory.Exists(e.FullPath) && autoBackupGame != null) {
//True if directory, else it's a file.
//Do stuff for backing up a directory here.
//Currently nothing written here.
}
else { //If Directory.Exists is false, the path is a file.
try {
var copyDestinationFullPath = new FileInfo(_specifiedAutoBackupFolder + newPath);
if (!Directory.Exists(copyDestinationFullPath.DirectoryName))
Directory.CreateDirectory(copyDestinationFullPath.DirectoryName);
File.Copy(e.FullPath, copyDestinationFullPath.ToString(), true);
}
catch (FileNotFoundException ex) {
Logger.Log(ex); //My class, Logger, writes the exception text to a file.
}
}
}
break;
}
}
最佳答案
我的帖子考虑了有关泰拉瑞亚的评论。我还没有尝试过这个,但如果我想在复制文件的同时防止泰拉瑞亚崩溃,我将采用以下方法解决该问题。
问题
我的猜测是泰拉瑞亚崩溃了,因为当您尝试复制文件时它需要对文件进行写访问。我也因此假设 File.Copy
打开文件没有共享访问权限,因此需要使用不同的机制来打开和复制文件来解决该问题。
解决方案
您需要某种方式来打开和读取文件,同时仍保留泰拉瑞亚同时打开和写入文件的权利。我并不 100% 肯定这种方法可以做到这一点,这取决于泰拉瑞亚尝试打开文件的方式,但您至少可以打开文件,以便仍然允许共享。而不是 File.Copy
,请尝试:
using (FileStream inStream = new FileStream(filename, FileMode.Open,
FileAccess.Read, FileShare.ReadWrite) {
// Specifying shared access as FileShare.ReadWrite is the key, as it lets
// Terraria open the file with write access.
// Then add code here to copy the file out to your destination...
// this depends a bit on which version of .Net you are using, but
// inStream.CopyTo(outStream) is probably the easiest as long as you
// are using .Net 4+.
}
结论
如果您的打开失败,或者您的复制失败(异常),或者您的 FileSystemWatcher 在您复制时发出信号,则意味着泰拉瑞亚正在使用该文件,您需要稍后重试。无论如何,希望您能够远离泰拉瑞亚。
我很想知道这种方法是否适合您。如果您尝试一下,请发表评论。
关于c# - 为什么我在使用 File.Copy() 时看到 IO 异常,该异常似乎来自读取文件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21736033/