santBart santBart - 11 months ago 55
C# Question

Set AssemblyInfo Version numbers with MSI setup version

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?

Answer Source

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:

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;
    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");

        if (!File.Exists(fileName)) {
            ("Error: Can not find file \"" + fileName + "\"");

        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);

        File.Move(fileName + ".out", fileName);

    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]);
            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.
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyDescription("Flavor=Debug")] // a.k.a. "Comments"
[assembly: AssemblyConfiguration("Retail")]
[assembly: AssemblyDescription("Flavor=Retail")] // a.k.a. "Comments"

[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("")]

// 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("")] // 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