mirror of
https://github.com/BeyondDimension/SteamTools.git
synced 2026-10-29 07:27:36 +08:00
✨ Impl -clt plugins -l {AppServicesLevel} -m {插件名} -n {PipeName} -p {ProcessId} -a {插件需要解析的参数}
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using dotnetCampus.Ipc.Pipes;
|
||||
using ExitCode = BD.WTTS.Startup.CommandExitCode;
|
||||
|
||||
namespace BD.WTTS.Services;
|
||||
|
||||
@@ -24,11 +25,6 @@ public interface IPCSubProcessService : IDisposable
|
||||
/// <returns></returns>
|
||||
T? GetService<T>() where T : class;
|
||||
|
||||
const int ExitCode_EmptyArrayArgs = 4001;
|
||||
const int ExitCode_EmptyPipeName = 4002;
|
||||
const int ExitCode_EmptyMainProcessId = 4003;
|
||||
const int ExitCode_NotFoundMainProcessId = 4004;
|
||||
|
||||
private static IPCSubProcessServiceImpl? iPCSubProcessServiceImpl;
|
||||
|
||||
static IPCSubProcessService Instance => iPCSubProcessServiceImpl.ThrowIsNull();
|
||||
@@ -37,8 +33,8 @@ public interface IPCSubProcessService : IDisposable
|
||||
/// 子进程 IPC 程序启动通用函数
|
||||
/// </summary>
|
||||
/// <param name="moduleName"></param>
|
||||
/// <param name="configureServices"></param>
|
||||
/// <param name="configureIpcProvider"></param>
|
||||
/// <param name="configureServices">配置子进程的依赖注入服务</param>
|
||||
/// <param name="configureIpcProvider">配置 IPC 服务</param>
|
||||
/// <param name="args"></param>
|
||||
/// <returns></returns>
|
||||
static async Task<int> MainAsync(
|
||||
@@ -48,15 +44,15 @@ public interface IPCSubProcessService : IDisposable
|
||||
params string[] args)
|
||||
{
|
||||
if (args.Length < 2)
|
||||
return ExitCode_EmptyArrayArgs;
|
||||
return (int)ExitCode.EmptyArrayArgs;
|
||||
var pipeName = args[0];
|
||||
if (string.IsNullOrWhiteSpace(pipeName))
|
||||
return ExitCode_EmptyPipeName;
|
||||
return (int)ExitCode.EmptyPipeName;
|
||||
if (!int.TryParse(args[1], out var pid))
|
||||
return ExitCode_EmptyMainProcessId;
|
||||
return (int)ExitCode.EmptyMainProcessId;
|
||||
var mainProcess = Process.GetProcessById(pid);
|
||||
if (mainProcess == null)
|
||||
return ExitCode_NotFoundMainProcessId;
|
||||
return (int)ExitCode.NotFoundMainProcessId;
|
||||
|
||||
TaskCompletionSource tcs = new();
|
||||
mainProcess.EnableRaisingEvents = true;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using dotnetCampus.Ipc.Pipes;
|
||||
|
||||
namespace BD.WTTS.Plugins.Abstractions;
|
||||
|
||||
/// <summary>
|
||||
@@ -69,4 +71,14 @@ public interface IPlugin : IExplicitHasValue
|
||||
void OnUnhandledException(Exception ex, string name, bool? isTerminating = null);
|
||||
|
||||
ValueTask OnExit();
|
||||
|
||||
/// <summary>
|
||||
/// 启动子进程 IPC 程序的 Main 函数
|
||||
/// </summary>
|
||||
/// <param name="moduleName"></param>
|
||||
/// <param name="pipeName"></param>
|
||||
/// <param name="processId"></param>
|
||||
/// <param name="encodedArgs"></param>
|
||||
/// <returns></returns>
|
||||
Task<int> RunSubProcessMainAsync(string moduleName, string pipeName, string processId, string encodedArgs);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using dotnetCampus.Ipc.Pipes;
|
||||
|
||||
namespace BD.WTTS.Plugins.Abstractions;
|
||||
|
||||
[DebuggerDisplay("{DebuggerDisplay,nq}")]
|
||||
@@ -68,4 +70,45 @@ public abstract class PluginBase<TPlugin> : IPlugin where TPlugin : PluginBase<T
|
||||
{
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将参数解析为字符串
|
||||
/// </summary>
|
||||
/// <param name="args"></param>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected static string DecodeArgs(string args)
|
||||
=> HttpUtility.UrlDecode(args);
|
||||
|
||||
/// <summary>
|
||||
/// 将参数解析为字符串数组
|
||||
/// </summary>
|
||||
/// <param name="args"></param>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected static string[] DecodeToArrayArgs(string args)
|
||||
=> HttpUtility.UrlDecode(args).Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
public virtual async Task<int> RunSubProcessMainAsync(string moduleName, string pipeName, string processId, string encodedArgs)
|
||||
{
|
||||
var subProcessBootConfiguration = GetSubProcessBootConfiguration(encodedArgs ?? string.Empty);
|
||||
if (subProcessBootConfiguration == default)
|
||||
return (int)Startup.CommandExitCode.GetSubProcessBootConfigurationFail;
|
||||
|
||||
var exitCode = await IPCSubProcessService.MainAsync(moduleName,
|
||||
subProcessBootConfiguration.configureServices,
|
||||
subProcessBootConfiguration.configureIpcProvider,
|
||||
new[] { pipeName, processId });
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取子进程 IPC 程序启动配置
|
||||
/// </summary>
|
||||
/// <param name="args"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual (Action<IServiceCollection>? configureServices, Action<IpcProvider>? configureIpcProvider) GetSubProcessBootConfiguration(string args)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,20 +7,28 @@ public static class PluginsCore
|
||||
|
||||
static readonly HashSet<string> ignoreDirNames = new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
"Update",
|
||||
Update,
|
||||
};
|
||||
|
||||
internal static HashSet<Assembly>? LoadAssemblies()
|
||||
public const string Accelerator = "Accelerator";
|
||||
public const string GameAccount = "GameAccount";
|
||||
public const string GameList = "GameList";
|
||||
public const string ArchiSteamFarmPlus = "ArchiSteamFarmPlus";
|
||||
public const string Authenticator = "Authenticator";
|
||||
public const string GameTools = "GameTools";
|
||||
public const string Update = "Update";
|
||||
|
||||
internal static HashSet<Assembly>? LoadAssemblies(params string[] loadModules)
|
||||
{
|
||||
HashSet<Assembly>? assemblies = null;
|
||||
#if DEBUG // DEBUG 模式遍历项目查找模块
|
||||
var modules = new[] {
|
||||
"Accelerator",
|
||||
"GameAccount",
|
||||
"GameList",
|
||||
"ArchiSteamFarmPlus",
|
||||
"Authenticator",
|
||||
"GameTools",
|
||||
var modules = loadModules.Any_Nullable() ? loadModules : new[] {
|
||||
Accelerator,
|
||||
GameAccount,
|
||||
GameList,
|
||||
ArchiSteamFarmPlus,
|
||||
Authenticator,
|
||||
GameTools,
|
||||
};
|
||||
foreach (var item in modules)
|
||||
{
|
||||
@@ -70,12 +78,12 @@ public static class PluginsCore
|
||||
var pluginsPath = Path.Combine(AppContext.BaseDirectory, "modules");
|
||||
if (Directory.Exists(pluginsPath))
|
||||
{
|
||||
var directories = Directory.EnumerateDirectories(pluginsPath);
|
||||
foreach (var directory in directories)
|
||||
bool EachDirectories(string directory, string? dirName = null)
|
||||
{
|
||||
var dirName = Path.GetDirectoryName(directory);
|
||||
if (dirName == null) continue;
|
||||
if (ignoreDirNames.Contains(dirName)) continue;
|
||||
dirName ??= Path.GetDirectoryName(directory);
|
||||
if (dirName == null) return true;
|
||||
if (ignoreDirNames.Contains(dirName))
|
||||
return true;
|
||||
var dllPath = Path.Combine(directory,
|
||||
$"Steam++.Plugins.{dirName}.dll");
|
||||
if (File.Exists(dllPath))
|
||||
@@ -90,13 +98,35 @@ public static class PluginsCore
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(TAG, e, $"AssemblyLoadFrom fail, assemblyPath: {assemblyPath}");
|
||||
continue;
|
||||
return true;
|
||||
}
|
||||
|
||||
assemblies ??= new();
|
||||
assemblies.Add(assembly);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (loadModules.Any_Nullable())
|
||||
{
|
||||
foreach (var loadModule in loadModules)
|
||||
{
|
||||
var directory = Path.Combine(pluginsPath, loadModule);
|
||||
if (!Directory.Exists(directory))
|
||||
continue;
|
||||
if (EachDirectories(directory, loadModule))
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var directories = Directory.EnumerateDirectories(pluginsPath);
|
||||
foreach (var directory in directories)
|
||||
{
|
||||
if (EachDirectories(directory))
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -122,9 +152,9 @@ public static class PluginsCore
|
||||
}
|
||||
}
|
||||
|
||||
internal static HashSet<IPlugin>? InitPlugins()
|
||||
internal static HashSet<IPlugin>? InitPlugins(params string[] loadModules)
|
||||
{
|
||||
HashSet<Assembly>? assemblies = LoadAssemblies();
|
||||
HashSet<Assembly>? assemblies = LoadAssemblies(loadModules);
|
||||
if (!assemblies.Any_Nullable()) return null;
|
||||
|
||||
var assemblies_ = VerifyAssemblies(assemblies).ToArray();
|
||||
|
||||
@@ -178,24 +178,21 @@ public sealed partial class IPCMainProcessServiceImpl : IPCMainProcessService
|
||||
|
||||
#if WINDOWS
|
||||
// 启动管理员权限服务进程
|
||||
if (IPlatformService.Instance is WindowsPlatformServiceImpl windowsPlatformService)
|
||||
Task2.InBackground(async () =>
|
||||
{
|
||||
Task2.InBackground(async () =>
|
||||
var processPath = Environment.ProcessPath;
|
||||
processPath.ThrowIsNull();
|
||||
var pipeName = ipcProvider.ThrowIsNull().IpcContext.PipeName;
|
||||
const string arguments_ =
|
||||
$"-clt {IPlatformService.IPCRoot.CommandName} {IPlatformService.IPCRoot.args_PipeName} {{0}} {IPlatformService.IPCRoot.args_ProcessId} {{1}}";
|
||||
var arguments = string.Format(arguments_, pipeName, pid);
|
||||
await AddDaemonWithStartSubProcessAsync(IPlatformService.IPCRoot.moduleName, async _ =>
|
||||
{
|
||||
var processPath = Environment.ProcessPath;
|
||||
processPath.ThrowIsNull();
|
||||
var pipeName = ipcProvider.ThrowIsNull().IpcContext.PipeName;
|
||||
const string arguments_ =
|
||||
$"-clt {IPlatformService.IPCRoot.CommandName} {IPlatformService.IPCRoot.args_PipeName} {{0}} {IPlatformService.IPCRoot.args_ProcessId} {{1}}";
|
||||
var arguments = string.Format(arguments_, pipeName, pid);
|
||||
await AddDaemonWithStartSubProcessAsync(IPlatformService.IPCRoot.moduleName, async _ =>
|
||||
{
|
||||
var process = await windowsPlatformService.StartAsAdministrator(processPath, arguments);
|
||||
return process;
|
||||
});
|
||||
await IPlatformService.IPCRoot.SetIPC(this);
|
||||
var process = await WindowsPlatformServiceImpl.StartAsAdministrator(processPath, arguments);
|
||||
return process;
|
||||
});
|
||||
}
|
||||
await IPlatformService.IPCRoot.SetIPC(this);
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
+2
-3
@@ -13,14 +13,13 @@ partial class ScheduledTaskServiceImpl
|
||||
/// 使用 PowerShell 实现的开机启动
|
||||
/// <para>https://docs.microsoft.com/en-us/powershell/module/scheduledtasks</para>
|
||||
/// </summary>
|
||||
/// <param name="platformService"></param>
|
||||
/// <param name="isAutoStart"></param>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="userName"></param>
|
||||
/// <param name="tdName"></param>
|
||||
/// <param name="programName"></param>
|
||||
static async void SetBootAutoStartByPowerShell(IPlatformService platformService, bool isAutoStart, string name, string userId, string userName, string tdName, string programName)
|
||||
static async void SetBootAutoStartByPowerShell(bool isAutoStart, string name, string userId, string userName, string tdName, string programName)
|
||||
{
|
||||
if (isAutoStart)
|
||||
{
|
||||
@@ -32,7 +31,7 @@ partial class ScheduledTaskServiceImpl
|
||||
if (string.IsNullOrWhiteSpace(userName)) userName = userId;
|
||||
else userName = SecurityElement.Escape(userName);
|
||||
var description = SecurityElement.Escape(GetDescription(name));
|
||||
var xml = $"<?xml version=\"1.0\" encoding=\"UTF-16\"?><Task version=\"1.2\" xmlns=\"http://schemas.microsoft.com/windows/2004/02/mit/task\"><RegistrationInfo><Description>{description}</Description></RegistrationInfo><Triggers><LogonTrigger><Enabled>true</Enabled><UserId>{userName}</UserId></LogonTrigger></Triggers><Principals><Principal id=\"Author\"><UserId>{userId}</UserId><LogonType>InteractiveToken</LogonType><RunLevel>{(platformService.IsAdministrator ? "HighestAvailable" : "LeastPrivilege")}</RunLevel></Principal></Principals><Settings><MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy><DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries><StopIfGoingOnBatteries>false</StopIfGoingOnBatteries><AllowHardTerminate>false</AllowHardTerminate><StartWhenAvailable>false</StartWhenAvailable><RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable><IdleSettings><Duration>PT10M</Duration><WaitTimeout>PT1H</WaitTimeout><StopOnIdleEnd>true</StopOnIdleEnd><RestartOnIdle>false</RestartOnIdle></IdleSettings><AllowStartOnDemand>true</AllowStartOnDemand><Enabled>true</Enabled><Hidden>false</Hidden><RunOnlyIfIdle>false</RunOnlyIfIdle><WakeToRun>false</WakeToRun><ExecutionTimeLimit>PT0S</ExecutionTimeLimit><Priority>5</Priority></Settings><Actions Context=\"Author\"><Exec><Command>{programName}</Command><Arguments>{arguments}</Arguments><WorkingDirectory>{workingDirectory}</WorkingDirectory></Exec></Actions></Task>";
|
||||
var xml = $"<?xml version=\"1.0\" encoding=\"UTF-16\"?><Task version=\"1.2\" xmlns=\"http://schemas.microsoft.com/windows/2004/02/mit/task\"><RegistrationInfo><Description>{description}</Description></RegistrationInfo><Triggers><LogonTrigger><Enabled>true</Enabled><UserId>{userName}</UserId></LogonTrigger></Triggers><Principals><Principal id=\"Author\"><UserId>{userId}</UserId><LogonType>InteractiveToken</LogonType><RunLevel>{(IPlatformService._IsAdministrator.Value ? "HighestAvailable" : "LeastPrivilege")}</RunLevel></Principal></Principals><Settings><MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy><DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries><StopIfGoingOnBatteries>false</StopIfGoingOnBatteries><AllowHardTerminate>false</AllowHardTerminate><StartWhenAvailable>false</StartWhenAvailable><RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable><IdleSettings><Duration>PT10M</Duration><WaitTimeout>PT1H</WaitTimeout><StopOnIdleEnd>true</StopOnIdleEnd><RestartOnIdle>false</RestartOnIdle></IdleSettings><AllowStartOnDemand>true</AllowStartOnDemand><Enabled>true</Enabled><Hidden>false</Hidden><RunOnlyIfIdle>false</RunOnlyIfIdle><WakeToRun>false</WakeToRun><ExecutionTimeLimit>PT0S</ExecutionTimeLimit><Priority>5</Priority></Settings><Actions Context=\"Author\"><Exec><Command>{programName}</Command><Arguments>{arguments}</Arguments><WorkingDirectory>{workingDirectory}</WorkingDirectory></Exec></Actions></Task>";
|
||||
await RunPowerShellSinglePipeAsync($"Register-ScheduledTask -Force -TaskName '{Escape(tdName)}' -Xml '{Escape(xml)}';exit");
|
||||
}
|
||||
else
|
||||
|
||||
+2
-2
@@ -6,7 +6,7 @@ namespace BD.WTTS.Services.Implementation;
|
||||
|
||||
sealed partial class ScheduledTaskServiceImpl : IScheduledTaskService
|
||||
{
|
||||
void IScheduledTaskService.SetBootAutoStart(IPlatformService platformService, bool isAutoStart, string name)
|
||||
void IScheduledTaskService.SetBootAutoStart(bool isAutoStart, string name)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -16,7 +16,7 @@ sealed partial class ScheduledTaskServiceImpl : IScheduledTaskService
|
||||
var userId = hasSid ? identity.User!.ToString() : userName;
|
||||
var tdName = hasSid ? userId : userId.Replace(Path.DirectorySeparatorChar, '_');
|
||||
tdName = $"{name}_{{{tdName}}}";
|
||||
SetBootAutoStartByPowerShell(platformService, isAutoStart, name, userId, userName, tdName, IApplication.ProgramName);
|
||||
SetBootAutoStartByPowerShell(isAutoStart, name, userId, userName, tdName, IApplication.ProgramName);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
+19
-9
@@ -44,7 +44,7 @@ partial class WindowsPlatformServiceImpl
|
||||
/// <param name="arguments"></param>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static async Task<Process?> StartAsAdministratorByBypassUAC(string fileName, string? arguments = null)
|
||||
internal static async Task<Process?> StartAsAdministratorByBypassUAC(string fileName, string? arguments = null)
|
||||
{
|
||||
/** 33. Author: winscripting.blog
|
||||
* https://github.com/hfiref0x/UACME
|
||||
@@ -68,6 +68,15 @@ partial class WindowsPlatformServiceImpl
|
||||
if (!File.Exists(targetName))
|
||||
return null;
|
||||
|
||||
var processPath = Environment.ProcessPath;
|
||||
processPath.ThrowIsNull();
|
||||
if (fileName != processPath) // 仅允许启动自己
|
||||
throw new ArgumentOutOfRangeException(nameof(fileName));
|
||||
|
||||
var processName = Path.GetFileNameWithoutExtension(fileName);
|
||||
if (string.IsNullOrWhiteSpace(processName))
|
||||
throw new ArgumentNullException(nameof(processName));
|
||||
|
||||
classes = Registry.CurrentUser.OpenSubKey(@"Software\Classes", true);
|
||||
using var command = classes!.CreateSubKey(@"ms-settings\Shell\Open\command", true);
|
||||
command.SetValue("DelegateExecute", "");
|
||||
@@ -80,19 +89,21 @@ partial class WindowsPlatformServiceImpl
|
||||
WindowStyle = ProcessWindowStyle.Hidden,
|
||||
FileName = targetName,
|
||||
};
|
||||
var processs_start_before = new HashSet<int>(
|
||||
Process.GetProcessesByName(processName).Select(x => x.Id));
|
||||
var process = Process.Start(psi);
|
||||
process!.WaitForExit(6000);
|
||||
|
||||
var processName = Path.GetFileNameWithoutExtension(fileName);
|
||||
for (int i = 0; i < 7; i++)
|
||||
{
|
||||
var processs = Process.GetProcessesByName(processName);
|
||||
foreach (var item in processs)
|
||||
var processs_start_after = Process.GetProcessesByName(processName);
|
||||
foreach (var item in processs_start_after)
|
||||
{
|
||||
if (processs_start_before.Contains(item.Id))
|
||||
continue;
|
||||
|
||||
if (IsProcessElevated(item))
|
||||
{
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
await Task.Delay(800);
|
||||
@@ -128,10 +139,9 @@ partial class WindowsPlatformServiceImpl
|
||||
/// <param name="fileName"></param>
|
||||
/// <param name="arguments"></param>
|
||||
/// <returns></returns>
|
||||
public async ValueTask<Process?> StartAsAdministrator(string fileName, string? arguments = null)
|
||||
internal static async ValueTask<Process?> StartAsAdministrator(string fileName, string? arguments = null)
|
||||
{
|
||||
IPlatformService thiz = this;
|
||||
if (thiz.IsAdministrator)
|
||||
if (IPlatformService._IsAdministrator.Value)
|
||||
return Process2.Start(fileName, arguments);
|
||||
|
||||
var process = await StartAsAdministratorByBypassUAC(fileName, arguments);
|
||||
|
||||
+1
-1
@@ -51,7 +51,7 @@ partial class WindowsPlatformServiceImpl
|
||||
{
|
||||
var scheduledTaskService = IScheduledTaskService.Instance;
|
||||
scheduledTaskService.ThrowIsNull();
|
||||
scheduledTaskService.SetBootAutoStart(this, isAutoStart, name);
|
||||
scheduledTaskService.SetBootAutoStart(isAutoStart, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -122,8 +122,7 @@ sealed partial class WindowsPlatformServiceImpl : IPlatformService
|
||||
|
||||
public Process? StartAsInvoker(string fileName, string? arguments = null)
|
||||
{
|
||||
IPlatformService thiz = this;
|
||||
if (thiz.IsAdministrator)
|
||||
if (IPlatformService._IsAdministrator.Value)
|
||||
{
|
||||
// runas /trustlevel:0x20000 没有真正的降权,只是作为具有限制特权,使用 explorer 最好,但不接受参数,可以创建一个临时cmd脚本启动
|
||||
//return StartAsInvokerByRunas(fileName, arguments);
|
||||
|
||||
@@ -3,21 +3,21 @@ namespace BD.WTTS.Services;
|
||||
|
||||
partial interface IPlatformService
|
||||
{
|
||||
internal static readonly Lazy<bool> _IsAdministrator = new(() =>
|
||||
{
|
||||
#if WINDOWS
|
||||
using WindowsIdentity identity = WindowsIdentity.GetCurrent();
|
||||
return identity.IsSystem || new WindowsPrincipal(identity)
|
||||
.IsInRole(WindowsBuiltInRole.Administrator);
|
||||
#elif MACCATALYST || MACOS || LINUX || IOS || ANDROID
|
||||
return Interop.Libc.GetEUID() == 0;
|
||||
#else
|
||||
throw new PlatformNotSupportedException();
|
||||
#endif
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// 当前程序是否以 Administrator/System(Windows) 或 Root(FreeBSD/Linux/MacOS/Android/iOS) 权限运行
|
||||
/// </summary>
|
||||
bool IsAdministrator
|
||||
{
|
||||
get
|
||||
{
|
||||
#if WINDOWS
|
||||
using WindowsIdentity identity = WindowsIdentity.GetCurrent();
|
||||
return identity.IsSystem || new WindowsPrincipal(identity)
|
||||
.IsInRole(WindowsBuiltInRole.Administrator);
|
||||
#elif MACCATALYST || MACOS || LINUX || IOS || ANDROID
|
||||
return Interop.Libc.GetEUID() == 0;
|
||||
throw new PlatformNotSupportedException();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
bool IsAdministrator => _IsAdministrator.Value;
|
||||
}
|
||||
@@ -12,9 +12,8 @@ public interface IScheduledTaskService
|
||||
/// <summary>
|
||||
/// 开机启动使用 taskschd.msc 实现
|
||||
/// </summary>
|
||||
/// <param name="platformService"></param>
|
||||
/// <param name="isAutoStart"></param>
|
||||
/// <param name="name"></param>
|
||||
void SetBootAutoStart(IPlatformService platformService, bool isAutoStart, string name);
|
||||
void SetBootAutoStart(bool isAutoStart, string name);
|
||||
}
|
||||
#endif
|
||||
@@ -303,6 +303,13 @@ partial class Startup // 自定义控制台命令参数
|
||||
{
|
||||
Handler = CommandHandler.Create(async (string n, string p) =>
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(n) ||
|
||||
string.IsNullOrWhiteSpace(p))
|
||||
return 400;
|
||||
|
||||
if (!IPlatformService._IsAdministrator.Value)
|
||||
return 401;
|
||||
|
||||
RunUIApplication(AppServicesLevel.IPCRoot | AppServicesLevel.Hosts);
|
||||
await WaitConfiguredServices;
|
||||
|
||||
@@ -329,14 +336,35 @@ partial class Startup // 自定义控制台命令参数
|
||||
rootCommand.AddCommand(sudo);
|
||||
#endif
|
||||
|
||||
// -clt plugins {插件名} {插件需要解析的参数}
|
||||
// -clt plugins -l {AppServicesLevel} -m {插件名} -n {PipeName} -p {ProcessId} -a {插件需要解析的参数}
|
||||
var plugins = new Command("plugins", "插件使用的 IPC 服务进程")
|
||||
{
|
||||
Handler = CommandHandler.Create(() =>
|
||||
Handler = CommandHandler.Create(async (uint l, string m, string n, string p, string a) =>
|
||||
{
|
||||
// TODO
|
||||
if (string.IsNullOrWhiteSpace(n) ||
|
||||
string.IsNullOrWhiteSpace(p))
|
||||
return (int)CommandExitCode.HttpStatusBadRequest;
|
||||
|
||||
var level = (AppServicesLevel)l;
|
||||
RunUIApplication(level, loadModules: m);
|
||||
await WaitConfiguredServices;
|
||||
|
||||
if (!TryGetPlugins(out var plugins))
|
||||
return (int)CommandExitCode.GetPluginsFail;
|
||||
|
||||
var plugin = plugins.FirstOrDefault(x => x.Name == m);
|
||||
if (plugin == null)
|
||||
return (int)CommandExitCode.GetPluginFail;
|
||||
|
||||
var exitCode = await plugin.RunSubProcessMainAsync(m, n, p, a);
|
||||
return exitCode;
|
||||
}),
|
||||
};
|
||||
plugins.AddOption(new Option<uint>("-l", "AppServicesLevel"));
|
||||
plugins.AddOption(new Option<string>("-m", "需要加载的模块名称"));
|
||||
plugins.AddOption(new Option<string>("-a", "需要解析的参数,使用 HttpUtility.UrlDecode 解码"));
|
||||
plugins.AddOption(new Option<string>(IPlatformService.IPCRoot.args_PipeName, "IPC 管道名"));
|
||||
plugins.AddOption(new Option<string>(IPlatformService.IPCRoot.args_ProcessId, "主进程 Id"));
|
||||
rootCommand.AddCommand(plugins);
|
||||
|
||||
// -clt types
|
||||
@@ -345,8 +373,16 @@ partial class Startup // 自定义控制台命令参数
|
||||
Handler = CommandHandler.Create(() =>
|
||||
{
|
||||
int exitCode = default;
|
||||
using var assemblies_stream = new FileStream(Path.Combine(IOPath.CacheDirectory, "assemblies.txt"), FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite | FileShare.Delete);
|
||||
using var types_stream = new FileStream(Path.Combine(IOPath.CacheDirectory, "types.txt"), FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite | FileShare.Delete);
|
||||
using var assemblies_stream = new FileStream(Path.Combine(
|
||||
IOPath.CacheDirectory, "assemblies.txt"),
|
||||
FileMode.OpenOrCreate,
|
||||
FileAccess.Write,
|
||||
FileShare.ReadWrite | FileShare.Delete);
|
||||
using var types_stream = new FileStream(Path.Combine(
|
||||
IOPath.CacheDirectory, "types.txt"),
|
||||
FileMode.OpenOrCreate,
|
||||
FileAccess.Write,
|
||||
FileShare.ReadWrite | FileShare.Delete);
|
||||
try
|
||||
{
|
||||
var assemblies_ = AppDomain.CurrentDomain.GetAssemblies();
|
||||
@@ -386,12 +422,12 @@ partial class Startup // 自定义控制台命令参数
|
||||
}
|
||||
}
|
||||
}
|
||||
return exitCode = 200;
|
||||
return exitCode = (int)CommandExitCode.HttpStatusCodeOk;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex);
|
||||
return exitCode = 500;
|
||||
return exitCode = (int)CommandExitCode.HttpStatusCodeInternalServerError;
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -410,5 +446,60 @@ partial class Startup // 自定义控制台命令参数
|
||||
};
|
||||
rootCommand.AddCommand(types);
|
||||
}
|
||||
|
||||
public enum CommandExitCode
|
||||
{
|
||||
#region 100 ~ 5xx HttpStatusCode
|
||||
|
||||
/// <inheritdoc cref="HttpStatusCode.OK"/>
|
||||
HttpStatusCodeOk = 200,
|
||||
|
||||
/// <inheritdoc cref="HttpStatusCode.BadRequest"/>
|
||||
HttpStatusBadRequest = 400,
|
||||
|
||||
/// <inheritdoc cref="HttpStatusCode.InternalServerError"/>
|
||||
HttpStatusCodeInternalServerError = 500,
|
||||
|
||||
#endregion
|
||||
|
||||
#region xxxx 4 位数通用错误码
|
||||
|
||||
/// <summary>
|
||||
/// 参数不能为空数组
|
||||
/// </summary>
|
||||
EmptyArrayArgs = 4001,
|
||||
|
||||
/// <summary>
|
||||
/// IPC 管道名称不能为空
|
||||
/// </summary>
|
||||
EmptyPipeName = 4002,
|
||||
|
||||
/// <summary>
|
||||
/// 主进程 Id 不能为空
|
||||
/// </summary>
|
||||
EmptyMainProcessId = 4003,
|
||||
|
||||
/// <summary>
|
||||
/// 主进程 Id 找不到
|
||||
/// </summary>
|
||||
NotFoundMainProcessId = 4004,
|
||||
|
||||
/// <summary>
|
||||
/// 获取一组插件失败
|
||||
/// </summary>
|
||||
GetPluginsFail = 4040,
|
||||
|
||||
/// <summary>
|
||||
/// 获取单个插件失败
|
||||
/// </summary>
|
||||
GetPluginFail = 4041,
|
||||
|
||||
/// <summary>
|
||||
/// 获取子进程启动配置失败
|
||||
/// </summary>
|
||||
GetSubProcessBootConfigurationFail = 4042,
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -6,7 +6,10 @@ namespace BD.WTTS;
|
||||
partial class Startup // 配置 Host
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected void RunUIApplication(AppServicesLevel? level = null, Func<string>? sendMessage = null)
|
||||
protected void RunUIApplication(
|
||||
AppServicesLevel? level = null,
|
||||
Func<string>? sendMessage = null,
|
||||
params string[] loadModules)
|
||||
{
|
||||
#if STARTUP_WATCH_TRACE || DEBUG
|
||||
WatchTrace.Start();
|
||||
@@ -28,7 +31,7 @@ partial class Startup // 配置 Host
|
||||
#if (WINDOWS || MACCATALYST || MACOS || LINUX) && !(IOS || ANDROID)
|
||||
if (IsMainProcess)
|
||||
{
|
||||
plugins = PluginsCore.InitPlugins();
|
||||
plugins = PluginsCore.InitPlugins(loadModules);
|
||||
HasPlugins = plugins.Any_Nullable();
|
||||
#if STARTUP_WATCH_TRACE || DEBUG
|
||||
WatchTrace.Record("InitPlugins");
|
||||
|
||||
Reference in New Issue
Block a user