Самообновляющееся приложение с доменом приложения в другом потоке


Я работал над тем, чтобы служба 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 3

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);
Есть еще некоторые детали, но это общая концепция, и она, кажется, работает хорошо.