Click here to Skip to main content
Click here to Skip to main content

VS.NET Version Resource Auto-Increment Add-in

, 12 Dec 2004 CPOL
Rate this:
Please Sign up or sign in to vote.
A VS.NET version resource auto-increment add-in.

Sample Image

Introduction

Version number maintenance is a hassle. I think this is a key contributor to why so many people do it so many ways. Some update the version at various milestones, others just when they feel like it, and still others only on a full release. Then there’s the group that doesn’t do it at all unless asked. I won’t say which I fell into, but it isn’t a problem for me anymore.

This add-in is specifically tailored to .RC files as used by VC++ .NET. It automatically increments the last number (i.e.: 1.0.0.xxx) for every project in a given solution whenever Build or Rebuild is performed. The 1.0.0 part is your own to deal with. I expect many change the first three numbers rarely, so I’ve only automated the last digits.

Just install VSAutoVerSetup.msi, and you’re set! No hassle of adding macros, or any other overly complicated tasks.

Background

There’re lots of things to consider for such a simple task. Particularly, when you’re about to release it out of your own little world and into everyone else's. Things like version control, dirty flags, etc. all need to be taken into account as much as possible, as well as some of the more basic usage scenarios this add-in may be presented with.

It should also probably be said that I’m fairly new to C#, so please cut me some slack. This add-in being only the 2nd useful thing I’ve used C# for.

Using the code

And here’s where it all begins. Our add-in’s OnConnection method!

public void OnConnection(object application, 
        Extensibility.ext_ConnectMode connectMode, object addInInst, 
        ref System.Array custom)
{
    applicationObject = (_DTE)application;
    addInInstance = (AddIn)addInInst;

    Commands commands=applicationObject.Commands;

    cmdBuildSolution = commands.Item("Build.BuildSolution", 0);
    cmdRebuildSolution = commands.Item("Build.RebuildSolution", 0);

    commandEventsBuild = 
       applicationObject.Events.get_CommandEvents(cmdBuildSolution.Guid, 
       cmdBuildSolution.ID);
    commandEventsBuild.BeforeExecute += new 
      _dispCommandEvents_BeforeExecuteEventHandler(OnBeforeExecute);

    commandEventsRebuild = 
      applicationObject.Events.get_CommandEvents(cmdRebuildSolution.Guid, 
      cmdRebuildSolution.ID);
    commandEventsRebuild.BeforeExecute += new 
      _dispCommandEvents_BeforeExecuteEventHandler(OnBeforeExecute);
}

I think this is key to what makes this add-in so great compared to what's already available to solve the problem. We will inject ourselves into each of the two ‘Build’ commands shown above. BuildSolution and RebuildSolution should be all that’s necessary. It may make sense to add others, but I haven’t come up with any yet.

The BeforeExecute event chain is pretty straight forward, in that our OnBeforeExecute method will get called before the real Build.BuildSolution or Build.RebuildSolution do. Luckily, both are similar enough that we can stick with one event handler.

public void OnBeforeExecute(string Guid, int ID, object CustomIn, 
      object CustomOut, ref bool CancelDefault)
{
    if (cmdBuildSolution.ID != ID && cmdRebuildSolution.ID != ID)
        return;

    CancelDefault = false;

    try
    {
        if (applicationObject.Solution.IsOpen)
        {
            CloseRCViews();

            for (int i = 1; i <= applicationObject.Solution.Projects.Count; i++)
            {
                VCProject vcproj = 
                  applicationObject.Solution.Projects.Item(i).Object as VCProject;
                if (vcproj == null)
                                            continue;
                UpdateResourceVer(vcproj);
            }
        }
    }
    catch (Exception)
    {
    }
}

Doing the ID check at the beginning probably isn’t necessary since we bind to the specific commands we want. If we were to do something more generic with regards to the command event chain, like bind to Build.* instead, we would definitely want that ID check in there.

After we have determined that a solution is actually open (we may not even need to do that either), we must first close any open .RC files. Before I added the CloseRCViews method, I was quite perplexed at the behavior. Basically, the version number would get incremented in the RC file, but it seemed nothing would make the RC editors sync up with the RC file’s new values. Even exiting Visual Studio and reloading it, and the project didn’t help. It would still show the wrong numbers. I never found out where those numbers were getting cached at, bummer. Luckily for me, The Code Project had the answer for me, more specifically Roman Komary. You know, the gentleman who made the macros to do this very task. Closing the RC files was just what I needed to do in order to avoid ‘the fight’. Thanks Roman!

The method below is what does a good deal of the grunt work. I’ve tried to take source control into account as best as I could imagine. Roman’s macro based incrementor would check out a file in order to update the version resource. I didn’t find that to be quite reasonable. My work habits and environment have me checking out an entire project at a time. It could be argued that this is a bad way to work, but at the same time, having a developer make a conscious decision as to whether to increment a version number (by checking out the resource file himself) of a project he might not even need to work on is better than doing it for him and having him mad at me for f’ing it up. So, the short story is, if it isn’t checked out already, we don’t touch it. If all is good, we look for our version resource bits and send them across town to the body shop…

private void UpdateResourceVer(VCProject vcproj)
{
    SourceControl srcControl = applicationObject.SourceControl;

    IVCCollection files = vcproj.Files as IVCCollection;
    for (int i = 1; i <= files.Count; i++)
    {
        VCFile file = files.Item(i) as VCFile;
        string strName = file.Name;
        strName = strName.ToUpper();
        if (!strName.EndsWith(".RC"))
            continue;

        if (srcControl.IsItemUnderSCC(file.ItemName))
            if (!srcControl.IsItemCheckedOut(file.ItemName))
                continue;

        try 
        {
            StreamReader sr = new StreamReader(file.FullPath);

            String[] lines = new String[100000];
            int nCnt = 0; 
            String line;

            while ((line = sr.ReadLine()) != null) 
            {
                if (line.IndexOf("PRODUCTVERSION") != -1)
                    line = IncrementVer(line);
                if (line.IndexOf("FILEVERSION") != -1)
                    line = IncrementVer(line);
                if (line.IndexOf("VALUE \"ProductVersion\"") != -1)
                    line = IncrementVer(line);
                if (line.IndexOf("VALUE \"FileVersion\"") != -1)
                    line = IncrementVer(line);
                //wr.WriteLine(line);

                lines[nCnt] = line;
                nCnt++;
            }
            sr.Close();

            StreamWriter wr = new StreamWriter(file.FullPath);
            for (int linenum = 0; linenum < nCnt; linenum++)
                                   wr.WriteLine(lines[linenum]);
            wr.Close();
        }
        catch (Exception) 
        {
        }
    }
}

The version incrementing is just some fairly simple string parsing. The lastQuote makes it so we can use one function to handle both formats we’ll encounter in the resource file.

private string IncrementVer(string line)
{
    int lastComma = line.LastIndexOf(",");
    int lastQuote = line.LastIndexOf("\"");
    int end = line.Length;

    if (lastQuote != -1)
        end = lastQuote;

    int val = int.Parse(line.Substring(lastComma+1, end - (lastComma+1)));
    val++;
    line = line.Remove(lastComma+1, end - (lastComma+1));
    line = line.Insert(lastComma+1, val.ToString());

    return line;
}

Let's finish up by disconnecting our events when we’re unloaded…

public void OnDisconnection(Extensibility.ext_DisconnectMode 
             disconnectMode, ref System.Array custom)
{
    commandEventsBuild.BeforeExecute -= new 
        _dispCommandEvents_BeforeExecuteEventHandler(OnBeforeExecute);
    commandEventsBuild = null;

    commandEventsRebuild.BeforeExecute -= new 
        _dispCommandEvents_BeforeExecuteEventHandler(OnBeforeExecute);
    commandEventsRebuild = null;
}

Points of Interest

The Visual Studio automation Help helped me through the vast majority of this, as well as Roman’s sample to keep the version #’s showing what the RC file actually said. Not sure how or why it works, but it does, so I won’t complain. Total development time for this add-in was three hours not including the time spent on the article.

The most interesting thing about this article I think is just how easy it was in comparison to how long we’ve all been without a really easy to use solution.

History

  • 1.0 – Initial release.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Brandon Sneed
Web Developer
United States United States
Brandon is a developer. What else needs to be said?

Comments and Discussions

 
Questionsetup PinmemberAllen Maizenberg15-Feb-13 10:21 
GeneralUpdate for VS 2008 PinmemberMember 391202122-Feb-09 14:40 
QuestionVS.NET Version Resource Auto-Increment Add-in Pinmemberabhay4u23-Sep-08 19:04 
GeneralIt doesn't seem to work PinmemberTrevor Larkum26-Jul-05 4:32 
QuestionDifference between this and VS.NET's native support for auto-incrementing versions? PinmemberWaldenL20-Dec-04 5:33 
AnswerRe: Difference between this and VS.NET's native support for auto-incrementing versions? PinsussAnonymous20-Dec-04 5:40 
GeneralRe: Difference between this and VS.NET's native support for auto-incrementing versions? PinmemberWaldenL20-Dec-04 5:42 
GeneralRe: Difference between this and VS.NET's native support for auto-incrementing versions? PinmemberBoredSenseless18-Jan-05 14:48 
GeneralRe: Difference between this and VS.NET's native support for auto-incrementing versions? Pinmemberalex_vara23-Aug-06 5:30 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.1411023.1 | Last Updated 12 Dec 2004
Article Copyright 2004 by Brandon Sneed
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid