【问题标题】:Self-updating App with App Domain on Different Thread在不同线程上使用应用程序域自行更新应用程序
【发布时间】:2023-07-31 12:29:01
【问题描述】:

我一直在努力让 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();
}

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

【问题讨论】:

    标签: c#-4.0 task appdomain restart


    【解决方案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);
    

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

    【讨论】:

    • 很高兴听到您成功了!我正在做一些非常相似的事情,所以我有兴趣看到更多您的代码/伪代码进行比较,也许我们每个人都可以互相学习一些东西。我的主要目标是在数据库中托管程序集,但我设法实现了一些简洁的方面,例如避免需要公共依赖项(您的 IStartStop 接口,额外的 dll)。
    • 你是如何避免常见依赖的?
    最近更新 更多