✨ Impl -clt plugins -l {AppServicesLevel} -m {插件名} -n {PipeName} -p {ProcessId} -a {插件需要解析的参数}

This commit is contained in:
Aigio Liu
2023-05-29 12:09:46 +08:00
parent 6308a61d94
commit df84ad4271
14 changed files with 264 additions and 85 deletions
@@ -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;
}
}
+47 -17
View File
@@ -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
}
@@ -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
@@ -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)
{
@@ -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);
@@ -51,7 +51,7 @@ partial class WindowsPlatformServiceImpl
{
var scheduledTaskService = IScheduledTaskService.Instance;
scheduledTaskService.ThrowIsNull();
scheduledTaskService.SetBootAutoStart(this, isAutoStart, name);
scheduledTaskService.SetBootAutoStart(isAutoStart, name);
}
}
}
@@ -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
+98 -7
View File
@@ -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
+5 -2
View File
@@ -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");