Возникают проблемы с созданием приложения для самостоятельного обновления C#C#

Место общения программистов C#
Ответить
Anonymous
 Возникают проблемы с созданием приложения для самостоятельного обновления C#

Сообщение Anonymous »

Я работаю над системой самостоятельного обновления для моего приложения (MyApp). Идея состоит в том, что у меня есть сервис Windows (Myupdater) с таймером, который в интервале будет запускать проверку версии. Если есть более новая версия MyApp, она загрузит новый установщик MyAppSetup.exe (в настоящее время на основе настройки Inno). Новый установщик должен работать молча. Новая установка также может обновить службу Myupdater. Большинство все, кажется, работает, но когда новый myappsetup.exe запускается, он, кажется, начинается, а затем просто придерживается и никогда не завершается. Ничто не регистрируется у установщика, хотя он установлен, поэтому я не думаю, что установщик когда -либо начинает работать. Зритель событий ничего не показывает, если я не убью процесс, в котором он возвращается.MyUpdater.csproj



MyUpdater
net48
WinExe








Component


Component






< /code>
MyUpdater/app.config











< /code>
MyUpdater/Constants.cs

public class Constants
{
public const string WindowsServiceName = "MyAutoUpdate";

internal const string ProcessName = "MyApp";
internal const string VersionCheckerAppID = "MyAppID";

internal const string AppSettingsVersionCheckerBaseUrlKey = "VersionCheckerBaseUrl";
internal const string AppSettingsIntervalKey = "UpdateInterval";

internal static readonly TimeSpan WebClientTimeout = TimeSpan.FromHours(1);
internal static readonly TimeSpan DefaultUpdateInterval = TimeSpan.FromHours(3);
internal static readonly TimeSpan InstallerTimeout = TimeSpan.FromMinutes(3);

internal static string InstallerLogDirectory => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), WindowsServiceName, "Logs");
}
< /code>
MyUpdater/MyAutoUpdater.cs

public class MyAutoUpdater : ServiceBase
{
private readonly string _versionCheckerUrl;
private readonly TimeSpan _updateInterval;
private readonly Version _currentVersion;
private readonly Timer _timer;
private string _tempFilePath;

public MyAutoUpdater()
{
ServiceName = Constants.WindowsServiceName;
AutoLog = true;
CanShutdown = true;
CanStop = true;
ExitCode = 0;
_currentVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName(false).Version;

_versionCheckerUrl = ConfigurationManager.AppSettings[Constants.AppSettingsVersionCheckerBaseUrlKey];
if (string.IsNullOrEmpty(_versionCheckerUrl))
{
throw new ConfigurationErrorsException("No version checker URL was found in the config file.");
}

string intervalSettingString = ConfigurationManager.AppSettings[Constants.AppSettingsIntervalKey];
if (!string.IsNullOrEmpty(intervalSettingString))
{
if (!TimeSpan.TryParse(intervalSettingString, out _updateInterval))
{
_updateInterval = Constants.DefaultUpdateInterval;
}
}
else
{
_updateInterval = Constants.DefaultUpdateInterval;
}

_timer = new Timer
{
AutoReset = true,
Interval = _updateInterval.TotalMilliseconds
};
_timer.Elapsed += Timer_Elapsed;
}

public static void Main(string[] _)
{
Run(new MyAutoUpdater());
}

protected override void OnStart(string[] _)
{
_timer.Start();
}

protected override void OnStop()
{
ShutDown();
}

protected override void OnShutdown()
{
ShutDown();
}

private void ShutDown()
{
_timer.Stop();
_timer.Dispose();
}

private async void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
StringBuilder logBuilder = new StringBuilder();
logBuilder.AppendLine("Beginning update check.");

try
{
// Check internet connectivity
if (IsInternetAvailable())
{
logBuilder.AppendLine("Internet connection is available.");

VersionCheckerClient versionChecker = new VersionCheckerClient(new HttpClient { BaseAddress = new Uri(_versionCheckerUrl) });

logBuilder.AppendLine("Sending request to version checker.");
ApiResponse response = await versionChecker.CheckForUpdateAsync(Constants.VersionCheckerAppID, _currentVersion);

if (response.Successful)
{
logBuilder.AppendLine($"Version check successful. Latest version: {response.Model.Latest.Version}, Current version: {_currentVersion}, installer: {response.Model.LatestInstallerUrl}.");

if (response.Model.Latest.Version > _currentVersion && !string.IsNullOrEmpty(response.Model.LatestInstallerUrl))
{
string downloadUrl = response.Model.LatestInstallerUrl;
logBuilder.AppendLine($"Update available. Download URL: {downloadUrl}.");

// Extract the filename from the download URL
string fileName = Path.GetFileName(new Uri(downloadUrl).LocalPath);
string tempFilePath = Path.Combine(GetWritableTempPath(), fileName);

logBuilder.AppendLine($"Downloading update installer to: {tempFilePath}.");

// Download the update installer
using (HttpClient client = new HttpClient { Timeout = Constants.WebClientTimeout })
{
using HttpResponseMessage downloadResponse = await client.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead);
downloadResponse.EnsureSuccessStatusCode();
using FileStream fileStream = new FileStream(tempFilePath, FileMode.Create, FileAccess.Write, FileShare.None);
await downloadResponse.Content.CopyToAsync(fileStream);
}

_tempFilePath = tempFilePath;
logBuilder.AppendLine($"Update installer downloaded to: {_tempFilePath}.");
}
else
{
logBuilder.AppendLine("No update available.");
}
}
else
{
logBuilder.AppendLine($"Version check failed: {response.Message}");
}
}
else
{
logBuilder.AppendLine("No internet connection. Cannot check for updates.");
}

if (!string.IsNullOrEmpty(_tempFilePath))
{
if (!AppIsRunning())
{
// INSTALL!
logBuilder.AppendLine($"Application is not running. Installing update from: {_tempFilePath}.");

StartSilentInstall(_tempFilePath, logBuilder);
// This program won't exist anymore once the downloaded update begins uninstalling the old version and installing the new version.
// So we can't wait for the installer process to finish in order to delete it from the "NT Service\System" TEMP folder.
_tempFilePath = null; // Reset the temp file path
}
else
{
logBuilder.AppendLine("Application is running. Update will be applied later.");
}
}
}
catch (Exception ex)
{
logBuilder.AppendLine($"Failed to update. Exception: {ex.Message}");
_tempFilePath = null; // Reset the temp file path
}
finally
{
logBuilder.AppendLine("Ending update check.");
EventLog.WriteEntry(logBuilder.ToString(), EventLogEntryType.Information);
}

static bool IsInternetAvailable()
{
try
{
using TcpClient client = new TcpClient();
IAsyncResult result = client.BeginConnect("8.8.8.8", 53, null, null);
bool success = result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(3));
if (!success)
{
return false; // Timed out
}

client.EndConnect(result);
return true;
}
catch
{
return false;
}
}

static string GetWritableTempPath()
{
string tempDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "AutoUpdaterTemp");
if (!Directory.Exists(tempDirectory))
{
Directory.CreateDirectory(tempDirectory);
}
return tempDirectory;
}

static bool AppIsRunning()
{
Process[] appProcess = Process.GetProcessesByName(Constants.ProcessName);
return appProcess.Any();
}

static void StartSilentInstall(string setupPath, StringBuilder log)
{
string logDir = Constants.InstallerLogDirectory;
Directory.CreateDirectory(logDir);
string logPath = Path.Combine(logDir, $"install_{DateTime.Now:yyyyMMdd_HHmmss}.log");

Directory.CreateDirectory(Path.GetDirectoryName(logPath));
string args = $"/VERYSILENT /SUPPRESSMSGBOXES /NORESTART /CLOSEAPPLICATIONS /RESTARTAPPLICATIONS /LOG=\"{logPath}\"";

string handoffExe = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "MyUpdaterInstaller.exe");
using Process p = new Process();
p.StartInfo.FileName = handoffExe;
p.StartInfo.Arguments = $"--handoff \"{setupPath}\" {args}";
p.StartInfo.UseShellExecute = true;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.Verb = "runas"; // Run as administrator
p.Start();

/*if (!p.WaitForExit((int) Constants.InstallerTimeout.TotalMilliseconds))
{
try
{
p.Kill();
}
catch
{
}
log?.AppendLine($"Installer timed out after {Constants.InstallerTimeout.TotalMilliseconds} and was killed.");
throw new System.ServiceProcess.TimeoutException("Installer did not finish within allotted time.");
}

log?.AppendLine($"Installer exited with code {p.ExitCode}. Log: {logPath}");*/
//return p.ExitCode;
}
}
}
< /code>
MyUpdater/WindowsServiceInstaller.cs

[RunInstaller(true)]
public class WindowsServiceInstaller : Installer
{
public WindowsServiceInstaller()
{
ServiceProcessInstaller process = new ServiceProcessInstaller
{
Account = ServiceAccount.LocalSystem
};

ServiceInstaller service = new ServiceInstaller
{
ServiceName = Constants.WindowsServiceName,
DisplayName = "MyApp Updater",
Description = "Ensures that MyApp stays up to date.",
StartType = ServiceStartMode.Automatic
};

Installers.Add(process);
Installers.Add(service);
}
}
< /code>
MyUpdaterInstaller.csproj



MyUpdaterInstaller
net48
Exe









< /code>
MyUpdaterInstaller/Program.cs

internal class Program
{
private const string InstallArg = "--install";
private const string UninstallArg = "--uninstall";
private const string HandoffArg = "--handoff";
private const string ServiceExeName = "MyUpdater.exe";
private static string _serviceFullPath;

///
/// This program seems to solve the uninstaller's ability to actual remove the service when installing an update in the background.
/// Originally, the service could be executed to install/uninstall itself.
///
private static void Main(string[] args)
{
if (!EventLog.SourceExists(Constants.WindowsServiceName))
{
EventLog.CreateEventSource(Constants.WindowsServiceName, "Application");
}

_serviceFullPath = Path.Combine(AppContext.BaseDirectory, ServiceExeName);
if (!File.Exists(_serviceFullPath))
{
EventLog.WriteEntry(Constants.WindowsServiceName, $"The service exe is missing: {_serviceFullPath}", EventLogEntryType.Error);
return;
}

try
{
string argument = args.FirstOrDefault()?.Trim()?.ToLowerInvariant();
switch (argument)
{
case InstallArg:
EventLog.WriteEntry(Constants.WindowsServiceName, "Starting installation process.", EventLogEntryType.Information);
InstallAndStart();
EventLog.WriteEntry(Constants.WindowsServiceName, "Installation completed successfully.", EventLogEntryType.Information);
break;

case UninstallArg:
EventLog.WriteEntry(Constants.WindowsServiceName, "Starting uninstallation process.", EventLogEntryType.Information);
StopAndUninstall();
EventLog.WriteEntry(Constants.WindowsServiceName, "Uninstallation completed successfully.", EventLogEntryType.Information);
break;

case HandoffArg:
EventLog.WriteEntry(Constants.WindowsServiceName, "Starting handoff process.", EventLogEntryType.Information);
Handoff(args);
EventLog.WriteEntry(Constants.WindowsServiceName, "Handoff completed successfully.", EventLogEntryType.Information);
break;

default:
EventLog.WriteEntry(Constants.WindowsServiceName, $"Invalid argument: {argument}. Use {InstallArg}, {UninstallArg} or {HandoffArg}.", EventLogEntryType.Error);
break;
}
}
catch (Exception ex)
{
EventLog.WriteEntry(Constants.WindowsServiceName, $"An error occurred: {ex.Message}", EventLogEntryType.Error);
throw;
}
}

private static void InstallAndStart()
{
ManagedInstallerClass.InstallHelper(new[] { _serviceFullPath });
using (ServiceController sc = new ServiceController(Constants.WindowsServiceName))
{
sc.Start();
sc.WaitForStatus(ServiceControllerStatus.Running);
}
EventLog.WriteEntry(Constants.WindowsServiceName, "Service installed and started successfully.", EventLogEntryType.Information);

RunHidden("sc.exe", $@"failure {Constants.WindowsServiceName} reset= 86400 actions= restart/60000/restart/60000/none/0");
RunHidden("sc.exe", $@"failureflag {Constants.WindowsServiceName} 1");
RunHidden("sc.exe", $@"config {Constants.WindowsServiceName} start= delayed-auto");

static void RunHidden(string file, string args)
{
using Process p = new Process();
p.StartInfo.FileName = file;
p.StartInfo.Arguments = args;
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.Start();
p.WaitForExit();
}
}

private static void StopAndUninstall()
{
if (IsInstalled())
{
using (ServiceController sc = new ServiceController(Constants.WindowsServiceName))
{
if (sc.Status == ServiceControllerStatus.Running)
{
sc.Stop();
sc.WaitForStatus(ServiceControllerStatus.Stopped);
}
}
ManagedInstallerClass.InstallHelper(new[] { "/u", _serviceFullPath });
EventLog.WriteEntry(Constants.WindowsServiceName, "Service stopped and uninstalled successfully.", EventLogEntryType.Information);
}

static bool IsInstalled()
{
try
{
using ServiceController sc = new ServiceController(Constants.WindowsServiceName);
ServiceControllerStatus status = sc.Status;
return true;
}
catch
{
return false;
}
}
}

private static void Handoff(string[] args)
{
// stop the service if it's running so the installer isn't blocked
using (ServiceController sc = new ServiceController(Constants.WindowsServiceName))
{
if (sc.Status == ServiceControllerStatus.Running)
{
sc.Stop();
sc.WaitForStatus(ServiceControllerStatus.Stopped);
}
}

string installerPath = args.Length > 1 ? args[1] : null;
string installerArgs = args.Length > 2 ? string.Join(" ", args.Skip(2)) : string.Empty;
if (string.IsNullOrWhiteSpace(installerPath) || !File.Exists(installerPath))
{
EventLog.WriteEntry(Constants.WindowsServiceName, $"Handoff requires a valid installer path. Provided: '{installerPath}'", EventLogEntryType.Error);
Environment.ExitCode = 2;
}

EventLog.WriteEntry(Constants.WindowsServiceName, $"Handoff [{installerPath} {installerArgs}]", EventLogEntryType.Information);

using Process p = new Process();
p.StartInfo.FileName = installerPath;
p.StartInfo.Arguments = installerArgs;
p.StartInfo.UseShellExecute = true;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.Verb = "runas"; // Run as administrator
p.Start();

//p.WaitForExit();
//EventLog.WriteEntry(Constants.WindowsServiceName, $"Handoff installer exited with code {p.ExitCode}.", EventLogEntryType.Information);

// the installer should restart the service when it's done
}
}


Подробнее здесь: https://stackoverflow.com/questions/797 ... -sharp-app
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C#»