Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Setup .NET
uses: actions/setup-dotnet@v1
uses: actions/setup-dotnet@v2
with:
dotnet-version: 3.1.301
dotnet-version: 5.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ jobs:
publish:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Setup .NET
uses: actions/setup-dotnet@v1
uses: actions/setup-dotnet@v2
with:
dotnet-version: 3.1.301
dotnet-version: 5.0.x
- name: get version
id: version
uses: notiz-dev/github-action-json-property@release
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ This port is intended to be used for [Flow Launcher](https://github.com/Flow-Lau

**New with this port:**

Use {*} flag in your setting argument to allow infinite arguments passed through the query window. For this to work the setting argument needs to end with `{*}`
- Use {*} flag in your setting argument to allow infinite arguments passed through the query window. For this to work the setting argument needs to end with `{*}`

Use {0} in your setting argument to pass just one argument. For this to work the setting argument needs to end with `{0}`, e.g. `-p {0}`. You can also specify multiple additional arguments e.g. `-h {0} -p {1}` with query `r shortcut1 myremotecomp 22`, this will pass the arguments in as `-h myremotecomp -p 22`.
- Use {0} in your setting argument to pass just one argument. For this to work the setting argument needs to end with `{0}`, e.g. `-p {0}`. You can also specify multiple additional arguments e.g. `-h {0} -p {1}` with query `r shortcut1 myremotecomp 22`, this will pass the arguments in as `-h myremotecomp -p 22`.

- Use the currently open file explorer window as the working directory for various commands. For example `code .` would open current folder in VSCode

**Installation**

Expand Down
10 changes: 5 additions & 5 deletions Wox.Plugin.Runner/Command.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ namespace Wox.Plugin.Runner
{
public class Command
{
public string Shortcut { get; set; }
public string Description { get; set; }
public string Path { get; set; }
public string WorkingDirectory { get; set; }
public string ArgumentsFormat { get; set; }
public string Shortcut { get; set; } = "";
public string Description { get; set; } = "";
public string Path { get; set; } = "";
public string WorkingDirectory { get; set; } = "";
public string ArgumentsFormat { get; set; } = "";
}
}
2 changes: 1 addition & 1 deletion Wox.Plugin.Runner/ConfigurationLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public IEnumerable<Command> LoadCommands()
{
var text = File.ReadAllText(configFile);
if (!string.IsNullOrEmpty(text))
return JsonSerializer.Deserialize<IEnumerable<Command>>(text);
return JsonSerializer.Deserialize<IEnumerable<Command>>(text) ?? new List<Command>();

return new List<Command>();
}
Expand Down
7 changes: 2 additions & 5 deletions Wox.Plugin.Runner/Infrastructure/NullToVisibilityConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,9 @@ namespace Wox.Plugin.Runner.Infrastructure
{
class NullToVisibilityConverter : IValueConverter
{
public object Convert( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture )
public object Convert( object? value, Type targetType, object parameter, System.Globalization.CultureInfo culture )
{
if ( value == null )
return Visibility.Hidden;
else
return Visibility.Visible;
return value == null ? Visibility.Hidden : Visibility.Visible;
}

public object ConvertBack( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture )
Expand Down
36 changes: 23 additions & 13 deletions Wox.Plugin.Runner/Runner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ namespace Wox.Plugin.Runner
{
public class Runner : IPlugin, ISettingProvider
{
internal static PluginInitContext Context;
RunnerSettingsViewModel viewModel;
internal static PluginInitContext Context = null!;
RunnerSettingsViewModel? viewModel;

public void Init(PluginInitContext context)
{
Expand Down Expand Up @@ -93,16 +93,18 @@ private List<Result> FuzzySearchCommand(string shortcut, string[] terms)

public Control CreateSettingPanel()
{
return new RunnerSettings(viewModel);
return new RunnerSettings(viewModel!);
}

private bool RunCommand(ActionContext e, Command command, IEnumerable<string> terms = null)
private bool RunCommand(ActionContext e, Command command, IEnumerable<string>? terms = null)
{
try
{
var args = GetProcessArguments(command, terms);
var startInfo = new ProcessStartInfo(args.FileName, args.Arguments);
startInfo.UseShellExecute = true;
var startInfo = new ProcessStartInfo(args.FileName, args.Arguments)
{
UseShellExecute = true
};
if (args.WorkingDirectory != null)
{
startInfo.WorkingDirectory = args.WorkingDirectory;
Expand All @@ -116,14 +118,15 @@ private bool RunCommand(ActionContext e, Command command, IEnumerable<string> te
if (w32Ex.Message != "The operation was canceled by the user")
throw;
}
catch (FormatException)
catch (FormatException ex)
{
Context.API.ShowMsg("There was a problem. Please check the arguments format for the command.");
Context.API.LogException(nameof(Runner), "Argument format was invalid", ex);
}
return true;
}

private ProcessArguments GetProcessArguments(Command c, IEnumerable<string> terms)
private ProcessArguments GetProcessArguments(Command c, IEnumerable<string>? terms)
{
var argString = string.Empty;

Expand All @@ -135,7 +138,7 @@ private ProcessArguments GetProcessArguments(Command c, IEnumerable<string> term
// remove '{*}' flag from arguments
argString = c.ArgumentsFormat.Remove(c.ArgumentsFormat.Length - 3, 3);
// add user specified arguments to the arguments to be passed
argString = argString + string.Join(" ", terms);
argString += terms != null ? string.Join(" ", terms) : "";
}
// command's arguments HAS flag/s, thus user is able to manually pass in arguments e.g. settings: {0} {1}
// or command's arguments HAS set normal text arguments e.g. settings: -h myremotecomp -p 22
Expand All @@ -148,11 +151,18 @@ private ProcessArguments GetProcessArguments(Command c, IEnumerable<string> term
}

var workingDir = c.WorkingDirectory;
if (workingDir == "{explorer}")
Copy link
Author

@stefnotch stefnotch Jul 15, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Working directory can be set to {explorer}, which will trigger the new behavior where it tries to get the path of the topmost file explorer window.

{
var openExplorerPaths = ExplorerPathsService.GetOpenExplorerPaths();
workingDir = openExplorerPaths.FirstOrDefault();
}

if (string.IsNullOrEmpty(workingDir))
{
// Use directory where executable is based.
workingDir = Path.GetDirectoryName(c.Path);
}
}

return new ProcessArguments
{
FileName = c.Path,
Expand All @@ -163,9 +173,9 @@ private ProcessArguments GetProcessArguments(Command c, IEnumerable<string> term

class ProcessArguments
{
public string FileName { get; set; }
public string Arguments { get; set; }
public string WorkingDirectory { get; set; }
public string FileName { get; set; } = "";
public string Arguments { get; set; } = "";
public string? WorkingDirectory { get; set; }
}
}
}
4 changes: 2 additions & 2 deletions Wox.Plugin.Runner/RunnerConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace Wox.Plugin.Runner
{
static class RunnerConfiguration
{
private static IConfigurationLoader loader;
private static IConfigurationLoader? loader;
public static IConfigurationLoader Loader
{
get
Expand All @@ -21,7 +21,7 @@ public static IConfigurationLoader Loader
}
}

private static IEnumerable<Command> commands;
private static IEnumerable<Command>? commands;
public static IEnumerable<Command> Commands
{
get
Expand Down
91 changes: 91 additions & 0 deletions Wox.Plugin.Runner/Services/ExplorerPathsService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;

namespace Wox.Plugin.Runner
{
class ExplorerPathsService
{
public static List<string> GetOpenExplorerPaths()
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This uses the "Shell" to iterate over all file explorer windows and read the file paths and HWNDs from them.
Then it uses "Win32" stuff to iterate over all open windows (sorted by how close to the user they are), and read the HWNDs from there.

Finally, it combines that info to return a sorted list of open file explorer windows. This means that GetOpenExplorerPaths().First() will always return the topmost open file explorer.

{
Type? type = Type.GetTypeFromProgID("Shell.Application");
if (type == null) return new List<string>();
dynamic? shell = Activator.CreateInstance(type);
if (shell == null) return new List<string>();
try
{
var fullResults = new List<ExplorerResult>();

var openWindows = shell.Windows();
for (int i = 0; i < openWindows.Count; i++)
{
var window = openWindows.Item(i);
if (window == null) continue;
var fileName = Path.GetFileName((string)window.FullName);

// Other things which are returned include the internet explorer and the classic control panel
// We only want file explorer windows
if (fileName.ToLower() == "explorer.exe")
{
string locationUrl = window.LocationURL;
if (!string.IsNullOrEmpty(locationUrl))
{
fullResults.Add(new ExplorerResult(new IntPtr(window.HWND), new Uri(locationUrl).LocalPath));
}
}
}

int zIndex = fullResults.Count;
// EnumWindows iterates over windows in ZIndex order, basically the foremost window will be the first one
EnumWindows((IntPtr hwnd, IntPtr param) =>
{
var result = fullResults.Find(v => v.HWND == hwnd);
if(result != null)
{
result.ZIndex = zIndex;
zIndex -= 1;
}
// zIndex is also used as a counter: how many more windows do we have to find
return zIndex > 0;
}, IntPtr.Zero);

// sort descending and return the paths
return fullResults
.OrderByDescending(v => v.ZIndex)
.Select(v => v.Path)
.ToList();
}
finally
{
Marshal.FinalReleaseComObject(shell);
}
}

private class ExplorerResult
{
/// <summary>
/// Higher values means that the window is closer to the user
/// </summary>
public int ZIndex { get; set; } = 0;
public IntPtr HWND { get; }
public string Path { get; }

public ExplorerResult(IntPtr hwnd, string path)
{
HWND = hwnd;
Path = path;
}
}

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EnumWindows(EnumWindowsProc enumProc, IntPtr lParam);

// Delegate to filter which windows to include
public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);


}
}
Loading