Самообновляющееся приложение с доменом приложения в другой теме - PullRequest
3 голосов
/ 16 декабря 2011

Я работаю над тем, чтобы сервис 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: с тех пор я обнаружил, что строка AppDomain.Unload () не нужна.Без этого служба по-прежнему обновляет приложение новой версией, а затем запускает новую версию приложения.Моя большая проблема - вызывать Cancel () в источнике токена.Мне бы хотелось, чтобы приложение могло корректно завершать работу, а не просто закрывать его.

1 Ответ

4 голосов
/ 21 января 2012

Путем проб и ошибок и обучения я, кажется, нашел достойное решение. Я думал, что выложу это здесь на случай, если это кому-нибудь поможет.

Во-первых, я создал интерфейс в отдельном проекте, поэтому самообновляющемуся приложению не нужно будет ссылаться на приложение, которое фактически запускается в его собственном домене приложения. Этот интерфейс был использован для того, чтобы самообновляющееся приложение могло вызывать 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);

Есть еще несколько деталей, но это общая концепция, и, похоже, она работает хорошо.

...