Preface: I'm very new to coding and mostly self-taught.
I'm writing a little app that will automatically copy my game save files to my backup directory. It uses the FileSystemWatcher class to watch a game's save directory and look for changes while I'm playing and calls OnChanged if a file is resized or written to.
The problem I'm having is that when my program is copying a file, it sometimes crashes the GAME (in this case Terraria). I'm guessing this has something to do with access restrictions, which I don't have much experience with. I'm surprised that File.Copy() would cause issues, since it's just reading, but it's a consistent crash with Terraria.
Stepping through the OnChanged calls has shown that Terraria writes temporary files, then copies them to the actual save files, and delete the temps. Pretty common.
So here's my code with the klutzy workaround I've got. It uses two timers: _delayTimer that starts when OnChanged is first called, and _canBackupTimer when _delayTimer elapses. I haven't had any issues in the games I've tested it with.
For each Timer, the interval is 5 seconds and AutoReset = True, so it will stop after elapsing once.
Is this the only way I can avoid IO Exceptions with the game being monitored? There has to be a better way, but I'm not sure where to look. I'm surprised that File.Copy would be restricting access to the game's save process.
Should I look at file access rights?
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;
}
}