diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Programs/Win32Tests.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Programs/Win32Tests.cs index 8f59814ba3..7ed1110306 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Programs/Win32Tests.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Programs/Win32Tests.cs @@ -8,6 +8,7 @@ using System.Linq; using Moq; using NUnit.Framework; using Wox.Infrastructure; +using Wox.Infrastructure.FileSystemHelper; using Wox.Plugin; namespace Microsoft.Plugin.Program.UnitTests.Programs @@ -23,7 +24,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs ExecutableName = "notepad.exe", FullPath = "c:\\windows\\system32\\notepad.exe", LnkResolvedPath = "c:\\users\\powertoys\\appdata\\roaming\\microsoft\\windows\\start menu\\programs\\accessories\\notepad.lnk", - AppType = 2, + AppType = Win32Program.ApplicationType.Win32Application, }; private static readonly Win32Program _notepadUsers = new Win32Program @@ -32,7 +33,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs ExecutableName = "notepad.exe", FullPath = "c:\\windows\\system32\\notepad.exe", LnkResolvedPath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\accessories\\notepad.lnk", - AppType = 2, + AppType = Win32Program.ApplicationType.Win32Application, }; private static readonly Win32Program _azureCommandPrompt = new Win32Program @@ -41,7 +42,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs ExecutableName = "cmd.exe", FullPath = "c:\\windows\\system32\\cmd.exe", LnkResolvedPath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\microsoft azure\\microsoft azure sdk for .net\\v2.9\\microsoft azure command prompt - v2.9.lnk", - AppType = 2, + AppType = Win32Program.ApplicationType.Win32Application, }; private static readonly Win32Program _visualStudioCommandPrompt = new Win32Program @@ -50,7 +51,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs ExecutableName = "cmd.exe", FullPath = "c:\\windows\\system32\\cmd.exe", LnkResolvedPath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\visual studio 2019\\visual studio tools\\vc\\x64 native tools command prompt for vs 2019.lnk", - AppType = 2, + AppType = Win32Program.ApplicationType.Win32Application, }; private static readonly Win32Program _commandPrompt = new Win32Program @@ -59,7 +60,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs ExecutableName = "cmd.exe", FullPath = "c:\\windows\\system32\\cmd.exe", LnkResolvedPath = "c:\\users\\powertoys\\appdata\\roaming\\microsoft\\windows\\start menu\\programs\\system tools\\command prompt.lnk", - AppType = 2, + AppType = Win32Program.ApplicationType.Win32Application, }; private static readonly Win32Program _fileExplorer = new Win32Program @@ -68,7 +69,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs ExecutableName = "File Explorer.lnk", FullPath = "c:\\users\\powertoys\\appdata\\roaming\\microsoft\\windows\\start menu\\programs\\system tools\\file explorer.lnk", LnkResolvedPath = null, - AppType = 2, + AppType = Win32Program.ApplicationType.Win32Application, }; private static readonly Win32Program _wordpad = new Win32Program @@ -77,7 +78,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs ExecutableName = "wordpad.exe", FullPath = "c:\\program files\\windows nt\\accessories\\wordpad.exe", LnkResolvedPath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\accessories\\wordpad.lnk", - AppType = 2, + AppType = Win32Program.ApplicationType.Win32Application, }; private static readonly Win32Program _wordpadDuplicate = new Win32Program @@ -86,7 +87,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs ExecutableName = "WORDPAD.EXE", FullPath = "c:\\program files\\windows nt\\accessories\\wordpad.exe", LnkResolvedPath = null, - AppType = 2, + AppType = Win32Program.ApplicationType.Win32Application, }; private static readonly Win32Program _twitterChromePwa = new Win32Program @@ -122,7 +123,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs ExecutableName = "msedge.exe", FullPath = "c:\\program files (x86)\\microsoft\\edge\\application\\msedge.exe", LnkResolvedPath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\microsoft edge.lnk", - AppType = 2, + AppType = Win32Program.ApplicationType.Win32Application, }; private static readonly Win32Program _chrome = new Win32Program @@ -131,7 +132,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs ExecutableName = "chrome.exe", FullPath = "c:\\program files (x86)\\google\\chrome\\application\\chrome.exe", LnkResolvedPath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\google chrome.lnk", - AppType = 2, + AppType = Win32Program.ApplicationType.Win32Application, }; private static readonly Win32Program _dummyProxyApp = new Win32Program @@ -140,7 +141,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs ExecutableName = "test_proxy.exe", FullPath = "c:\\program files (x86)\\microsoft\\edge\\application\\test_proxy.exe", LnkResolvedPath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\test proxy.lnk", - AppType = 2, + AppType = Win32Program.ApplicationType.Win32Application, }; private static readonly Win32Program _cmdRunCommand = new Win32Program @@ -149,7 +150,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs ExecutableName = "cmd.exe", FullPath = "c:\\windows\\system32\\cmd.exe", LnkResolvedPath = null, - AppType = 3, // Run command + AppType = Win32Program.ApplicationType.RunCommand, // Run command }; private static readonly Win32Program _cmderRunCommand = new Win32Program @@ -159,7 +160,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs ExecutableName = "Cmder.exe", FullPath = "c:\\tools\\cmder\\cmder.exe", LnkResolvedPath = null, - AppType = 3, // Run command + AppType = Win32Program.ApplicationType.RunCommand, // Run command }; private static readonly Win32Program _dummyInternetShortcutApp = new Win32Program @@ -169,7 +170,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs FullPath = "steam://rungameid/1258080", ParentDirectory = "C:\\Users\\temp\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Steam", LnkResolvedPath = null, - AppType = 1, + AppType = Win32Program.ApplicationType.InternetShortcutApplication, }; private static readonly Win32Program _dummyInternetShortcutAppDuplicate = new Win32Program @@ -179,9 +180,59 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs FullPath = "steam://rungameid/1258080", ParentDirectory = "C:\\Users\\temp\\Desktop", LnkResolvedPath = null, - AppType = 1, + AppType = Win32Program.ApplicationType.InternetShortcutApplication, }; + private static readonly Win32Program _dummyAppRefApp = new Win32Program + { + Name = "Dummy AppRef Application", + ExecutableName = "dummy.appref-ms", + FullPath = "C:\\dummy.appref-ms", + ParentDirectory = "C:\\", + LnkResolvedPath = null, + AppType = Win32Program.ApplicationType.ApprefApplication, + }; + + private static readonly Win32Program _dummyShortcutApp = new Win32Program + { + Name = "Dummy Shortcut Application", + ExecutableName = "application.lnk", + FullPath = "C:\\application.lnk", + ParentDirectory = "C:\\", + LnkResolvedPath = "C:\\application.lnk", + AppType = Win32Program.ApplicationType.ShortcutApplication, + }; + + private static readonly Win32Program _dummyFolderApp = new Win32Program + { + Name = "Dummy Folder", + ExecutableName = "application.lnk", + FullPath = "C:\\dummy\\folder", + ParentDirectory = "C:\\dummy\\", + LnkResolvedPath = "C:\\tools\\application.lnk", + AppType = Win32Program.ApplicationType.Folder, + }; + + private static readonly Win32Program _dummyGenericFileApp = new Win32Program + { + Name = "Dummy Folder", + ExecutableName = "application.lnk", + FullPath = "C:\\dummy\\file.pdf", + ParentDirectory = "C:\\dummy\\", + LnkResolvedPath = "C:\\tools\\application.lnk", + AppType = Win32Program.ApplicationType.GenericFile, + }; + + private static IDirectoryWrapper GetMockedDirectoryWrapper() + { + var mockDirectory = new Mock(); + + // Check if the file has no extension. This is not actually true since there can be files without extensions, but this is sufficient for the purpose of a mock function + Func returnValue = arg => string.IsNullOrEmpty(System.IO.Path.GetExtension(arg)); + mockDirectory.Setup(m => m.Exists(It.IsAny())).Returns(returnValue); + return mockDirectory.Object; + } + [Test] public void DedupFunctionWhenCalledMustRemoveDuplicateNotepads() { @@ -427,6 +478,68 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs Assert.AreEqual(contextMenuResults[2].Title, Properties.Resources.wox_plugin_program_open_in_console); } + [Test] + public void AppRefApplicationShouldReturnContextMenuWithOpenInConsoleWhenContextMenusIsCalled() + { + // Arrange + var mock = new Mock(); + + // Act + List contextMenuResults = _dummyAppRefApp.ContextMenus(mock.Object); + + // Assert + Assert.AreEqual(contextMenuResults.Count, 3); + Assert.AreEqual(contextMenuResults[0].Title, Properties.Resources.wox_plugin_program_run_as_administrator); + Assert.AreEqual(contextMenuResults[1].Title, Properties.Resources.wox_plugin_program_open_containing_folder); + Assert.AreEqual(contextMenuResults[2].Title, Properties.Resources.wox_plugin_program_open_in_console); + } + + [Test] + public void ShortcutApplicationShouldReturnContextMenuWithOpenInConsoleWhenContextMenusIsCalled() + { + // Arrange + var mock = new Mock(); + + // Act + List contextMenuResults = _dummyShortcutApp.ContextMenus(mock.Object); + + // Assert + Assert.AreEqual(contextMenuResults.Count, 3); + Assert.AreEqual(contextMenuResults[0].Title, Properties.Resources.wox_plugin_program_run_as_administrator); + Assert.AreEqual(contextMenuResults[1].Title, Properties.Resources.wox_plugin_program_open_containing_folder); + Assert.AreEqual(contextMenuResults[2].Title, Properties.Resources.wox_plugin_program_open_in_console); + } + + [Test] + public void FolderApplicationShouldReturnContextMenuWithOpenInConsoleWhenContextMenusIsCalled() + { + // Arrange + var mock = new Mock(); + + // Act + List contextMenuResults = _dummyFolderApp.ContextMenus(mock.Object); + + // Assert + Assert.AreEqual(contextMenuResults.Count, 2); + Assert.AreEqual(contextMenuResults[0].Title, Properties.Resources.wox_plugin_program_open_containing_folder); + Assert.AreEqual(contextMenuResults[1].Title, Properties.Resources.wox_plugin_program_open_in_console); + } + + [Test] + public void GenericFileApplicationShouldReturnContextMenuWithOpenInConsoleWhenContextMenusIsCalled() + { + // Arrange + var mock = new Mock(); + + // Act + List contextMenuResults = _dummyGenericFileApp.ContextMenus(mock.Object); + + // Assert + Assert.AreEqual(contextMenuResults.Count, 2); + Assert.AreEqual(contextMenuResults[0].Title, Properties.Resources.wox_plugin_program_open_containing_folder); + Assert.AreEqual(contextMenuResults[1].Title, Properties.Resources.wox_plugin_program_open_in_console); + } + [Test] public void Win32AppsShouldSetNameAsTitleWhileCreatingResult() { @@ -440,5 +553,21 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs Assert.IsTrue(result.Title.Equals(_cmderRunCommand.Name, StringComparison.Ordinal)); Assert.IsFalse(result.Title.Equals(_cmderRunCommand.Description, StringComparison.Ordinal)); } + + [TestCase("C:\\Program Files\\dummy.exe", ExpectedResult = Win32Program.ApplicationType.Win32Application)] + [TestCase("C:\\Program Files\\dummy.msc", ExpectedResult = Win32Program.ApplicationType.Win32Application)] + [TestCase("C:\\Program Files\\dummy.lnk", ExpectedResult = Win32Program.ApplicationType.ShortcutApplication)] + [TestCase("C:\\Program Files\\dummy.appref-ms", ExpectedResult = Win32Program.ApplicationType.ApprefApplication)] + [TestCase("C:\\Program Files\\dummy.url", ExpectedResult = Win32Program.ApplicationType.InternetShortcutApplication)] + [TestCase("C:\\Program Files\\dummy", ExpectedResult = Win32Program.ApplicationType.Folder)] + [TestCase("C:\\Program Files\\dummy.txt", ExpectedResult = Win32Program.ApplicationType.GenericFile)] + public Win32Program.ApplicationType GetAppTypeFromPathShouldReturnCorrectAppTypeWhenAppPathIsPassedAsArgument(string path) + { + // Directory.Exists must be mocked + Win32Program.DirectoryWrapper = GetMockedDirectoryWrapper(); + + // Act + return Win32Program.GetAppTypeFromPath(path); + } } } diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Storage/Win32ProgramRepositoryTest.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Storage/Win32ProgramRepositoryTest.cs index 61036c34a9..5b45a5fba3 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Storage/Win32ProgramRepositoryTest.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Storage/Win32ProgramRepositoryTest.cs @@ -84,7 +84,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Storage // Assert Assert.AreEqual(win32ProgramRepository.Count(), 1); - Assert.AreEqual(win32ProgramRepository.ElementAt(0).AppType, 2); + Assert.AreEqual(win32ProgramRepository.ElementAt(0).AppType, Win32Program.ApplicationType.ApprefApplication); } [TestCase("directory", "path.appref-ms")] @@ -145,7 +145,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Storage // Assert Assert.AreEqual(win32ProgramRepository.Count(), 1); - Assert.AreEqual(win32ProgramRepository.ElementAt(0).AppType, 2); + Assert.AreEqual(win32ProgramRepository.ElementAt(0).AppType, Win32Program.ApplicationType.Win32Application); } [TestCase("directory", "path.exe")] @@ -216,7 +216,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Storage // Assert Assert.AreEqual(win32ProgramRepository.Count(), 1); - Assert.AreEqual(win32ProgramRepository.ElementAt(0).AppType, 1); // Internet Shortcut Application + Assert.AreEqual(win32ProgramRepository.ElementAt(0).AppType, Win32Program.ApplicationType.InternetShortcutApplication); // Internet Shortcut Application } [TestCase("path.url")] @@ -336,7 +336,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Storage // Assert Assert.AreEqual(win32ProgramRepository.Count(), 1); - Assert.AreEqual(win32ProgramRepository.ElementAt(0).AppType, 2); + Assert.AreEqual(win32ProgramRepository.ElementAt(0).AppType, Win32Program.ApplicationType.Win32Application); } [TestCase("directory", "path.lnk")] diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/Win32Program.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/Win32Program.cs index 13a54a3cd2..3e851ef008 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/Win32Program.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/Win32Program.cs @@ -52,7 +52,7 @@ namespace Microsoft.Plugin.Program.Programs public string Location => ParentDirectory; - public uint AppType { get; set; } + public ApplicationType AppType { get; set; } // Wrappers for File Operations public static IFileVersionInfoWrapper FileVersionInfoWrapper { get; set; } = new FileVersionInfoWrapper(); @@ -61,20 +61,26 @@ namespace Microsoft.Plugin.Program.Programs public static IShellLinkHelper Helper { get; set; } = new ShellLinkHelper(); + public static IDirectoryWrapper DirectoryWrapper { get; set; } = new DirectoryWrapper(); + private const string ShortcutExtension = "lnk"; private const string ApplicationReferenceExtension = "appref-ms"; - private const string ExeExtension = "exe"; private const string InternetShortcutExtension = "url"; + private static readonly HashSet ExecutableApplicationExtensions = new HashSet(StringComparer.OrdinalIgnoreCase) { "exe", "bat", "bin", "com", "msc", "msi", "cmd", "ps1", "job", "msp", "mst", "sct", "ws", "wsh", "wsf" }; private const string ProxyWebApp = "_proxy.exe"; private const string AppIdArgument = "--app-id"; - private enum ApplicationTypes + public enum ApplicationType { - WEB_APPLICATION = 0, - INTERNET_SHORTCUT_APPLICATION = 1, - WIN32_APPLICATION = 2, - RUN_COMMAND = 3, + WebApplication = 0, + InternetShortcutApplication = 1, + Win32Application = 2, + ShortcutApplication = 3, + ApprefApplication = 4, + RunCommand = 5, + Folder = 6, + GenericFile = 7, } // Function to calculate the score of a result @@ -106,7 +112,7 @@ namespace Microsoft.Plugin.Program.Programs } // Set the subtitle to 'Web Application' - AppType = (uint)ApplicationTypes.WEB_APPLICATION; + AppType = ApplicationType.WebApplication; string[] subqueries = query?.Split() ?? Array.Empty(); bool nameContainsQuery = false; @@ -132,22 +138,30 @@ namespace Microsoft.Plugin.Program.Programs // Function to set the subtitle based on the Type of application private string SetSubtitle() { - if (AppType == (uint)ApplicationTypes.WIN32_APPLICATION) + if (AppType == ApplicationType.Win32Application || AppType == ApplicationType.ShortcutApplication || AppType == ApplicationType.ApprefApplication) { return Properties.Resources.powertoys_run_plugin_program_win32_application; } - else if (AppType == (uint)ApplicationTypes.INTERNET_SHORTCUT_APPLICATION) + else if (AppType == ApplicationType.InternetShortcutApplication) { return Properties.Resources.powertoys_run_plugin_program_internet_shortcut_application; } - else if (AppType == (uint)ApplicationTypes.WEB_APPLICATION) + else if (AppType == ApplicationType.WebApplication) { return Properties.Resources.powertoys_run_plugin_program_web_application; } - else if (AppType == (uint)ApplicationTypes.RUN_COMMAND) + else if (AppType == ApplicationType.RunCommand) { return Properties.Resources.powertoys_run_plugin_program_run_command; } + else if (AppType == ApplicationType.Folder) + { + return Properties.Resources.powertoys_run_plugin_program_folder_type; + } + else if (AppType == ApplicationType.GenericFile) + { + return Properties.Resources.powertoys_run_plugin_program_generic_file_type; + } else { return string.Empty; @@ -156,7 +170,7 @@ namespace Microsoft.Plugin.Program.Programs public bool QueryEqualsNameForRunCommands(string query) { - if (query != null && AppType == (uint)ApplicationTypes.RUN_COMMAND + if (query != null && AppType == ApplicationType.RunCommand && !query.Equals(Name, StringComparison.OrdinalIgnoreCase)) { return false; @@ -240,7 +254,7 @@ namespace Microsoft.Plugin.Program.Programs var contextMenus = new List(); - if (AppType != (uint)ApplicationTypes.INTERNET_SHORTCUT_APPLICATION) + if (AppType != ApplicationType.InternetShortcutApplication && AppType != ApplicationType.Folder && AppType != ApplicationType.GenericFile) { contextMenus.Add(new ContextMenuResult { @@ -330,7 +344,7 @@ namespace Microsoft.Plugin.Program.Programs Description = string.Empty, Valid = true, Enabled = true, - AppType = (uint)ApplicationTypes.WIN32_APPLICATION, + AppType = ApplicationType.Win32Application, }; return p; } @@ -394,7 +408,7 @@ namespace Microsoft.Plugin.Program.Programs ParentDirectory = Directory.GetParent(path).FullName, Valid = true, Enabled = true, - AppType = (uint)ApplicationTypes.INTERNET_SHORTCUT_APPLICATION, + AppType = ApplicationType.InternetShortcutApplication, }; return p; } @@ -421,14 +435,11 @@ namespace Microsoft.Plugin.Program.Programs if (!string.IsNullOrEmpty(target)) { - var extension = Extension(target); - if (extension == ExeExtension && File.Exists(target)) + if (File.Exists(target) || Directory.Exists(target)) { program.LnkResolvedPath = program.FullPath; program.FullPath = Path.GetFullPath(target).ToLower(CultureInfo.CurrentCulture); - program.ExecutableName = Path.GetFileName(target); - program.HasArguments = Helper.HasArguments; - program.Arguments = Helper.Arguments; + program.AppType = GetAppTypeFromPath(target); var description = Helper.Description; if (!string.IsNullOrEmpty(description)) @@ -494,6 +505,43 @@ namespace Microsoft.Plugin.Program.Programs } } + // Function to get the application type, given the path to the application + public static ApplicationType GetAppTypeFromPath(string path) + { + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + + string extension = Extension(path); + ApplicationType appType = ApplicationType.GenericFile; + + if (ExecutableApplicationExtensions.Contains(extension)) + { + appType = ApplicationType.Win32Application; + } + else if (extension.Equals(ShortcutExtension, StringComparison.OrdinalIgnoreCase)) + { + appType = ApplicationType.ShortcutApplication; + } + else if (extension.Equals(ApplicationReferenceExtension, StringComparison.OrdinalIgnoreCase)) + { + appType = ApplicationType.ApprefApplication; + } + else if (extension.Equals(InternetShortcutExtension, StringComparison.OrdinalIgnoreCase)) + { + appType = ApplicationType.InternetShortcutApplication; + } + + // If the path exists, check if it is a directory + else if (DirectoryWrapper.Exists(path)) + { + appType = ApplicationType.Folder; + } + + return appType; + } + // Function to get the Win32 application, given the path to the application public static Win32Program GetAppFromPath(string path) { @@ -503,26 +551,23 @@ namespace Microsoft.Plugin.Program.Programs } Win32Program app = null; - const string exeExtension = ".exe"; - const string lnkExtension = ".lnk"; - const string urlExtenion = ".url"; - const string apprefExtension = ".appref-ms"; - string extension = Path.GetExtension(path); + ApplicationType appType = GetAppTypeFromPath(path); - if (extension.Equals(exeExtension, StringComparison.OrdinalIgnoreCase)) + if (appType == ApplicationType.Win32Application) { app = ExeProgram(path); } - else if (extension.Equals(lnkExtension, StringComparison.OrdinalIgnoreCase)) + else if (appType == ApplicationType.ShortcutApplication) { app = LnkProgram(path); } - else if (extension.Equals(apprefExtension, StringComparison.OrdinalIgnoreCase)) + else if (appType == ApplicationType.ApprefApplication) { app = CreateWin32Program(path); + app.AppType = ApplicationType.ApprefApplication; } - else if (extension.Equals(urlExtenion, StringComparison.OrdinalIgnoreCase)) + else if (appType == ApplicationType.InternetShortcutApplication) { app = InternetShortcutProgram(path); } @@ -626,13 +671,13 @@ namespace Microsoft.Plugin.Program.Programs var paths = listToAdd.Distinct().ToArray(); - var programs1 = paths.AsParallel().Where(p => Extension(p) == ExeExtension).Select(ExeProgram); - var programs2 = paths.AsParallel().Where(p => Extension(p) == ShortcutExtension).Select(ExeProgram); + var programs1 = paths.AsParallel().Where(p => ExecutableApplicationExtensions.Contains(Extension(p))).Select(ExeProgram); + var programs2 = paths.AsParallel().Where(p => Extension(p) == ShortcutExtension).Select(LnkProgram); var programs3 = from p in paths.AsParallel() let e = Extension(p) - where e != ShortcutExtension && e != ExeExtension + where e != ShortcutExtension && !ExecutableApplicationExtensions.Contains(e) select CreateWin32Program(p); - return programs1.Concat(programs2).Concat(programs3); + return programs1.Concat(programs2).Where(p => p.Valid).Concat(programs3).Where(p => p.Valid); } // Function to obtain the list of applications, the locations of which have been added to the env variable PATH @@ -662,14 +707,14 @@ namespace Microsoft.Plugin.Program.Programs var programs1 = allPaths.AsParallel().Where(p => Extension(p).Equals(ShortcutExtension, StringComparison.OrdinalIgnoreCase)).Select(LnkProgram); var programs2 = allPaths.AsParallel().Where(p => Extension(p).Equals(ApplicationReferenceExtension, StringComparison.OrdinalIgnoreCase)).Select(CreateWin32Program); var programs3 = allPaths.AsParallel().Where(p => Extension(p).Equals(InternetShortcutExtension, StringComparison.OrdinalIgnoreCase)).Select(InternetShortcutProgram); - var programs4 = allPaths.AsParallel().Where(p => Extension(p).Equals(ExeExtension, StringComparison.OrdinalIgnoreCase)).Select(ExeProgram); + var programs4 = allPaths.AsParallel().Where(p => ExecutableApplicationExtensions.Contains(Extension(p))).Select(ExeProgram); var allPrograms = programs1.Concat(programs2).Where(p => p.Valid) .Concat(programs3).Where(p => p.Valid) .Concat(programs4).Where(p => p.Valid) .Select(p => { - p.AppType = (uint)ApplicationTypes.RUN_COMMAND; + p.AppType = ApplicationType.RunCommand; return p; }); @@ -696,7 +741,7 @@ 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(CreateWin32Program); 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); + var programs4 = paths.AsParallel().Where(p => ExecutableApplicationExtensions.Contains(Extension(p))).Select(ExeProgram); return programs1.Concat(programs2).Where(p => p.Valid) .Concat(programs3).Where(p => p.Valid) @@ -858,7 +903,7 @@ namespace Microsoft.Plugin.Program.Programs // Deduplication code public static Win32Program[] DeduplicatePrograms(ParallelQuery programs) { - var uniqueExePrograms = programs.Where(x => !(string.IsNullOrEmpty(x.LnkResolvedPath) && (Extension(x.FullPath) == ExeExtension) && !(x.AppType == (uint)ApplicationTypes.RUN_COMMAND))); + var uniqueExePrograms = programs.Where(x => !(string.IsNullOrEmpty(x.LnkResolvedPath) && ExecutableApplicationExtensions.Contains(Extension(x.FullPath)) && !(x.AppType == ApplicationType.RunCommand))); var uniquePrograms = uniqueExePrograms.Distinct(new RemoveDuplicatesComparer()); return uniquePrograms.ToArray(); } diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Properties/Resources.Designer.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Properties/Resources.Designer.cs index 862c29f894..d80fc7f989 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Properties/Resources.Designer.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Properties/Resources.Designer.cs @@ -78,6 +78,24 @@ namespace Microsoft.Plugin.Program.Properties { } } + /// + /// Looks up a localized string similar to Folder. + /// + public static string powertoys_run_plugin_program_folder_type { + get { + return ResourceManager.GetString("powertoys_run_plugin_program_folder_type", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to File. + /// + public static string powertoys_run_plugin_program_generic_file_type { + get { + return ResourceManager.GetString("powertoys_run_plugin_program_generic_file_type", resourceCulture); + } + } + /// /// Looks up a localized string similar to Internet shortcut application. /// diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Properties/Resources.resx b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Properties/Resources.resx index 79eac7169e..01a5d1cd8d 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Properties/Resources.resx +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Properties/Resources.resx @@ -153,6 +153,12 @@ Path + + Folder + + + File + Unable to start diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Storage/Win32ProgramRepository.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Storage/Win32ProgramRepository.cs index fabd43100d..d7bec7c65f 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Storage/Win32ProgramRepository.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Storage/Win32ProgramRepository.cs @@ -69,6 +69,7 @@ namespace Microsoft.Plugin.Program.Storage string newPath = e.FullPath; string extension = Path.GetExtension(newPath); + Win32Program.ApplicationType appType = Win32Program.GetAppTypeFromPath(newPath); Programs.Win32Program newApp = Programs.Win32Program.GetAppFromPath(newPath); Programs.Win32Program oldApp = null; @@ -78,11 +79,11 @@ namespace Microsoft.Plugin.Program.Storage // This situation is not encountered for other application types because the fullPath is the path itself, instead of being computed by using the path to the app. try { - if (extension.Equals(LnkExtension, StringComparison.OrdinalIgnoreCase)) + if (appType == Win32Program.ApplicationType.ShortcutApplication) { oldApp = new Win32Program() { Name = Path.GetFileNameWithoutExtension(e.OldName), ExecutableName = newApp.ExecutableName, FullPath = newApp.FullPath }; } - else if (extension.Equals(UrlExtension, StringComparison.OrdinalIgnoreCase)) + else if (appType == Win32Program.ApplicationType.InternetShortcutApplication) { oldApp = new Win32Program() { Name = Path.GetFileNameWithoutExtension(e.OldName), ExecutableName = Path.GetFileName(e.OldName), FullPath = newApp.FullPath }; } diff --git a/src/modules/launcher/Wox.Infrastructure/FileSystemHelper/DirectoryWrapper.cs b/src/modules/launcher/Wox.Infrastructure/FileSystemHelper/DirectoryWrapper.cs new file mode 100644 index 0000000000..561f45e7f4 --- /dev/null +++ b/src/modules/launcher/Wox.Infrastructure/FileSystemHelper/DirectoryWrapper.cs @@ -0,0 +1,18 @@ +// 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. + +namespace Wox.Infrastructure.FileSystemHelper +{ + public class DirectoryWrapper : IDirectoryWrapper + { + public DirectoryWrapper() + { + } + + public bool Exists(string path) + { + return System.IO.Directory.Exists(path); + } + } +} diff --git a/src/modules/launcher/Wox.Infrastructure/FileSystemHelper/IDirectoryWrapper.cs b/src/modules/launcher/Wox.Infrastructure/FileSystemHelper/IDirectoryWrapper.cs new file mode 100644 index 0000000000..cbe1a97c79 --- /dev/null +++ b/src/modules/launcher/Wox.Infrastructure/FileSystemHelper/IDirectoryWrapper.cs @@ -0,0 +1,11 @@ +// 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. + +namespace Wox.Infrastructure.FileSystemHelper +{ + public interface IDirectoryWrapper + { + bool Exists(string path); + } +}