Самообновляющееся приложение с доменом приложения в другом потоке
Я работал над тем, чтобы служба windows автоматически перезапускала приложение, когда появляется новая версия. Мой код приведен ниже, и он действительно работает. Мой вопрос вот в чем: есть ли здесь где-то большое "нет-нет"? Я новичок в создании доменов приложений в отдельных потоках, и мне хотелось бы знать, есть ли более элегантный способ для этого. Я также обеспокоен тем, что, возможно, у меня есть большое слепое пятно относительно того, как я справляюсь с этим. Любой совет ценится.
Есть две общие части: Запущенная служба Windows запускает мое приложение в другом процессе, а затем выполняет опрос, чтобы определить, доступна ли новая версия этого приложения. Вторая часть-это фактическое приложение, которое запускается, обновляется, перезапускается.
Во-первых, когда служба запускается, она вызывает LoadApp (), чтобы приложение начало работать.
private void LoadApp()
{
// ** appDomainSetup here **
this._appDomain = AppDomain.CreateDomain("MyUpdatedApp", AppDomain.CurrentDomain.Evidence, appDomainSetup);
this._tokenSource = new CancellationTokenSource();
Task.Factory.StartNew(() =>
{
try
{
this._appDomain.ExecuteAssembly(assembly);
}
catch (AppDomainUnloadedException ex)
{
Debug.WriteLine(ex.ToString());
}
}, _tokenSource.Token);
}
Затем каждые 10 секунд таймер вызывает этот метод:
private void Process(object stateInfo)
{
if (!Monitor.TryEnter(_locker)) { return; }
try
{
// Check for new binaries. If new, copy the files and restart the app domain.
if (!NewAppVersionReady()) { return; }
DeleteFiles();
CopyFiles();
RestartAppDomain();
}
finally
{
Monitor.Exit(_locker);
}
}
RestartAppDomain () выглядит так. Обратите внимание, что я получаю исключение, когда выгружаю домен приложения на вторая строка в этом методе. (Я обхожу это, ловя исключение и в основном игнорируя его.)
private void RestartAppDomain()
{
this._tokenSource.Cancel();
AppDomain.Unload(this._appDomain);
LoadApp();
}
Edit: с тех пор я узнал, что домен приложений.Строка Unload() не нужна. Без него служба по-прежнему обновляет приложение с новой версией, а затем запускает новую версию приложения. Моя большая проблема-вызов Cancel () на источнике токена. Я хотел бы, чтобы приложение было в состоянии завершить работу изящно, а не просто заставить его закрыться.
1 ответ:
Путем некоторых проб и ошибок, а также обучения, я, кажется, нашел достойное решение. Я подумал, что могу оставить его здесь на случай, если это поможет кому-то еще.
Во-первых, я создал интерфейс в отдельном проекте, поэтому самообновляющемуся приложению не придется ссылаться на приложение, которое фактически запускается в своем собственном домене приложения. Этот интерфейс был использован для того, чтобы самообновляющееся приложение могло вызывать Start и Stop на другом приложении.
public interface IStartStop { void Start(); void Stop(); }
В приложении, которое запускается в отдельном домене, у меня был класс производное от MarshalByRefObject и реализация IStartStop.
public class PrestoTaskRunnerController : MarshalByRefObject, IStartStop, IDisposable
Это позволяет мне запускать и останавливать это приложение из моего самообновляющегося приложения:
this._appDomain = AppDomain.CreateDomain(this._appName, AppDomain.CurrentDomain.Evidence, appDomainSetup); this._startStopControllerToRun = (IStartStop)this._appDomain.CreateInstanceFromAndUnwrap(assemblyName, this._fullyQualifiedClassName); this._startStopControllerToRun.Start();
И остановка его делается так:
Есть еще некоторые детали, но это общая концепция, и она, кажется, работает хорошо.this._startStopControllerToRun.Stop(); AppDomain.Unload(this._appDomain);