55

Is there a way (hacky will do) to allow a user to go back to a previous version of a ClickOnce network deployed application?

I've looked in the docs and API and there seems to be no way. You can selectively choose if you would like to update, but once updated there is, seemingly, no way back.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Jan Bannister
  • 4,859
  • 8
  • 38
  • 45

9 Answers9

92

You can revert back to an older version on the server side by changing the server manifest file. When the client restarts the application, it will see that it has a different version than what the server says is the 'current' version, and it will download the new one. This server manifest file usually always points to the most recent version, but it doesn't have to.

Here's how to change it (I published using Visual Studio 2008. Other versions might have a different publish folder structure).

In the same folder as the publish.htm is an XML document called [appName].application. This is the server-side manifest file that the client uses to compare its current version against. Contained in this document includes the 'current' version that the client should be running as well as the location on the server that the deployment files can be found.

In the same location as the publish.htm is also a folder called 'Application Files'. This folder contains subfolders for each of the previous publishes. Within each of these sub-folders is another XML document with the same name I mentioned above called [appName].application. Copy this file (from whatever folder that contains the version you want to revert back to) and paste it into the same folder as publish.htm (a couple of levels up). When the client application restarts, it will appear just like a new version is available, download it, and run it. The client will now be running a previous version.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Jason Hornor
  • 921
  • 1
  • 6
  • 2
  • Thanks Jason, I ended up using just such and approach :) – Jan Bannister Nov 03 '09 at 10:14
  • 2
    Additionally, you could manipulate the publish.htm file to reflect the corresponding version number (inside the AppInfo block) – jan Sep 14 '11 at 14:47
  • 3
    Sadly this doesn't work when the MinimumRequiredVersion is set, and it's higher than the version we want to roll back to – belidzs Sep 29 '14 at 07:23
  • This is the easiest way to rollback to a previous version, simple and reliable! Just gotta keep in mind what belidzs said, that if a MinimumRequiredVersion was set it was higher than the version you want to rollback, then it wont work. Apart from that kudos. – Luishg Jan 24 '18 at 02:25
21

ClickOnce will use whatever version you send them. If you send them an old version, they will rollback to that old version.

Back in May my buddy David wrote an article on how to do this on a per-user basis. We can literally have every user on a different version. The application even tells the database which version the user wants, so they could in theory change their version and then simply restart the application.

Fine Grained Versioning with ClickOnce

Jonathan Allen
  • 68,373
  • 70
  • 259
  • 447
9

You can go in Add/Remove application and select your application and choose to get the last installation instead.

Patrick Desjardins
  • 136,852
  • 88
  • 292
  • 341
5

Just used this to roll back a clickonce application developed in Visual Studio 2017. In my case, in the root folder, there were just two files; one called [applicationName].manifest, the other setup.exe.

The [applicationName].manifest contained a number references to the current version number but each were linked to a publicKeyToken value so I was reluctant to manually edit it.

So, in the Application Files folder, under to the sub folder containing the version I wanted to roll back to, I found another [applicationName].manifest, which I copied to the root folder (having backed up the original).

And that was it. It worked for me and was a really simple solution. However I'm not using a minimum required version so can't say whether that would affect it.

Jon Roberts
  • 2,262
  • 2
  • 13
  • 17
2

If you look at your deployment location, you'll see every previous version, in a separate folder with the version number appended, as well as the deployment manifest, also with the version number appended.

You can rename any one of them to be the current deployment, and the next time you update that application, it'll pull in the version you rolled-back to.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
rjrapson
  • 1,987
  • 1
  • 21
  • 28
  • I don't know if that would work. The deployment manifest has a path to the application manifest, but the app manifest I believe has a version in it as well. You also can't just update it without resigning. – The Muffin Man Oct 06 '14 at 05:48
2

You can use MAGEUI to rollback to a previous manifest version on the server. Check this out.

Gulzar Nazim
  • 51,744
  • 26
  • 128
  • 170
  • You can, provided (a) the currently installed version has never had the minimum required version set in the manifest, and (b) the possibility of not being able to read the previous version's app settings. See my comment on this answer http://stackoverflow.com/a/273719/607117 for more information about that last point. – Derreck Dean Dec 01 '16 at 19:16
1

I understand ClickOnce version check algorythm as follows:

  1. If version installed on client = version deployed to server - do nothing
  2. If client version < server version - upgrade
  3. If client version > server version:
    1. If minimumVersion specified on client >= server version - show error as we have
    2. If minimumVersion specified on client < server version - downgrade
    3. If minimumVersion is not specified on client - downgrade
Vitaliy Ulantikov
  • 10,157
  • 3
  • 61
  • 54
  • Step 3.1 : there is no error of MinVersion = ServerVersion. Step 3.2 : Downgrade if MinVersion <= ServerVersion – Ama Dec 05 '19 at 01:17
0

This can be done via reflection if you know the publisher URI and the name, version language public key token and processor architecture of both the deployment and the application.

The below code will try to rollback the "coolapp.app" ClickOnce application. If it can't roll back, it will try to uninstall it.

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 });
            }
        }
    }
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Hasani Blackwell
  • 2,026
  • 1
  • 13
  • 10
0

I just had to do one of these on my live production server, and it was nice to have all these notes. My solution was a bit different, and I wanted to add this as a fix as well. Before I do a production deployment I always backup the entire containing folder beforehand. I was able to copy my entire folder structure back into its original state and everything worked fine.

Notes of caution with this method:

  • Your backup will be large if the application is quite big, or there are a lot of versions already published in the application files folder. Make sure you have enough room (for me storage is no object).
  • Permissions have a nasty tendency of biting you this way. Ensure if your deployment location is hosted for external access that you verify all permissions before and after restoration.
  • It was helpful for me to recycle my application pool in IIS.
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Matt
  • 1,441
  • 1
  • 15
  • 29