使用 StorageLibraryChangeTracker 监视文件夹时出现 0x80080222 错误

Posted

技术标签:

【中文标题】使用 StorageLibraryChangeTracker 监视文件夹时出现 0x80080222 错误【英文标题】:0x80080222 error when monitoring folder with StorageLibraryChangeTracker 【发布时间】:2019-08-12 18:50:26 【问题描述】:

我们正在使用StorageLibraryChangeTracker 类来监视USB 连接磁盘驱动器上的文件夹是否有更改。我们的实现使用了一个每 500 毫秒检查一次更改的计时器。

这工作正常,除非笔记本电脑进入睡眠状态。在笔记本电脑进入睡眠状态的情况下,即使在重新启动应用程序之后,我们也会从 CheckForFolderChangesAsync() 方法中收到一个带有 80080222 错误的异常。

我已搜索但找不到有关 80080222 错误的任何信息。

让一切恢复正常的唯一方法是重新启动机器,但这显然是不可接受的。

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;
        
    


【问题讨论】:

您可能应该在笔记本电脑进入睡眠状态之前停止跟踪并在它恢复后启动 @PavelAnikhouski - 我同意这是有道理的。更改跟踪器在 API 上只有几个方法。据我所知没有停止跟踪。此处的文档:docs.microsoft.com/en-gb/uwp/api/… 您好,您是否尝试过使用硬盘驱动器上的文件夹进行测试?在我的测试中,硬盘上的文件夹被正常跟踪。所以这个错误的原因可能是USB设备在睡眠时与电脑断开连接,导致trace失败。 @Richasy - 感谢您的意见。你的直觉是正确的。我们似乎只是间歇性地收到 USB 磁盘的错误。但是,为了满足项目要求,我们必须能够可靠地监控 USB 连接驱动器上接收的文件。监视 UWP 应用程序中的更改并不像标准 .NET 应用程序那样直接。这个 API 是我找到的最好的方法,除了关于 USB 驱动器和挂起的这个错误之外,对于我们的目的来说是可以的。我要么需要找到一种彻底处理挂起的方法,要么找到另一个 API 来检测复制到磁盘上的文件。 更多有用的信息。这似乎只发生在 USB 磁盘上。一旦我们在关闭应用程序后开始监控,我们就无法安全地断开 USB 驱动器。看起来监控器锁定了驱动器并阻止它被移除。但是,我在文档中找不到有关停止监视或清理的任何内容。文档明确指出:“StorageLibraryChangeTracker 适用于用户库或本地计算机上的任何文件夹。这包括辅助驱动器或可移动驱动器,但不包括 NAS 驱动器或网络驱动器。” 【参考方案1】:

使用以下帖子中的代码来跟踪计算机何时进入睡眠或唤醒状态: How to check when the computer is going to sleep or waking up

编写代码,在计算机进入睡眠状态时调用 Endtracking,在计算机唤醒时调用 StartTracking。

【讨论】:

该方法不适用于 UWP 应用。我也不知道 StorageLibraryChangeTracker 有任何必要的清理步骤,或者确实不知道停止跟踪,即使在应用程序关闭时也会发生这种情况。如果有人可以建议我应该执行清理逻辑,那就太好了。跟踪文档在这里:docs.microsoft.com/en-gb/windows/uwp/files/…【参考方案2】:

@Ross - 感谢您使用更改跟踪器并报告问题

没有可用的“停止更改跟踪”功能,因为内部系统组件将始终需要更改磁盘跟踪。大多数 API 实际上只是围绕访问系统更改跟踪器的一个薄包装器,没有停止它的选项,因为它不应该被停止。

但是,在您的情况下,这一切似乎都出错了,有几件事可能会有所帮助:

从我们在本地得知的情况来看,您收到的错误代码意味着更改跟踪器不知道您正在查看的文件夹。显然,由于多种原因,这是一件坏事,所以我们正在考虑在这里修复它。

不管怎样,这个错误应该包含在更改跟踪器的StorageLibraryChangeType.ChangeTrackingLost 条目中。您可以尝试通过以下方式解决它吗:

    尝试杀死并重新启动 wsearch 服务。 (net stop wsearch 来自管理员提示)。如果更改跟踪再次开始工作,这意味着问题在于搜索索引器在睡眠时失去了它的范围,正如我们所怀疑的那样。 在获取错误代码后再次尝试调用 changeTracker.Enable() 以使系统恢复正常。

当然,您必须将此视为更改跟踪丢失。在索引位置上,这意味着您可以使用“System.Search.GatherTime 属性来获取最近索引的项目以便快速赶上。

如果这不起作用,请告诉我们,我们会尝试提出其他方法。

【讨论】:

我们现在不再遇到更新某些驱动程序后暂停笔记本电脑后中断跟踪的问题。但是,我的同事可以通过移除并重新连接 USB 驱动器来再次发生错误。当他停止并重新启动 wsearch 跟踪器时,我们可以在不重新启动的情况下再次监视更改。有趣的是,在移除并重新连接驱动器后,我们在计时器循环中调用 enable 时遇到不同的错误(RPC 服务器不可用 800706BA)。当我们重新启动刷新 _currentFolder 变量的整个过程时,这种情况就会消失。 HTH 我们找到了一些可靠的步骤来重现和解决错误: 1) 监控 USB 驱动器。 2) 格式化 U 盘。 3) 发生错误 4) 终止 wsearch 服务 5) wsearch 自动重新启动 6) 错误仍然存​​在 7) 重新启动应用程序并重新请求监视文件夹 8) 错误已清除。在我们看来,格式化驱动器后,我们要么必须重新启动计算机,要么重新启动 wsearch 服务以监控该驱动器。 HTH

以上是关于使用 StorageLibraryChangeTracker 监视文件夹时出现 0x80080222 错误的主要内容,如果未能解决你的问题,请参考以下文章

在使用加载数据流步骤的猪中,使用(使用 PigStorage)和不使用它有啥区别?

今目标使用教程 今目标任务使用篇

Qt静态编译时使用OpenSSL有三种方式(不使用,动态使用,静态使用,默认是动态使用)

MySQL db 在按日期排序时使用“使用位置;使用临时;使用文件排序”

使用“使用严格”作为“使用强”的备份

Kettle java脚本组件的使用说明(简单使用升级使用)