fix summary dialog start issues

This commit is contained in:
Oliver Kieselbach
2026-03-07 19:27:30 +01:00
parent 323f46944e
commit 65e26e59dd
5 changed files with 86 additions and 9 deletions
@@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Security.AccessControl;
using System.Security.Principal;
using System.Threading;
using System.Threading.Tasks;
using AutopilotMonitor.Agent.Core.Configuration;
@@ -608,15 +610,20 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Core
return;
}
// Create temp folder for the dialog (survives agent self-destruct).
// Use ProgramData instead of Path.GetTempPath() because the agent runs as SYSTEM
// and SYSTEM's temp (C:\WINDOWS\SystemTemp) is not accessible to the user session.
// Create temp folder for the dialog OUTSIDE the AutopilotMonitor folder.
// The agent's self-destruct deletes ProgramData\AutopilotMonitor entirely,
// so the dialog files must live elsewhere to survive.
var tempDir = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
"AutopilotMonitor",
"Summary-" + Guid.NewGuid().ToString("N").Substring(0, 8));
"AutopilotMonitor-Summary");
// Clean up any leftover folder from a previous run, then create fresh
try { if (Directory.Exists(tempDir)) Directory.Delete(tempDir, true); } catch { }
Directory.CreateDirectory(tempDir);
// Grant the interactive user delete permissions so the dialog can self-cleanup
GrantUserDeletePermission(tempDir);
// Copy dialog EXE + Newtonsoft.Json.dll + final-status.json to temp
var tempDialogExe = Path.Combine(tempDir, "AutopilotMonitor.SummaryDialog.exe");
File.Copy(dialogExe, tempDialogExe);
@@ -625,6 +632,10 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Core
if (File.Exists(jsonDll))
File.Copy(jsonDll, Path.Combine(tempDir, "Newtonsoft.Json.dll"));
var exeConfig = dialogExe + ".config";
if (File.Exists(exeConfig))
File.Copy(exeConfig, tempDialogExe + ".config");
var tempStatusFile = Path.Combine(tempDir, "final-status.json");
File.Copy(statusFile, tempStatusFile);
@@ -667,6 +678,31 @@ namespace AutopilotMonitor.Agent.Core.Monitoring.Core
}
}
/// <summary>
/// Grants the built-in Users group full control on a directory so the user-session
/// SummaryDialog process can delete it during self-cleanup.
/// </summary>
private void GrantUserDeletePermission(string directoryPath)
{
try
{
var dirInfo = new DirectoryInfo(directoryPath);
var security = dirInfo.GetAccessControl();
var usersIdentity = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null);
security.AddAccessRule(new FileSystemAccessRule(
usersIdentity,
FileSystemRights.FullControl,
InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit,
PropagationFlags.None,
AccessControlType.Allow));
dirInfo.SetAccessControl(security);
}
catch (Exception ex)
{
_logger.Debug($"Could not set ACL on summary folder: {ex.Message}");
}
}
public void Dispose()
{
_uploadTimer?.Dispose();
@@ -1 +1 @@
306
308
@@ -12,14 +12,42 @@ namespace AutopilotMonitor.SummaryDialog
internal static string BrandingImageUrl { get; private set; }
internal static bool? ForceTheme { get; private set; } // true=dark, false=light, null=auto
private static string _logFile;
public App()
{
// Set up crash logging as early as possible to diagnose launch failures
// (e.g. when launched via CreateProcessAsUser from SYSTEM service)
try
{
var exeDir = Path.GetDirectoryName(
System.Reflection.Assembly.GetExecutingAssembly().Location) ?? ".";
_logFile = Path.Combine(exeDir, "SummaryDialog.log");
Log("App constructor — process started");
}
catch { }
AppDomain.CurrentDomain.UnhandledException += (s, e) =>
{
Log($"UNHANDLED EXCEPTION: {e.ExceptionObject}");
};
DispatcherUnhandledException += (s, e) =>
{
Log($"DISPATCHER EXCEPTION: {e.Exception}");
};
}
protected override void OnStartup(StartupEventArgs e)
{
Log($"OnStartup — args: {string.Join(" ", e.Args)}");
base.OnStartup(e);
ParseArguments(e.Args);
}
protected override void OnExit(ExitEventArgs e)
{
Log("OnExit — shutting down");
base.OnExit(e);
SelfCleanup();
}
@@ -67,15 +95,16 @@ namespace AutopilotMonitor.SummaryDialog
var exeDir = Path.GetDirectoryName(exePath);
// Only self-cleanup if running from a known staging folder
// (agent copies the dialog to ProgramData\AutopilotMonitor\Summary-* or legacy TEMP)
if (exeDir == null)
return;
var programData = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
var agentStagingRoot = Path.Combine(programData, "AutopilotMonitor");
var summaryStaging = Path.Combine(programData, "AutopilotMonitor-Summary");
var legacyStaging = Path.Combine(programData, "AutopilotMonitor");
var tempRoot = Path.GetTempPath().TrimEnd('\\');
var isStagedCopy = exeDir.StartsWith(agentStagingRoot, StringComparison.OrdinalIgnoreCase)
var isStagedCopy = exeDir.StartsWith(summaryStaging, StringComparison.OrdinalIgnoreCase)
|| exeDir.StartsWith(legacyStaging, StringComparison.OrdinalIgnoreCase)
|| exeDir.StartsWith(tempRoot, StringComparison.OrdinalIgnoreCase);
if (!isStagedCopy)
return;
@@ -99,5 +128,16 @@ namespace AutopilotMonitor.SummaryDialog
// Best-effort cleanup, ignore errors
}
}
internal static void Log(string message)
{
try
{
if (_logFile != null)
File.AppendAllText(_logFile,
$"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] {message}{Environment.NewLine}");
}
catch { }
}
}
}
@@ -7,6 +7,7 @@
<UseWPF>true</UseWPF>
<AssemblyName>AutopilotMonitor.SummaryDialog</AssemblyName>
<RootNamespace>AutopilotMonitor.SummaryDialog</RootNamespace>
<ApplicationIcon>app.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB