mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-06 03:07:04 +02:00
Functionality to detect Win32 apps which are installed, deleted or renamed while PowerToys is running (#4960)
* Added file system wrapper and interface * added win32program repository which would load store app and also handle new apps being added/deleted * Added event handlers to win32 program repo * added paths to monitor and setting FSWs for each location * Events firing as expected * filter extensions added, events fire as expected * override gethashcode so that duplicates don't show up. OnCreated and OnDeleted events trigger as expected * implemented setter for filters in FileSystemWatcher * Rename adds item but does not seem to delete the previous app * catching an exception when a duplicate item is inserted * Removed notify filter for directory because we only need to monitor files * Added exe programs to be indexed in the desktop and startmenu * created a new class to init FileSystemHelpers instead of main * Added fix for shortcut applications to work as expected while renaming and deleting them * Added wrappers for file system operations * Added some tests * Added all tests for appref-ms and added a condition to search in sub directories * Added tests for Exe applications * Added lnk app tests * Added tests for shortcut applications * removed unnecessary wrappers * override Equals for win32 * removed debug statements * Fixed exe issue * Fixed internet shortcut exception * fixed renaming shortcut apps * Added a retry block for ReadAllLines * capitalized method name - RetrieveTargetPath * made naming consistent, helper variables start with underscore * Added the exception condition back * renamed Win32ProgramRepositoryHelper to Win32ProgramFileSystemWatchers * made win32Program repository variable static * changed list to ilist * disposed file system watchers * make retrieveTargetPath upper case in tests
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.Plugin.Program.Programs
|
||||
{
|
||||
public interface IShellLinkHelper
|
||||
{
|
||||
string RetrieveTargetPath(string path);
|
||||
string description { get; set; }
|
||||
string Arguments { get; set; }
|
||||
bool hasArguments { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -6,10 +6,11 @@ using System.IO;
|
||||
using Accessibility;
|
||||
using System.Runtime.InteropServices.ComTypes;
|
||||
using System.Security.Policy;
|
||||
using Microsoft.Plugin.Program.Logger;
|
||||
|
||||
namespace Microsoft.Plugin.Program.Programs
|
||||
{
|
||||
class ShellLinkHelper
|
||||
public class ShellLinkHelper : IShellLinkHelper
|
||||
{
|
||||
[Flags()]
|
||||
public enum SLGP_FLAGS
|
||||
@@ -99,19 +100,29 @@ namespace Microsoft.Plugin.Program.Programs
|
||||
{
|
||||
}
|
||||
|
||||
// To initialize the app description
|
||||
public String description = String.Empty;
|
||||
// Contains the description of the app
|
||||
public string description { get; set; } = String.Empty;
|
||||
|
||||
// Sets to true if the program takes in arguments
|
||||
public String Arguments = String.Empty;
|
||||
public bool hasArguments = false;
|
||||
// Contains the arguments to the app
|
||||
public string Arguments { get; set; } = String.Empty;
|
||||
public bool hasArguments { get; set; } = false;
|
||||
|
||||
// Retrieve the target path using Shell Link
|
||||
public string retrieveTargetPath(string path)
|
||||
public string RetrieveTargetPath(string path)
|
||||
{
|
||||
var link = new ShellLink();
|
||||
const int STGM_READ = 0;
|
||||
((IPersistFile)link).Load(path, STGM_READ);
|
||||
|
||||
try
|
||||
{
|
||||
((IPersistFile)link).Load(path, STGM_READ);
|
||||
}
|
||||
catch(System.IO.FileNotFoundException ex)
|
||||
{
|
||||
ProgramLogger.LogException($"|Win32| ShellLinkHelper.retrieveTargetPath | {path} | Path could not be retrieved", ex);
|
||||
return String.Empty;
|
||||
}
|
||||
|
||||
var hwnd = new _RemotableHandle();
|
||||
((IShellLinkW)link).Resolve(ref hwnd, 0);
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ using System.Windows.Input;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
using Wox.Infrastructure.Logger;
|
||||
using Wox.Infrastructure.FileSystemHelper;
|
||||
|
||||
namespace Microsoft.Plugin.Program.Programs
|
||||
{
|
||||
@@ -36,6 +37,10 @@ namespace Microsoft.Plugin.Program.Programs
|
||||
public string Arguments { get; set; } = String.Empty;
|
||||
public string Location => ParentDirectory;
|
||||
public uint AppType { get; set; }
|
||||
// Wrappers for File Operations
|
||||
public static IFileVersionInfoWrapper _fileVersionInfoWrapper = new FileVersionInfoWrapper();
|
||||
public static IFileWrapper _fileWrapper = new FileWrapper();
|
||||
public static IShellLinkHelper _helper = new ShellLinkHelper();
|
||||
|
||||
private const string ShortcutExtension = "lnk";
|
||||
private const string ApplicationReferenceExtension = "appref-ms";
|
||||
@@ -313,7 +318,7 @@ namespace Microsoft.Plugin.Program.Programs
|
||||
// This function filters Internet Shortcut programs
|
||||
private static Win32 InternetShortcutProgram(string path)
|
||||
{
|
||||
string[] lines = System.IO.File.ReadAllLines(path);
|
||||
string[] lines = _fileWrapper.ReadAllLines(path);
|
||||
string appName = string.Empty;
|
||||
string iconPath = string.Empty;
|
||||
string urlPath = string.Empty;
|
||||
@@ -382,8 +387,8 @@ namespace Microsoft.Plugin.Program.Programs
|
||||
{
|
||||
const int MAX_PATH = 260;
|
||||
StringBuilder buffer = new StringBuilder(MAX_PATH);
|
||||
ShellLinkHelper _helper = new ShellLinkHelper();
|
||||
string target = _helper.retrieveTargetPath(path);
|
||||
|
||||
string target = _helper.RetrieveTargetPath(path);
|
||||
|
||||
if (!string.IsNullOrEmpty(target))
|
||||
{
|
||||
@@ -403,8 +408,8 @@ namespace Microsoft.Plugin.Program.Programs
|
||||
}
|
||||
else
|
||||
{
|
||||
var info = FileVersionInfo.GetVersionInfo(target);
|
||||
if (!string.IsNullOrEmpty(info.FileDescription))
|
||||
var info = _fileVersionInfoWrapper.GetVersionInfo(target);
|
||||
if (!string.IsNullOrEmpty(info?.FileDescription))
|
||||
{
|
||||
program.Description = info.FileDescription;
|
||||
}
|
||||
@@ -439,9 +444,9 @@ namespace Microsoft.Plugin.Program.Programs
|
||||
try
|
||||
{
|
||||
var program = Win32Program(path);
|
||||
var info = FileVersionInfo.GetVersionInfo(path);
|
||||
var info = _fileVersionInfoWrapper.GetVersionInfo(path);
|
||||
|
||||
if (!string.IsNullOrEmpty(info.FileDescription))
|
||||
if (!string.IsNullOrEmpty(info?.FileDescription))
|
||||
{
|
||||
program.Description = info.FileDescription;
|
||||
}
|
||||
@@ -457,6 +462,46 @@ namespace Microsoft.Plugin.Program.Programs
|
||||
}
|
||||
}
|
||||
|
||||
// Function to get the Win32 application, given the path to the application
|
||||
public static Win32 GetAppFromPath(string path)
|
||||
{
|
||||
Win32 app = null;
|
||||
const string exeExtension = ".exe";
|
||||
const string lnkExtension = ".lnk";
|
||||
const string urlExtenion = ".url";
|
||||
const string apprefExtension = ".appref-ms";
|
||||
|
||||
string extension = Path.GetExtension(path);
|
||||
|
||||
if(extension.Equals(exeExtension, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
app = ExeProgram(path);
|
||||
}
|
||||
else if(extension.Equals(lnkExtension, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
app = LnkProgram(path);
|
||||
}
|
||||
else if(extension.Equals(apprefExtension, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
app = Win32Program(path);
|
||||
}
|
||||
else if(extension.Equals(urlExtenion, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
app = InternetShortcutProgram(path);
|
||||
}
|
||||
|
||||
// if the app is valid, only then return the application, else return null
|
||||
if(app?.Valid ?? false)
|
||||
{
|
||||
return app;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static IEnumerable<string> ProgramPaths(string directory, string[] suffixes, bool recursiveSearch = true)
|
||||
{
|
||||
if (!Directory.Exists(directory))
|
||||
@@ -609,8 +654,11 @@ namespace Microsoft.Plugin.Program.Programs
|
||||
var programs1 = paths.AsParallel().Where(p => Extension(p).Equals(ShortcutExtension, StringComparison.OrdinalIgnoreCase)).Select(LnkProgram);
|
||||
var programs2 = paths.AsParallel().Where(p => Extension(p).Equals(ApplicationReferenceExtension, StringComparison.OrdinalIgnoreCase)).Select(Win32Program);
|
||||
var programs3 = paths.AsParallel().Where(p => Extension(p).Equals(InternetShortcutExtension, StringComparison.OrdinalIgnoreCase)).Select(InternetShortcutProgram);
|
||||
var programs4 = paths.AsParallel().Where(p => Extension(p).Equals(ExeExtension, StringComparison.OrdinalIgnoreCase)).Select(ExeProgram);
|
||||
|
||||
return programs1.Concat(programs2).Where(p => p.Valid).Concat(programs3).Where(p => p.Valid);
|
||||
return programs1.Concat(programs2).Where(p => p.Valid)
|
||||
.Concat(programs3).Where(p => p.Valid)
|
||||
.Concat(programs4).Where(p => p.Valid);
|
||||
}
|
||||
|
||||
private static ParallelQuery<Win32> StartMenuPrograms(string[] suffixes)
|
||||
@@ -712,6 +760,19 @@ namespace Microsoft.Plugin.Program.Programs
|
||||
return entry;
|
||||
}
|
||||
|
||||
// Overriding the object.GetHashCode() function to aid in removing duplicates while adding and removing apps from the concurrent dictionary storage
|
||||
public override int GetHashCode()
|
||||
{
|
||||
removeDuplicatesComparer _removeDuplicatesHelper = new removeDuplicatesComparer();
|
||||
return _removeDuplicatesHelper.GetHashCode(this);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
removeDuplicatesComparer _removeDuplicatesHelper = new removeDuplicatesComparer();
|
||||
return obj is Win32 win && _removeDuplicatesHelper.Equals(this, win);
|
||||
}
|
||||
|
||||
public class removeDuplicatesComparer : IEqualityComparer<Win32>
|
||||
{
|
||||
public bool Equals(Win32 app1, Win32 app2)
|
||||
@@ -802,6 +863,6 @@ namespace Microsoft.Plugin.Program.Programs
|
||||
return new Win32[0];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user