diff --git a/src/BD.WTTS.Client.IPC/Services/IPCSubProcessService.cs b/src/BD.WTTS.Client.IPC/Services/IPCSubProcessService.cs index ba9f0f60b..8f08309f6 100644 --- a/src/BD.WTTS.Client.IPC/Services/IPCSubProcessService.cs +++ b/src/BD.WTTS.Client.IPC/Services/IPCSubProcessService.cs @@ -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 /// T? GetService() 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 程序启动通用函数 /// /// - /// - /// + /// 配置子进程的依赖注入服务 + /// 配置 IPC 服务 /// /// static async Task 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; diff --git a/src/BD.WTTS.Client/Plugins/Abstractions/IPlugin.cs b/src/BD.WTTS.Client/Plugins/Abstractions/IPlugin.cs index 00fb9bd08..e169313f3 100644 --- a/src/BD.WTTS.Client/Plugins/Abstractions/IPlugin.cs +++ b/src/BD.WTTS.Client/Plugins/Abstractions/IPlugin.cs @@ -1,3 +1,5 @@ +using dotnetCampus.Ipc.Pipes; + namespace BD.WTTS.Plugins.Abstractions; /// @@ -69,4 +71,14 @@ public interface IPlugin : IExplicitHasValue void OnUnhandledException(Exception ex, string name, bool? isTerminating = null); ValueTask OnExit(); + + /// + /// 启动子进程 IPC 程序的 Main 函数 + /// + /// + /// + /// + /// + /// + Task RunSubProcessMainAsync(string moduleName, string pipeName, string processId, string encodedArgs); } diff --git a/src/BD.WTTS.Client/Plugins/Abstractions/PluginBase.cs b/src/BD.WTTS.Client/Plugins/Abstractions/PluginBase.cs index 94fa7151b..36d293256 100644 --- a/src/BD.WTTS.Client/Plugins/Abstractions/PluginBase.cs +++ b/src/BD.WTTS.Client/Plugins/Abstractions/PluginBase.cs @@ -1,3 +1,5 @@ +using dotnetCampus.Ipc.Pipes; + namespace BD.WTTS.Plugins.Abstractions; [DebuggerDisplay("{DebuggerDisplay,nq}")] @@ -68,4 +70,45 @@ public abstract class PluginBase : IPlugin where TPlugin : PluginBase + /// 将参数解析为字符串 + /// + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected static string DecodeArgs(string args) + => HttpUtility.UrlDecode(args); + + /// + /// 将参数解析为字符串数组 + /// + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected static string[] DecodeToArrayArgs(string args) + => HttpUtility.UrlDecode(args).Split(' ', StringSplitOptions.RemoveEmptyEntries); + + public virtual async Task 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; + } + + /// + /// 获取子进程 IPC 程序启动配置 + /// + /// + /// + protected virtual (Action? configureServices, Action? configureIpcProvider) GetSubProcessBootConfiguration(string args) + { + return default; + } } diff --git a/src/BD.WTTS.Client/Plugins/PluginsCore.cs b/src/BD.WTTS.Client/Plugins/PluginsCore.cs index fc04311fd..d4b05d400 100644 --- a/src/BD.WTTS.Client/Plugins/PluginsCore.cs +++ b/src/BD.WTTS.Client/Plugins/PluginsCore.cs @@ -7,20 +7,28 @@ public static class PluginsCore static readonly HashSet ignoreDirNames = new(StringComparer.OrdinalIgnoreCase) { - "Update", + Update, }; - internal static HashSet? 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? LoadAssemblies(params string[] loadModules) { HashSet? 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? InitPlugins() + internal static HashSet? InitPlugins(params string[] loadModules) { - HashSet? assemblies = LoadAssemblies(); + HashSet? assemblies = LoadAssemblies(loadModules); if (!assemblies.Any_Nullable()) return null; var assemblies_ = VerifyAssemblies(assemblies).ToArray(); diff --git a/src/BD.WTTS.Client/Services.Implementation/IPC/IPCMainProcessServiceImpl.cs b/src/BD.WTTS.Client/Services.Implementation/IPC/IPCMainProcessServiceImpl.cs index e1986bc41..226eb0c17 100644 --- a/src/BD.WTTS.Client/Services.Implementation/IPC/IPCMainProcessServiceImpl.cs +++ b/src/BD.WTTS.Client/Services.Implementation/IPC/IPCMainProcessServiceImpl.cs @@ -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 } diff --git a/src/BD.WTTS.Client/Services.Implementation/Platform/Windows/ScheduledTaskServiceImpl.PowerShell.cs b/src/BD.WTTS.Client/Services.Implementation/Platform/Windows/ScheduledTaskServiceImpl.PowerShell.cs index 69136eb94..68734910a 100644 --- a/src/BD.WTTS.Client/Services.Implementation/Platform/Windows/ScheduledTaskServiceImpl.PowerShell.cs +++ b/src/BD.WTTS.Client/Services.Implementation/Platform/Windows/ScheduledTaskServiceImpl.PowerShell.cs @@ -13,14 +13,13 @@ partial class ScheduledTaskServiceImpl /// 使用 PowerShell 实现的开机启动 /// https://docs.microsoft.com/en-us/powershell/module/scheduledtasks /// - /// /// /// /// /// /// /// - 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 = $"{description}true{userName}{userId}InteractiveToken{(platformService.IsAdministrator ? "HighestAvailable" : "LeastPrivilege")}IgnoreNewfalsefalsefalsefalsefalsePT10MPT1HtruefalsetruetruefalsefalsefalsePT0S5{programName}{arguments}{workingDirectory}"; + var xml = $"{description}true{userName}{userId}InteractiveToken{(IPlatformService._IsAdministrator.Value ? "HighestAvailable" : "LeastPrivilege")}IgnoreNewfalsefalsefalsefalsefalsePT10MPT1HtruefalsetruetruefalsefalsefalsePT0S5{programName}{arguments}{workingDirectory}"; await RunPowerShellSinglePipeAsync($"Register-ScheduledTask -Force -TaskName '{Escape(tdName)}' -Xml '{Escape(xml)}';exit"); } else diff --git a/src/BD.WTTS.Client/Services.Implementation/Platform/Windows/ScheduledTaskServiceImpl.cs b/src/BD.WTTS.Client/Services.Implementation/Platform/Windows/ScheduledTaskServiceImpl.cs index ceb9bbefa..569b6517e 100644 --- a/src/BD.WTTS.Client/Services.Implementation/Platform/Windows/ScheduledTaskServiceImpl.cs +++ b/src/BD.WTTS.Client/Services.Implementation/Platform/Windows/ScheduledTaskServiceImpl.cs @@ -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) { diff --git a/src/BD.WTTS.Client/Services.Implementation/Platform/Windows/WindowsPlatformServiceImpl.BypassUAC.cs b/src/BD.WTTS.Client/Services.Implementation/Platform/Windows/WindowsPlatformServiceImpl.BypassUAC.cs index 13cc53fd6..e2175f46e 100644 --- a/src/BD.WTTS.Client/Services.Implementation/Platform/Windows/WindowsPlatformServiceImpl.BypassUAC.cs +++ b/src/BD.WTTS.Client/Services.Implementation/Platform/Windows/WindowsPlatformServiceImpl.BypassUAC.cs @@ -44,7 +44,7 @@ partial class WindowsPlatformServiceImpl /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static async Task StartAsAdministratorByBypassUAC(string fileName, string? arguments = null) + internal static async Task 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( + 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 /// /// /// - public async ValueTask StartAsAdministrator(string fileName, string? arguments = null) + internal static async ValueTask 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); diff --git a/src/BD.WTTS.Client/Services.Implementation/Platform/Windows/WindowsPlatformServiceImpl.SystemOnOff.cs b/src/BD.WTTS.Client/Services.Implementation/Platform/Windows/WindowsPlatformServiceImpl.SystemOnOff.cs index 25bbef5c1..c02002d6e 100644 --- a/src/BD.WTTS.Client/Services.Implementation/Platform/Windows/WindowsPlatformServiceImpl.SystemOnOff.cs +++ b/src/BD.WTTS.Client/Services.Implementation/Platform/Windows/WindowsPlatformServiceImpl.SystemOnOff.cs @@ -51,7 +51,7 @@ partial class WindowsPlatformServiceImpl { var scheduledTaskService = IScheduledTaskService.Instance; scheduledTaskService.ThrowIsNull(); - scheduledTaskService.SetBootAutoStart(this, isAutoStart, name); + scheduledTaskService.SetBootAutoStart(isAutoStart, name); } } } diff --git a/src/BD.WTTS.Client/Services.Implementation/Platform/Windows/WindowsPlatformServiceImpl.cs b/src/BD.WTTS.Client/Services.Implementation/Platform/Windows/WindowsPlatformServiceImpl.cs index eb614a608..35f75da9c 100644 --- a/src/BD.WTTS.Client/Services.Implementation/Platform/Windows/WindowsPlatformServiceImpl.cs +++ b/src/BD.WTTS.Client/Services.Implementation/Platform/Windows/WindowsPlatformServiceImpl.cs @@ -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); diff --git a/src/BD.WTTS.Client/Services/Platform/IPlatformService.IsAdministrator.cs b/src/BD.WTTS.Client/Services/Platform/IPlatformService.IsAdministrator.cs index 01d247668..2cdc3ca22 100644 --- a/src/BD.WTTS.Client/Services/Platform/IPlatformService.IsAdministrator.cs +++ b/src/BD.WTTS.Client/Services/Platform/IPlatformService.IsAdministrator.cs @@ -3,21 +3,21 @@ namespace BD.WTTS.Services; partial interface IPlatformService { + internal static readonly Lazy _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 + }); + /// /// 当前程序是否以 Administrator/System(Windows) 或 Root(FreeBSD/Linux/MacOS/Android/iOS) 权限运行 /// - 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; } \ No newline at end of file diff --git a/src/BD.WTTS.Client/Services/Platform/Windows/IScheduledTaskService.cs b/src/BD.WTTS.Client/Services/Platform/Windows/IScheduledTaskService.cs index 6bbf24fb8..f0df6d0d7 100644 --- a/src/BD.WTTS.Client/Services/Platform/Windows/IScheduledTaskService.cs +++ b/src/BD.WTTS.Client/Services/Platform/Windows/IScheduledTaskService.cs @@ -12,9 +12,8 @@ public interface IScheduledTaskService /// /// 开机启动使用 taskschd.msc 实现 /// - /// /// /// - void SetBootAutoStart(IPlatformService platformService, bool isAutoStart, string name); + void SetBootAutoStart(bool isAutoStart, string name); } #endif \ No newline at end of file diff --git a/src/BD.WTTS.Client/Startup/Startup.Commands.cs b/src/BD.WTTS.Client/Startup/Startup.Commands.cs index a4eb6aa77..bf79bd445 100644 --- a/src/BD.WTTS.Client/Startup/Startup.Commands.cs +++ b/src/BD.WTTS.Client/Startup/Startup.Commands.cs @@ -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("-l", "AppServicesLevel")); + plugins.AddOption(new Option("-m", "需要加载的模块名称")); + plugins.AddOption(new Option("-a", "需要解析的参数,使用 HttpUtility.UrlDecode 解码")); + plugins.AddOption(new Option(IPlatformService.IPCRoot.args_PipeName, "IPC 管道名")); + plugins.AddOption(new Option(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 + + /// + HttpStatusCodeOk = 200, + + /// + HttpStatusBadRequest = 400, + + /// + HttpStatusCodeInternalServerError = 500, + + #endregion + + #region xxxx 4 位数通用错误码 + + /// + /// 参数不能为空数组 + /// + EmptyArrayArgs = 4001, + + /// + /// IPC 管道名称不能为空 + /// + EmptyPipeName = 4002, + + /// + /// 主进程 Id 不能为空 + /// + EmptyMainProcessId = 4003, + + /// + /// 主进程 Id 找不到 + /// + NotFoundMainProcessId = 4004, + + /// + /// 获取一组插件失败 + /// + GetPluginsFail = 4040, + + /// + /// 获取单个插件失败 + /// + GetPluginFail = 4041, + + /// + /// 获取子进程启动配置失败 + /// + GetSubProcessBootConfigurationFail = 4042, + + #endregion + } } #endif \ No newline at end of file diff --git a/src/BD.WTTS.Client/Startup/Startup.Host.cs b/src/BD.WTTS.Client/Startup/Startup.Host.cs index 093c670ab..cc2afad79 100644 --- a/src/BD.WTTS.Client/Startup/Startup.Host.cs +++ b/src/BD.WTTS.Client/Startup/Startup.Host.cs @@ -6,7 +6,10 @@ namespace BD.WTTS; partial class Startup // 配置 Host { [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected void RunUIApplication(AppServicesLevel? level = null, Func? sendMessage = null) + protected void RunUIApplication( + AppServicesLevel? level = null, + Func? 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");