Возникают проблемы с созданием самостоятельного приложения 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#»