Set AssemblyInfo Version numbers with MSI setup version

35,515

Solution 1

Projects have Assembly & File version numbers: (not setup versions I edited your question accordingly) enter image description here

Answer 1:

If you want to make the Setup projects version number set the Assembly & File version numbers you need to do it with a script/exe that gets triggered by the build.

enter image description here

This article on How To Update Assembly Version Number Automatically shows half the solution...

From the research I did it is not possible to use the SetupVersion in a PreBuildEvent. There isn't a $SetupVersion command for it: http://msdn.microsoft.com/en-us/library/42x5kfw4(v=vs.80).aspx

Having to change the PreBuildEvent each build as shown in this comment in the Code Project article using the -set: command is not ideal.

The solution we need is a PreBuildEvent to call the AssemblyInfoUtil.exe and have it read the "ProductVersion" from the vdproj project file. And then update the Assembly version number(s).

I have modified the code from the article to show you how to read the product version from the Setup.vdproj and this is how it can be called from a PreBuildEvent:

AssemblyInfoUtil.exe -setup:"C:\Program Files\MyProject1\Setup1\Setup1.vdproj" -ass:"C:\Program Files\MyProject1\AssemblyInfo.cs"

This is the modified code:

using System;
using System.IO;
using System.Text;

namespace AssemblyInfoUtil
{
    class AssemblyInfoUtil
    {
    private static int incParamNum = 0;    
    private static string fileName = "";  
    private static string setupfileName = "";       
    private static string versionStr = null;    
    private static bool isVB = false;
    [STAThread]
    static void Main(string[] args)
    {
        for (int i = 0; i < args.Length; i++) {
            if (args[i].StartsWith("-setup:")) {
           string s = args[i].Substring("-setup:".Length);
           setupfileName = int.Parse(s);
            }
            else if (args[i].StartsWith("-ass:")) {
           fileName = args[i].Substring("-ass:".Length);
            }
        }

        //Jeremy Thompson showing how to detect "ProductVersion" = "8:1.0.0" in vdproj
        string setupproj = System.IO.File.ReadAllText(setupfileName);
        int startPosOfProductVersion = setupproj.IndexOf("\"ProductVersion\" = \"") +20;
        int endPosOfProductVersion = setupproj.IndexOf(Environment.NewLine, startPosOfProductVersion) - startPosOfProductVersion;
        string versionStr = setupproj.Substring(startPosOfProductVersion, endPosOfProductVersion);
        versionStr = versionStr.Replace("\"", string.Empty).Replace("8:",string.Empty);

        if (Path.GetExtension(fileName).ToLower() == ".vb")
        isVB = true;

        if (fileName == "") {
        System.Console.WriteLine("Usage: AssemblyInfoUtil 
           <path to :Setup.vdproj file> and  <path to AssemblyInfo.cs or AssemblyInfo.vb file> [options]");
        System.Console.WriteLine("Options: ");
        System.Console.WriteLine("  -setup:Setup.vdproj file path");
        System.Console.WriteLine("  -ass:Assembly file path");
        return;
        }

        if (!File.Exists(fileName)) {
        System.Console.WriteLine
            ("Error: Can not find file \"" + fileName + "\"");
        return;
        }

        System.Console.Write("Processing \"" + fileName + "\"...");
        StreamReader reader = new StreamReader(fileName);
             StreamWriter writer = new StreamWriter(fileName + ".out");
        String line;

        while ((line = reader.ReadLine()) != null) {
        line = ProcessLine(line);
        writer.WriteLine(line);
        }
        reader.Close();
        writer.Close();

        File.Delete(fileName);
        File.Move(fileName + ".out", fileName);
        System.Console.WriteLine("Done!");
    }

    private static string ProcessLine(string line) {
        if (isVB) {
        line = ProcessLinePart(line, "<Assembly: AssemblyVersion(\"");
        line = ProcessLinePart(line, "<Assembly: AssemblyFileVersion(\"");
        } 
        else {
        line = ProcessLinePart(line, "[assembly: AssemblyVersion(\"");
        line = ProcessLinePart(line, "[assembly: AssemblyFileVersion(\"");
        }
        return line;
    }

    private static string ProcessLinePart(string line, string part) {
        int spos = line.IndexOf(part);
        if (spos >= 0) {
        spos += part.Length;
        int epos = line.IndexOf('"', spos);
        string oldVersion = line.Substring(spos, epos - spos);
        string newVersion = "";
        bool performChange = false;

        if (incParamNum > 0) {
            string[] nums = oldVersion.Split('.');
            if (nums.Length >= incParamNum && nums[incParamNum - 1] != "*") {
            Int64 val = Int64.Parse(nums[incParamNum - 1]);
            val++;
            nums[incParamNum - 1] = val.ToString();
            newVersion = nums[0]; 
            for (int i = 1; i < nums.Length; i++) {
                newVersion += "." + nums[i];
            }
            performChange = true;
            }
        }

        else if (versionStr != null) {
            newVersion = versionStr;
            performChange = true;
        }

        if (performChange) {
            StringBuilder str = new StringBuilder(line);
            str.Remove(spos, epos - spos);
            str.Insert(spos, newVersion);
            line = str.ToString();
        }
        } 
        return line;
    }
     }
}

Answer 2:

To my way of thinking a better way is to use a Shared Assembly Info class rather than individual AssemblyInfo class files.

To implement this, create a file in the solution folder named SharedAssemblyInfo.cs and then add a link in each project to SharedAssemblyInfo.cs. You can also move the linked SharedAssemblyInfo.cs into the Properties folder so that it sits side-by-side with the AssemblyInfo.cs that is specific to each project in the solution, as shown below.

enter image description here

Here is a sample SharedAssemblyInfo.cs file:

using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following 
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyCompany("Saint Bart Technologies")]
[assembly: AssemblyProduct("Demo")]
[assembly: AssemblyCopyright("Copyright ? Saint Bart 2013")]
[assembly: AssemblyTrademark("")]

// Make it easy to distinguish Debug and Release (i.e. Retail) builds;
// for example, through the file properties window.
#if DEBUG
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyDescription("Flavor=Debug")] // a.k.a. "Comments"
#else
[assembly: AssemblyConfiguration("Retail")]
[assembly: AssemblyDescription("Flavor=Retail")] // a.k.a. "Comments"
#endif

[assembly: CLSCompliant(true)]

// Setting ComVisible to false makes the types in this assembly not visible 
// to COM components.  If you need to access a type in this assembly from 
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]

// Note that the assembly version does not get incremented for every build
// to avoid problems with assembly binding (or requiring a policy or
// <bindingRedirect> in the config file).
//
// The AssemblyFileVersionAttribute is incremented with every build in order
// to distinguish one build from another. AssemblyFileVersion is specified
// in AssemblyVersionInfo.cs so that it can be easily incremented by the
// automated build process.
[assembly: AssemblyVersion("1.0.0.0")]

// By default, the "Product version" shown in the file properties window is
// the same as the value specified for AssemblyFileVersionAttribute.
// Set AssemblyInformationalVersionAttribute to be the same as
// AssemblyVersionAttribute so that the "Product version" in the file
// properties window matches the version displayed in the GAC shell extension.
[assembly: AssemblyInformationalVersion("1.0.0.0")] // a.k.a. "Product version"

Here is a sample AssemblyInfo.cs file:

// Note: Shared assembly information is specified in SharedAssemblyInfo.cs
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following 
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("WindowsFormsApplication2")]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("ffded14d-6c95-440b-a45d-e1f502476539")]

So each time you want to change all projects Assembly info you can do it in one spot. I assume you would want to set the MSI Setup Version the same as the Assembly version number, one manual step.


Answer 3:

Consider switching to use MSBuild it has all these kinds of benefits but I'm not sure if you have the time to pick it up right now.


Answer 4:

Assemblies can auto-increment their build numbers using the following asterisk syntax within AssemblyInfo.cs:

[assembly: AssemblyVersion("1.0.0.*")]

This is a good method because the point of tracking a build number is to be able to recognize different builds. Having a pre-build changing build numbers defeats this purpose as the build has not yet occurred.


Answer 5:

The other CodeProject answer here assumes you want to update the ProductVersion, ProductCode, PackageCode in the Setup MSI Project file. I didn't interpret your question that way and according to this thread there are problems: pre-build event to change setup project's ProductVersion doesn't take effect until after the build

Answer 6 (new):

There is a few TFS Build plugins to set "Assembly Info": https://marketplace.visualstudio.com/items?itemName=bleddynrichards.Assembly-Info-Task https://marketplace.visualstudio.com/items?itemName=bool.update-assembly-info https://marketplace.visualstudio.com/items?itemName=ggarbuglia.setassemblyversion-task

Solution 2

I don't know if this solves your problem perfectly but you could implement a common class with all the configmanagment informations like:

public class VersionInfo{
    public const string cProductVersion = "1.0.0"
    //other version info
}

After you can update all your AssemblyInfo.cs with the new class:

[assembly: AssemblyVersion(VersionInfo.cProductVersion)]

I hope this helps.

Share:
35,515

Related videos on Youtube

santBart
Author by

santBart

Updated on July 05, 2022

Comments

  • santBart
    santBart almost 2 years

    I am using a setup project to publish my projects. I want the version of each project to be the same as the setup version.

    I want to change my setup version property in Visual Studio and after building, for all project versions to be updated from this property, is this possible?

    • Simon Mourier
      Simon Mourier about 11 years
      what is the "setup version"?
    • Jeremy Thompson
      Jeremy Thompson about 11 years
      @SimonMourier that confused me at first too, its 2nd screenshot shown in my answer.
    • Simon Mourier
      Simon Mourier about 11 years
      Oh, this "Version" property of setup project is not written in the final MSI file, as far as I know, so what's the point?
    • Jeremy Thompson
      Jeremy Thompson about 11 years
      I've edited my answer(s) - I hope they cover all bases. @SimonMourier FYI, the "Version" property is written in because you can get the Version from a MSI programatically. Problem is that could only be used with a PostBuildEvent which would use the last version not the most up-to-date. Cheers
    • Jeremy Thompson
      Jeremy Thompson about 11 years
      To clarify that last comment, you would have to build twice and the 2nd build the assembly.cs files would have the most up-to-date version
  • Jeremy Thompson
    Jeremy Thompson about 11 years
    See my #2 answer for the official way to do this, good try though
  • yaens
    yaens about 11 years
    thx for the hint, i will change my own project setup after your solution
  • Marco Guignard
    Marco Guignard about 7 years
    Great answer, but I have found some issue in the code sample of the answer 1. There is no need to parse args to integer for the SetupfileName (compilation error) and versionStr should not be re-declared as a local variable (make the script doing nothing without any error !).
  • Ian
    Ian over 4 years
    Does #2 work with VS Setup projects? I can't see a way for the setup project to recognise the .cs file as part of it's own assembly, rather than something to install.
  • Jeremy Thompson
    Jeremy Thompson over 4 years
    @Ian It doesn't work like that, you track the version of the build not the system used to do the build.
  • Ian
    Ian over 4 years
    Thanks @Jeremy, I'm afraid I don't understand. I'm trying to keep my application and installer project build versions in sync (as per the OP). Solution #2 looks simplest and neatest, but I can't see a way to make it work with the Installer Project (which doesn't have an AssemblyInfo.cs).
  • Jeremy Thompson
    Jeremy Thompson over 4 years
    @Ian - perhaps if the Setup Project is part of the Solution it would work that way. The thing is you change the AssemblyInfo.cs in respective projects. An Installer doesn't have it because its not a DLL or an EXE. Its a build project. So you want to track the build. #2 Solution allows all projects to share the same AssemblyInfo.cs similar to #1 solution I think you want to have a Build project can fetch that info
  • Jeremy Thompson
    Jeremy Thompson over 4 years
    This is the sister Q & A: stackoverflow.com/q/11474320/495455