如何回滚ClickOnce应用程序?

时间:2008-10-13 22:57:53

标签: clickonce

是否有办法(hacky会这样做)允许用户返回先前版本的ClickOnce网络部署的应用程序?

我查看了文档和API,似乎没办法。您可以有选择地选择是否要更新,但一旦更新,似乎就无法回复。

9 个答案:

答案 0 :(得分:83)

您可以通过更改服务器清单文件恢复服务器端的旧版本。当客户端重新启动应用程序时,它将看到它的版本与服务器所说的“当前”版本不同,它将下载新版本。此服务器清单文件通常始终指向最新版本,但它不必。

以下是如何更改它(我使用Visual Studio 2008发布。其他版本可能具有不同的发布文件夹结构)。

与publish.htm在同一文件夹中的是一个名为[appName].application的{​​{3}}文档。这是客户端用于比较其当前版本的服务器端清单文件。本文档中包含客户端应运行的“当前”版本以及可以找到部署文件的服务器上的位置。

在与publish.htm相同的位置,也是名为“应用程序文件”的文件夹。此文件夹包含每个先前发布的子文件夹。在每个子文件夹中都有另一个XML文档,它与我上面提到的名为[appName].application的名称相同。复制此文件(从包含要还原到的版本的任何文件夹)并将其粘贴到与publish.htm相同的文件夹中(几个级别)。当客户端应用程序重新启动时,它将显示为新版本可用,下载并运行它。客户端现在将运行以前的版本。

答案 1 :(得分:18)

ClickOnce将使用您发送的任何版本。如果您向他们发送旧版本,他们将回滚到旧版本。

早在五月,我的好友大卫写了一篇关于如何在每个用户的基础上做这件事的文章。我们可以让每个用户都拥有不同的版本。该应用程序甚至告诉数据库用户想要哪个版本,因此他们理论上可以更改其版本,然后只需重新启动应用程序。

Fine Grained Versioning with ClickOnce

答案 2 :(得分:8)

您可以进入“添加/删除”应用程序并选择您的应用程序,然后选择最后一次安装。

答案 3 :(得分:2)

您可以使用MAGEUI回滚到服务器上的先前清单版本。 Check this out

答案 4 :(得分:1)

如果查看部署位置,您将看到每个以前的版本,在附加版本号的单独文件夹中,以及部署清单中,还附加了版本号。

您可以将其中任何一个重命名为当前部署,下次更新该应用程序时,它会将您回滚的版本拉入其中。

答案 5 :(得分:1)

我理解ClickOnce版本检查algorythm如下:

  1. 如果客户端上安装的版本=部署到服务器的版本 - 什么都不做
  2. 如果客户端版本<服务器版本 - 升级
  3. 如果客户版本>服务器版本:
    1. 如果在客户端指定minimumVersion> =服务器版本 - 显示错误,我们有
    2. 如果在客户端上指定了minimumVersion<服务器版本 - 降级
    3. 如果未在客户端指定minimumVersion - 降级

答案 6 :(得分:0)

我只需在我的实时制作服务器上执行其中一个操作,并且很高兴获得所有这些注释。我的解决方案有点不同,我想将其添加为修复程序。在进行生产部署之前,我总是预先备份整个包含文件夹。我能够将整个文件夹结构复制回原始状态,一切正常。

注意这种方法的注意事项:

  • 如果应用程序很大,或者应用程序文件文件夹中已经发布了很多版本,那么备份将会很大。确保你有足够的空间(对我来说,存储不是对象)。
  • 权限有一种令人讨厌的倾向,就是这样咬你。确保您的部署位置是否为托管以进行外部访问,以便在还原之前和之后验证所有权限。
  • IIS中回收我的应用程序池很有帮助。

答案 7 :(得分:0)

如果您知道发布者URI以及部署和应用程序的名称,版本语言公钥令牌和处理器体系结构,则可以通过反射来完成此操作。

以下代码将尝试回滚“coolapp.app”ClickOnce应用程序。如果它无法回滚,它将尝试卸载它。

using System;
using System.Deployment.Application;
using System.Reflection;

namespace ClickOnceAppRollback
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        static void Main()
        {
            string appId = string.Format("{0}#{1}, Version={2}, Culture={3}, PublicKeyToken={4}, processorArchitecture={5}/{6}, Version={7}, Culture={8}, PublicKeyToken={9}, processorArchitecture={10}, type={11}",
                /*The URI location of the app*/@"http://www.microsoft.com/coolapp.exe.application",
                /*The application's assemblyIdentity name*/"coolapp.app",
                /*The application's assemblyIdentity version*/"10.8.62.17109",
                /*The application's assemblyIdentity language*/"neutral",
                /*The application's assemblyIdentity public Key Token*/"0000000000000000",
                /*The application's assemblyIdentity processor architecture*/"msil",
                /*The deployment's dependentAssembly name*/"coolapp.exe",
                /*The deployment's dependentAssembly version*/"10.8.62.17109",
                /*The deployment's dependentAssembly language*/"neutral",
                /*The deployment's dependentAssembly public Key Token*/"0000000000000000",
                /*The deployment's dependentAssembly processor architecture*/"msil",
                /*The deployment's dependentAssembly type*/"win32");

            var ctor = typeof(ApplicationDeployment).GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof(string) }, null);
            var appDeployment = ctor.Invoke(new object[] { appId });

            var subState = appDeployment.GetType().GetField("_subState", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(appDeployment);
            var subStore = appDeployment.GetType().GetField("_subStore", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(appDeployment);
            try
            {
                subStore.GetType().GetMethod("RollbackSubscription").Invoke(subStore, new object[] { subState });
            }
            catch
            {
                subStore.GetType().GetMethod("UninstallSubscription").Invoke(subStore, new object[] { subState });
            }
        }
    }
}

答案 8 :(得分:0)

仅以此为基础即可回滚Visual Studio 2017中开发的clickonce应用程序。在我的情况下,根文件夹中只有两个文件;一个叫做[applicationName] .manifest,另一个叫做setup.exe。

[applicationName] .manifest包含一个对当前版本号的引用,但是每个都链接到publicKeyToken值,因此我不愿意手动对其进行编辑。

因此,在Application Files文件夹中,包含要回滚的版本的子文件夹下,我找到了另一个[applicationName] .manifest,并将其复制到根文件夹中(已备份了原始文件)。

就是这样。它对我有用,并且是一个非常简单的解决方案。但是我没有使用最低要求的版本,所以不能说是否会影响它。