[PT Run] Find applications using the PATH env variable (#4418)

* Search for programs in the path env variable

* removing list of disabled programs

* Added env variable string to classify apps

* reverted the fullpath change

* removing full paths while calculating dups

* removed dups

* removed debugging code

* Renamed to run command

* Added condition to filter run commands unless there is an exact match

* renamed occurances to RUN COMMAND

* localized the subtitle - Run command

* Added tests

* add fullpath back to hash calculation

* renamed the function
This commit is contained in:
Alekhya 2020-06-23 11:40:11 -07:00 committed by GitHub
parent 147c08bd71
commit ca99f60964
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 128 additions and 5 deletions

View File

@ -38,5 +38,6 @@
<system:String x:Key="powertoys_run_plugin_program_win32_application">Win32-Anwendung</system:String>
<system:String x:Key="powertoys_run_plugin_program_internet_shortcut_application">Weblink-Anwendung</system:String>
<system:String x:Key="powertoys_run_plugin_program_web_application">Web-Anwendung</system:String>
<system:String x:Key="powertoys_run_plugin_program_run_command">Run command</system:String>
</ResourceDictionary>

View File

@ -49,4 +49,5 @@
<system:String x:Key="powertoys_run_plugin_program_win32_application">Win32 application</system:String>
<system:String x:Key="powertoys_run_plugin_program_internet_shortcut_application">Internet shortcut application</system:String>
<system:String x:Key="powertoys_run_plugin_program_web_application">Web application</system:String>
<system:String x:Key="powertoys_run_plugin_program_run_command">Run command</system:String>
</ResourceDictionary>

View File

@ -39,5 +39,6 @@
<system:String x:Key="powertoys_run_plugin_program_win32_application">Win32 application</system:String>
<system:String x:Key="powertoys_run_plugin_program_internet_shortcut_application">Internet shortcut application</system:String>
<system:String x:Key="powertoys_run_plugin_program_web_application">Web application</system:String>
<system:String x:Key="powertoys_run_plugin_program_run_command">Run command</system:String>
</ResourceDictionary>

View File

@ -38,5 +38,6 @@
<system:String x:Key="powertoys_run_plugin_program_win32_application">Win32 application</system:String>
<system:String x:Key="powertoys_run_plugin_program_internet_shortcut_application">Internet shortcut application</system:String>
<system:String x:Key="powertoys_run_plugin_program_web_application">Web application</system:String>
<system:String x:Key="powertoys_run_plugin_program_run_command">Run command</system:String>
</ResourceDictionary>

View File

@ -40,5 +40,6 @@
<system:String x:Key="powertoys_run_plugin_program_win32_application">Win32 application</system:String>
<system:String x:Key="powertoys_run_plugin_program_internet_shortcut_application">Internet shortcut application</system:String>
<system:String x:Key="powertoys_run_plugin_program_web_application">Web application</system:String>
<system:String x:Key="powertoys_run_plugin_program_run_command">Run command</system:String>
</ResourceDictionary>

View File

@ -40,5 +40,6 @@
<system:String x:Key="powertoys_run_plugin_program_win32_application">Win32 application</system:String>
<system:String x:Key="powertoys_run_plugin_program_internet_shortcut_application">Internet shortcut application</system:String>
<system:String x:Key="powertoys_run_plugin_program_web_application">Web application</system:String>
<system:String x:Key="powertoys_run_plugin_program_run_command">Run command</system:String>
</ResourceDictionary>

View File

@ -38,4 +38,6 @@
<system:String x:Key="powertoys_run_plugin_program_win32_application">Win32 application</system:String>
<system:String x:Key="powertoys_run_plugin_program_internet_shortcut_application">Internet shortcut application</system:String>
<system:String x:Key="powertoys_run_plugin_program_web_application">Web application</system:String>
<system:String x:Key="powertoys_run_plugin_program_run_command">Run command</system:String>
</ResourceDictionary>

View File

@ -48,7 +48,8 @@ namespace Microsoft.Plugin.Program.Programs
{
WEB_APPLICATION = 0,
INTERNET_SHORTCUT_APPLICATION = 1,
WIN32_APPLICATION = 2
WIN32_APPLICATION = 2,
RUN_COMMAND = 3
}
private int Score(string query)
@ -116,12 +117,27 @@ namespace Microsoft.Plugin.Program.Programs
{
return api.GetTranslation("powertoys_run_plugin_program_web_application");
}
else if(AppType == (uint)ApplicationTypes.RUN_COMMAND)
{
return api.GetTranslation("powertoys_run_plugin_program_run_command");
}
else
{
return String.Empty;
}
}
public bool QueryEqualsNameForRunCommands(string query)
{
if (AppType == (uint)ApplicationTypes.RUN_COMMAND
&& !query.Equals(Name, StringComparison.OrdinalIgnoreCase))
{
return false;
}
return true;
}
public Result Result(string query, IPublicAPI api)
{
var score = Score(query);
@ -144,6 +160,12 @@ namespace Microsoft.Plugin.Program.Programs
}
}
// NOTE: This is to display run commands only when there is an exact match, like in start menu
if(!QueryEqualsNameForRunCommands(query))
{
return null;
}
var result = new Result
{
SubTitle = SetSubtitle(AppType, api),
@ -431,7 +453,7 @@ namespace Microsoft.Plugin.Program.Programs
}
}
private static IEnumerable<string> ProgramPaths(string directory, string[] suffixes)
private static IEnumerable<string> ProgramPaths(string directory, string[] suffixes, bool recursiveSearch = true)
{
if (!Directory.Exists(directory))
{
@ -468,6 +490,12 @@ namespace Microsoft.Plugin.Program.Programs
try
{
// If the search is set to be non-recursive, then do not enqueue the child directories.
if(!recursiveSearch)
{
continue;
}
foreach (var childDirectory in Directory.EnumerateDirectories(currentDirectory, "*", SearchOption.TopDirectoryOnly))
{
folderQueue.Enqueue(childDirectory);
@ -518,6 +546,45 @@ namespace Microsoft.Plugin.Program.Programs
return programs1.Concat(programs2).Concat(programs3);
}
// Function to obtain the list of applications, the locations of which have been added to the env variable PATH
private static ParallelQuery<Win32> PathEnvironmentPrograms(string[] suffixes)
{
// To get all the locations stored in the PATH env variable
var pathEnvVariable = Environment.GetEnvironmentVariable("PATH");
string[] searchPaths = pathEnvVariable.Split(Path.PathSeparator);
IEnumerable<String> toFilterAllPaths = new List<String>();
bool isRecursiveSearch = true;
foreach(string path in searchPaths)
{
if(path.Length > 0)
{
// to expand any environment variables present in the path
string directory = Environment.ExpandEnvironmentVariables(path);
var paths = ProgramPaths(directory, suffixes, !isRecursiveSearch);
toFilterAllPaths = toFilterAllPaths.Concat(paths);
}
}
var allPaths = toFilterAllPaths
.Distinct()
.ToArray();
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(Win32Program);
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 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; return p; });
return allPrograms;
}
private static ParallelQuery<Win32> IndexPath(string[] suffixes, List<string> IndexLocation)
{
var disabledProgramsList = Main._settings.DisabledProgramSources;
@ -650,8 +717,8 @@ namespace Microsoft.Plugin.Program.Programs
&& !string.IsNullOrEmpty(app1.ExecutableName) && !string.IsNullOrEmpty(app2.ExecutableName)
&& !string.IsNullOrEmpty(app1.FullPath) && !string.IsNullOrEmpty(app2.FullPath))
{
return app1.Name.Equals(app2.Name, StringComparison.OrdinalIgnoreCase)
&& app1.ExecutableName.Equals(app2.ExecutableName, StringComparison.OrdinalIgnoreCase)
return app1.Name.Equals(app2.Name, StringComparison.OrdinalIgnoreCase)
&& app1.ExecutableName.Equals(app2.ExecutableName, StringComparison.OrdinalIgnoreCase)
&& app1.FullPath.Equals(app2.FullPath, StringComparison.OrdinalIgnoreCase);
}
return false;
@ -676,7 +743,7 @@ namespace Microsoft.Plugin.Program.Programs
// Deduplication code
public static Func<ParallelQuery<Win32>, Win32[]> DeduplicatePrograms = (programs) =>
{
var uniqueExePrograms = programs.Where(x => !string.IsNullOrEmpty(x.LnkResolvedPath) || Extension(x.FullPath) != ExeExtension);
var uniqueExePrograms = programs.Where(x => !(string.IsNullOrEmpty(x.LnkResolvedPath) && (Extension(x.FullPath) == ExeExtension) && !(x.AppType == (uint)ApplicationTypes.RUN_COMMAND)));
var uniquePrograms = uniqueExePrograms.Distinct(new removeDuplicatesComparer());
return uniquePrograms.ToArray();
};
@ -702,6 +769,12 @@ namespace Microsoft.Plugin.Program.Programs
programs = programs.Concat(startMenu);
}
if (settings.EnablePathEnvironmentVariableSource)
{
var appPathEnvironment = PathEnvironmentPrograms(settings.ProgramSuffixes);
programs = programs.Concat(appPathEnvironment);
}
if (settings.EnableDesktopSource)
{
var desktop = DesktopPrograms(settings.ProgramSuffixes);

View File

@ -17,6 +17,8 @@ namespace Microsoft.Plugin.Program
public bool EnableRegistrySource { get; set; } = true;
public bool EnablePathEnvironmentVariableSource { get; set; } = true;
internal const char SuffixSeparator = ';';
/// <summary>

View File

@ -126,6 +126,24 @@ namespace Wox.Test.Plugins
LnkResolvedPath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\test proxy.lnk"
};
Win32 cmd_run_command = new Win32
{
Name = "cmd",
ExecutableName = "cmd.exe",
FullPath = "c:\\windows\\system32\\cmd.exe",
LnkResolvedPath = null,
AppType = 3 // Run command
};
Win32 cmder_run_command = new Win32
{
Name = "Cmder",
ExecutableName = "Cmder.exe",
FullPath = "c:\\tools\\cmder\\cmder.exe",
LnkResolvedPath = null,
AppType = 3 // Run command
};
Win32 dummy_internetShortcut_app = new Win32
{
Name = "Shop Titans",
@ -293,5 +311,27 @@ namespace Wox.Test.Plugins
// unreachable code
return true;
}
[TestCase("Command Prompt")]
[TestCase("cmd")]
[TestCase("cmd.exe")]
[TestCase("ignoreQueryText")]
public void Win32Applications_ShouldNotBeFiltered_WhenFilteringRunCommands(string query)
{
// Even if there is an exact match in the name or exe name, win32 applications should never be filtered
Assert.IsTrue(command_prompt.QueryEqualsNameForRunCommands(query));
}
[TestCase("cmd")]
[TestCase("Cmd")]
[TestCase("CMD")]
public void RunCommands_ShouldNotBeFiltered_OnExactMatch(string query)
{
// Partial matches should be filtered as cmd is not equal to cmder
Assert.IsFalse(cmder_run_command.QueryEqualsNameForRunCommands(query));
// the query matches the name (cmd) and is therefore not filtered (case-insensitive)
Assert.IsTrue(cmd_run_command.QueryEqualsNameForRunCommands(query));
}
}
}