Kill PowerToys Run process if it is already running (#11407)

This commit is contained in:
Mykhailo Pylyp
2021-05-24 12:05:17 +03:00
committed by GitHub
parent 0be526bd7e
commit d537280454
5 changed files with 88 additions and 40 deletions

View File

@@ -144,6 +144,10 @@ public
return gcnew String(CommonSharedConstants::RUN_SEND_SETTINGS_TELEMETRY_EVENT);
}
static String ^ RunExitEvent() {
return gcnew String(CommonSharedConstants::RUN_EXIT_EVENT);
}
static String ^ ColorPickerSendSettingsTelemetryEvent() {
return gcnew String(CommonSharedConstants::COLOR_PICKER_SEND_SETTINGS_TELEMETRY_EVENT);
}

View File

@@ -17,6 +17,8 @@ namespace CommonSharedConstants
const wchar_t RUN_SEND_SETTINGS_TELEMETRY_EVENT[] = L"Local\\PowerToysRunInvokeEvent-638ec522-0018-4b96-837d-6bd88e06f0d6";
const wchar_t RUN_EXIT_EVENT[] = L"Local\\PowerToysRunExitEvent-3e38e49d-a762-4ef1-88f2-fd4bc7481516";
const wchar_t COLOR_PICKER_SEND_SETTINGS_TELEMETRY_EVENT[] = L"Local\\ColorPickerSettingsTelemetryEvent-6c7071d8-4014-46ec-b687-913bd8a422f1";
// Path to the event used to show Color Picker

View File

@@ -82,6 +82,27 @@ private:
HANDLE send_telemetry_event;
// Handle a case when a user started standalone PowerToys Run or for some reason the process is leaked
void TerminateRunningInstance()
{
auto exitEvent = CreateEvent(nullptr, false, false, CommonSharedConstants::RUN_EXIT_EVENT);
if (!exitEvent)
{
Logger::warn(L"Failed to create exitEvent. {}", get_last_error_or_default(GetLastError()));
}
else
{
Logger::trace(L"Signaled exitEvent");
if (!SetEvent(exitEvent))
{
Logger::warn(L"Failed to signal exitEvent. {}", get_last_error_or_default(GetLastError()));
}
ResetEvent(exitEvent);
CloseHandle(exitEvent);
}
}
public:
// Constructor
Microsoft_Launcher()
@@ -181,12 +202,12 @@ public:
// Enable the powertoy
virtual void enable()
{
Logger::info("Launcher is enabling");
Logger::info("Microsoft_Launcher::enable()");
ResetEvent(m_hEvent);
ResetEvent(send_telemetry_event);
unsigned long powertoys_pid = GetCurrentProcessId();
TerminateRunningInstance();
if (!is_process_elevated(false))
{
Logger::trace("Starting PowerToys Run from not elevated process");

View File

@@ -7,6 +7,7 @@ using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Windows;
using interop;
using ManagedCommon;
using Microsoft.PowerLauncher.Telemetry;
using Microsoft.PowerToys.Common.UI;
@@ -29,7 +30,6 @@ namespace PowerLauncher
{
public static PublicAPIInstance API { get; private set; }
private const string Unique = "PowerToys_PowerToysRun_InstanceMutex";
private static bool _disposed;
private PowerToysRunSettings _settings;
private MainViewModel _mainVM;
@@ -43,49 +43,40 @@ namespace PowerLauncher
public static void Main()
{
Log.Info($"Starting PowerToys Run with PID={Process.GetCurrentProcess().Id}", typeof(App));
if (SingleInstance<App>.InitializeAsFirstInstance(Unique))
if (SingleInstance<App>.InitializeAsFirstInstance())
{
using (var application = new App())
{
application.InitializeComponent();
NativeEventWaiter.WaitForEventLoop(Constants.RunExitEvent(), () =>
{
Log.Warn("RunExitEvent was signaled. Exiting PowerToys", typeof(App));
ExitPowerToys(application);
});
int powerToysPid = GetPowerToysPId();
if (powerToysPid != 0)
{
Log.Info($"Runner pid={powerToysPid}", typeof(App));
RunnerHelper.WaitForPowerToysRunner(powerToysPid, () =>
{
Log.Info($"Runner with pid={powerToysPid} exited. Exiting PowerToys Run", typeof(App));
ExitPowerToys(application);
});
}
application.Run();
}
}
else
{
Log.Info("There is already running PowerToys Run instance", typeof(App));
Log.Error("There is already running PowerToys Run instance. Exiting PowerToys Run", typeof(App));
}
}
private void OnStartup(object sender, StartupEventArgs e)
{
Log.Info("On Startup.", GetType());
for (int i = 0; i + 1 < e.Args.Length; i++)
{
if (e.Args[i] == "-powerToysPid")
{
int powerToysPid;
if (int.TryParse(e.Args[i + 1], out powerToysPid))
{
Log.Info($"Runner pid={powerToysPid}", GetType());
RunnerHelper.WaitForPowerToysRunner(powerToysPid, () =>
{
Log.Info($"Runner with pid={powerToysPid} exited. Exiting PowerToys Run", GetType());
try
{
Dispose();
}
finally
{
Environment.Exit(0);
}
});
}
break;
}
}
var bootTime = new System.Diagnostics.Stopwatch();
bootTime.Start();
Stopwatch.Normal("App.OnStartup - Startup cost", () =>
@@ -143,6 +134,40 @@ namespace PowerLauncher
});
}
private static void ExitPowerToys(App app)
{
SingleInstance<App>.SingleInstanceMutex.Close();
try
{
app.Dispose();
}
finally
{
Environment.Exit(0);
}
}
private static int GetPowerToysPId()
{
var args = Environment.GetCommandLineArgs();
for (int i = 0; i + 1 < args.Length; i++)
{
if (args[i] == "-powerToysPid")
{
int powerToysPid;
if (int.TryParse(args[i + 1], out powerToysPid))
{
return powerToysPid;
}
break;
}
}
return 0;
}
private void RegisterExitEvents()
{
AppDomain.CurrentDomain.ProcessExit += (s, e) => Dispose();

View File

@@ -11,6 +11,7 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using interop;
// http://blogs.microsoft.co.il/arik/2010/05/28/wpf-single-instance-application/
// modified to allow single instance restart
@@ -44,11 +45,6 @@ namespace PowerLauncher.Helper
/// </summary>
private const string ChannelNameSuffix = "SingeInstanceIPCChannel";
/// <summary>
/// Prefix to the names of mutexes which ensures they are unique in a Windows session.
/// </summary>
private const string LocalMutexPrefix = @"Local\";
/// <summary>
/// Gets or sets application mutex.
/// </summary>
@@ -59,15 +55,15 @@ namespace PowerLauncher.Helper
/// If not, activates the first instance.
/// </summary>
/// <returns>True if this is the first instance of the application.</returns>
internal static bool InitializeAsFirstInstance(string uniqueName)
internal static bool InitializeAsFirstInstance()
{
string mutexName = @"Local\PowerToys_Run_InstanceMutex";
// Build unique application Id and the IPC channel name.
string applicationIdentifier = uniqueName + Environment.UserName;
string applicationIdentifier = mutexName + Environment.UserName;
string channelName = string.Concat(applicationIdentifier, Delimiter, ChannelNameSuffix);
// Create mutex based on unique application Id to check if this is the first instance of the application.
string mutexName = string.Concat(LocalMutexPrefix, uniqueName);
SingleInstanceMutex = new Mutex(true, mutexName, out bool firstInstance);
if (firstInstance)
{