mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-05 18:57:19 +02:00
Add delay to File System watchers to prevent Calibre installation issue (#6821)
* add a separate task to dequeue and create an app on installation * Added tests to validate the behavior of the event handler * release unmanaged memory
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
|
||||
namespace Microsoft.Plugin.Program.Storage
|
||||
{
|
||||
public static class EventHandler
|
||||
{
|
||||
// To obtain the path of the app when multiple events are added to the Concurrent queue across multiple threads.
|
||||
// On the first occurence of a different file path, the existing app path is to be returned without removing any more elements from the queue.
|
||||
public static string GetAppPathFromQueue(ConcurrentQueue<string> eventHandlingQueue, int dequeueDelay)
|
||||
{
|
||||
if (eventHandlingQueue == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(eventHandlingQueue));
|
||||
}
|
||||
|
||||
string previousAppPath = string.Empty;
|
||||
|
||||
// To obtain the last event associated with a particular app.
|
||||
while (eventHandlingQueue.TryPeek(out string currentAppPath))
|
||||
{
|
||||
if (string.IsNullOrEmpty(previousAppPath) || previousAppPath.Equals(currentAppPath, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// To dequeue a path only if it is the first one in the queue or if the path was the same as thre previous one (to avoid trying to create apps on duplicate events)
|
||||
previousAppPath = currentAppPath;
|
||||
eventHandlingQueue.TryDequeue(out _);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// This delay has been added to account for the delay in events being triggered during app installation.
|
||||
Thread.Sleep(dequeueDelay);
|
||||
}
|
||||
|
||||
return previousAppPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,13 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Wox.Infrastructure.Logger;
|
||||
using Wox.Infrastructure.Storage;
|
||||
using Win32Program = Microsoft.Plugin.Program.Programs.Win32Program;
|
||||
@@ -25,6 +28,8 @@ namespace Microsoft.Plugin.Program.Storage
|
||||
private int _numberOfPathsToWatch;
|
||||
private Collection<string> extensionsToWatch = new Collection<string> { "*.exe", $"*{LnkExtension}", "*.appref-ms", $"*{UrlExtension}" };
|
||||
|
||||
private static ConcurrentQueue<string> commonEventHandlingQueue = new ConcurrentQueue<string>();
|
||||
|
||||
public Win32ProgramRepository(IList<IFileSystemWatcherWrapper> fileSystemWatcherHelpers, IStorage<IList<Win32Program>> storage, ProgramPluginSettings settings, string[] pathsToWatch)
|
||||
{
|
||||
_fileSystemWatcherHelpers = fileSystemWatcherHelpers;
|
||||
@@ -33,6 +38,28 @@ namespace Microsoft.Plugin.Program.Storage
|
||||
_pathsToWatch = pathsToWatch;
|
||||
_numberOfPathsToWatch = pathsToWatch.Length;
|
||||
InitializeFileSystemWatchers();
|
||||
|
||||
// This task would always run in the background trying to dequeue file paths from the queue at regular intervals.
|
||||
Task.Run(() =>
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
int dequeueDelay = 500;
|
||||
string appPath = EventHandler.GetAppPathFromQueue(commonEventHandlingQueue, dequeueDelay);
|
||||
|
||||
// To allow for the installation process to finish.
|
||||
Thread.Sleep(5000);
|
||||
|
||||
if (!string.IsNullOrEmpty(appPath))
|
||||
{
|
||||
Programs.Win32Program app = Programs.Win32Program.GetAppFromPath(appPath);
|
||||
if (app != null)
|
||||
{
|
||||
Add(app);
|
||||
}
|
||||
}
|
||||
}
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private void InitializeFileSystemWatchers()
|
||||
@@ -175,7 +202,7 @@ namespace Microsoft.Plugin.Program.Storage
|
||||
private void OnAppCreated(object sender, FileSystemEventArgs e)
|
||||
{
|
||||
string path = e.FullPath;
|
||||
if (!Path.GetExtension(path).Equals(UrlExtension, StringComparison.CurrentCultureIgnoreCase))
|
||||
if (!Path.GetExtension(path).Equals(UrlExtension, StringComparison.CurrentCultureIgnoreCase) && !Path.GetExtension(path).Equals(LnkExtension, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
Programs.Win32Program app = Programs.Win32Program.GetAppFromPath(path);
|
||||
if (app != null)
|
||||
@@ -188,13 +215,11 @@ namespace Microsoft.Plugin.Program.Storage
|
||||
private void OnAppChanged(object sender, FileSystemEventArgs e)
|
||||
{
|
||||
string path = e.FullPath;
|
||||
if (Path.GetExtension(path).Equals(UrlExtension, StringComparison.CurrentCultureIgnoreCase))
|
||||
if (Path.GetExtension(path).Equals(UrlExtension, StringComparison.CurrentCultureIgnoreCase) || Path.GetExtension(path).Equals(LnkExtension, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
Programs.Win32Program app = Programs.Win32Program.GetAppFromPath(path);
|
||||
if (app != null)
|
||||
{
|
||||
Add(app);
|
||||
}
|
||||
// When a url or lnk app is installed, multiple created and changed events are triggered.
|
||||
// To prevent the code from acting on the first such event (which may still be during app installation), the events are added a common queue and dequeued by a background task at regular intervals - https://github.com/microsoft/PowerToys/issues/6429.
|
||||
commonEventHandlingQueue.Enqueue(path);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user