We are utilising the StorageLibraryChangeTracker
class to monitor a folder on a USB attached disk drive for changes. Our implementation uses a timer that checks every 500ms for changes.
This works fine, unless the laptop goes to sleep. In the case where the laptop goes to sleep, even after restarting the app, we receive an exception with a 80080222 error from within the CheckForFolderChangesAsync()
method.
I have searched but cannot find any information on the 80080222 error.
The only way to get everything working again is to restart the machine but this is obviously unacceptable.
public class ImageFolderMonitoringService
{
private ILogger _log = LogManagerFactory.DefaultLogManager.GetLogger<ImageFolderMonitoringService>();
private StorageFolder _currentFolder;
private PdgDiskId _currentDiskId;
private Timer _timer;
private HashSet<string> _foundRawFiles = new HashSet<string>();
private HashSet<string> _foundJpgFiles = new HashSet<string>();
private int _errorCount = 0;
public ImageFolderMonitoringService()
{
}
public event EventHandler<FileReceivedEvent> FileReceived;
public async Task<StartTrackingResult> StartTracking(StorageFolder folder)
{
_log.Trace("StartTracking()");
_currentFolder = folder;
var diskResult = await TryGetDiskId(folder);
if (!diskResult.WasSuccessful)
{
_log.Trace($"Folder tracking startup unsuccessful due to disk id: {diskResult.Error}");
return new StartTrackingResult { WasSuccessful = false, Error = diskResult.Error };
}
_currentDiskId = diskResult.DiskId;
var changeTracker = _currentFolder.TryGetChangeTracker();
changeTracker.Enable();
_timer = new Timer(CheckForFolderChanges);
_timer.Change(TimeSpan.Zero, Timeout.InfiniteTimeSpan);
_log.Trace($"Folder tracking startup successful");
return new StartTrackingResult { WasSuccessful = true, TrackingDisk = diskResult.DiskId };
}
public void EndTracking()
{
_log.Trace("EndTracking()");
var changeTracker = _currentFolder.TryGetChangeTracker();
if (changeTracker != null)
{
changeTracker.Reset();
_timer = null;
changeTracker = null;
}
}
protected virtual void OnFileReceived(FileReceivedEvent e)
{
FileReceived?.Invoke(this, e);
}
private void CheckForFolderChanges(object state)
{
var ignored = CheckForFolderChangesAsync();
}
private async Task CheckForFolderChangesAsync()
{
_log.Trace("CheckForFolderChangesAsync()");
try
{
var changeTracker = _currentFolder.TryGetChangeTracker();
changeTracker.Enable();
var changeReader = changeTracker.GetChangeReader();
var changes = await changeReader.ReadBatchAsync();
foreach (var change in changes)
{
_log.Trace($"File changed ({change.ChangeType}): {change.Path}");
if (change.ChangeType == StorageLibraryChangeType.ChangeTrackingLost)
{
// We are in trouble. Nothing else is going to be valid.
_log.Trace("Change tracker indicates lost files. Resetting the change tracker");
changeTracker.Reset();
break;
}
if (change.ChangeType == StorageLibraryChangeType.Created)
{
string extension = Path.GetExtension(change.Path);
string filename = Path.GetFileNameWithoutExtension(change.Path);
switch (extension.ToLower())
{
case ".arw":
case ".cr2":
if (_foundJpgFiles.Contains(filename))
{
EmitFoundFile(filename + ".jpg");
_foundJpgFiles.Remove(filename);
}
else
{
_foundRawFiles.Add(filename);
}
break;
case ".jpg":
if (_foundRawFiles.Contains(filename))
{
EmitFoundFile(filename + ".jpg");
_foundRawFiles.Remove(filename);
}
else
{
_foundJpgFiles.Add(filename);
}
break;
}
}
}
await changeReader.AcceptChangesAsync();
_errorCount = 0;
_timer.Change(TimeSpan.FromMilliseconds(500), Timeout.InfiniteTimeSpan);
}
catch (Exception ex)
{
if (_errorCount < 20)
{
_errorCount++;
}
_log.Error("Error receiving folder changes. Slowing down change checking frequency to avoid filling logs.", ex);
_timer.Change(TimeSpan.FromMilliseconds(500 * _errorCount), Timeout.InfiniteTimeSpan);
}
}
private void EmitFoundFile(string filename)
{
_log.Trace($"Emitting file found event: {filename}");
OnFileReceived(new FileReceivedEvent() { FileName = filename, Folder = _currentFolder.Path, DiskId = _currentDiskId.DiskId, FileReceivedDate = DateTime.UtcNow });
}
private async Task<GetDiskIdResult> TryGetDiskId(StorageFolder folder)
{
_log.Trace("TryGetDiskId()");
GetDiskIdResult result = new GetDiskIdResult();
result.WasSuccessful = false;
try
{
var file = await folder.TryGetItemAsync("PdgDiskId.txt");
if (file == null)
{
result.Error = "Disk was not formatted using PDG tools and is missing PdgDiskId file";
return result;
}
using (Stream stream = await folder.OpenStreamForReadAsync("PdgDiskId.txt"))
using (StreamReader sr = new StreamReader(stream))
{
string fileText = await sr.ReadToEndAsync();
Stream diskKeyPairStream = null;
try
{
var jsonFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///DiskSigningKey.json"));
diskKeyPairStream = (await jsonFile.OpenReadAsync()).AsStream();
var keyPair = await DiskSigningKeyPair.Load(diskKeyPairStream);
PdgDiskId diskLoader = new PdgDiskId(keyPair);
var diskIdRecord = diskLoader.Load(fileText);
result.WasSuccessful = true;
result.DiskId = diskIdRecord;
return result;
}
catch (FormatException ex)
{
result.Error = ex.Message;
return result;
}
finally
{
if (diskKeyPairStream != null)
{
diskKeyPairStream.Dispose();
diskKeyPairStream = null;
}
}
}
}
catch (Exception ex)
{
_log.Error("Unexpected error inspecting disk", ex);
result.Error = "Unexpected error inspecting disk";
return result;
}
}
}