Automatically Increment Minimum Required Version in a ClickOnce Application

Introduction

ClickOnce deployments make publishing .NET applications simple and easy. One nice feature is forcing the user to update to the latest available version. This can be very important on sensitive applications that require any potential fixes or patches to be immediately sent to the users.

One way to do this is to set the minimum required version on the publish page to the current version. ClickOnce then knows to automatically download the latest version whenever a user goes to run the application. The problem with this can be that you have to remember to increment the value every time you do a publish. This post will make life easier by automating the increase of the minimum required version every time you preform a publish.

We’ll do so by adding an MSBuild event to the project. This post assumes you don’t have any experience with MS build and will introduce you to the relevant concepts; if you’re already familiar with that, feel free to jump ahead to Step 6 and copy and paste the code into your .proj file.

Introduction to Project Editor

1) First, in solution explorer, right click on your project and select unload project

2) Once the project has become unavailable, right click again and select edit [project_name].csproj

  • Note: This unlocks full set of IntelliSense of all the MSBuild features

Introduction to MS Build

3) MS Build notation uses properties with key/value pairs to extract information. We’ll use the notation $(key) in the implementation section to reference the .proj elements in Step 4.

  • ex. Using the property name as an alias, you can use $(OutputPath) to obtain the value for the element <OutputPath>.\bin</OutputPath>. So in this case $(OutputPath) = .\bin

4) We’ll use the following properties generated for a ClickOnce deployment

<MinimumRequiredVersion>1.0.0.6</MinimumRequiredVersion>  
<ApplicationRevision>7</ApplicationRevision>  
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>  
  • The ApplicationVersion element specifies the build number. The last value of %2a is set by Visual Studio when the option to “Automatically increment revision with each publish” is checked on. In that case, it substitutes the %2a placeholder with whatever value is in the ApplicationRevision element
  • The MinimumRequiredVersion element is what we’d like to set with each publish so that it automatically increments as well.

5) MSBuild Tasks can be specified in the .proj file and invoked during a build event.

  • FormatVersion is a built-in task for .NET 4.0 and later that formats the ApplicationVersion and ApplicationRevision into a single version number

Implementation

6) Copy and Paste the following code into the opened project file as a child element to the root <Project> element:

<Target Name="AutoSetMinimumRequiredVersion"
        BeforeTargets="GenerateDeploymentManifest">
  <FormatVersion Version="$(ApplicationVersion)" 
                 Revision="$(ApplicationRevision)">
    <Output PropertyName="MinimumRequiredVersion" 
            TaskParameter="OutputVersion"  />
  </FormatVersion>
  <FormatVersion Version="$(ApplicationVersion)" 
                 Revision="$(ApplicationRevision)">
    <Output PropertyName="_DeploymentBuiltMinimumRequiredVersion"
            TaskParameter="OutputVersion"  />
  </FormatVersion>
</Target>
  • This code will take ApplicationVersion and ApplicationRevision as parameters in the Format Version task and will save the output by overwriting the MinimumRequiredVersion with the full publish version

7) Save and reload your project. Every ClickOnce deployment will now automatically update without a prompt with the ability to skip.

5 comments:

  1. Can't you use a variation of this to simply update the ApplicationRevision value?

    ReplyDelete
  2. May you please note that %2a is just escaped via urlencoding? For example, MSBuild will sometimes try to expand (for Items, for example) glob expressions but lets the developer escape a literal ‘*’ using %2a. I don’t think that the escape that guards against globbing is necessary in a PropertyGroup, though because it only has a meaning inside of Include/Exclude attributes inside an ItemGroup. Though, I suppose, if $(ApplicationVersion) is ever expanded inside of an ItemGroup maybe the escape is useful…

    The documentation for the FormatVersion Task describes how star expansion is performed on a version which is similar to how AssemblyVersionAttribute() works. ClickOnce uses the FormatVersion task with the Version parameter set to ApplicationVersion and the Revision parmeter set to ApplicationRevision to calculate the version to use in its publish.

    ReplyDelete
  3. Doesnt change the MinimumRequiredVersion in VS 2015.

    Any ideas?

    <MinimumRequiredVersion>1.0.0.12</MinimumRequiredVersion>
    <ApplicationRevision>21</ApplicationRevision>
    <ApplicationVersion>1.0.0.%2a</ApplicationVersion>

    <Target Name="AutoSetMinimumRequiredVersion" BeforeTargets="GenerateDeploymentManifest">
    <FormatVersion Version="$(ApplicationVersion)" Revision="$(ApplicationRevision)">
    <Output PropertyName="MinimumRequiredVersion" TaskParameter="OutputVersion" />
    </FormatVersion>
    <FormatVersion Version="$(ApplicationVersion)" Revision="$(ApplicationRevision)">
    <Output PropertyName="_DeploymentBuiltMinimumRequiredVersion" TaskParameter="OutputVersion" />
    </FormatVersion>
    </Target>

    ReplyDelete
  4. If you are publishing your ClickOnce application from Visual Studio then just install the AutoUpdateProjectsMinimumRequiredClickOnceVersion NuGet Package (https://www.nuget.org/packages/AutoUpdateProjectsMinimumRequiredClickOnceVersion) in your project and you're good to go.

    If you are publishing from a build server or other script, then you can use the Set-ProjectFilesClickOnceVersion PowerShell script (https://github.com/deadlydog/Set-ProjectFilesClickOnceVersion). My blog describes in more detail how to setup your build server to accommodate publishing ClickOnce applications (http://blog.danskingdom.com/continuously-deploy-your-clickonce-application-from-your-build-server/).

    ReplyDelete