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());
|
PublicApi.Instance.ShowMsgBox(Localize.restartToDisablePortableMode());
|
||||||
|
|
||||||
UpdateManager.RestartApp(Constant.ApplicationFileName);
|
PublicApi.Instance.RestartApp();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
|
@ -66,7 +66,7 @@ namespace Flow.Launcher.Core.Configuration
|
||||||
|
|
||||||
PublicApi.Instance.ShowMsgBox(Localize.restartToEnablePortableMode());
|
PublicApi.Instance.ShowMsgBox(Localize.restartToEnablePortableMode());
|
||||||
|
|
||||||
UpdateManager.RestartApp(Constant.ApplicationFileName);
|
PublicApi.Instance.RestartApp();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,19 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Linq;
|
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using Flow.Launcher.Plugin.SharedCommands;
|
|
||||||
using Flow.Launcher.Infrastructure;
|
using Flow.Launcher.Infrastructure;
|
||||||
using Flow.Launcher.Infrastructure.Http;
|
using Flow.Launcher.Infrastructure.Http;
|
||||||
using Flow.Launcher.Infrastructure.UserSettings;
|
using Flow.Launcher.Infrastructure.UserSettings;
|
||||||
using Flow.Launcher.Plugin;
|
using Flow.Launcher.Plugin;
|
||||||
|
using Flow.Launcher.Plugin.SharedCommands;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Squirrel;
|
using Squirrel;
|
||||||
|
|
||||||
|
|
@ -91,7 +91,7 @@ namespace Flow.Launcher.Core
|
||||||
if (_api.ShowMsgBox(newVersionTips, Localize.update_flowlauncher_new_update(),
|
if (_api.ShowMsgBox(newVersionTips, Localize.update_flowlauncher_new_update(),
|
||||||
MessageBoxButton.YesNo) == MessageBoxResult.Yes)
|
MessageBoxButton.YesNo) == MessageBoxResult.Yes)
|
||||||
{
|
{
|
||||||
UpdateManager.RestartApp(Constant.ApplicationFileName);
|
_api.RestartApp();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ namespace Flow.Launcher.Infrastructure
|
||||||
private static readonly Assembly Assembly = Assembly.GetExecutingAssembly();
|
private static readonly Assembly Assembly = Assembly.GetExecutingAssembly();
|
||||||
public static readonly string ProgramDirectory = Directory.GetParent(Assembly.Location.NonNull()).ToString();
|
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 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 ApplicationDirectory = Directory.GetParent(ProgramDirectory).ToString();
|
||||||
public static readonly string RootDirectory = Directory.GetParent(ApplicationDirectory).ToString();
|
public static readonly string RootDirectory = Directory.GetParent(ApplicationDirectory).ToString();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -226,7 +226,7 @@ namespace Flow.Launcher.Infrastructure.Http
|
||||||
Log.Debug(ClassName, $"Url <{url}>");
|
Log.Debug(ClassName, $"Url <{url}>");
|
||||||
return await client.GetStringAsync(url, token);
|
return await client.GetStringAsync(url, token);
|
||||||
}
|
}
|
||||||
catch (System.Exception e)
|
catch (System.Exception)
|
||||||
{
|
{
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -88,9 +88,25 @@ EVENT_SYSTEM_DIALOGEND
|
||||||
DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS
|
DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS
|
||||||
WM_POWERBROADCAST
|
WM_POWERBROADCAST
|
||||||
PBT_APMRESUMEAUTOMATIC
|
PBT_APMRESUMEAUTOMATIC
|
||||||
|
|
||||||
PBT_APMRESUMESUSPEND
|
PBT_APMRESUMESUSPEND
|
||||||
PowerRegisterSuspendResumeNotification
|
PowerRegisterSuspendResumeNotification
|
||||||
PowerUnregisterSuspendResumeNotification
|
PowerUnregisterSuspendResumeNotification
|
||||||
|
|
||||||
|
OpenProcessToken
|
||||||
|
GetCurrentProcess
|
||||||
|
LookupPrivilegeValue
|
||||||
|
SE_INCREASE_QUOTA_NAME
|
||||||
|
CloseHandle
|
||||||
|
TOKEN_PRIVILEGES
|
||||||
|
AdjustTokenPrivileges
|
||||||
|
GetShellWindow
|
||||||
|
GetWindowThreadProcessId
|
||||||
|
OpenProcess
|
||||||
|
GetProcessId
|
||||||
|
DuplicateTokenEx
|
||||||
|
CreateProcessWithTokenW
|
||||||
|
|
||||||
DeviceNotifyCallbackRoutine
|
DeviceNotifyCallbackRoutine
|
||||||
|
|
||||||
MonitorFromWindow
|
MonitorFromWindow
|
||||||
|
|
|
||||||
|
|
@ -483,6 +483,8 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
||||||
public bool HideWhenDeactivated { get; set; } = true;
|
public bool HideWhenDeactivated { get; set; } = true;
|
||||||
public bool ShowTaskbarWhenInvoked { get; set; } = false;
|
public bool ShowTaskbarWhenInvoked { get; set; } = false;
|
||||||
|
|
||||||
|
public bool AlwaysRunAsAdministrator { get; set; } = false;
|
||||||
|
|
||||||
private bool _showAtTopmost = false;
|
private bool _showAtTopmost = false;
|
||||||
public bool ShowAtTopmost
|
public bool ShowAtTopmost
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Security.Principal;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
|
@ -19,6 +20,7 @@ using Microsoft.Win32.SafeHandles;
|
||||||
using Windows.Win32;
|
using Windows.Win32;
|
||||||
using Windows.Win32.Foundation;
|
using Windows.Win32.Foundation;
|
||||||
using Windows.Win32.Graphics.Dwm;
|
using Windows.Win32.Graphics.Dwm;
|
||||||
|
using Windows.Win32.Security;
|
||||||
using Windows.Win32.System.Power;
|
using Windows.Win32.System.Power;
|
||||||
using Windows.Win32.System.Threading;
|
using Windows.Win32.System.Threading;
|
||||||
using Windows.Win32.UI.Input.KeyboardAndMouse;
|
using Windows.Win32.UI.Input.KeyboardAndMouse;
|
||||||
|
|
@ -690,6 +692,7 @@ namespace Flow.Launcher.Infrastructure
|
||||||
{
|
{
|
||||||
try
|
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 });
|
Process.Start(new ProcessStartInfo("ms-settings:regionlanguage") { UseShellExecute = true });
|
||||||
}
|
}
|
||||||
catch (System.Exception)
|
catch (System.Exception)
|
||||||
|
|
@ -1017,6 +1020,164 @@ namespace Flow.Launcher.Infrastructure
|
||||||
|
|
||||||
#endregion
|
#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
|
#region Taskbar
|
||||||
|
|
||||||
public static unsafe void ShowTaskbar()
|
public static unsafe void ShowTaskbar()
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
@ -29,13 +30,21 @@ namespace Flow.Launcher.Plugin
|
||||||
void ChangeQuery(string query, bool requery = false);
|
void ChangeQuery(string query, bool requery = false);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Restart Flow Launcher
|
/// Restart Flow Launcher without changing the user privileges.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void RestartApp();
|
void RestartApp();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Restart Flow Launcher as administrator.
|
||||||
|
/// </summary>
|
||||||
|
void RestartAppAsAdmin();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Run a shell command
|
/// Run a shell command
|
||||||
/// </summary>
|
/// </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="cmd">The command or program to run</param>
|
||||||
/// <param name="filename">the shell type to run, e.g. powershell.exe</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>
|
/// <exception cref="FileNotFoundException">Thrown when unable to find the file specified in the command </exception>
|
||||||
|
|
@ -637,5 +646,35 @@ namespace Flow.Launcher.Plugin
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
string GetLogDirectory();
|
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
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio Version 17
|
# Visual Studio Version 18
|
||||||
VisualStudioVersion = 17.3.32901.215
|
VisualStudioVersion = 18.4.11519.219 insiders
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher", "Flow.Launcher\Flow.Launcher.csproj", "{DB90F671-D861-46BB-93A3-F1304F5BA1C5}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher", "Flow.Launcher\Flow.Launcher.csproj", "{DB90F671-D861-46BB-93A3-F1304F5BA1C5}"
|
||||||
ProjectSection(ProjectDependencies) = postProject
|
ProjectSection(ProjectDependencies) = postProject
|
||||||
{0B9DE348-9361-4940-ADB6-F5953BFFCCEC} = {0B9DE348-9361-4940-ADB6-F5953BFFCCEC}
|
{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}
|
{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}
|
{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
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Test", "Flow.Launcher.Test\Flow.Launcher.Test.csproj", "{FF742965-9A80-41A5-B042-D6C7D3A21708}"
|
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
|
LICENSE = LICENSE
|
||||||
Scripts\post_build.ps1 = Scripts\post_build.ps1
|
Scripts\post_build.ps1 = Scripts\post_build.ps1
|
||||||
README.md = README.md
|
README.md = README.md
|
||||||
SolutionAssemblyInfo.cs = SolutionAssemblyInfo.cs
|
|
||||||
Settings.XamlStyler = Settings.XamlStyler
|
Settings.XamlStyler = Settings.XamlStyler
|
||||||
|
SolutionAssemblyInfo.cs = SolutionAssemblyInfo.cs
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
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}"
|
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
|
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}"
|
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
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flow.Launcher.Command", "Flow.Launcher.Command\Flow.Launcher.Command.csproj", "{A9976C5C-B73A-4D29-B654-EF1C0C4C9C8C}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
|
@ -81,8 +85,19 @@ Global
|
||||||
Release|x86 = Release|x86
|
Release|x86 = Release|x86
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
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.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.ActiveCfg = Debug|Any CPU
|
||||||
{FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|x64.Build.0 = 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
|
{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|x64.Build.0 = Release|Any CPU
|
||||||
{8451ECDD-2EA4-4966-BB0A-7BBC40138E80}.Release|x86.ActiveCfg = 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
|
{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.ActiveCfg = Debug|Any CPU
|
||||||
{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Debug|Any CPU.Build.0 = 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
|
{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|x64.Build.0 = Release|Any CPU
|
||||||
{5043CECE-E6A7-4867-9CBE-02D27D83747A}.Release|x86.ActiveCfg = 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
|
{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
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
@ -57,6 +59,13 @@ namespace Flow.Launcher
|
||||||
|
|
||||||
public App()
|
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
|
// Do not use bitmap cache since it can cause WPF second window freezing issue
|
||||||
ShadowAssist.UseBitmapCache = false;
|
ShadowAssist.UseBitmapCache = false;
|
||||||
|
|
||||||
|
|
@ -293,12 +302,23 @@ namespace Flow.Launcher
|
||||||
{
|
{
|
||||||
try
|
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)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
// but if it fails (permissions, etc) then don't keep retrying
|
// But if it fails for other reasons then do not keep retrying,
|
||||||
// this also gives the user a visual indication in the Settings widget
|
// set startup to false to give users a visual indication in the general page
|
||||||
_settings.StartFlowLauncherOnSystemStartup = false;
|
_settings.StartFlowLauncherOnSystemStartup = false;
|
||||||
API.ShowMsgError(Localize.setAutoStartFailed(), e.Message);
|
API.ShowMsgError(Localize.setAutoStartFailed(), e.Message);
|
||||||
}
|
}
|
||||||
|
|
@ -408,6 +428,59 @@ namespace Flow.Launcher
|
||||||
|
|
||||||
#endregion
|
#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
|
#region IDisposable
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
|
|
|
||||||
|
|
@ -18,18 +18,18 @@ public class AutoStartup
|
||||||
private const string LogonTaskName = $"{Constant.FlowLauncher} Startup";
|
private const string LogonTaskName = $"{Constant.FlowLauncher} Startup";
|
||||||
private const string LogonTaskDesc = $"{Constant.FlowLauncher} Auto 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,
|
// 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
|
// 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();
|
var registryEnabled = CheckRegistry();
|
||||||
if (useLogonTaskForStartup)
|
if (useLogonTaskForStartup)
|
||||||
{
|
{
|
||||||
// Enable logon task
|
// Enable logon task
|
||||||
if (!logonTaskEnabled)
|
if (!logonTaskEnabled)
|
||||||
{
|
{
|
||||||
Enable(true);
|
Enable(true, alwaysRunAsAdministrator);
|
||||||
}
|
}
|
||||||
// Disable registry
|
// Disable registry
|
||||||
if (registryEnabled)
|
if (registryEnabled)
|
||||||
|
|
@ -42,7 +42,7 @@ public class AutoStartup
|
||||||
// Enable registry
|
// Enable registry
|
||||||
if (!registryEnabled)
|
if (!registryEnabled)
|
||||||
{
|
{
|
||||||
Enable(false);
|
Enable(false, alwaysRunAsAdministrator);
|
||||||
}
|
}
|
||||||
// Disable logon task
|
// Disable logon task
|
||||||
if (logonTaskEnabled)
|
if (logonTaskEnabled)
|
||||||
|
|
@ -52,7 +52,7 @@ public class AutoStartup
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool CheckLogonTask()
|
private static bool CheckLogonTask(bool alwaysRunAsAdministrator)
|
||||||
{
|
{
|
||||||
using var taskService = new TaskService();
|
using var taskService = new TaskService();
|
||||||
var task = taskService.RootFolder.AllTasks.FirstOrDefault(t => t.Name == LogonTaskName);
|
var task = taskService.RootFolder.AllTasks.FirstOrDefault(t => t.Name == LogonTaskName);
|
||||||
|
|
@ -60,22 +60,47 @@ public class AutoStartup
|
||||||
{
|
{
|
||||||
try
|
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)
|
if (task.Definition.Actions.FirstOrDefault() is Microsoft.Win32.TaskScheduler.Action taskAction)
|
||||||
{
|
{
|
||||||
var action = taskAction.ToString().Trim();
|
var action = taskAction.ToString().Trim();
|
||||||
var needsRecreation = !action.Equals(Constant.ExecutablePath, StringComparison.OrdinalIgnoreCase)
|
var pathCorrect = action.Equals(Constant.ExecutablePath, StringComparison.OrdinalIgnoreCase);
|
||||||
|| task.Definition.Settings.Priority != ProcessPriorityClass.Normal;
|
var runLevelCorrect = CheckRunLevel(task.Definition.Principal.RunLevel, alwaysRunAsAdministrator);
|
||||||
if (needsRecreation)
|
var priorityCorrect = task.Definition.Settings.Priority == ProcessPriorityClass.Normal;
|
||||||
|
|
||||||
|
if (Win32Helper.IsAdministrator())
|
||||||
{
|
{
|
||||||
UnscheduleLogonTask();
|
// If path, run level or priority is not correct, we need to unschedule and reschedule the task
|
||||||
ScheduleLogonTask();
|
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;
|
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)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
App.API.LogError(ClassName, $"Failed to check logon task: {e}");
|
App.API.LogError(ClassName, $"Failed to check logon task: {e}");
|
||||||
|
|
@ -86,6 +111,11 @@ public class AutoStartup
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool CheckRunLevel(TaskRunLevel rl, bool alwaysRunAsAdministrator)
|
||||||
|
{
|
||||||
|
return alwaysRunAsAdministrator ? rl == TaskRunLevel.Highest : rl != TaskRunLevel.Highest;
|
||||||
|
}
|
||||||
|
|
||||||
private static bool CheckRegistry()
|
private static bool CheckRegistry()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
@ -120,16 +150,19 @@ public class AutoStartup
|
||||||
Disable(false);
|
Disable(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ChangeToViaLogonTask()
|
public static void ChangeToViaLogonTask(bool alwaysRunAsAdministrator)
|
||||||
{
|
{
|
||||||
Disable(false);
|
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()
|
public static void ChangeToViaRegistry()
|
||||||
{
|
{
|
||||||
Disable(true);
|
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)
|
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
|
try
|
||||||
{
|
{
|
||||||
if (logonTask)
|
if (logonTask)
|
||||||
{
|
{
|
||||||
ScheduleLogonTask();
|
ScheduleLogonTask(alwaysRunAsAdministrator);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -172,14 +205,15 @@ public class AutoStartup
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool ScheduleLogonTask()
|
private static bool ScheduleLogonTask(bool alwaysRunAsAdministrator)
|
||||||
{
|
{
|
||||||
using var td = TaskService.Instance.NewTask();
|
using var td = TaskService.Instance.NewTask();
|
||||||
td.RegistrationInfo.Description = LogonTaskDesc;
|
td.RegistrationInfo.Description = LogonTaskDesc;
|
||||||
td.Triggers.Add(new LogonTrigger { UserId = WindowsIdentity.GetCurrent().Name, Delay = TimeSpan.FromSeconds(2) });
|
td.Triggers.Add(new LogonTrigger { UserId = WindowsIdentity.GetCurrent().Name, Delay = TimeSpan.FromSeconds(2) });
|
||||||
td.Actions.Add(Constant.ExecutablePath);
|
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;
|
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()
|
private static bool UnscheduleRegistry()
|
||||||
{
|
{
|
||||||
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
|
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="pluginStillInitializingSubtitle">Select this result to requery</system:String>
|
||||||
<system:String x:Key="pluginFailedToRespond">{0}: Failed to respond!</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="pluginFailedToRespondSubtitle">Select this result for more info</system:String>
|
||||||
|
<system:String x:Key="admin">(Admin)</system:String>
|
||||||
|
|
||||||
<!-- Setting General -->
|
<!-- Setting General -->
|
||||||
<system:String x:Key="flowlauncher_settings">Settings</system:String>
|
<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="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="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="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 -->
|
<!-- Setting Plugin -->
|
||||||
<system:String x:Key="searchplugin">Search Plugin</system:String>
|
<system:String x:Key="searchplugin">Search Plugin</system:String>
|
||||||
|
|
|
||||||
|
|
@ -765,9 +765,13 @@ namespace Flow.Launcher
|
||||||
|
|
||||||
private void InitializeNotifyIcon()
|
private void InitializeNotifyIcon()
|
||||||
{
|
{
|
||||||
|
var text = Win32Helper.IsAdministrator() ?
|
||||||
|
Constant.FlowLauncherFullName + " " + App.API.GetTranslation("admin") :
|
||||||
|
Constant.FlowLauncherFullName;
|
||||||
|
|
||||||
_notifyIcon = new NotifyIcon
|
_notifyIcon = new NotifyIcon
|
||||||
{
|
{
|
||||||
Text = Constant.FlowLauncherFullName,
|
Text = text,
|
||||||
Icon = Constant.Version == "1.0.0" ? Properties.Resources.dev : Properties.Resources.app,
|
Icon = Constant.Version == "1.0.0" ? Properties.Resources.dev : Properties.Resources.app,
|
||||||
Visible = !_settings.HideNotifyIcon
|
Visible = !_settings.HideNotifyIcon
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
@ -73,7 +75,12 @@ namespace Flow.Launcher
|
||||||
_mainVM.ChangeQueryText(query, requery);
|
_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();
|
_mainVM.Hide();
|
||||||
|
|
||||||
|
|
@ -85,7 +92,7 @@ namespace Flow.Launcher
|
||||||
// Restart requires Squirrel's Update.exe to be present in the parent folder,
|
// 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,
|
// 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.
|
// the project may not restart or just terminates. This is expected.
|
||||||
UpdateManager.RestartApp(Constant.ApplicationFileName);
|
App.RestartApp(runAsAdmin);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ShowMainWindow() => _mainVM.Show();
|
public void ShowMainWindow() => _mainVM.Show();
|
||||||
|
|
@ -151,8 +158,7 @@ namespace Flow.Launcher
|
||||||
{
|
{
|
||||||
var args = filename == "cmd.exe" ? $"/C {cmd}" : $"{cmd}";
|
var args = filename == "cmd.exe" ? $"/C {cmd}" : $"{cmd}";
|
||||||
|
|
||||||
var startInfo = ShellCommand.SetProcessStartInfo(filename, arguments: args, createNoWindow: true);
|
StartProcess(filename, arguments: args, createNoWindow: true);
|
||||||
ShellCommand.Execute(startInfo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD100:Avoid async void methods", Justification = "<Pending>")]
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD100:Avoid async void methods", Justification = "<Pending>")]
|
||||||
|
|
@ -450,11 +456,7 @@ namespace Flow.Launcher
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Process.Start(new ProcessStartInfo()
|
StartProcess(uri.AbsoluteUri, arguments: string.Empty, useShellExecute: true);
|
||||||
{
|
|
||||||
FileName = uri.AbsoluteUri,
|
|
||||||
UseShellExecute = true
|
|
||||||
})?.Dispose();
|
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
|
@ -623,6 +625,85 @@ namespace Flow.Launcher
|
||||||
|
|
||||||
public string GetLogDirectory() => DataLocation.VersionLogDirectory;
|
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
|
#endregion
|
||||||
|
|
||||||
#region Private Methods
|
#region Private Methods
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ namespace Flow.Launcher.Resources.Pages
|
||||||
{
|
{
|
||||||
if (Settings.UseLogonTaskForStartup)
|
if (Settings.UseLogonTaskForStartup)
|
||||||
{
|
{
|
||||||
AutoStartup.ChangeToViaLogonTask();
|
AutoStartup.ChangeToViaLogonTask(Settings.AlwaysRunAsAdministrator);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Windows;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using CommunityToolkit.Mvvm.Input;
|
using CommunityToolkit.Mvvm.Input;
|
||||||
using Flow.Launcher.Core;
|
using Flow.Launcher.Core;
|
||||||
|
|
@ -43,6 +44,8 @@ public partial class SettingsPaneGeneralViewModel : BaseModel
|
||||||
get => Settings.StartFlowLauncherOnSystemStartup;
|
get => Settings.StartFlowLauncherOnSystemStartup;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
|
if (Settings.StartFlowLauncherOnSystemStartup == value) return;
|
||||||
|
|
||||||
Settings.StartFlowLauncherOnSystemStartup = value;
|
Settings.StartFlowLauncherOnSystemStartup = value;
|
||||||
|
|
||||||
try
|
try
|
||||||
|
|
@ -51,7 +54,7 @@ public partial class SettingsPaneGeneralViewModel : BaseModel
|
||||||
{
|
{
|
||||||
if (UseLogonTaskForStartup)
|
if (UseLogonTaskForStartup)
|
||||||
{
|
{
|
||||||
AutoStartup.ChangeToViaLogonTask();
|
AutoStartup.ChangeToViaLogonTask(AlwaysRunAsAdministrator);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -67,6 +70,13 @@ public partial class SettingsPaneGeneralViewModel : BaseModel
|
||||||
{
|
{
|
||||||
App.API.ShowMsgError(Localize.setAutoStartFailed(), e.Message);
|
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;
|
get => Settings.UseLogonTaskForStartup;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
|
if (UseLogonTaskForStartup == value) return;
|
||||||
|
|
||||||
Settings.UseLogonTaskForStartup = value;
|
Settings.UseLogonTaskForStartup = value;
|
||||||
|
|
||||||
if (StartFlowLauncherOnSystemStartup)
|
if (StartFlowLauncherOnSystemStartup)
|
||||||
|
|
@ -83,7 +95,7 @@ public partial class SettingsPaneGeneralViewModel : BaseModel
|
||||||
{
|
{
|
||||||
if (value)
|
if (value)
|
||||||
{
|
{
|
||||||
AutoStartup.ChangeToViaLogonTask();
|
AutoStartup.ChangeToViaLogonTask(AlwaysRunAsAdministrator);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -94,10 +106,61 @@ public partial class SettingsPaneGeneralViewModel : BaseModel
|
||||||
{
|
{
|
||||||
App.API.ShowMsgError(Localize.setAutoStartFailed(), e.Message);
|
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; } =
|
public List<SearchWindowScreenData> SearchWindowScreens { get; } =
|
||||||
DropdownDataGeneric<SearchWindowScreens>.GetValues<SearchWindowScreenData>("SearchWindowScreen");
|
DropdownDataGeneric<SearchWindowScreens>.GetValues<SearchWindowScreenData>("SearchWindowScreen");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -49,8 +49,8 @@
|
||||||
OnContent="{DynamicResource enable}" />
|
OnContent="{DynamicResource enable}" />
|
||||||
</ui:SettingsCard>
|
</ui:SettingsCard>
|
||||||
</ui:SettingsExpander.Items>
|
</ui:SettingsExpander.Items>
|
||||||
|
|
||||||
</ui:SettingsExpander>
|
</ui:SettingsExpander>
|
||||||
|
|
||||||
<ui:SettingsCard
|
<ui:SettingsCard
|
||||||
Margin="0 4 0 0"
|
Margin="0 4 0 0"
|
||||||
Description="{DynamicResource hideOnStartupToolTip}"
|
Description="{DynamicResource hideOnStartupToolTip}"
|
||||||
|
|
@ -65,6 +65,20 @@
|
||||||
OnContent="{DynamicResource enable}" />
|
OnContent="{DynamicResource enable}" />
|
||||||
</ui:SettingsCard>
|
</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:SettingsCard Margin="0 12 0 0" Header="{DynamicResource hideFlowLauncherWhenLoseFocus}">
|
||||||
<ui:ToggleSwitch
|
<ui:ToggleSwitch
|
||||||
IsOn="{Binding Settings.HideWhenDeactivated}"
|
IsOn="{Binding Settings.HideWhenDeactivated}"
|
||||||
|
|
|
||||||
|
|
@ -344,11 +344,7 @@ namespace Flow.Launcher.Plugin.Explorer
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Process.Start(new ProcessStartInfo()
|
Context.API.StartProcess(editorPath, arguments: record.FullPath);
|
||||||
{
|
|
||||||
FileName = editorPath,
|
|
||||||
ArgumentList = { record.FullPath }
|
|
||||||
});
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
|
@ -377,10 +373,7 @@ namespace Flow.Launcher.Plugin.Explorer
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Process.Start(new ProcessStartInfo()
|
Context.API.StartProcess(shellPath, workingDirectory: record.FullPath, arguments: string.Empty);
|
||||||
{
|
|
||||||
FileName = shellPath, WorkingDirectory = record.FullPath
|
|
||||||
});
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
|
@ -444,6 +437,7 @@ namespace Flow.Launcher.Plugin.Explorer
|
||||||
Arguments = "srchadmin.dll"
|
Arguments = "srchadmin.dll"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// No need to de-elevate since we are opening windows settings which cannot bring security risks
|
||||||
Process.Start(psi);
|
Process.Start(psi);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -468,6 +462,7 @@ namespace Flow.Launcher.Plugin.Explorer
|
||||||
SubTitle = Localize.plugin_explorer_openwith_subtitle(),
|
SubTitle = Localize.plugin_explorer_openwith_subtitle(),
|
||||||
Action = _ =>
|
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}");
|
Process.Start("rundll32.exe", $"{Path.Combine(Environment.SystemDirectory, "shell32.dll")},OpenAs_RunDLL {record.FullPath}");
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything
|
||||||
}
|
}
|
||||||
|
|
||||||
Settings.EverythingInstalledPath = installedPath;
|
Settings.EverythingInstalledPath = installedPath;
|
||||||
Process.Start(installedPath, "-startup");
|
Main.Context.API.StartProcess(installedPath, arguments: "-startup");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -506,6 +506,7 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels
|
||||||
Arguments = Constants.WindowsIndexingOptions
|
Arguments = Constants.WindowsIndexingOptions
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// No need to de-elevate since we are opening windows settings which cannot bring security risks
|
||||||
Process.Start(psi);
|
Process.Start(psi);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,19 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Security.Principal;
|
using System.Security.Principal;
|
||||||
|
using System.Threading.Channels;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Input;
|
||||||
using System.Windows.Media.Imaging;
|
using System.Windows.Media.Imaging;
|
||||||
using Windows.ApplicationModel;
|
using System.Xml;
|
||||||
using Windows.Management.Deployment;
|
|
||||||
using Flow.Launcher.Plugin.Program.Logger;
|
using Flow.Launcher.Plugin.Program.Logger;
|
||||||
using Flow.Launcher.Plugin.SharedModels;
|
using Flow.Launcher.Plugin.SharedModels;
|
||||||
using System.Threading.Channels;
|
|
||||||
using System.Xml;
|
|
||||||
using Windows.ApplicationModel.Core;
|
|
||||||
using System.Windows.Input;
|
|
||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
|
using Windows.ApplicationModel;
|
||||||
|
using Windows.ApplicationModel.Core;
|
||||||
|
using Windows.Management.Deployment;
|
||||||
|
|
||||||
namespace Flow.Launcher.Plugin.Program.Programs
|
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 elevated = e.SpecialKeyState.ToModifierKeys() == (ModifierKeys.Control | ModifierKeys.Shift);
|
||||||
|
|
||||||
bool shouldRunElevated = elevated && CanRunElevated;
|
bool shouldRunElevated = elevated && CanRunElevated;
|
||||||
_ = Task.Run(() => Launch(shouldRunElevated)).ConfigureAwait(false);
|
|
||||||
|
Launch(shouldRunElevated);
|
||||||
|
|
||||||
if (elevated && !shouldRunElevated)
|
if (elevated && !shouldRunElevated)
|
||||||
{
|
{
|
||||||
var title = api.GetTranslation("flowlauncher_plugin_program_disable_dlgtitle_error");
|
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"),
|
Title = api.GetTranslation("flowlauncher_plugin_program_run_as_administrator"),
|
||||||
Action = c =>
|
Action = c =>
|
||||||
{
|
{
|
||||||
_ = Task.Run(() => Launch(true)).ConfigureAwait(false);
|
Launch(true);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
IcoPath = "Images/cmd.png",
|
IcoPath = "Images/cmd.png",
|
||||||
|
|
@ -510,12 +512,14 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
||||||
|
|
||||||
private void Launch(bool elevated = false)
|
private void Launch(bool elevated = false)
|
||||||
{
|
{
|
||||||
string command = "shell:AppsFolder\\" + UserModelId;
|
var command = "shell:AppsFolder\\" + UserModelId;
|
||||||
command = Environment.ExpandEnvironmentVariables(command.Trim());
|
command = Environment.ExpandEnvironmentVariables(command.Trim());
|
||||||
|
|
||||||
var info = new ProcessStartInfo(command) { UseShellExecute = true, Verb = elevated ? "runas" : "" };
|
_ = Task.Run(() => Main.Context.API.StartProcess(
|
||||||
|
command,
|
||||||
Main.StartProcess(Process.Start, info);
|
arguments: string.Empty,
|
||||||
|
useShellExecute: true,
|
||||||
|
verb: elevated ? "runas" : ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool IfAppCanRunElevated(XmlNode appNode)
|
internal static bool IfAppCanRunElevated(XmlNode appNode)
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,21 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Security;
|
using System.Security;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading.Channels;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Win32;
|
using System.Windows.Input;
|
||||||
using Flow.Launcher.Plugin.Program.Logger;
|
using Flow.Launcher.Plugin.Program.Logger;
|
||||||
|
using Flow.Launcher.Plugin.Program.Views.Models;
|
||||||
using Flow.Launcher.Plugin.SharedCommands;
|
using Flow.Launcher.Plugin.SharedCommands;
|
||||||
using Flow.Launcher.Plugin.SharedModels;
|
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 IniParser;
|
||||||
using System.Windows.Input;
|
|
||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
|
using Microsoft.Win32;
|
||||||
|
|
||||||
namespace Flow.Launcher.Plugin.Program.Programs
|
namespace Flow.Launcher.Plugin.Program.Programs
|
||||||
{
|
{
|
||||||
|
|
@ -196,15 +196,7 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
||||||
// Ctrl + Shift + Enter to run as admin
|
// Ctrl + Shift + Enter to run as admin
|
||||||
bool runAsAdmin = c.SpecialKeyState.ToModifierKeys() == (ModifierKeys.Control | ModifierKeys.Shift);
|
bool runAsAdmin = c.SpecialKeyState.ToModifierKeys() == (ModifierKeys.Control | ModifierKeys.Shift);
|
||||||
|
|
||||||
var info = new ProcessStartInfo
|
Launch(runAsAdmin);
|
||||||
{
|
|
||||||
FileName = FullPath,
|
|
||||||
WorkingDirectory = ParentDirectory,
|
|
||||||
UseShellExecute = true,
|
|
||||||
Verb = runAsAdmin ? "runas" : "",
|
|
||||||
};
|
|
||||||
|
|
||||||
_ = Task.Run(() => Main.StartProcess(Process.Start, info));
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -213,6 +205,15 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
||||||
return result;
|
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)
|
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"),
|
Title = api.GetTranslation("flowlauncher_plugin_program_run_as_administrator"),
|
||||||
Action = c =>
|
Action = c =>
|
||||||
{
|
{
|
||||||
var info = new ProcessStartInfo
|
Launch(true);
|
||||||
{
|
|
||||||
FileName = FullPath,
|
|
||||||
WorkingDirectory = ParentDirectory,
|
|
||||||
Verb = "runas",
|
|
||||||
UseShellExecute = true
|
|
||||||
};
|
|
||||||
|
|
||||||
_ = Task.Run(() => Main.StartProcess(Process.Start, info));
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ namespace Flow.Launcher.Plugin.Shell
|
||||||
!c.SpecialKeyState.AltPressed &&
|
!c.SpecialKeyState.AltPressed &&
|
||||||
!c.SpecialKeyState.WinPressed;
|
!c.SpecialKeyState.WinPressed;
|
||||||
|
|
||||||
Execute(Process.Start, PrepareProcessStartInfo(m, runAsAdministrator));
|
Execute(StartProcess, PrepareProcessStartInfo(m, runAsAdministrator));
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
CopyText = m
|
CopyText = m
|
||||||
|
|
@ -121,7 +121,7 @@ namespace Flow.Launcher.Plugin.Shell
|
||||||
!c.SpecialKeyState.AltPressed &&
|
!c.SpecialKeyState.AltPressed &&
|
||||||
!c.SpecialKeyState.WinPressed;
|
!c.SpecialKeyState.WinPressed;
|
||||||
|
|
||||||
Execute(Process.Start, PrepareProcessStartInfo(m.Key, runAsAdministrator));
|
Execute(StartProcess, PrepareProcessStartInfo(m.Key, runAsAdministrator));
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
CopyText = m.Key
|
CopyText = m.Key
|
||||||
|
|
@ -151,7 +151,7 @@ namespace Flow.Launcher.Plugin.Shell
|
||||||
!c.SpecialKeyState.AltPressed &&
|
!c.SpecialKeyState.AltPressed &&
|
||||||
!c.SpecialKeyState.WinPressed;
|
!c.SpecialKeyState.WinPressed;
|
||||||
|
|
||||||
Execute(Process.Start, PrepareProcessStartInfo(cmd, runAsAdministrator));
|
Execute(StartProcess, PrepareProcessStartInfo(cmd, runAsAdministrator));
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
CopyText = cmd
|
CopyText = cmd
|
||||||
|
|
@ -176,7 +176,7 @@ namespace Flow.Launcher.Plugin.Shell
|
||||||
!c.SpecialKeyState.AltPressed &&
|
!c.SpecialKeyState.AltPressed &&
|
||||||
!c.SpecialKeyState.WinPressed;
|
!c.SpecialKeyState.WinPressed;
|
||||||
|
|
||||||
Execute(Process.Start, PrepareProcessStartInfo(m.Key, runAsAdministrator));
|
Execute(StartProcess, PrepareProcessStartInfo(m.Key, runAsAdministrator));
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
CopyText = m.Key
|
CopyText = m.Key
|
||||||
|
|
@ -328,6 +328,17 @@ namespace Flow.Launcher.Plugin.Shell
|
||||||
return info;
|
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)
|
private void Execute(Func<ProcessStartInfo, Process> startProcess, ProcessStartInfo info)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
@ -464,7 +475,7 @@ namespace Flow.Launcher.Plugin.Shell
|
||||||
Title = Localize.flowlauncher_plugin_cmd_run_as_administrator(),
|
Title = Localize.flowlauncher_plugin_cmd_run_as_administrator(),
|
||||||
Action = c =>
|
Action = c =>
|
||||||
{
|
{
|
||||||
Execute(Process.Start, PrepareProcessStartInfo(selectedResult.Title, true));
|
Execute(StartProcess, PrepareProcessStartInfo(selectedResult.Title, true));
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
IcoPath = "Images/admin.png",
|
IcoPath = "Images/admin.png",
|
||||||
|
|
|
||||||
|
|
@ -219,6 +219,7 @@ namespace Flow.Launcher.Plugin.Sys
|
||||||
if (EnableShutdownPrivilege())
|
if (EnableShutdownPrivilege())
|
||||||
PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_SHUTDOWN | EXIT_WINDOWS_FLAGS.EWX_POWEROFF, REASON);
|
PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_SHUTDOWN | EXIT_WINDOWS_FLAGS.EWX_POWEROFF, REASON);
|
||||||
else
|
else
|
||||||
|
// No need to de-elevate since we already have message box asking for confirmation
|
||||||
Process.Start("shutdown", "/s /t 0");
|
Process.Start("shutdown", "/s /t 0");
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -244,6 +245,7 @@ namespace Flow.Launcher.Plugin.Sys
|
||||||
if (EnableShutdownPrivilege())
|
if (EnableShutdownPrivilege())
|
||||||
PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_REBOOT, REASON);
|
PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_REBOOT, REASON);
|
||||||
else
|
else
|
||||||
|
// No need to de-elevate since we already have message box asking for confirmation
|
||||||
Process.Start("shutdown", "/r /t 0");
|
Process.Start("shutdown", "/r /t 0");
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -271,6 +273,7 @@ namespace Flow.Launcher.Plugin.Sys
|
||||||
if (EnableShutdownPrivilege())
|
if (EnableShutdownPrivilege())
|
||||||
PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_REBOOT | EXIT_WINDOWS_FLAGS.EWX_BOOTOPTIONS, REASON);
|
PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_REBOOT | EXIT_WINDOWS_FLAGS.EWX_BOOTOPTIONS, REASON);
|
||||||
else
|
else
|
||||||
|
// No need to de-elevate since we already have message box asking for confirmation
|
||||||
Process.Start("shutdown", "/r /o /t 0");
|
Process.Start("shutdown", "/r /o /t 0");
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -335,6 +338,7 @@ namespace Flow.Launcher.Plugin.Sys
|
||||||
Glyph = new GlyphInfo (FontFamily:"/Resources/#Segoe Fluent Icons", Glyph:"\xe773"),
|
Glyph = new GlyphInfo (FontFamily:"/Resources/#Segoe Fluent Icons", Glyph:"\xe773"),
|
||||||
Action = c =>
|
Action = c =>
|
||||||
{
|
{
|
||||||
|
// No need to de-elevate since we are opening windows settings which cannot bring security risks
|
||||||
Process.Start("control.exe", "srchadmin.dll");
|
Process.Start("control.exe", "srchadmin.dll");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -368,6 +372,7 @@ namespace Flow.Launcher.Plugin.Sys
|
||||||
CopyText = recycleBinFolder,
|
CopyText = recycleBinFolder,
|
||||||
Action = c =>
|
Action = c =>
|
||||||
{
|
{
|
||||||
|
// No need to de-elevate since we are opening windows settings which cannot bring security risks
|
||||||
Process.Start("explorer", recycleBinFolder);
|
Process.Start("explorer", recycleBinFolder);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -198,6 +198,7 @@ namespace Flow.Launcher.Plugin.WindowsSettings.Helper
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// No need to de-elevate since we are opening windows settings which cannot bring security risks
|
||||||
Process.Start(processStartInfo);
|
Process.Start(processStartInfo);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -207,6 +208,7 @@ namespace Flow.Launcher.Plugin.WindowsSettings.Helper
|
||||||
{
|
{
|
||||||
processStartInfo.UseShellExecute = true;
|
processStartInfo.UseShellExecute = true;
|
||||||
processStartInfo.Verb = "runas";
|
processStartInfo.Verb = "runas";
|
||||||
|
// No need to de-elevate since we are opening windows settings which cannot bring security risks
|
||||||
Process.Start(processStartInfo);
|
Process.Start(processStartInfo);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue