55

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

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

4

9 回答 9

91

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

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

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

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

于 2009-11-02T22:24:14.417 回答
21

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

早在 5 月,我的好友 David 写了一篇文章,介绍如何在每个用户的基础上执行此操作。我们实际上可以让每个用户使用不同的版本。应用程序甚至告诉数据库用户想要哪个版本,所以理论上他们可以更改他们的版本,然后简单地重新启动应用程序。

使用 ClickOnce 进行细粒度版本控制

于 2008-10-13T23:20:21.277 回答
9

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

于 2008-10-13T22:59:43.300 回答
4

只是用它来回滚在 Visual Studio 2017 中开发的 clickonce 应用程序。在我的例子中,在根文件夹中,只有两个文件;一个名为 [applicationName].manifest,另一个名为 setup.exe。

[applicationName].manifest 包含对当前版本号的数字引用,但每个都链接到 publicKeyToken 值,所以我不愿意手动编辑它。

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

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

于 2018-11-29T08:58:44.783 回答
2

您可以使用MAGEUI回滚到服务器上以前的清单版本。看看这个

于 2008-10-13T23:29:01.273 回答
1

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

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

于 2008-10-13T23:16:03.813 回答
1

我理解 ClickOnce 版本检查算法如下:

  1. 如果客户端上安装的版本 = 部署到服务器的版本 - 什么也不做
  2. 如果客户端版本 < 服务器版本 - 升级
  3. 如果客户端版本 > 服务器版本:
    1. 如果客户端上指定的 minimumVersion >= 服务器版本 - 显示错误,因为我们有
    2. 如果客户端上指定的 minimumVersion < 服务器版本 - 降级
    3. 如果未在客户端上指定 minimumVersion - 降级
于 2013-09-24T15:55:23.213 回答
0

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

此方法的注意事项:

  • 如果应用程序很大,或者应用程序文件夹中已经发布了很多版本,您的备份将很大。确保你有足够的空间(对我来说存储不是对象)。
  • 权限有一种以这种方式咬你的讨厌趋势。如果您的部署位置是为外部访问托管的,请确保您在还原前后验证所有权限。
  • IIS中回收我的应用程序池对我很有帮助。
于 2012-02-22T17:37:58.200 回答
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 });
            }
        }
    }
}
于 2012-06-28T05:32:14.497 回答