mirror of
https://github.com/wwiinnddyy/LanMountainDesktop.git
synced 2026-06-24 10:34:26 +08:00
fix.我们试验性地修复了启动器无法正常启动的问题,原因可能是这个画面没有启动,就GUI没显示。然后还把编译问题修了一下。
This commit is contained in:
@@ -9,6 +9,15 @@ namespace LanMountainDesktop.Launcher.Services;
|
|||||||
|
|
||||||
internal sealed class LauncherFlowCoordinator
|
internal sealed class LauncherFlowCoordinator
|
||||||
{
|
{
|
||||||
|
private static readonly string[] LauncherOnlyOptions =
|
||||||
|
[
|
||||||
|
"debug", "show-loading-details", "plugins-dir", "source", "result",
|
||||||
|
LauncherIpcConstants.LauncherPidEnvVar,
|
||||||
|
LauncherIpcConstants.PackageRootEnvVar,
|
||||||
|
LauncherIpcConstants.VersionEnvVar,
|
||||||
|
LauncherIpcConstants.CodenameEnvVar
|
||||||
|
];
|
||||||
|
|
||||||
private readonly CommandContext _context;
|
private readonly CommandContext _context;
|
||||||
private readonly DeploymentLocator _deploymentLocator;
|
private readonly DeploymentLocator _deploymentLocator;
|
||||||
private readonly OobeStateService _oobeStateService;
|
private readonly OobeStateService _oobeStateService;
|
||||||
@@ -167,11 +176,11 @@ internal sealed class LauncherFlowCoordinator
|
|||||||
var processExitTask = hostProcess.WaitForExitAsync();
|
var processExitTask = hostProcess.WaitForExitAsync();
|
||||||
|
|
||||||
// 等待主程序就绪或进程退出(取先发生者)
|
// 等待主程序就绪或进程退出(取先发生者)
|
||||||
// 延长超时到 120 秒,给主程序足够的加载时间
|
// 30 秒超时,宿主端有 10 秒兜底机制确保 Ready 信号发送
|
||||||
var readyOrTimeoutOrExit = Task.WhenAny(
|
var readyOrTimeoutOrExit = Task.WhenAny(
|
||||||
hostReadyTcs.Task,
|
hostReadyTcs.Task,
|
||||||
processExitTask,
|
processExitTask,
|
||||||
Task.Delay(TimeSpan.FromSeconds(120)));
|
Task.Delay(TimeSpan.FromSeconds(30)));
|
||||||
|
|
||||||
var completedTask = await readyOrTimeoutOrExit;
|
var completedTask = await readyOrTimeoutOrExit;
|
||||||
|
|
||||||
@@ -315,32 +324,55 @@ internal sealed class LauncherFlowCoordinator
|
|||||||
EnsureExecutable(hostPath);
|
EnsureExecutable(hostPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var hostWorkingDir = Path.GetDirectoryName(hostPath) ?? _deploymentLocator.GetAppRoot();
|
||||||
|
var versionInfo = _deploymentLocator.GetVersionInfo();
|
||||||
|
|
||||||
|
// 构建命令行参数:转发用户参数 + IPC 环境信息通过命令行传递
|
||||||
|
// UseShellExecute = true 确保 Shell 启动子进程,使其正确关联到交互式桌面窗口站(WinSta0),
|
||||||
|
// 避免子进程窗口创建成功但不可见的问题。
|
||||||
|
var arguments = new System.Text.StringBuilder();
|
||||||
|
|
||||||
|
// 转发命令行参数给主程序(排除 Launcher 自己的命令和选项)
|
||||||
|
// 只过滤 Launcher 专属的选项,保留宿主程序需要的参数(如 --restart-parent-pid)
|
||||||
|
foreach (var arg in _context.RawArgs)
|
||||||
|
{
|
||||||
|
if (arg == _context.Command || arg == _context.SubCommand)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (arg.StartsWith("--"))
|
||||||
|
{
|
||||||
|
var key = arg[2..];
|
||||||
|
var equalsIndex = key.IndexOf('=');
|
||||||
|
if (equalsIndex >= 0) key = key[..equalsIndex];
|
||||||
|
|
||||||
|
if (LauncherOnlyOptions.Contains(key, StringComparer.OrdinalIgnoreCase))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments.Length > 0) arguments.Append(' ');
|
||||||
|
arguments.Append(QuoteArgument(arg));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通过命令行参数传递 IPC 连接信息(UseShellExecute=true 时不支持 EnvironmentVariables)
|
||||||
|
if (arguments.Length > 0) arguments.Append(' ');
|
||||||
|
arguments.Append($"--{LauncherIpcConstants.LauncherPidEnvVar}={Environment.ProcessId}");
|
||||||
|
arguments.Append($" --{LauncherIpcConstants.PackageRootEnvVar}={QuoteArgument(_deploymentLocator.GetAppRoot())}");
|
||||||
|
arguments.Append($" --{LauncherIpcConstants.VersionEnvVar}={versionInfo.Version}");
|
||||||
|
arguments.Append($" --{LauncherIpcConstants.CodenameEnvVar}={versionInfo.Codename}");
|
||||||
|
|
||||||
var processStartInfo = new ProcessStartInfo
|
var processStartInfo = new ProcessStartInfo
|
||||||
{
|
{
|
||||||
FileName = hostPath,
|
FileName = hostPath,
|
||||||
UseShellExecute = false,
|
UseShellExecute = true,
|
||||||
WorkingDirectory = Path.GetDirectoryName(hostPath) ?? _deploymentLocator.GetAppRoot()
|
WorkingDirectory = hostWorkingDir,
|
||||||
|
Arguments = arguments.ToString()
|
||||||
};
|
};
|
||||||
|
|
||||||
// 转发命令行参数给主程序(排除 Launcher 自己的命令和选项)
|
// 同时设置环境变量作为备选(当 UseShellExecute=true 时 EnvironmentVariables 仍会被子进程继承)
|
||||||
foreach (var arg in _context.RawArgs)
|
|
||||||
{
|
|
||||||
// 跳过 Launcher 自己的命令和选项,只传递用户原始参数
|
|
||||||
if (arg == _context.Command || arg == _context.SubCommand || arg.StartsWith("--"))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
processStartInfo.ArgumentList.Add(arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 传递环境变量供 IPC 使用
|
|
||||||
processStartInfo.EnvironmentVariables[LauncherIpcConstants.LauncherPidEnvVar] =
|
processStartInfo.EnvironmentVariables[LauncherIpcConstants.LauncherPidEnvVar] =
|
||||||
Environment.ProcessId.ToString();
|
Environment.ProcessId.ToString();
|
||||||
processStartInfo.EnvironmentVariables[LauncherIpcConstants.PackageRootEnvVar] =
|
processStartInfo.EnvironmentVariables[LauncherIpcConstants.PackageRootEnvVar] =
|
||||||
_deploymentLocator.GetAppRoot();
|
_deploymentLocator.GetAppRoot();
|
||||||
|
|
||||||
// 传递版本信息
|
|
||||||
var versionInfo = _deploymentLocator.GetVersionInfo();
|
|
||||||
processStartInfo.EnvironmentVariables[LauncherIpcConstants.VersionEnvVar] = versionInfo.Version;
|
processStartInfo.EnvironmentVariables[LauncherIpcConstants.VersionEnvVar] = versionInfo.Version;
|
||||||
processStartInfo.EnvironmentVariables[LauncherIpcConstants.CodenameEnvVar] = versionInfo.Codename;
|
processStartInfo.EnvironmentVariables[LauncherIpcConstants.CodenameEnvVar] = versionInfo.Codename;
|
||||||
|
|
||||||
@@ -483,6 +515,36 @@ internal sealed class LauncherFlowCoordinator
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string QuoteArgument(string value)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(value))
|
||||||
|
{
|
||||||
|
return "\"\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!value.Contains('"') && !value.Contains(' ') && !value.Contains('\t'))
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
var builder = new System.Text.StringBuilder();
|
||||||
|
builder.Append('"');
|
||||||
|
foreach (var ch in value)
|
||||||
|
{
|
||||||
|
if (ch == '"')
|
||||||
|
{
|
||||||
|
builder.Append("\\\"");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
builder.Append(ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.Append('"');
|
||||||
|
return builder.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
private static void EnsureExecutable(string path)
|
private static void EnsureExecutable(string path)
|
||||||
{
|
{
|
||||||
if (OperatingSystem.IsWindows())
|
if (OperatingSystem.IsWindows())
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ public partial class App : Application
|
|||||||
EnsureNotificationService();
|
EnsureNotificationService();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async void OnFrameworkInitializationCompleted()
|
public override void OnFrameworkInitializationCompleted()
|
||||||
{
|
{
|
||||||
if (Design.IsDesignMode)
|
if (Design.IsDesignMode)
|
||||||
{
|
{
|
||||||
@@ -152,12 +152,8 @@ public partial class App : Application
|
|||||||
|
|
||||||
AppLogger.Info("App", "Framework initialization completed.");
|
AppLogger.Info("App", "Framework initialization completed.");
|
||||||
|
|
||||||
// 初始化 Launcher IPC 客户端(如果从 Launcher 启动)
|
|
||||||
await InitializeLauncherIpcAsync();
|
|
||||||
|
|
||||||
RegisterUiUnhandledExceptionGuard();
|
RegisterUiUnhandledExceptionGuard();
|
||||||
LinuxDesktopEntryInstaller.EnsureInstalled();
|
LinuxDesktopEntryInstaller.EnsureInstalled();
|
||||||
ReportStartupProgress(StartupStage.LoadingSettings, 20, "正在加载设置...");
|
|
||||||
DesktopBootstrap.InitializeApplication(this, InitializeDesktopShell);
|
DesktopBootstrap.InitializeApplication(this, InitializeDesktopShell);
|
||||||
|
|
||||||
if (!Design.IsDesignMode && OperatingSystem.IsWindows())
|
if (!Design.IsDesignMode && OperatingSystem.IsWindows())
|
||||||
@@ -166,6 +162,10 @@ public partial class App : Application
|
|||||||
}
|
}
|
||||||
|
|
||||||
base.OnFrameworkInitializationCompleted();
|
base.OnFrameworkInitializationCompleted();
|
||||||
|
|
||||||
|
// IPC 初始化移到窗口创建之后,避免 async void 中的 await 导致窗口创建延迟
|
||||||
|
// 使用 fire-and-forget 模式,不阻塞主流程
|
||||||
|
_ = InitializeLauncherIpcAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task InitializeLauncherIpcAsync()
|
private async Task InitializeLauncherIpcAsync()
|
||||||
@@ -189,9 +189,10 @@ public partial class App : Application
|
|||||||
|
|
||||||
// 注册系统初始化加载项
|
// 注册系统初始化加载项
|
||||||
_loadingStateManager.RegisterItem("system.init", LoadingItemType.System, "系统初始化", "初始化系统核心组件");
|
_loadingStateManager.RegisterItem("system.init", LoadingItemType.System, "系统初始化", "初始化系统核心组件");
|
||||||
_loadingStateManager.StartItem("system.init", "正在连接启动器...");
|
_loadingStateManager.StartItem("system.init", "已连接启动器");
|
||||||
|
|
||||||
ReportStartupProgress(StartupStage.Initializing, 10, "正在初始化...");
|
ReportStartupProgress(StartupStage.Initializing, 10, "正在初始化...");
|
||||||
|
ReportStartupProgress(StartupStage.LoadingSettings, 20, "正在加载设置...");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -227,7 +228,7 @@ public partial class App : Application
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 同步向 Launcher 报告启动进度,确保关键消息可靠送达
|
/// 向 Launcher 报告关键启动进度,使用后台线程避免阻塞 UI
|
||||||
/// 用于 Ready 等关键状态报告
|
/// 用于 Ready 等关键状态报告
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void ReportStartupProgressSync(StartupStage stage, int percent, string message)
|
private void ReportStartupProgressSync(StartupStage stage, int percent, string message)
|
||||||
@@ -237,27 +238,27 @@ public partial class App : Application
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 使用同步等待确保消息发送完成
|
_ = Task.Run(async () =>
|
||||||
var task = _launcherIpcClient.ReportProgressAsync(new StartupProgressMessage
|
|
||||||
{
|
{
|
||||||
Stage = stage,
|
try
|
||||||
ProgressPercent = percent,
|
{
|
||||||
Message = message
|
await _launcherIpcClient.ReportProgressAsync(new StartupProgressMessage
|
||||||
|
{
|
||||||
|
Stage = stage,
|
||||||
|
ProgressPercent = percent,
|
||||||
|
Message = message
|
||||||
|
});
|
||||||
|
AppLogger.Info("LauncherIpc", $"Successfully reported stage: {stage}");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
AppLogger.Warn("LauncherIpc", $"Failed to report progress: {ex.Message}");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 等待最多 5 秒,确保消息发送成功
|
|
||||||
if (!task.Wait(TimeSpan.FromSeconds(5)))
|
|
||||||
{
|
|
||||||
AppLogger.Warn("LauncherIpc", "Report progress timeout after 5 seconds");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
AppLogger.Info("LauncherIpc", $"Successfully reported stage: {stage}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
AppLogger.Warn("LauncherIpc", $"Failed to report progress synchronously: {ex.Message}");
|
AppLogger.Warn("LauncherIpc", $"Failed to launch progress report task: {ex.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -980,6 +981,27 @@ public partial class App : Application
|
|||||||
// 使用 Opened 事件确保所有资源已加载完毕
|
// 使用 Opened 事件确保所有资源已加载完毕
|
||||||
mainWindow.Opened += OnMainWindowOpened;
|
mainWindow.Opened += OnMainWindowOpened;
|
||||||
|
|
||||||
|
// 兜底机制:如果 Opened 事件 10 秒内未触发,强制发送 Ready 信号
|
||||||
|
// 防止因渲染问题导致 Opened 不触发,启动器 Splash 窗口一直显示
|
||||||
|
_ = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await Task.Delay(TimeSpan.FromSeconds(10));
|
||||||
|
if (_launcherIpcClient is not null && _launcherIpcClient.IsConnected)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _launcherIpcClient.ReportProgressAsync(new StartupProgressMessage
|
||||||
|
{
|
||||||
|
Stage = StartupStage.Ready,
|
||||||
|
ProgressPercent = 100,
|
||||||
|
Message = "就绪"
|
||||||
|
});
|
||||||
|
AppLogger.Warn("App", "Ready signal sent via fallback (Opened event did not fire within 10s)");
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return mainWindow;
|
return mainWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -100,12 +100,15 @@ public static class AppRestartService
|
|||||||
var startInfo = new ProcessStartInfo
|
var startInfo = new ProcessStartInfo
|
||||||
{
|
{
|
||||||
FileName = executablePath,
|
FileName = executablePath,
|
||||||
UseShellExecute = false,
|
UseShellExecute = true,
|
||||||
WorkingDirectory = ResolveWorkingDirectory(executablePath, entryAssemblyPath)
|
WorkingDirectory = ResolveWorkingDirectory(executablePath, entryAssemblyPath)
|
||||||
};
|
};
|
||||||
|
|
||||||
AppendArguments(startInfo, commandLineArgs);
|
// UseShellExecute=true 时使用 Arguments 字符串而非 ArgumentList
|
||||||
AppendRestartParentProcessArgument(startInfo);
|
var args = new System.Text.StringBuilder();
|
||||||
|
AppendArgumentsToString(args, commandLineArgs);
|
||||||
|
AppendRestartParentProcessArgumentToString(args);
|
||||||
|
startInfo.Arguments = args.ToString();
|
||||||
return startInfo;
|
return startInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,13 +125,16 @@ public static class AppRestartService
|
|||||||
var startInfo = new ProcessStartInfo
|
var startInfo = new ProcessStartInfo
|
||||||
{
|
{
|
||||||
FileName = dotnetHostPath,
|
FileName = dotnetHostPath,
|
||||||
UseShellExecute = false,
|
UseShellExecute = true,
|
||||||
WorkingDirectory = ResolveWorkingDirectory(dotnetHostPath, entryAssemblyPath)
|
WorkingDirectory = ResolveWorkingDirectory(dotnetHostPath, entryAssemblyPath)
|
||||||
};
|
};
|
||||||
|
|
||||||
startInfo.ArgumentList.Add(entryAssemblyPath);
|
// UseShellExecute=true 时使用 Arguments 字符串
|
||||||
AppendArguments(startInfo, commandLineArgs);
|
var args = new System.Text.StringBuilder();
|
||||||
AppendRestartParentProcessArgument(startInfo);
|
args.Append(QuoteArgument(entryAssemblyPath));
|
||||||
|
AppendArgumentsToString(args, commandLineArgs);
|
||||||
|
AppendRestartParentProcessArgumentToString(args);
|
||||||
|
startInfo.Arguments = args.ToString();
|
||||||
return startInfo;
|
return startInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,11 +151,61 @@ public static class AppRestartService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void AppendArgumentsToString(System.Text.StringBuilder builder, IReadOnlyList<string> commandLineArgs)
|
||||||
|
{
|
||||||
|
for (var i = 1; i < commandLineArgs.Count; i++)
|
||||||
|
{
|
||||||
|
if (TryParseRestartParentProcessId(commandLineArgs[i], out _))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (builder.Length > 0) builder.Append(' ');
|
||||||
|
builder.Append(QuoteArgument(commandLineArgs[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void AppendRestartParentProcessArgument(ProcessStartInfo startInfo)
|
private static void AppendRestartParentProcessArgument(ProcessStartInfo startInfo)
|
||||||
{
|
{
|
||||||
startInfo.ArgumentList.Add($"{RestartParentPidArgumentPrefix}{Environment.ProcessId}");
|
startInfo.ArgumentList.Add($"{RestartParentPidArgumentPrefix}{Environment.ProcessId}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void AppendRestartParentProcessArgumentToString(System.Text.StringBuilder builder)
|
||||||
|
{
|
||||||
|
if (builder.Length > 0) builder.Append(' ');
|
||||||
|
builder.Append($"{RestartParentPidArgumentPrefix}{Environment.ProcessId}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string QuoteArgument(string value)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(value))
|
||||||
|
{
|
||||||
|
return "\"\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!value.Contains('"') && !value.Contains(' ') && !value.Contains('\t'))
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
var builder = new System.Text.StringBuilder();
|
||||||
|
builder.Append('"');
|
||||||
|
foreach (var ch in value)
|
||||||
|
{
|
||||||
|
if (ch == '"')
|
||||||
|
{
|
||||||
|
builder.Append("\\\"");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
builder.Append(ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.Append('"');
|
||||||
|
return builder.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
private static bool TryParseRestartParentProcessId(string? argument, out int processId)
|
private static bool TryParseRestartParentProcessId(string? argument, out int processId)
|
||||||
{
|
{
|
||||||
processId = 0;
|
processId = 0;
|
||||||
|
|||||||
@@ -17,6 +17,11 @@ public class LauncherIpcClient : IDisposable
|
|||||||
private bool _isConnected;
|
private bool _isConnected;
|
||||||
private readonly object _writeLock = new();
|
private readonly object _writeLock = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否已连接到 Launcher
|
||||||
|
/// </summary>
|
||||||
|
public bool IsConnected => _isConnected && _pipeClient?.IsConnected == true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 协议:每条消息以 4 字节小端 int32 长度前缀开头,后跟 UTF-8 JSON 正文。
|
/// 协议:每条消息以 4 字节小端 int32 长度前缀开头,后跟 UTF-8 JSON 正文。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -92,11 +97,28 @@ public class LauncherIpcClient : IDisposable
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 检查是否从 Launcher 启动
|
/// 检查是否从 Launcher 启动
|
||||||
|
/// 优先检查环境变量,回退到命令行参数(UseShellExecute=true 时环境变量仍可继承,
|
||||||
|
/// 命令行参数作为备选确保兼容性)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static bool IsLaunchedByLauncher()
|
public static bool IsLaunchedByLauncher()
|
||||||
{
|
{
|
||||||
return !string.IsNullOrEmpty(
|
// 优先检查环境变量
|
||||||
Environment.GetEnvironmentVariable(LauncherIpcConstants.LauncherPidEnvVar));
|
if (!string.IsNullOrEmpty(
|
||||||
|
Environment.GetEnvironmentVariable(LauncherIpcConstants.LauncherPidEnvVar)))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 回退到命令行参数检查(格式: --LMD_LAUNCHER_PID=<value>)
|
||||||
|
foreach (var arg in Environment.GetCommandLineArgs())
|
||||||
|
{
|
||||||
|
if (arg.StartsWith($"--{LauncherIpcConstants.LauncherPidEnvVar}=", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Timers;
|
using System.Timers;
|
||||||
|
using LanMountainDesktop.Shared.Contracts.Launcher;
|
||||||
|
|
||||||
namespace LanMountainDesktop.Services.Loading;
|
namespace LanMountainDesktop.Services.Loading;
|
||||||
|
|
||||||
|
|||||||
@@ -5,13 +5,18 @@
|
|||||||
For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
|
For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
|
||||||
<assemblyIdentity version="1.0.0.0" name="LanMountainDesktop.Desktop"/>
|
<assemblyIdentity version="1.0.0.0" name="LanMountainDesktop.Desktop"/>
|
||||||
|
|
||||||
|
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||||
|
<security>
|
||||||
|
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<!-- 明确指定不需要管理员权限,以调用者权限运行 -->
|
||||||
|
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||||
|
</requestedPrivileges>
|
||||||
|
</security>
|
||||||
|
</trustInfo>
|
||||||
|
|
||||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||||
<application>
|
<application>
|
||||||
<!-- A list of the Windows versions that this application has been tested on
|
<!-- Windows 10/11 -->
|
||||||
and is designed to work with. Uncomment the appropriate elements
|
|
||||||
and Windows will automatically select the most compatible environment. -->
|
|
||||||
|
|
||||||
<!-- Windows 10 -->
|
|
||||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||||
</application>
|
</application>
|
||||||
</compatibility>
|
</compatibility>
|
||||||
|
|||||||
Reference in New Issue
Block a user