Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Visual Studio 2005 Auto-versioning Add-in

0.00/5 (No votes)
21 Oct 2007 1  
An add-in that handles incrementing assembly and file version numbers under certain conditions.

Introduction

Very often I've found myself unable to distinguish between several versions of my smaller programs, because I rarely bother to change version numbers every time I make a tiny change in code. Visual Basic 6 had the option to automatically increment the build/revision version number at each compile, and I thought this can be a good thing sometimes... However, I couldn't find any really satisfactory working solution for this.

Background

What I did find was a slew of tricks (including pre/post-build commands etc.) that were sometimes more cumbersome than changing the version manually, and one or two add-in ideas that made the changes programmatically. One of them was called VAV and it was a pre-alpha SourceForge project; my source code uses the name VAVE, which I admit I blatantly ripped off by adding 'Enhanced' to its name, 'VisualStudio Assembly Versionner'.

However, what that code did was not at all to my liking. The base idea was perfect - hooking into commands from the environment. But the actual work was being done in strange and unusual ways, while Visual Studio's object model was there all along to handle the details.

Using the Code

The actual code is concentrated in the add-in class, which Visual Studio names by default 'Connect'. The class implements IDTExtensibility2 and IDTCommandTarget, which allow it to be loaded as an add-in and respond to calls from the IDE, respectively.

First and foremost, the primary job of the add-in is incrementing the revision number on each build. But since I'm the kind of person that provides options even for a 'Hello world' application, this couldn't be it. It would be relatively simple to provide a command which increments the build number (third part of the version number) and resets the revision, and the same thing could be done for the minor version, the second part of the version information.

Incrementing the revision is done automatically by hooking into certain commands provided by Visual Studio; first and foremost, the Build.BuildSolution and Build.RebuildSolution, and Build.BuildOnlyProject and Build.RebuildOnlyProject commands are quite obvious. The Build.BuildSelection and Build.RebuildSelection build the selected project, i.e., the project currently being highlighted in the Solution Explorer (or to which the current open file belongs) - this is the command issued by the 'Build <project name>' buttons and item menus. Furthermore, the revision could also be incremented when starting a debug session, since that does a build too (Debug.Start and Debug.StartWithoutDebugging), but only if the developer actually wants that.

Hooking into a command is done by holding a reference to that command (while it isn't really necessary right now, I thought I could use it, but more on that later) and to an associated CommandEvents object:

Private _cmdBuildSolution As Command
Private WithEvents cmdBuildSolutionEvents As CommandEvents 

When the class connects to the IDE, namely in the OnConnection sub, the objects are set to their respective instances:

_cmdBuildSolution = commands.Item("Build.BuildSolution")
cmdBuildSolutionEvents = _applicationObject.Events.CommandEvents(_cmdBuildSolution.Guid, _
                         _cmdBuildSolution.ID) 

and the CommandEvent's two events are being pointed to our two handlers:

AddHandler cmdBuildSolutionEvents.BeforeExecute, _
    New _dispCommandEvents_BeforeExecuteEventHandler(AddressOf OnBeforeBuildAny)
AddHandler cmdBuildSolutionEvents.AfterExecute, _
    New _dispCommandEvents_AfterExecuteEventHandler(AddressOf OnAfterBuild)

Once all commands are pointed the right way, issuing that command in the IDE executes our OnBeforeBuildAny sub, then its actual command, then our OnAfterBuild sub. I used the OnAfterBuild only to output messages to the output window, since outputting anything before there's any default pane in the output tool-window is slightly problematic. The OnBeforeBuildAny simply accesses the solution object of the IDE, iterates through all the projects, and increments the version properties of each ('AssemblyVersion' and 'AssemblyFileVersion') - that simple. Of course, it does some extra checks and considers some extra options I threw in:

Private Sub Update(ByVal part As VersionParts)
  If Not _enabled Then Exit Sub

  Dim build as SolutionBuild = _applicationObject.Solution.SolutionBuild

  If _release AndAlso build.ActiveConfiguration.Name <> "Release" Then
    Exit Sub
  End If

  Dim pp As Project
  _queuedMsg = ""
  For j As Integer = 1 To _applicationObject.Solution.Projects.Count
    Try
      pp = _applicationObject.Solution.Projects.Item(j)
      Dim v1 As New Version(pp.Properties.Item("AssemblyVersion").Value.ToString)
      Dim v2 As New Version(pp.Properties.Item("AssemblyFileVersion").Value.ToString)

      Dim vv1 As Version, vv2 As Version

      Select Case part
        Case VersionParts.Build
          vv1 = New Version(v1.Major, v1.Minor, v1.Build + 1, 0)
          vv2 = New Version(v2.Major, v2.Minor, v2.Build + 1, 0)
        Case VersionParts.Minor
          vv1 = New Version(v1.Major, v1.Minor + 1, 0, 0)
          vv2 = New Version(v2.Major, v2.Minor + 1, 0, 0)
        Case VersionParts.Major
          vv1 = New Version(v1.Major + 1, 0, 0, 0)
          vv2 = New Version(v2.Major + 1, 0, 0, 0)
        Case VersionParts.None
          Exit Sub
        Case Else    'revision is also default for unknown values

          vv1 = New Version(v1.Major, v1.Minor, v1.Build, v1.Revision + 1)
          vv2 = New Version(v2.Major, v2.Minor, v2.Build, v2.Revision + 1)
      End Select

      pp.Properties.Item("AssemblyVersion").Value = vv1.ToString
      _queuedMsg &= " ---> VAVE: Updated assembly version for project " & pp.Name & _
                    " to " & vv1.ToString & " ---" & vbCrLf
      pp.Properties.Item("AssemblyFileVersion").Value = vv2.ToString
      _queuedMsg &= " ---> VAVE: Updated output file version for project " & pp.Name & _
                    " to " & vv2.ToString & " ---" & vbCrLf

      pp.Save()
    Catch
      _queuedMsg &= " ---> VAVE: Failed to increment version for one 
                      ---> of the projects. ---" & vbCrLf
    End Try
  Next
End Sub

As I said, I couldn't help myself and threw in a couple of extra options; there are two extra commands the add-in handles, 'Increment Build' and 'Increment Minor', a dialog that helps set options, and the actual options: enable/disable all functionality, increment revision only on build or on build and start with or without debugging, and only increment when the selected configuration is Release.

Points of Interest

Visual Studio boasts an extended extensibility model, but the documentation for it is terribly scant. These few lines of code took much trial and error, Reflection and tracing, but I finally took it to a point where I'm satisfied with what it does.

There are several things that might not please everybody, of course: since the BeforeExecute event happens before compiling, the version gets incremented even if the build fails for some reason (well, there really isn't a way around that, is there, if we want the build result's properties changed, unless we want to modify the result assemblies directly?); adding the commands to the Tools menu is actually done quite clumsily, but I was sick of inconsistent behaviors - if anyone can get it to work like it's supposed to, give me a pointer; when incrementing the revision, all the projects in the current solution are processed, which admittedly isn't very nice; and of course, if anyone enhances this thing with more options/functionality, post here and I'll incorporate changes whenever possible.

Also note that compiling and testing the add-in from the source might need adjusting the build settings, since after building, the results should be copied to one of Visual Studio's add-in locations and the IDE started with the /resetaddin <addin name> switch. My post-build command looks like this:

xcopy "$(TargetPath)" "C:\Documents and Settings\[user]\My Documents\
       Visual Studio 2005\Addins\" /Y &
xcopy "$(ProjectDir)VAVE.Addin" "C:\Documents and Settings\[user]\My Documents\
       Visual Studio 2005\Addins\" /Y  

History

  • 2007-10-21: Initial draft.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here