mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
Merge 6b2b7ac304 into 24f6c90f72
This commit is contained in:
commit
1df228510b
29 changed files with 840 additions and 131 deletions
47
Flow.Launcher.Command/Flow.Launcher.Command.csproj
Normal file
47
Flow.Launcher.Command/Flow.Launcher.Command.csproj
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<RootNamespace>Flow.Launcher.Command</RootNamespace>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<ApplicationIcon>app.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>portable</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\Output\Debug\Command\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<UseVSHostingProcess>true</UseVSHostingProcess>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>..\Output\Release\Command\</OutputPath>
|
||||
<DefineConstants>TRACE;RELEASE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="app.ico" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\SolutionAssemblyInfo.cs" Link="Properties\SolutionAssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
132
Flow.Launcher.Command/Program.cs
Normal file
132
Flow.Launcher.Command/Program.cs
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
using System.Diagnostics;
|
||||
|
||||
namespace Flow.Launcher.Command;
|
||||
|
||||
internal static class Program
|
||||
{
|
||||
[STAThread]
|
||||
private static int Main(string[] args)
|
||||
{
|
||||
if (args.Length == 0) return -1;
|
||||
|
||||
// Start process with arguments
|
||||
// Usage: Flow.Launcher.Command -StartProcess -FileName <file> -WorkingDirectory <directory> -Arguments <args> -UseShellExecute <true|false> -Verb <verb> -CreateNoWindow <true|false>
|
||||
if (args[0] == @"-StartProcess")
|
||||
{
|
||||
var fileName = string.Empty;
|
||||
var workingDirectory = Environment.CurrentDirectory;
|
||||
var argumentList = new List<string>();
|
||||
var useShellExecute = true;
|
||||
var verb = string.Empty;
|
||||
var createNoWindow = false;
|
||||
var isArguments = false;
|
||||
|
||||
for (int i = 1; i < args.Length; i++)
|
||||
{
|
||||
switch (args[i])
|
||||
{
|
||||
case "-FileName":
|
||||
if (i + 1 < args.Length)
|
||||
fileName = args[++i];
|
||||
isArguments = false;
|
||||
break;
|
||||
|
||||
case "-WorkingDirectory":
|
||||
if (i + 1 < args.Length)
|
||||
workingDirectory = args[++i];
|
||||
isArguments = false;
|
||||
break;
|
||||
|
||||
case "-Arguments":
|
||||
if (i + 1 < args.Length)
|
||||
argumentList.Add(args[++i]);
|
||||
isArguments = true;
|
||||
break;
|
||||
|
||||
case "-UseShellExecute":
|
||||
if (i + 1 < args.Length && bool.TryParse(args[++i], out bool useShell))
|
||||
useShellExecute = useShell;
|
||||
isArguments = false;
|
||||
break;
|
||||
|
||||
case "-Verb":
|
||||
if (i + 1 < args.Length)
|
||||
verb = args[++i];
|
||||
isArguments = false;
|
||||
break;
|
||||
|
||||
case "-CreateNoWindow":
|
||||
if (i + 1 < args.Length && bool.TryParse(args[++i], out bool createNoWin))
|
||||
createNoWindow = createNoWin;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (isArguments)
|
||||
argumentList.Add(args[i]);
|
||||
else
|
||||
Console.WriteLine($"Unknown parameter: {args[i]}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(fileName))
|
||||
{
|
||||
Console.WriteLine("Error: -FileName is required.");
|
||||
return -2;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ProcessStartInfo info;
|
||||
if (argumentList.Count == 0)
|
||||
{
|
||||
info = new ProcessStartInfo
|
||||
{
|
||||
FileName = fileName,
|
||||
WorkingDirectory = workingDirectory,
|
||||
UseShellExecute = useShellExecute,
|
||||
Verb = verb,
|
||||
CreateNoWindow = createNoWindow
|
||||
};
|
||||
}
|
||||
else if (argumentList.Count == 1)
|
||||
{
|
||||
info = new ProcessStartInfo
|
||||
{
|
||||
FileName = fileName,
|
||||
WorkingDirectory = workingDirectory,
|
||||
Arguments = argumentList[0],
|
||||
UseShellExecute = useShellExecute,
|
||||
Verb = verb,
|
||||
CreateNoWindow = createNoWindow
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
info = new ProcessStartInfo
|
||||
{
|
||||
FileName = fileName,
|
||||
WorkingDirectory = workingDirectory,
|
||||
UseShellExecute = useShellExecute,
|
||||
Verb = verb,
|
||||
CreateNoWindow = createNoWindow
|
||||
};
|
||||
foreach (var arg in argumentList)
|
||||
{
|
||||
info.ArgumentList.Add(arg);
|
||||
}
|
||||
}
|
||||
Process.Start(info)?.Dispose();
|
||||
Console.WriteLine("Success.");
|
||||
return 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error: {ex.Message}");
|
||||
return -3;
|
||||
}
|
||||
}
|
||||
|
||||
return -4;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
-->
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<PublishProtocol>FileSystem</PublishProtocol>
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Any CPU</Platform>
|
||||
<TargetFramework>net9.0-windows10.0.19041.0</TargetFramework>
|
||||
<PublishDir>..\Output\Release\</PublishDir>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<SelfContained>true</SelfContained>
|
||||
<PublishSingleFile>False</PublishSingleFile>
|
||||
<PublishReadyToRun>False</PublishReadyToRun>
|
||||
<PublishTrimmed>False</PublishTrimmed>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
BIN
Flow.Launcher.Command/app.ico
Normal file
BIN
Flow.Launcher.Command/app.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 114 KiB |
|
|
@ -43,7 +43,7 @@ namespace Flow.Launcher.Core.Configuration
|
|||
|
||||
PublicApi.Instance.ShowMsgBox(Localize.restartToDisablePortableMode());
|
||||
|
||||
UpdateManager.RestartApp(Constant.ApplicationFileName);
|
||||
PublicApi.Instance.RestartApp();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
|
@ -66,7 +66,7 @@ namespace Flow.Launcher.Core.Configuration
|
|||
|
||||
PublicApi.Instance.ShowMsgBox(Localize.restartToEnablePortableMode());
|
||||
|
||||
UpdateManager.RestartApp(Constant.ApplicationFileName);
|
||||
PublicApi.Instance.RestartApp();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Sockets;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using Flow.Launcher.Plugin.SharedCommands;
|
||||
using Flow.Launcher.Infrastructure;
|
||||
using Flow.Launcher.Infrastructure.Http;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Flow.Launcher.Plugin.SharedCommands;
|
||||
using JetBrains.Annotations;
|
||||
using Squirrel;
|
||||
|
||||
|
|
@ -91,7 +91,7 @@ namespace Flow.Launcher.Core
|
|||
if (_api.ShowMsgBox(newVersionTips, Localize.update_flowlauncher_new_update(),
|
||||
MessageBoxButton.YesNo) == MessageBoxResult.Yes)
|
||||
{
|
||||
UpdateManager.RestartApp(Constant.ApplicationFileName);
|
||||
_api.RestartApp();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ namespace Flow.Launcher.Infrastructure
|
|||
private static readonly Assembly Assembly = Assembly.GetExecutingAssembly();
|
||||
public static readonly string ProgramDirectory = Directory.GetParent(Assembly.Location.NonNull()).ToString();
|
||||
public static readonly string ExecutablePath = Path.Combine(ProgramDirectory, FlowLauncher + ".exe");
|
||||
public static readonly string CommandExecutablePath = Path.Combine(ProgramDirectory, "Command", "Flow.Launcher.Command.exe");
|
||||
public static readonly string ApplicationDirectory = Directory.GetParent(ProgramDirectory).ToString();
|
||||
public static readonly string RootDirectory = Directory.GetParent(ApplicationDirectory).ToString();
|
||||
|
||||
|
|
|
|||
|
|
@ -226,7 +226,7 @@ namespace Flow.Launcher.Infrastructure.Http
|
|||
Log.Debug(ClassName, $"Url <{url}>");
|
||||
return await client.GetStringAsync(url, token);
|
||||
}
|
||||
catch (System.Exception e)
|
||||
catch (System.Exception)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,9 +88,25 @@ EVENT_SYSTEM_DIALOGEND
|
|||
DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS
|
||||
WM_POWERBROADCAST
|
||||
PBT_APMRESUMEAUTOMATIC
|
||||
|
||||
PBT_APMRESUMESUSPEND
|
||||
PowerRegisterSuspendResumeNotification
|
||||
PowerUnregisterSuspendResumeNotification
|
||||
|
||||
OpenProcessToken
|
||||
GetCurrentProcess
|
||||
LookupPrivilegeValue
|
||||
SE_INCREASE_QUOTA_NAME
|
||||
CloseHandle
|
||||
TOKEN_PRIVILEGES
|
||||
AdjustTokenPrivileges
|
||||
GetShellWindow
|
||||
GetWindowThreadProcessId
|
||||
OpenProcess
|
||||
GetProcessId
|
||||
DuplicateTokenEx
|
||||
CreateProcessWithTokenW
|
||||
|
||||
DeviceNotifyCallbackRoutine
|
||||
|
||||
MonitorFromWindow
|
||||
MonitorFromWindow
|
||||
|
|
|
|||
|
|
@ -483,6 +483,8 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
public bool HideWhenDeactivated { get; set; } = true;
|
||||
public bool ShowTaskbarWhenInvoked { get; set; } = false;
|
||||
|
||||
public bool AlwaysRunAsAdministrator { get; set; } = false;
|
||||
|
||||
private bool _showAtTopmost = false;
|
||||
public bool ShowAtTopmost
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using System.Globalization;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Principal;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
|
|
@ -19,6 +20,7 @@ using Microsoft.Win32.SafeHandles;
|
|||
using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.Graphics.Dwm;
|
||||
using Windows.Win32.Security;
|
||||
using Windows.Win32.System.Power;
|
||||
using Windows.Win32.System.Threading;
|
||||
using Windows.Win32.UI.Input.KeyboardAndMouse;
|
||||
|
|
@ -690,6 +692,7 @@ namespace Flow.Launcher.Infrastructure
|
|||
{
|
||||
try
|
||||
{
|
||||
// No need to de-elevate since we are opening windows settings which cannot bring security risks
|
||||
Process.Start(new ProcessStartInfo("ms-settings:regionlanguage") { UseShellExecute = true });
|
||||
}
|
||||
catch (System.Exception)
|
||||
|
|
@ -1017,6 +1020,164 @@ namespace Flow.Launcher.Infrastructure
|
|||
|
||||
#endregion
|
||||
|
||||
#region Administrator Mode
|
||||
|
||||
public static bool IsAdministrator()
|
||||
{
|
||||
using var identity = WindowsIdentity.GetCurrent();
|
||||
var principal = new WindowsPrincipal(identity);
|
||||
return principal.IsInRole(WindowsBuiltInRole.Administrator);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inspired by <see href="https://github.com/jay/RunAsDesktopUser">
|
||||
/// Document: <see href="https://learn.microsoft.com/en-us/archive/blogs/aaron_margosis/faq-how-do-i-start-a-program-as-the-desktop-user-from-an-elevated-app">
|
||||
/// </summary>
|
||||
public static unsafe bool RunAsDesktopUser(string app, string currentDir, string cmdLine, bool loadProfile, bool createNoWindow, out string errorInfo)
|
||||
{
|
||||
STARTUPINFOW si = new()
|
||||
{
|
||||
cb = (uint)Marshal.SizeOf<STARTUPINFOW>()
|
||||
};
|
||||
PROCESS_INFORMATION pi = new();
|
||||
errorInfo = string.Empty;
|
||||
HANDLE hShellProcess = HANDLE.Null, hShellProcessToken = HANDLE.Null, hPrimaryToken = HANDLE.Null;
|
||||
HWND hwnd;
|
||||
uint dwPID;
|
||||
|
||||
// 1. Enable the SeIncreaseQuotaPrivilege in your current token
|
||||
if (!PInvoke.OpenProcessToken(PInvoke.GetCurrentProcess_SafeHandle(), TOKEN_ACCESS_MASK.TOKEN_ADJUST_PRIVILEGES, out var hProcessToken))
|
||||
{
|
||||
errorInfo = $"OpenProcessToken failed: {Marshal.GetLastWin32Error()}";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!PInvoke.LookupPrivilegeValue(null, PInvoke.SE_INCREASE_QUOTA_NAME, out var luid))
|
||||
{
|
||||
errorInfo = $"LookupPrivilegeValue failed: {Marshal.GetLastWin32Error()}";
|
||||
hProcessToken.Dispose();
|
||||
return false;
|
||||
}
|
||||
|
||||
var tp = new TOKEN_PRIVILEGES
|
||||
{
|
||||
PrivilegeCount = 1,
|
||||
Privileges = new()
|
||||
{
|
||||
e0 = new LUID_AND_ATTRIBUTES
|
||||
{
|
||||
Luid = luid,
|
||||
Attributes = TOKEN_PRIVILEGES_ATTRIBUTES.SE_PRIVILEGE_ENABLED
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
PInvoke.AdjustTokenPrivileges(hProcessToken, false, &tp, 0, null, null);
|
||||
var lastError = Marshal.GetLastWin32Error();
|
||||
hProcessToken.Dispose();
|
||||
|
||||
if (lastError != 0)
|
||||
{
|
||||
errorInfo = $"AdjustTokenPrivileges failed: {lastError}";
|
||||
return false;
|
||||
}
|
||||
|
||||
retry:
|
||||
// 2. Get an HWND representing the desktop shell
|
||||
hwnd = PInvoke.GetShellWindow();
|
||||
if (hwnd == HWND.Null)
|
||||
{
|
||||
errorInfo = "No desktop shell is present.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3. Get the Process ID (PID) of the process associated with that window
|
||||
_ = PInvoke.GetWindowThreadProcessId(hwnd, &dwPID);
|
||||
if (dwPID == 0)
|
||||
{
|
||||
errorInfo = "Unable to get PID of desktop shell.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// 4. Open that process
|
||||
hShellProcess = PInvoke.OpenProcess(PROCESS_ACCESS_RIGHTS.PROCESS_QUERY_INFORMATION, false, dwPID);
|
||||
if (hShellProcess == HANDLE.Null)
|
||||
{
|
||||
errorInfo = $"Can't open desktop shell process: {Marshal.GetLastWin32Error()}";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hwnd != PInvoke.GetShellWindow())
|
||||
{
|
||||
PInvoke.CloseHandle(hShellProcess);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
_ = PInvoke.GetWindowThreadProcessId(hwnd, &dwPID);
|
||||
if (dwPID != PInvoke.GetProcessId(hShellProcess))
|
||||
{
|
||||
PInvoke.CloseHandle(hShellProcess);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
// 5. Get the access token from that process
|
||||
if (!PInvoke.OpenProcessToken(hShellProcess, TOKEN_ACCESS_MASK.TOKEN_DUPLICATE, &hShellProcessToken))
|
||||
{
|
||||
errorInfo = $"Can't get process token of desktop shell: {Marshal.GetLastWin32Error()}";
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// 6. Make a primary token with that token
|
||||
var tokenRights = TOKEN_ACCESS_MASK.TOKEN_QUERY | TOKEN_ACCESS_MASK.TOKEN_ASSIGN_PRIMARY |
|
||||
TOKEN_ACCESS_MASK.TOKEN_DUPLICATE | TOKEN_ACCESS_MASK.TOKEN_ADJUST_DEFAULT |
|
||||
TOKEN_ACCESS_MASK.TOKEN_ADJUST_SESSIONID;
|
||||
if (!PInvoke.DuplicateTokenEx(hShellProcessToken, tokenRights, null, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, TOKEN_TYPE.TokenPrimary, &hPrimaryToken))
|
||||
{
|
||||
errorInfo = $"Can't get primary token: {Marshal.GetLastWin32Error()}";
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// 7. Start the new process with that primary token
|
||||
fixed (char* appPtr = app)
|
||||
// Because argv[0] is the module name, C programmers generally repeat the module name as the first token in the command line
|
||||
// So we add one more dash before the command line to make command line work correctly
|
||||
fixed (char* cmdLinePtr = $"- {cmdLine}")
|
||||
fixed (char* currentDirPtr = currentDir)
|
||||
{
|
||||
if (!PInvoke.CreateProcessWithToken(
|
||||
hPrimaryToken,
|
||||
// If you need to access content in HKEY_CURRENT_USER, please set loadProfile to true
|
||||
loadProfile ? CREATE_PROCESS_LOGON_FLAGS.LOGON_WITH_PROFILE : 0,
|
||||
appPtr,
|
||||
cmdLinePtr,
|
||||
// If you do not want to create a window for console app, please set createNoWindow to true
|
||||
createNoWindow ? PROCESS_CREATION_FLAGS.CREATE_NO_WINDOW : 0,
|
||||
null,
|
||||
currentDirPtr,
|
||||
&si,
|
||||
&pi))
|
||||
{
|
||||
errorInfo = $"CreateProcessWithTokenW failed: {Marshal.GetLastWin32Error()}";
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (pi.hProcess != HANDLE.Null) PInvoke.CloseHandle(pi.hProcess);
|
||||
if (pi.hThread != HANDLE.Null) PInvoke.CloseHandle(pi.hThread);
|
||||
if (hShellProcessToken != HANDLE.Null) PInvoke.CloseHandle(hShellProcessToken);
|
||||
if (hPrimaryToken != HANDLE.Null) PInvoke.CloseHandle(hPrimaryToken);
|
||||
if (hShellProcess != HANDLE.Null) PInvoke.CloseHandle(hShellProcess);
|
||||
return true;
|
||||
|
||||
cleanup:
|
||||
if (hShellProcessToken != HANDLE.Null) PInvoke.CloseHandle(hShellProcessToken);
|
||||
if (hPrimaryToken != HANDLE.Null) PInvoke.CloseHandle(hPrimaryToken);
|
||||
if (hShellProcess != HANDLE.Null) PInvoke.CloseHandle(hShellProcess);
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Taskbar
|
||||
|
||||
public static unsafe void ShowTaskbar()
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
|
@ -29,13 +30,21 @@ namespace Flow.Launcher.Plugin
|
|||
void ChangeQuery(string query, bool requery = false);
|
||||
|
||||
/// <summary>
|
||||
/// Restart Flow Launcher
|
||||
/// Restart Flow Launcher without changing the user privileges.
|
||||
/// </summary>
|
||||
void RestartApp();
|
||||
|
||||
/// <summary>
|
||||
/// Restart Flow Launcher as administrator.
|
||||
/// </summary>
|
||||
void RestartAppAsAdmin();
|
||||
|
||||
/// <summary>
|
||||
/// Run a shell command
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// It can help to start a de-elevated process and show user account control dialog when Flow is running as administrator.
|
||||
/// </remarks>
|
||||
/// <param name="cmd">The command or program to run</param>
|
||||
/// <param name="filename">the shell type to run, e.g. powershell.exe</param>
|
||||
/// <exception cref="FileNotFoundException">Thrown when unable to find the file specified in the command </exception>
|
||||
|
|
@ -637,5 +646,35 @@ namespace Flow.Launcher.Plugin
|
|||
/// </summary>
|
||||
/// <returns></returns>
|
||||
string GetLogDirectory();
|
||||
|
||||
/// <summary>
|
||||
/// Start a process with support for handling administrative privileges
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// It can help to start a de-elevated process and show user account control dialog when Flow is running as administrator.
|
||||
/// </remarks>
|
||||
/// <param name="fileName">File name</param>
|
||||
/// <param name="workingDirectory">Working directory. If not specified, the current directory will be used</param>
|
||||
/// <param name="arguments">Optional arguments to pass to the process. If not specified, no arguments will be passed</param>
|
||||
/// <param name="useShellExecute">Whether to use shell to execute the process</param>
|
||||
/// <param name="verb">Verb to use when starting the process, e.g. "runas" for elevated permissions. If not specified, no verb will be used.</param>
|
||||
/// <param name="createNoWindow">Whether to create console window</param>
|
||||
/// <returns>Whether process is started successfully</returns>
|
||||
public bool StartProcess(string fileName, string workingDirectory = "", string arguments = "", bool useShellExecute = false, string verb = "", bool createNoWindow = false);
|
||||
|
||||
/// <summary>
|
||||
/// Start a process with support for handling administrative privileges
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// It can help to start a de-elevated process and show user account control dialog when Flow is running as administrator.
|
||||
/// </remarks>
|
||||
/// <param name="fileName">File name</param>
|
||||
/// <param name="workingDirectory">Working directory. If not specified, the current directory will be used</param>
|
||||
/// <param name="argumentList">Optional argument list to pass to the process. If not specified, no arguments will be passed</param>
|
||||
/// <param name="useShellExecute">Whether to use shell to execute the process</param>
|
||||
/// <param name="verb">Verb to use when starting the process, e.g. "runas" for elevated permissions. If not specified, no verb will be used.</param>
|
||||
/// <param name="createNoWindow">Whether to create console window</param>
|
||||
/// <returns>Whether process is started successfully</returns>
|
||||
public bool StartProcess(string fileName, string workingDirectory = "", Collection<string> argumentList = null, bool useShellExecute = false, string verb = "", bool createNoWindow = false);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +1,23 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.3.32901.215
|
||||
# Visual Studio Version 18
|
||||
VisualStudioVersion = 18.4.11519.219 insiders
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher", "Flow.Launcher\Flow.Launcher.csproj", "{DB90F671-D861-46BB-93A3-F1304F5BA1C5}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{0B9DE348-9361-4940-ADB6-F5953BFFCCEC} = {0B9DE348-9361-4940-ADB6-F5953BFFCCEC}
|
||||
{4792A74A-0CEA-4173-A8B2-30E6764C6217} = {4792A74A-0CEA-4173-A8B2-30E6764C6217}
|
||||
{FDB3555B-58EF-4AE6-B5F1-904719637AB4} = {FDB3555B-58EF-4AE6-B5F1-904719637AB4}
|
||||
{F9C4C081-4CC3-4146-95F1-E102B4E10A5F} = {F9C4C081-4CC3-4146-95F1-E102B4E10A5F}
|
||||
{59BD9891-3837-438A-958D-ADC7F91F6F7E} = {59BD9891-3837-438A-958D-ADC7F91F6F7E}
|
||||
{C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0} = {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}
|
||||
{9B130CC5-14FB-41FF-B310-0A95B6894C37} = {9B130CC5-14FB-41FF-B310-0A95B6894C37}
|
||||
{FDED22C8-B637-42E8-824A-63B5B6E05A3A} = {FDED22C8-B637-42E8-824A-63B5B6E05A3A}
|
||||
{A3DCCBCA-ACC1-421D-B16E-210896234C26} = {A3DCCBCA-ACC1-421D-B16E-210896234C26}
|
||||
{5043CECE-E6A7-4867-9CBE-02D27D83747A} = {5043CECE-E6A7-4867-9CBE-02D27D83747A}
|
||||
{403B57F2-1856-4FC7-8A24-36AB346B763E} = {403B57F2-1856-4FC7-8A24-36AB346B763E}
|
||||
{4792A74A-0CEA-4173-A8B2-30E6764C6217} = {4792A74A-0CEA-4173-A8B2-30E6764C6217}
|
||||
{5043CECE-E6A7-4867-9CBE-02D27D83747A} = {5043CECE-E6A7-4867-9CBE-02D27D83747A}
|
||||
{588088F4-3262-4F9F-9663-A05DE12534C3} = {588088F4-3262-4F9F-9663-A05DE12534C3}
|
||||
{59BD9891-3837-438A-958D-ADC7F91F6F7E} = {59BD9891-3837-438A-958D-ADC7F91F6F7E}
|
||||
{9B130CC5-14FB-41FF-B310-0A95B6894C37} = {9B130CC5-14FB-41FF-B310-0A95B6894C37}
|
||||
{A3DCCBCA-ACC1-421D-B16E-210896234C26} = {A3DCCBCA-ACC1-421D-B16E-210896234C26}
|
||||
{A9976C5C-B73A-4D29-B654-EF1C0C4C9C8C} = {A9976C5C-B73A-4D29-B654-EF1C0C4C9C8C}
|
||||
{C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0} = {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}
|
||||
{F9C4C081-4CC3-4146-95F1-E102B4E10A5F} = {F9C4C081-4CC3-4146-95F1-E102B4E10A5F}
|
||||
{FDB3555B-58EF-4AE6-B5F1-904719637AB4} = {FDB3555B-58EF-4AE6-B5F1-904719637AB4}
|
||||
{FDED22C8-B637-42E8-824A-63B5B6E05A3A} = {FDED22C8-B637-42E8-824A-63B5B6E05A3A}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Test", "Flow.Launcher.Test\Flow.Launcher.Test.csproj", "{FF742965-9A80-41A5-B042-D6C7D3A21708}"
|
||||
|
|
@ -53,8 +55,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
|||
LICENSE = LICENSE
|
||||
Scripts\post_build.ps1 = Scripts\post_build.ps1
|
||||
README.md = README.md
|
||||
SolutionAssemblyInfo.cs = SolutionAssemblyInfo.cs
|
||||
Settings.XamlStyler = Settings.XamlStyler
|
||||
SolutionAssemblyInfo.cs = SolutionAssemblyInfo.cs
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Shell", "Plugins\Flow.Launcher.Plugin.Shell\Flow.Launcher.Plugin.Shell.csproj", "{C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}"
|
||||
|
|
@ -71,6 +73,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Plugin
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.WindowsSettings", "Plugins\Flow.Launcher.Plugin.WindowsSettings\Flow.Launcher.Plugin.WindowsSettings.csproj", "{5043CECE-E6A7-4867-9CBE-02D27D83747A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flow.Launcher.Command", "Flow.Launcher.Command\Flow.Launcher.Command.csproj", "{A9976C5C-B73A-4D29-B654-EF1C0C4C9C8C}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
|
@ -81,8 +85,19 @@ Global
|
|||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|x64.Build.0 = Release|Any CPU
|
||||
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|x86.Build.0 = Release|Any CPU
|
||||
{FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
|
|
@ -105,18 +120,6 @@ Global
|
|||
{8451ECDD-2EA4-4966-BB0A-7BBC40138E80}.Release|x64.Build.0 = Release|Any CPU
|
||||
{8451ECDD-2EA4-4966-BB0A-7BBC40138E80}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{8451ECDD-2EA4-4966-BB0A-7BBC40138E80}.Release|x86.Build.0 = Release|Any CPU
|
||||
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|x64.Build.0 = Release|Any CPU
|
||||
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|x86.Build.0 = Release|Any CPU
|
||||
{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
|
|
@ -286,6 +289,18 @@ Global
|
|||
{5043CECE-E6A7-4867-9CBE-02D27D83747A}.Release|x64.Build.0 = Release|Any CPU
|
||||
{5043CECE-E6A7-4867-9CBE-02D27D83747A}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{5043CECE-E6A7-4867-9CBE-02D27D83747A}.Release|x86.Build.0 = Release|Any CPU
|
||||
{A9976C5C-B73A-4D29-B654-EF1C0C4C9C8C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A9976C5C-B73A-4D29-B654-EF1C0C4C9C8C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A9976C5C-B73A-4D29-B654-EF1C0C4C9C8C}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{A9976C5C-B73A-4D29-B654-EF1C0C4C9C8C}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{A9976C5C-B73A-4D29-B654-EF1C0C4C9C8C}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{A9976C5C-B73A-4D29-B654-EF1C0C4C9C8C}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{A9976C5C-B73A-4D29-B654-EF1C0C4C9C8C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A9976C5C-B73A-4D29-B654-EF1C0C4C9C8C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A9976C5C-B73A-4D29-B654-EF1C0C4C9C8C}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{A9976C5C-B73A-4D29-B654-EF1C0C4C9C8C}.Release|x64.Build.0 = Release|Any CPU
|
||||
{A9976C5C-B73A-4D29-B654-EF1C0C4C9C8C}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{A9976C5C-B73A-4D29-B654-EF1C0C4C9C8C}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
|
@ -57,6 +59,13 @@ namespace Flow.Launcher
|
|||
|
||||
public App()
|
||||
{
|
||||
// Check if the application is running as administrator
|
||||
if (_settings.AlwaysRunAsAdministrator && !Win32Helper.IsAdministrator())
|
||||
{
|
||||
RestartApp(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Do not use bitmap cache since it can cause WPF second window freezing issue
|
||||
ShadowAssist.UseBitmapCache = false;
|
||||
|
||||
|
|
@ -293,12 +302,23 @@ namespace Flow.Launcher
|
|||
{
|
||||
try
|
||||
{
|
||||
Helper.AutoStartup.CheckIsEnabled(_settings.UseLogonTaskForStartup);
|
||||
Helper.AutoStartup.CheckIsEnabled(_settings.UseLogonTaskForStartup, _settings.AlwaysRunAsAdministrator);
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
// If it fails for permission, we need to ask the user to restart as administrator
|
||||
if (API.ShowMsgBox(
|
||||
API.GetTranslation("runAsAdministratorChangeAndRestart"),
|
||||
API.GetTranslation("runAsAdministratorChange"),
|
||||
MessageBoxButton.YesNo) == MessageBoxResult.Yes)
|
||||
{
|
||||
RestartApp(true);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// but if it fails (permissions, etc) then don't keep retrying
|
||||
// this also gives the user a visual indication in the Settings widget
|
||||
// But if it fails for other reasons then do not keep retrying,
|
||||
// set startup to false to give users a visual indication in the general page
|
||||
_settings.StartFlowLauncherOnSystemStartup = false;
|
||||
API.ShowMsgError(Localize.setAutoStartFailed(), e.Message);
|
||||
}
|
||||
|
|
@ -408,6 +428,59 @@ namespace Flow.Launcher
|
|||
|
||||
#endregion
|
||||
|
||||
#region Restart
|
||||
|
||||
/// <summary>
|
||||
/// Restart the application without changing the user privileges.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Since Squirrel does not provide a way to restart the app as administrator,
|
||||
/// we need to do it manually by starting the update.exe with the runas verb
|
||||
/// </remarks>
|
||||
/// <param name="forceAdmin">
|
||||
/// If true, the application will be restarted as administrator.
|
||||
/// If false, it will be restarted with the same privileges as the current user.
|
||||
/// </param>
|
||||
/// <exception cref="Exception">Thrown when the Update.exe is not found in the expected location</exception>
|
||||
public static void RestartApp(bool forceAdmin = false)
|
||||
{
|
||||
// Restart requires Squirrel's Update.exe to be present in the parent folder,
|
||||
// it is only published from the project's release pipeline. When debugging without it,
|
||||
// the project may not restart or just terminates. This is expected.
|
||||
var startInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = getUpdateExe(),
|
||||
Arguments = $"--processStartAndWait \"{Constant.ExecutablePath}\"",
|
||||
UseShellExecute = true,
|
||||
Verb = Win32Helper.IsAdministrator() || forceAdmin ? "runas" : ""
|
||||
};
|
||||
// No need to de-elevate since we are restarting Flow Launcher which cannot bring security risks
|
||||
Process.Start(startInfo);
|
||||
Thread.Sleep(500);
|
||||
Environment.Exit(0);
|
||||
|
||||
// Local function
|
||||
static string getUpdateExe()
|
||||
{
|
||||
Assembly entryAssembly = Assembly.GetEntryAssembly();
|
||||
if (entryAssembly != null && Path.GetFileName(entryAssembly.Location).Equals("update.exe", StringComparison.OrdinalIgnoreCase) && entryAssembly.Location.IndexOf("app-", StringComparison.OrdinalIgnoreCase) == -1 && entryAssembly.Location.IndexOf("SquirrelTemp", StringComparison.OrdinalIgnoreCase) == -1)
|
||||
{
|
||||
return Path.GetFullPath(entryAssembly.Location);
|
||||
}
|
||||
|
||||
entryAssembly = Assembly.GetEntryAssembly() ?? Assembly.GetExecutingAssembly();
|
||||
FileInfo fileInfo = new FileInfo(Path.Combine(Path.GetDirectoryName(entryAssembly.Location), "..\\Update.exe"));
|
||||
if (!fileInfo.Exists)
|
||||
{
|
||||
throw new Exception("Update.exe not found, not a Squirrel-installed app?");
|
||||
}
|
||||
|
||||
return fileInfo.FullName;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDisposable
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
|
|
|
|||
|
|
@ -18,18 +18,18 @@ public class AutoStartup
|
|||
private const string LogonTaskName = $"{Constant.FlowLauncher} Startup";
|
||||
private const string LogonTaskDesc = $"{Constant.FlowLauncher} Auto Startup";
|
||||
|
||||
public static void CheckIsEnabled(bool useLogonTaskForStartup)
|
||||
public static void CheckIsEnabled(bool useLogonTaskForStartup, bool alwaysRunAsAdministrator)
|
||||
{
|
||||
// We need to check both because if both of them are enabled,
|
||||
// Hide Flow Launcher on startup will not work since the later one will trigger main window show event
|
||||
var logonTaskEnabled = CheckLogonTask();
|
||||
var logonTaskEnabled = CheckLogonTask(alwaysRunAsAdministrator);
|
||||
var registryEnabled = CheckRegistry();
|
||||
if (useLogonTaskForStartup)
|
||||
{
|
||||
// Enable logon task
|
||||
if (!logonTaskEnabled)
|
||||
{
|
||||
Enable(true);
|
||||
Enable(true, alwaysRunAsAdministrator);
|
||||
}
|
||||
// Disable registry
|
||||
if (registryEnabled)
|
||||
|
|
@ -42,7 +42,7 @@ public class AutoStartup
|
|||
// Enable registry
|
||||
if (!registryEnabled)
|
||||
{
|
||||
Enable(false);
|
||||
Enable(false, alwaysRunAsAdministrator);
|
||||
}
|
||||
// Disable logon task
|
||||
if (logonTaskEnabled)
|
||||
|
|
@ -52,7 +52,7 @@ public class AutoStartup
|
|||
}
|
||||
}
|
||||
|
||||
private static bool CheckLogonTask()
|
||||
private static bool CheckLogonTask(bool alwaysRunAsAdministrator)
|
||||
{
|
||||
using var taskService = new TaskService();
|
||||
var task = taskService.RootFolder.AllTasks.FirstOrDefault(t => t.Name == LogonTaskName);
|
||||
|
|
@ -60,22 +60,47 @@ public class AutoStartup
|
|||
{
|
||||
try
|
||||
{
|
||||
// Check if the action is the same as the current executable path
|
||||
// If not, we need to unschedule and reschedule the task
|
||||
if (task.Definition.Actions.FirstOrDefault() is Microsoft.Win32.TaskScheduler.Action taskAction)
|
||||
{
|
||||
var action = taskAction.ToString().Trim();
|
||||
var needsRecreation = !action.Equals(Constant.ExecutablePath, StringComparison.OrdinalIgnoreCase)
|
||||
|| task.Definition.Settings.Priority != ProcessPriorityClass.Normal;
|
||||
if (needsRecreation)
|
||||
var action = taskAction.ToString().Trim();
|
||||
var pathCorrect = action.Equals(Constant.ExecutablePath, StringComparison.OrdinalIgnoreCase);
|
||||
var runLevelCorrect = CheckRunLevel(task.Definition.Principal.RunLevel, alwaysRunAsAdministrator);
|
||||
var priorityCorrect = task.Definition.Settings.Priority == ProcessPriorityClass.Normal;
|
||||
|
||||
if (Win32Helper.IsAdministrator())
|
||||
{
|
||||
UnscheduleLogonTask();
|
||||
ScheduleLogonTask();
|
||||
// If path, run level or priority is not correct, we need to unschedule and reschedule the task
|
||||
if (!pathCorrect || !runLevelCorrect || !priorityCorrect)
|
||||
{
|
||||
UnscheduleLogonTask();
|
||||
ScheduleLogonTask(alwaysRunAsAdministrator);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If run level is not correct, we cannot edit it because we are not administrator
|
||||
// So we just throw an exception to let the user know
|
||||
if (!runLevelCorrect)
|
||||
{
|
||||
throw new UnauthorizedAccessException("Cannot edit task run level because the app is not running as administrator.");
|
||||
}
|
||||
|
||||
// If run level is correct and path or priority is not correct, we need to unschedule and reschedule the task
|
||||
if (!pathCorrect || !priorityCorrect)
|
||||
{
|
||||
UnscheduleLogonTask();
|
||||
ScheduleLogonTask(alwaysRunAsAdministrator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (UnauthorizedAccessException e)
|
||||
{
|
||||
App.API.LogError(ClassName, $"Failed to check logon task: {e}");
|
||||
throw; // Throw exception so that App.AutoStartup can show error message
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
App.API.LogError(ClassName, $"Failed to check logon task: {e}");
|
||||
|
|
@ -86,6 +111,11 @@ public class AutoStartup
|
|||
return false;
|
||||
}
|
||||
|
||||
private static bool CheckRunLevel(TaskRunLevel rl, bool alwaysRunAsAdministrator)
|
||||
{
|
||||
return alwaysRunAsAdministrator ? rl == TaskRunLevel.Highest : rl != TaskRunLevel.Highest;
|
||||
}
|
||||
|
||||
private static bool CheckRegistry()
|
||||
{
|
||||
try
|
||||
|
|
@ -120,16 +150,19 @@ public class AutoStartup
|
|||
Disable(false);
|
||||
}
|
||||
|
||||
public static void ChangeToViaLogonTask()
|
||||
public static void ChangeToViaLogonTask(bool alwaysRunAsAdministrator)
|
||||
{
|
||||
Disable(false);
|
||||
Enable(true);
|
||||
Disable(true); // Remove old logon task so that we can create a new one
|
||||
Enable(true, alwaysRunAsAdministrator);
|
||||
}
|
||||
|
||||
public static void ChangeToViaRegistry()
|
||||
{
|
||||
Disable(true);
|
||||
Enable(false);
|
||||
Disable(false); // Remove old registry so that we can create a new one
|
||||
// We do not need to use alwaysRunAsAdministrator for registry, so we just set false here
|
||||
Enable(false, false);
|
||||
}
|
||||
|
||||
private static void Disable(bool logonTask)
|
||||
|
|
@ -152,13 +185,13 @@ public class AutoStartup
|
|||
}
|
||||
}
|
||||
|
||||
private static void Enable(bool logonTask)
|
||||
private static void Enable(bool logonTask, bool alwaysRunAsAdministrator)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (logonTask)
|
||||
{
|
||||
ScheduleLogonTask();
|
||||
ScheduleLogonTask(alwaysRunAsAdministrator);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -172,14 +205,15 @@ public class AutoStartup
|
|||
}
|
||||
}
|
||||
|
||||
private static bool ScheduleLogonTask()
|
||||
private static bool ScheduleLogonTask(bool alwaysRunAsAdministrator)
|
||||
{
|
||||
using var td = TaskService.Instance.NewTask();
|
||||
td.RegistrationInfo.Description = LogonTaskDesc;
|
||||
td.Triggers.Add(new LogonTrigger { UserId = WindowsIdentity.GetCurrent().Name, Delay = TimeSpan.FromSeconds(2) });
|
||||
td.Actions.Add(Constant.ExecutablePath);
|
||||
|
||||
if (IsCurrentUserIsAdmin())
|
||||
// Only if the app is running as administrator, we can set the run level to highest
|
||||
if (Win32Helper.IsAdministrator() && alwaysRunAsAdministrator)
|
||||
{
|
||||
td.Principal.RunLevel = TaskRunLevel.Highest;
|
||||
}
|
||||
|
|
@ -216,13 +250,6 @@ public class AutoStartup
|
|||
}
|
||||
}
|
||||
|
||||
private static bool IsCurrentUserIsAdmin()
|
||||
{
|
||||
var identity = WindowsIdentity.GetCurrent();
|
||||
var principal = new WindowsPrincipal(identity);
|
||||
return principal.IsInRole(WindowsBuiltInRole.Administrator);
|
||||
}
|
||||
|
||||
private static bool UnscheduleRegistry()
|
||||
{
|
||||
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@
|
|||
<system:String x:Key="pluginStillInitializingSubtitle">Select this result to requery</system:String>
|
||||
<system:String x:Key="pluginFailedToRespond">{0}: Failed to respond!</system:String>
|
||||
<system:String x:Key="pluginFailedToRespondSubtitle">Select this result for more info</system:String>
|
||||
<system:String x:Key="admin">(Admin)</system:String>
|
||||
|
||||
<!-- Setting General -->
|
||||
<system:String x:Key="flowlauncher_settings">Settings</system:String>
|
||||
|
|
@ -185,6 +186,10 @@
|
|||
<system:String x:Key="showUnknownSourceWarningToolTip">Show warning when installing plugins from unknown sources</system:String>
|
||||
<system:String x:Key="autoUpdatePlugins">Auto update plugins</system:String>
|
||||
<system:String x:Key="autoUpdatePluginsToolTip">Automatically check plugin updates and notify if there are any updates available</system:String>
|
||||
<system:String x:Key="alwaysRunAsAdministrator">Always run as administrator</system:String>
|
||||
<system:String x:Key="alwaysRunAsAdministratorToolTip">Run Flow Launcher as administrator on startup</system:String>
|
||||
<system:String x:Key="runAsAdministratorChange">Administrator Mode Change</system:String>
|
||||
<system:String x:Key="runAsAdministratorChangeAndRestart">Do you want to restart as administrator to apply this change? Otherwise, you will need to run as administrator manually on the next start.</system:String>
|
||||
|
||||
<!-- Setting Plugin -->
|
||||
<system:String x:Key="searchplugin">Search Plugin</system:String>
|
||||
|
|
|
|||
|
|
@ -765,9 +765,13 @@ namespace Flow.Launcher
|
|||
|
||||
private void InitializeNotifyIcon()
|
||||
{
|
||||
var text = Win32Helper.IsAdministrator() ?
|
||||
Constant.FlowLauncherFullName + " " + App.API.GetTranslation("admin") :
|
||||
Constant.FlowLauncherFullName;
|
||||
|
||||
_notifyIcon = new NotifyIcon
|
||||
{
|
||||
Text = Constant.FlowLauncherFullName,
|
||||
Text = text,
|
||||
Icon = Constant.Version == "1.0.0" ? Properties.Resources.dev : Properties.Resources.app,
|
||||
Visible = !_settings.HideNotifyIcon
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
|
@ -73,7 +75,12 @@ namespace Flow.Launcher
|
|||
_mainVM.ChangeQueryText(query, requery);
|
||||
}
|
||||
|
||||
public void RestartApp()
|
||||
public void RestartApp() => RestartApp(false);
|
||||
|
||||
public void RestartAppAsAdmin() => RestartApp(true);
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD100:Avoid async void methods", Justification = "<Pending>")]
|
||||
private async void RestartApp(bool runAsAdmin)
|
||||
{
|
||||
_mainVM.Hide();
|
||||
|
||||
|
|
@ -85,7 +92,7 @@ namespace Flow.Launcher
|
|||
// Restart requires Squirrel's Update.exe to be present in the parent folder,
|
||||
// it is only published from the project's release pipeline. When debugging without it,
|
||||
// the project may not restart or just terminates. This is expected.
|
||||
UpdateManager.RestartApp(Constant.ApplicationFileName);
|
||||
App.RestartApp(runAsAdmin);
|
||||
}
|
||||
|
||||
public void ShowMainWindow() => _mainVM.Show();
|
||||
|
|
@ -151,8 +158,7 @@ namespace Flow.Launcher
|
|||
{
|
||||
var args = filename == "cmd.exe" ? $"/C {cmd}" : $"{cmd}";
|
||||
|
||||
var startInfo = ShellCommand.SetProcessStartInfo(filename, arguments: args, createNoWindow: true);
|
||||
ShellCommand.Execute(startInfo);
|
||||
StartProcess(filename, arguments: args, createNoWindow: true);
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD100:Avoid async void methods", Justification = "<Pending>")]
|
||||
|
|
@ -450,11 +456,7 @@ namespace Flow.Launcher
|
|||
{
|
||||
try
|
||||
{
|
||||
Process.Start(new ProcessStartInfo()
|
||||
{
|
||||
FileName = uri.AbsoluteUri,
|
||||
UseShellExecute = true
|
||||
})?.Dispose();
|
||||
StartProcess(uri.AbsoluteUri, arguments: string.Empty, useShellExecute: true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
|
@ -623,6 +625,85 @@ namespace Flow.Launcher
|
|||
|
||||
public string GetLogDirectory() => DataLocation.VersionLogDirectory;
|
||||
|
||||
public bool StartProcess(string fileName, string workingDirectory = "", string arguments = "", bool useShellExecute = false, string verb = "", bool createNoWindow = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
workingDirectory = string.IsNullOrEmpty(workingDirectory) ? Environment.CurrentDirectory : workingDirectory;
|
||||
|
||||
// Use command executer to run the process as desktop user if running as admin
|
||||
if (Win32Helper.IsAdministrator())
|
||||
{
|
||||
var result = Win32Helper.RunAsDesktopUser(
|
||||
Constant.CommandExecutablePath,
|
||||
Environment.CurrentDirectory,
|
||||
$"-StartProcess " +
|
||||
$"-FileName {AddDoubleQuotes(fileName)} " +
|
||||
$"-WorkingDirectory {AddDoubleQuotes(workingDirectory)} " +
|
||||
$"-Arguments {AddDoubleQuotes(arguments)} " +
|
||||
$"-UseShellExecute {useShellExecute} " +
|
||||
$"-Verb {AddDoubleQuotes(verb)} " +
|
||||
$"-CreateNoWindow {createNoWindow}",
|
||||
false,
|
||||
true, // Do not show the command window
|
||||
out var errorInfo);
|
||||
if (!string.IsNullOrEmpty(errorInfo))
|
||||
{
|
||||
LogError(ClassName, $"Failed to start process {fileName} with arguments {arguments} under {workingDirectory}: {errorInfo}");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
var info = new ProcessStartInfo
|
||||
{
|
||||
FileName = fileName,
|
||||
WorkingDirectory = workingDirectory,
|
||||
Arguments = arguments,
|
||||
UseShellExecute = useShellExecute,
|
||||
Verb = verb,
|
||||
CreateNoWindow = createNoWindow
|
||||
};
|
||||
Process.Start(info)?.Dispose();
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LogException(ClassName, $"Failed to start process {fileName} with arguments {arguments} under {workingDirectory}", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool StartProcess(string fileName, string workingDirectory = "", Collection<string> argumentList = null, bool useShellExecute = false, string verb = "", bool createNoWindow = false) =>
|
||||
StartProcess(fileName, workingDirectory, JoinArgumentList(argumentList), useShellExecute, verb, createNoWindow);
|
||||
|
||||
private static string AddDoubleQuotes(string arg)
|
||||
{
|
||||
if (string.IsNullOrEmpty(arg))
|
||||
return "\"\"";
|
||||
|
||||
// If already wrapped in double quotes, return as is
|
||||
if (arg.Length >= 2 && arg[0] == '"' && arg[^1] == '"')
|
||||
return arg;
|
||||
|
||||
return $"\"{arg}\"";
|
||||
}
|
||||
|
||||
private static string JoinArgumentList(Collection<string> args)
|
||||
{
|
||||
if (args == null || args.Count == 0)
|
||||
return string.Empty;
|
||||
|
||||
return string.Join(" ", args.Select(arg =>
|
||||
{
|
||||
if (string.IsNullOrEmpty(arg))
|
||||
return "\"\"";
|
||||
|
||||
// Add double quotes
|
||||
return AddDoubleQuotes(arg);
|
||||
}));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ namespace Flow.Launcher.Resources.Pages
|
|||
{
|
||||
if (Settings.UseLogonTaskForStartup)
|
||||
{
|
||||
AutoStartup.ChangeToViaLogonTask();
|
||||
AutoStartup.ChangeToViaLogonTask(Settings.AlwaysRunAsAdministrator);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Forms;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Flow.Launcher.Core;
|
||||
|
|
@ -43,6 +44,8 @@ public partial class SettingsPaneGeneralViewModel : BaseModel
|
|||
get => Settings.StartFlowLauncherOnSystemStartup;
|
||||
set
|
||||
{
|
||||
if (Settings.StartFlowLauncherOnSystemStartup == value) return;
|
||||
|
||||
Settings.StartFlowLauncherOnSystemStartup = value;
|
||||
|
||||
try
|
||||
|
|
@ -51,7 +54,7 @@ public partial class SettingsPaneGeneralViewModel : BaseModel
|
|||
{
|
||||
if (UseLogonTaskForStartup)
|
||||
{
|
||||
AutoStartup.ChangeToViaLogonTask();
|
||||
AutoStartup.ChangeToViaLogonTask(AlwaysRunAsAdministrator);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -67,6 +70,13 @@ public partial class SettingsPaneGeneralViewModel : BaseModel
|
|||
{
|
||||
App.API.ShowMsgError(Localize.setAutoStartFailed(), e.Message);
|
||||
}
|
||||
|
||||
// If we have enabled logon task startup, we need to check if we need to restart the app
|
||||
// even if we encounter an error while setting the startup method
|
||||
if (value && UseLogonTaskForStartup)
|
||||
{
|
||||
CheckAdminChangeAndAskForRestart();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -75,6 +85,8 @@ public partial class SettingsPaneGeneralViewModel : BaseModel
|
|||
get => Settings.UseLogonTaskForStartup;
|
||||
set
|
||||
{
|
||||
if (UseLogonTaskForStartup == value) return;
|
||||
|
||||
Settings.UseLogonTaskForStartup = value;
|
||||
|
||||
if (StartFlowLauncherOnSystemStartup)
|
||||
|
|
@ -83,7 +95,7 @@ public partial class SettingsPaneGeneralViewModel : BaseModel
|
|||
{
|
||||
if (value)
|
||||
{
|
||||
AutoStartup.ChangeToViaLogonTask();
|
||||
AutoStartup.ChangeToViaLogonTask(AlwaysRunAsAdministrator);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -94,10 +106,61 @@ public partial class SettingsPaneGeneralViewModel : BaseModel
|
|||
{
|
||||
App.API.ShowMsgError(Localize.setAutoStartFailed(), e.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we have enabled logon task startup, we need to check if we need to restart the app
|
||||
// even if we encounter an error while setting the startup method
|
||||
if (StartFlowLauncherOnSystemStartup && value)
|
||||
{
|
||||
CheckAdminChangeAndAskForRestart();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool AlwaysRunAsAdministrator
|
||||
{
|
||||
get => Settings.AlwaysRunAsAdministrator;
|
||||
set
|
||||
{
|
||||
if (AlwaysRunAsAdministrator == value) return;
|
||||
|
||||
Settings.AlwaysRunAsAdministrator = value;
|
||||
|
||||
if (StartFlowLauncherOnSystemStartup && UseLogonTaskForStartup)
|
||||
{
|
||||
try
|
||||
{
|
||||
AutoStartup.ChangeToViaLogonTask(value);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
App.API.ShowMsg(App.API.GetTranslation("setAutoStartFailed"), e.Message);
|
||||
}
|
||||
|
||||
// If we have enabled logon task startup, we need to check if we need to restart the app
|
||||
// even if we encounter an error while setting the startup method
|
||||
CheckAdminChangeAndAskForRestart();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckAdminChangeAndAskForRestart()
|
||||
{
|
||||
// When we change from non-admin to admin, we need to restart the app as administrator to apply the changes
|
||||
// Under non-administrator, we cannot delete or set the logon task which is run as administrator
|
||||
if (AlwaysRunAsAdministrator && !Win32Helper.IsAdministrator())
|
||||
{
|
||||
if (App.API.ShowMsgBox(
|
||||
App.API.GetTranslation("runAsAdministratorChangeAndRestart"),
|
||||
App.API.GetTranslation("runAsAdministratorChange"),
|
||||
MessageBoxButton.YesNo) == MessageBoxResult.Yes)
|
||||
{
|
||||
// Restart the app as administrator
|
||||
App.API.RestartAppAsAdmin();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<SearchWindowScreenData> SearchWindowScreens { get; } =
|
||||
DropdownDataGeneric<SearchWindowScreens>.GetValues<SearchWindowScreenData>("SearchWindowScreen");
|
||||
|
||||
|
|
|
|||
|
|
@ -49,8 +49,8 @@
|
|||
OnContent="{DynamicResource enable}" />
|
||||
</ui:SettingsCard>
|
||||
</ui:SettingsExpander.Items>
|
||||
|
||||
</ui:SettingsExpander>
|
||||
|
||||
<ui:SettingsCard
|
||||
Margin="0 4 0 0"
|
||||
Description="{DynamicResource hideOnStartupToolTip}"
|
||||
|
|
@ -65,6 +65,20 @@
|
|||
OnContent="{DynamicResource enable}" />
|
||||
</ui:SettingsCard>
|
||||
|
||||
<ui:SettingsCard
|
||||
Margin="0 4 0 0"
|
||||
Description="{DynamicResource alwaysRunAsAdministratorToolTip}"
|
||||
Header="{DynamicResource alwaysRunAsAdministrator}">
|
||||
<ui:SettingsCard.HeaderIcon>
|
||||
<ui:FontIcon Glyph="" />
|
||||
</ui:SettingsCard.HeaderIcon>
|
||||
|
||||
<ui:ToggleSwitch
|
||||
IsOn="{Binding AlwaysRunAsAdministrator}"
|
||||
OffContent="{DynamicResource disable}"
|
||||
OnContent="{DynamicResource enable}" />
|
||||
</ui:SettingsCard>
|
||||
|
||||
<ui:SettingsCard Margin="0 12 0 0" Header="{DynamicResource hideFlowLauncherWhenLoseFocus}">
|
||||
<ui:ToggleSwitch
|
||||
IsOn="{Binding Settings.HideWhenDeactivated}"
|
||||
|
|
|
|||
|
|
@ -344,11 +344,7 @@ namespace Flow.Launcher.Plugin.Explorer
|
|||
{
|
||||
try
|
||||
{
|
||||
Process.Start(new ProcessStartInfo()
|
||||
{
|
||||
FileName = editorPath,
|
||||
ArgumentList = { record.FullPath }
|
||||
});
|
||||
Context.API.StartProcess(editorPath, arguments: record.FullPath);
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
|
|
@ -377,10 +373,7 @@ namespace Flow.Launcher.Plugin.Explorer
|
|||
{
|
||||
try
|
||||
{
|
||||
Process.Start(new ProcessStartInfo()
|
||||
{
|
||||
FileName = shellPath, WorkingDirectory = record.FullPath
|
||||
});
|
||||
Context.API.StartProcess(shellPath, workingDirectory: record.FullPath, arguments: string.Empty);
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
|
|
@ -444,6 +437,7 @@ namespace Flow.Launcher.Plugin.Explorer
|
|||
Arguments = "srchadmin.dll"
|
||||
};
|
||||
|
||||
// No need to de-elevate since we are opening windows settings which cannot bring security risks
|
||||
Process.Start(psi);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -468,6 +462,7 @@ namespace Flow.Launcher.Plugin.Explorer
|
|||
SubTitle = Localize.plugin_explorer_openwith_subtitle(),
|
||||
Action = _ =>
|
||||
{
|
||||
// No need to de-elevate since we are opening windows settings which cannot bring security risks
|
||||
Process.Start("rundll32.exe", $"{Path.Combine(Environment.SystemDirectory, "shell32.dll")},OpenAs_RunDLL {record.FullPath}");
|
||||
return true;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything
|
|||
}
|
||||
|
||||
Settings.EverythingInstalledPath = installedPath;
|
||||
Process.Start(installedPath, "-startup");
|
||||
Main.Context.API.StartProcess(installedPath, arguments: "-startup");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -506,6 +506,7 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels
|
|||
Arguments = Constants.WindowsIndexingOptions
|
||||
};
|
||||
|
||||
// No need to de-elevate since we are opening windows settings which cannot bring security risks
|
||||
Process.Start(psi);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,20 +1,19 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Principal;
|
||||
using System.Threading.Channels;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media.Imaging;
|
||||
using Windows.ApplicationModel;
|
||||
using Windows.Management.Deployment;
|
||||
using System.Xml;
|
||||
using Flow.Launcher.Plugin.Program.Logger;
|
||||
using Flow.Launcher.Plugin.SharedModels;
|
||||
using System.Threading.Channels;
|
||||
using System.Xml;
|
||||
using Windows.ApplicationModel.Core;
|
||||
using System.Windows.Input;
|
||||
using MemoryPack;
|
||||
using Windows.ApplicationModel;
|
||||
using Windows.ApplicationModel.Core;
|
||||
using Windows.Management.Deployment;
|
||||
|
||||
namespace Flow.Launcher.Plugin.Program.Programs
|
||||
{
|
||||
|
|
@ -455,7 +454,9 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
bool elevated = e.SpecialKeyState.ToModifierKeys() == (ModifierKeys.Control | ModifierKeys.Shift);
|
||||
|
||||
bool shouldRunElevated = elevated && CanRunElevated;
|
||||
_ = Task.Run(() => Launch(shouldRunElevated)).ConfigureAwait(false);
|
||||
|
||||
Launch(shouldRunElevated);
|
||||
|
||||
if (elevated && !shouldRunElevated)
|
||||
{
|
||||
var title = api.GetTranslation("flowlauncher_plugin_program_disable_dlgtitle_error");
|
||||
|
|
@ -497,7 +498,8 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
Title = api.GetTranslation("flowlauncher_plugin_program_run_as_administrator"),
|
||||
Action = c =>
|
||||
{
|
||||
_ = Task.Run(() => Launch(true)).ConfigureAwait(false);
|
||||
Launch(true);
|
||||
|
||||
return true;
|
||||
},
|
||||
IcoPath = "Images/cmd.png",
|
||||
|
|
@ -510,12 +512,14 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
|
||||
private void Launch(bool elevated = false)
|
||||
{
|
||||
string command = "shell:AppsFolder\\" + UserModelId;
|
||||
var command = "shell:AppsFolder\\" + UserModelId;
|
||||
command = Environment.ExpandEnvironmentVariables(command.Trim());
|
||||
|
||||
var info = new ProcessStartInfo(command) { UseShellExecute = true, Verb = elevated ? "runas" : "" };
|
||||
|
||||
Main.StartProcess(Process.Start, info);
|
||||
_ = Task.Run(() => Main.Context.API.StartProcess(
|
||||
command,
|
||||
arguments: string.Empty,
|
||||
useShellExecute: true,
|
||||
verb: elevated ? "runas" : ""));
|
||||
}
|
||||
|
||||
internal static bool IfAppCanRunElevated(XmlNode appNode)
|
||||
|
|
|
|||
|
|
@ -1,21 +1,21 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security;
|
||||
using System.Text;
|
||||
using System.Threading.Channels;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Win32;
|
||||
using System.Windows.Input;
|
||||
using Flow.Launcher.Plugin.Program.Logger;
|
||||
using Flow.Launcher.Plugin.Program.Views.Models;
|
||||
using Flow.Launcher.Plugin.SharedCommands;
|
||||
using Flow.Launcher.Plugin.SharedModels;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Threading.Channels;
|
||||
using Flow.Launcher.Plugin.Program.Views.Models;
|
||||
using IniParser;
|
||||
using System.Windows.Input;
|
||||
using MemoryPack;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace Flow.Launcher.Plugin.Program.Programs
|
||||
{
|
||||
|
|
@ -196,15 +196,7 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
// Ctrl + Shift + Enter to run as admin
|
||||
bool runAsAdmin = c.SpecialKeyState.ToModifierKeys() == (ModifierKeys.Control | ModifierKeys.Shift);
|
||||
|
||||
var info = new ProcessStartInfo
|
||||
{
|
||||
FileName = FullPath,
|
||||
WorkingDirectory = ParentDirectory,
|
||||
UseShellExecute = true,
|
||||
Verb = runAsAdmin ? "runas" : "",
|
||||
};
|
||||
|
||||
_ = Task.Run(() => Main.StartProcess(Process.Start, info));
|
||||
Launch(runAsAdmin);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -213,6 +205,15 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
return result;
|
||||
}
|
||||
|
||||
private void Launch(bool runAsAdmin = false)
|
||||
{
|
||||
_ = Task.Run(() => Main.Context.API.StartProcess(
|
||||
FullPath,
|
||||
workingDirectory: ParentDirectory,
|
||||
arguments: string.Empty,
|
||||
useShellExecute: true,
|
||||
verb: runAsAdmin ? "runas" : ""));
|
||||
}
|
||||
|
||||
public List<Result> ContextMenus(IPublicAPI api)
|
||||
{
|
||||
|
|
@ -240,15 +241,7 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
Title = api.GetTranslation("flowlauncher_plugin_program_run_as_administrator"),
|
||||
Action = c =>
|
||||
{
|
||||
var info = new ProcessStartInfo
|
||||
{
|
||||
FileName = FullPath,
|
||||
WorkingDirectory = ParentDirectory,
|
||||
Verb = "runas",
|
||||
UseShellExecute = true
|
||||
};
|
||||
|
||||
_ = Task.Run(() => Main.StartProcess(Process.Start, info));
|
||||
Launch(true);
|
||||
|
||||
return true;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ namespace Flow.Launcher.Plugin.Shell
|
|||
!c.SpecialKeyState.AltPressed &&
|
||||
!c.SpecialKeyState.WinPressed;
|
||||
|
||||
Execute(Process.Start, PrepareProcessStartInfo(m, runAsAdministrator));
|
||||
Execute(StartProcess, PrepareProcessStartInfo(m, runAsAdministrator));
|
||||
return true;
|
||||
},
|
||||
CopyText = m
|
||||
|
|
@ -121,7 +121,7 @@ namespace Flow.Launcher.Plugin.Shell
|
|||
!c.SpecialKeyState.AltPressed &&
|
||||
!c.SpecialKeyState.WinPressed;
|
||||
|
||||
Execute(Process.Start, PrepareProcessStartInfo(m.Key, runAsAdministrator));
|
||||
Execute(StartProcess, PrepareProcessStartInfo(m.Key, runAsAdministrator));
|
||||
return true;
|
||||
},
|
||||
CopyText = m.Key
|
||||
|
|
@ -151,7 +151,7 @@ namespace Flow.Launcher.Plugin.Shell
|
|||
!c.SpecialKeyState.AltPressed &&
|
||||
!c.SpecialKeyState.WinPressed;
|
||||
|
||||
Execute(Process.Start, PrepareProcessStartInfo(cmd, runAsAdministrator));
|
||||
Execute(StartProcess, PrepareProcessStartInfo(cmd, runAsAdministrator));
|
||||
return true;
|
||||
},
|
||||
CopyText = cmd
|
||||
|
|
@ -176,7 +176,7 @@ namespace Flow.Launcher.Plugin.Shell
|
|||
!c.SpecialKeyState.AltPressed &&
|
||||
!c.SpecialKeyState.WinPressed;
|
||||
|
||||
Execute(Process.Start, PrepareProcessStartInfo(m.Key, runAsAdministrator));
|
||||
Execute(StartProcess, PrepareProcessStartInfo(m.Key, runAsAdministrator));
|
||||
return true;
|
||||
},
|
||||
CopyText = m.Key
|
||||
|
|
@ -328,6 +328,17 @@ namespace Flow.Launcher.Plugin.Shell
|
|||
return info;
|
||||
}
|
||||
|
||||
private static Process StartProcess(ProcessStartInfo info)
|
||||
{
|
||||
Context.API.StartProcess(
|
||||
info.FileName,
|
||||
workingDirectory: info.WorkingDirectory,
|
||||
argumentList: info.ArgumentList,
|
||||
useShellExecute: info.UseShellExecute,
|
||||
verb: info.Verb);
|
||||
return null;
|
||||
}
|
||||
|
||||
private void Execute(Func<ProcessStartInfo, Process> startProcess, ProcessStartInfo info)
|
||||
{
|
||||
try
|
||||
|
|
@ -464,7 +475,7 @@ namespace Flow.Launcher.Plugin.Shell
|
|||
Title = Localize.flowlauncher_plugin_cmd_run_as_administrator(),
|
||||
Action = c =>
|
||||
{
|
||||
Execute(Process.Start, PrepareProcessStartInfo(selectedResult.Title, true));
|
||||
Execute(StartProcess, PrepareProcessStartInfo(selectedResult.Title, true));
|
||||
return true;
|
||||
},
|
||||
IcoPath = "Images/admin.png",
|
||||
|
|
|
|||
|
|
@ -219,6 +219,7 @@ namespace Flow.Launcher.Plugin.Sys
|
|||
if (EnableShutdownPrivilege())
|
||||
PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_SHUTDOWN | EXIT_WINDOWS_FLAGS.EWX_POWEROFF, REASON);
|
||||
else
|
||||
// No need to de-elevate since we already have message box asking for confirmation
|
||||
Process.Start("shutdown", "/s /t 0");
|
||||
}
|
||||
return true;
|
||||
|
|
@ -244,6 +245,7 @@ namespace Flow.Launcher.Plugin.Sys
|
|||
if (EnableShutdownPrivilege())
|
||||
PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_REBOOT, REASON);
|
||||
else
|
||||
// No need to de-elevate since we already have message box asking for confirmation
|
||||
Process.Start("shutdown", "/r /t 0");
|
||||
}
|
||||
return true;
|
||||
|
|
@ -271,6 +273,7 @@ namespace Flow.Launcher.Plugin.Sys
|
|||
if (EnableShutdownPrivilege())
|
||||
PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_REBOOT | EXIT_WINDOWS_FLAGS.EWX_BOOTOPTIONS, REASON);
|
||||
else
|
||||
// No need to de-elevate since we already have message box asking for confirmation
|
||||
Process.Start("shutdown", "/r /o /t 0");
|
||||
}
|
||||
return true;
|
||||
|
|
@ -335,6 +338,7 @@ namespace Flow.Launcher.Plugin.Sys
|
|||
Glyph = new GlyphInfo (FontFamily:"/Resources/#Segoe Fluent Icons", Glyph:"\xe773"),
|
||||
Action = c =>
|
||||
{
|
||||
// No need to de-elevate since we are opening windows settings which cannot bring security risks
|
||||
Process.Start("control.exe", "srchadmin.dll");
|
||||
return true;
|
||||
}
|
||||
|
|
@ -368,6 +372,7 @@ namespace Flow.Launcher.Plugin.Sys
|
|||
CopyText = recycleBinFolder,
|
||||
Action = c =>
|
||||
{
|
||||
// No need to de-elevate since we are opening windows settings which cannot bring security risks
|
||||
Process.Start("explorer", recycleBinFolder);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -198,6 +198,7 @@ namespace Flow.Launcher.Plugin.WindowsSettings.Helper
|
|||
|
||||
try
|
||||
{
|
||||
// No need to de-elevate since we are opening windows settings which cannot bring security risks
|
||||
Process.Start(processStartInfo);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -207,6 +208,7 @@ namespace Flow.Launcher.Plugin.WindowsSettings.Helper
|
|||
{
|
||||
processStartInfo.UseShellExecute = true;
|
||||
processStartInfo.Verb = "runas";
|
||||
// No need to de-elevate since we are opening windows settings which cannot bring security risks
|
||||
Process.Start(processStartInfo);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue