Programmatically change the AssemblyVersion and AssemblyFileVersion attributes
Asked Answered
S

3

9

During a setup creation process, I am planning to do the following (within a C# Script):

  1. Read the AssemblyVersion and AssemblyFileVersion attribute values of one DLL.
  2. Iterate all DLLs and executables in the project and apply those version to their AssemblyVersion and AssemblyFileVersion attribute values.

Question now is: How to do step 2?

I successfully can do step 1, but for step 2 I don't see a real start point. What I probably have to do is to use some native P/Invoke methods since the attributes directly modify the version information resource information of a DLL/executable.

Any hints on this?

Schechinger answered 11/10, 2009 at 9:18 Comment(1)
If you have access to the sources, take the advice from Graviton If you don't, you might be in trouble. Possibly you can disassemble with ILDASM and reassemble with ILASM. But this won't work out for strong-named assemblies.Standardize
S
13

Why don't you, during build process, read the AssemblyVersion and AssemblyFileVersion of one DLL, and save it back to other csproject's AssemblyInfo.cs, only then compile it?

As a matter of fact, I don't know whether it is possible to modify the DLL file directly, without resorting to something fanciful.

Or alternatively, make sure that all your DLLs share one common AssemblyInfo.cs. You can do this by adding the AssemblyInfo.cs as "Add As Link" when you add a new item in csproject. In this way when you do a compilation, all the DLLs will share the same AssemblyInfo.cs, and thus output the same AssemblyVersion.

Somebody answered 11/10, 2009 at 9:20 Comment(5)
yes, that would be much easier! Set the version numbers straight at build time, e.g. using a continuous integration build serverColostrum
That would be the right way, changing the compiled files break clean information line between code and binaries.Cumberland
Thank you very much, I do like the idea of sharing the AssemblyInfo.cs file!Schechinger
Not a good idea to share the same AssemblyInfo.cs - if your DLLs has ComVisible, then you can't differentiate GUID and other info on assemblySpeedwell
Just to remind the AssemblyInfo.cs contains not just the File & Assembly Version info but other information like ["Title","Description", "Company", "Product","Copyright","TradeMark"] and they cannot share the same name.Glyph
M
6

In my case, I created a T4 template for changing the AssemblyVersion and AssemblyFileVersion. I called the template Assembly.tt, and made it a linked file (when you add it choose Add Link, instead of Add). All my assemblies pull in the linked Assembly.tt file.

You can then run the T4 template from one location and it will update all the AssemblyVersion and AssemblyFileVersions. You don't have to call the file AssemblyInfo.cs for the information to be pulled into your dlls.

The code for the Assembly.tt file is:

<#@ template language="C#" hostspecific="true" #>
// 
// This code was generated by a tool. Any changes made manually will be lost
// the next time this code is regenerated.
// 

using System.Reflection;

[assembly: AssemblyVersion("4.<#= this.RevisionYear #>.<#= this.RevisionNumber #>.<#= this.RevisionTime #>")]
[assembly: AssemblyFileVersion("4.<#= this.RevisionYear #>.<#= this.RevisionNumber #>.<#= this.RevisionTime #>")]
<#+
    int RevisionYear = DateTime.UtcNow.Year;
    int RevisionNumber = (int)(DateTime.UtcNow - new DateTime(DateTime.UtcNow.Year,1,1)).TotalDays;
    int RevisionTime = (int)(DateTime.UtcNow - new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day)).TotalMinutes;
#>

The output of the T4 template above will be:

// 
// This code was generated by a tool. Any changes made manually will be lost
// the next time this code is regenerated.
// 

using System.Reflection;

[assembly: AssemblyVersion("4.2016.284.1066")]
[assembly: AssemblyFileVersion("4.2016.284.1066")]
Manicurist answered 11/10, 2016 at 17:48 Comment(1)
This actually will work as an answer to dynamically change the dll version on compile time, i wonder why this is not marked as answer.Glyph
G
4

The attribute [AssemblyVersion()] and [AssemblyFileVersion()] both accepts a constant string as parameter.

So I can create a static class with a const string in the current assembly, and can supply it to these attributes as parameters as below

    public static class App
    {
        public const string Version = "1.0.0.1";
    }

and update the AssemblyInfo.cs as

[assembly: AssemblyVersion(App.Version)]
[assembly: AssemblyFileVersion(App.Version)]

This is an alternative suggestion then the automation/trick suggested by @Kelly R and @Gravtion above.


Update 1 - Using T4 template

In case you want to go for T4 template as @KellyR suggested the following can help you with the version:


private string Version()
{
    DateTime START = new DateTime(2021,2, 14); //StartDate
    DateTime NOW = DateTime.Now;
    int numMonths = (int)(NOW.Subtract(START).Days / (365.25 / 12));
    int Major = numMonths / 12;
    int Minor = numMonths % 12;
    int Build = (int)DateTime.Now.Day;
    string Revision = $"{(int)DateTime.Now.Hour}{DateTime.Now.Minute}";
    
    return $"{Major}.{Minor}.{Build}.{Revision}";
}

The START stores the start date of the project which is used to calculate the number of months passed till now. Then it's dividing the numMonths by 12 to get the number of year passed(considering it as Major version) and keeps the remaining months as Minor version then the current day as Build and hour + Minutes as Revision.

With that every time you build you will get the latest version of your product.

This can also be used in the T4 template as follows:

<#@ output extension=".cs" #>
<#@ template debug="true" language="C#" hostspecific="false" #>

using System.Reflection;

[assembly: AssemblyVersion("<#= NewVersion() #>")]
[assembly: AssemblyFileVersion("<#= NewVersion() #>")]



<#+
    private string NewVersion()
    {
        DateTime START = new DateTime(2021,2, 14); //StartDate
        DateTime NOW = DateTime.Now;
        int numMonths = (int)(NOW.Subtract(START).Days / (365.25 / 12));
        int Major = numMonths / 12;
        int Minor = numMonths % 12;
        int Build = (int)DateTime.Now.Day;
        string Revision = $"{(int)DateTime.Now.Hour}{DateTime.Now.Minute}";
    
        return $"{Major}.{Minor}.{Build}.{Revision}";

    }
    //Started On = 12-5-2021;
    
    
#>

Just remember to comment AssemblyVersion and AssemblyFileVersion in AssemblyInfo.cs when using it as T4 template.

//[assembly: AssemblyVersion("1.0.0.0")]
//[assembly: AssemblyFileVersion("1.0.0.0")]

Update 2 - Using MsBuild to rewrite AssemblyInfo.cs with regx

We can also change the assembly version by editing the .csproj file by adding the following:

  <Target Name="AssemblyVersion" BeforeTargets="CoreCompile" DependsOnTargets="PrepareForBuild">
    <PropertyGroup>
      <AssemblyInfo>Properties\AssemblyInfo.cs</AssemblyInfo>
      <AssemblyInfoContent>$([System.IO.File]::ReadAllText($(AssemblyInfo)))</AssemblyInfoContent>
      <VersionRegex>(\[\s*assembly\s*:\s*AssemblyVersion\(\s*"(\d+)\.(\d+)\.(\d+)(\.)(\d+)("\)\s*\]))</VersionRegex>
      <BuildAndRevisionRegex>(\d+\.\d+")</BuildAndRevisionRegex>
      <AssemblyVersion>$([System.Text.RegularExpressions.Regex]::Match('$(AssemblyInfoContent)', '$(VersionRegex)'))</AssemblyVersion>

      <!-- New AssemblyVersion Block -->
      <NowTime>$([System.DateTime]::Now.Year)</NowTime>
      <MajorVersion>$([MSBuild]::Subtract($(NowTime), 2023).ToString("0"))</MajorVersion>
      <MinorBuildRevision>$([System.DateTime]::Now.ToString("M.d.hhm"))</MinorBuildRevision>
      
      <AssemblyVersion>[assembly: AssemblyVersion("$(MajorVersion).$(MinorBuildRevision)")]</AssemblyVersion>
    </PropertyGroup>
    <!-- Write New AssemblyVersion Block to AssemblyInfo.cs file -->
    <WriteLinesToFile File="$(AssemblyInfo)" Lines="$([System.Text.RegularExpressions.Regex]::Replace($(AssemblyInfoContent), $(VersionRegex), $(AssemblyVersion)))" Overwrite="true" />
  </Target>

What it does is it reads the AssemblyInfo.cs file and replaces the AssemblyVersion using regularexpressions and DateTime values.

Look for the below code where we can change the value 2023 to the any current/past year to generate the MajorVersion.

<MajorVersion>$([MSBuild]::Subtract($(NowTime), 2023).ToString("0"))</MajorVersion>
<MinorBuildRevision>$([System.DateTime]::Now.ToString("M.d.hhm"))</MinorBuildRevision>

The MajorVersion is calculated as DateTime.Now.Year - 2023 so expect it to be 0 if the first release year is 2023. The Minor,Build & Revision are dynamic in nature as it is derived with the DateTime component giving the value in format "M.d.hhm"

DateTime.Now.ToString("M.d.hhm")

At the time of this update it is giving a value 8.23.0458 and with the major version it becomes 0.8.23.0458


Update 3 - creating NewVersion.cs with AssemblyFileVersion

There is one more better way is to generate a new NewVersion.cs with AssemblyFileVersion attribute. Which can be achieved with WriteCodeFragment as:

  <Target Name="BeforeBuild">
    <PropertyGroup>
      <NowTime>$([System.DateTime]::Now.Year)</NowTime>
      <MajorVersion>$([MSBuild]::Subtract($(NowTime), 2023).ToString())</MajorVersion>
      <NewVersion>$(MajorVersion).$([System.DateTime]::Now.ToString("M.d.hhm"))</NewVersion>
    </PropertyGroup>
    <ItemGroup>
      <AssemblyAttributes Include="AssemblyFileVersion">
        <_Parameter1>$(NewVersion)</_Parameter1>
      </AssemblyAttributes>
    </ItemGroup>
    <WriteCodeFragment AssemblyAttributes="@(AssemblyAttributes)"
        Language="C#"
       OutputDirectory="$(IntermediateOutputPath)"
       OutputFile="NewVersion.cs">
      <Output TaskParameter="OutputFile" ItemName="Compile" />
      <Output TaskParameter="OutputFile" ItemName="FileWrites" />
    </WriteCodeFragment>
  </Target>

Just add the above to csproj file at the end and comment the AssemblyFileVersion in AssemblyInfo.cs else the compiler will complain for duplicate attribute.

This will create a NewVersion.cs to obj/Debug folder and will use it for compilation and keep updating the file with a new version.

Glyph answered 8/4, 2022 at 15:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.