不同线程上App域的自我更新应用程序

时间:2011-12-16 18:41:45

标签: c#-4.0 task appdomain restart

我一直在努力让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()看起来像这样。请注意,当我在此方法的第二行卸载app域时,我收到异常。 (我通过捕获异常并基本忽略它来解决这个问题。)

private void RestartAppDomain()
{
    this._tokenSource.Cancel();
    AppDomain.Unload(this._appDomain);
    LoadApp();
}

编辑:我发现AppDomain.Unload()行是不必要的。没有它,该服务仍然使用新版本更新应用程序,然后启动应用程序的新版本。我最关心的是在令牌源上调用Cancel()。我希望该应用程序能够优雅地关闭,而不是仅仅强行关闭。

1 个答案:

答案 0 :(得分:4)

通过一些反复试验和学习,我似乎已经找到了一个体面的解决方案。我想我会把它发布在这里,以防它帮助其他人。

首先,我在一个单独的项目中创建了一个界面,因此自我更新的应用程序不必引用实际在其自己的应用程序域中运行的应用程序。使用此界面,以便自我更新的应用程序可以在另一个应用程序上调用“开始”和“停止”。

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);

还有一些细节,但这是一般概念,似乎运作良好。