Version Build

An automated method to generate meaningful version numbers in a MS Devstudio project's version resources has been a surprisingly popular topic. After following leads to a variety of solutions, none that I came across quite do what I would like. Here is a solution that suits my needs, perhaps it will suit yours too.

The resource version number is a structure of two double word values, the Most Significant Value (MSV) and the Least Significant Value (LSV). Each value is broken into two parts, the high word and the low word. In a resource script, this is represented by something like "FILEVERSION 1,0,0,1" or "PRODUCTVERSION 1,0,0,1".   The "1,0" is the high word/low word of the MSV. The "0,1" is the high word/low word of the LSV. There is also a string representation of the versions in the StringFileInfo block.

I find it desirable to to use the LSV to track incremental builds. Generally, marketing types like to dictate the MSV. So while I may be working on what I consider a Beta release, they want it stamped as version 2.1. But I digress.... :-)

The protocol I use for development versioning is to set the high word of the LSV to a value that indicates the year and day of the build. The low word counts the builds in the given day.

The way the high word is formulated is this: Using the ASCII decimal representation of the year, take the first digit ( e.g, 1998=1, 1999=1, 2000=2 ), multiply times 10, then add the last digit of the year (e.g., 1998=8, 1999=9, 2000=0), then multiply the sum by 1000 (e.g., 1998=18000, 1999=19000, 2000=20000). Now add the day of the year (e.g., Jan 1= 1, Jan 31=31, Feb 1=32). The high word for February 1, 1999 would therefore be 19032, the high word for December 25, 1998 would be 18356, while the high word for June 23, 2000 would be 20175.

The low word is started at 1 for the first build of the day, and incremented by 1 for each ensuing build.

Armed with this information, it is possible to go back in the source control application and find the version of the source that generated any given build. But of course it is tedious and demands more diligence than I can muster to reliably maintain the version by manual input. Thus I present this Devstudio macro that does all this for me, "VersionBuild".

When invoked, VersionBuild will attempt to locate the version resource. If successful, it will modify the FILEVERSION entry according to the protocol outlined above. If successful, VersionBuild will continue to build the project currently active.

By default, VersionBuild will synchronize the LSV of the PRODUCTVERSION as well as the StringFileInfo block's FileVersion and ProductVersion. There are two variables at the top of the macro, bPRODUCTVERSION and bStringFileInfo, which may be set appropriately to control which version types you want modified.

Sub VersionBuild()

'DESCRIPTION: Sets LSV of version number: Hi word = 1st digit of year + last digit of year + day of year; Lo word = 1 if new Hi word or previous + 1 if same Hi word

'Patrick Dell'Era

'patrickd@sirius.com

'

    Dim i

    Dim strHiWord

    Dim strLoWord

    Dim nHiWord, nOldHiWord

    Dim LSV

    Dim strTemp

    Dim strMsg

    Dim bPRODUCTVERSION, bStringFileInfo

    ' Set to True if you want the PRODUCTVERSION synchronized

    bPRODUCTVERSION = True

    ' Set to True if you want the StringFileInfo block synchronized

    bStringFileInfo = True

    strMsg = "There was a problem modifying your version resource. Could not locate "

    ' Open the resource file as text

    ' NOTE: You will probably be informed that the resource is

    ' already opened in the resource editor. You'll need to confirm

    ' that you want to close that and open the file as text. Otherwise,

    ' we will return an error informing you that the resource file is

    ' not in the documents collection

    ' Trap errors for likely suspects

    On Error Resume Next

    ' Attempt to open the document

    Documents.Open (ActiveProject.Name + ".rc"), "Text"

    If Err.Number <> 0 Then

        MsgBox ("Error 0x" & Hex(Err.Number) & ":  " & Err.Description)

        Exit Sub

    End If

    ' Try to bring it to the fore

    Windows(ActiveProject.Name + ".rc").Active = True

    If Err.Number <> 0 Then

        MsgBox ("Error 0x" & Hex(Err.Number) & ":  " & Err.Description)

        Exit Sub

    End If

    ' Seek out the version resource block

    If ActiveDocument.Selection.FindText("VS_VERSION_INFO VERSIONINFO", dsMatchWord + dsMatchCase) = False Then

        MsgBox strMsg + """VS_VERSION_INFO VERSIONINFO"""

        Exit Sub

    End If

    ' Seek the binary FILEVERSION entry

    If ActiveDocument.Selection.FindText("FILEVERSION", dsMatchWord + dsMatchCase) = False Then

        MsgBox strMsg + """FILEVERSION"""

        Exit Sub

    End If

    ' Skip first two entries for the MSV portion of the version

    For i = 1 To 2

        If ActiveDocument.Selection.FindText(",") = False Then

            MsgBox strMsg + "comma #" + Trim(CStr(i)) + " in ""FILEVERSION""."

            Exit Sub

        End If

    Next

    ' Move off comma

    ActiveDocument.Selection.CharRight

    ' Select the entire number

    ActiveDocument.Selection.WordRight dsExtend

    ' Copy the number string

    strTemp = ActiveDocument.Selection.Text

    ' Convert the number string into an integer

    nOldHiWord = CInt(strTemp)

    ' Get the current year as a string

    strTemp = CStr(DatePart("yyyy", Now))

    ' Make the high word of the LSV equal to the first digit

    ' of the year ( 1998 = 1; 1999 = 1; 2000 = 2 ...) and

    ' the last digit of the year ( 1998 = 8; 1999 = 9; 2000 = 0 ...)

    strHiWord = CStr(Left(strTemp, 1)) + CStr(Right(strTemp, 1))

    ' Convert the string into an integer value times 1000 to allow

    ' room for the day of the year value that is calculated next

    nHiWord = CInt(strHiWord) * 1000

    ' Find out the day of the year for today

    LSV = CInt(DatePart("y", Now))

    ' Add it to the previously calculated year-based value

    nHiWord = nHiWord + LSV

    ' Preserve a copy of the value as a string

    strHiWord = CStr(nHiWord)

    ' If the current value of the high word of the LSV is not the

    ' same as the newly calculated one, then replace the old with

    ' the new

    If nHiWord <> nOldHiWord Then   ' new day

        strHiWord = CStr(nHiWord)

        ActiveDocument.Selection = strHiWord

    End If

    ' Let's find the low word of least significant value and adjust it

    If ActiveDocument.Selection.FindText(",") = False Then

        MsgBox strMsg + "comma #3" + " in ""FILEVERSION""."

        Exit Sub

    End If

    ' Move off the comma

    ActiveDocument.Selection.CharRight

    ' Select the entire number

    ActiveDocument.Selection.WordRight dsExtend

    ' If the high word of the LSV has changed, then we have begun a

    ' new day of compilation fun. So reset the low word to 1

    If nHiWord <> nOldHiWord Then   ' new day, restart counter

        LSV = 1

    Else

        ' Same day, new compilation, so increment the low word of

        ' the LSV

        ' Grab the number into a text string

        strTemp = ActiveDocument.Selection.Text

        ' Convert it into an integer

        LSV = CInt(strTemp)

        ' Increment the integer

        LSV = LSV + 1

    End If

    ' Convert the low word of the LSV into a string

    strLoWord = CStr(LSV)

    ' Copy the string into the selected text

    ActiveDocument.Selection = strLoWord

    ' NOTE: The following code will synchronize the PRODUCTVERSION, FileVersion, and ProductVersion

    ' entries of the resource file with the FILEVERSION. This is done with the same techniques.

    ' Set the bPRODUCTVERSION and bStringFileInfo values at the top of the macro appropriately.

    If bPRODUCTVERSION Then

        ' Now seek out PRODUCTVERSION and synchronize

        If ActiveDocument.Selection.FindText("PRODUCTVERSION", dsMatchWord + dsMatchCase) = False Then

            MsgBox strMsg + """PRODUCTVERSION"""

            Exit Sub

        End If

        For i = 1 To 2

            If ActiveDocument.Selection.FindText(",") = False Then

                MsgBox strMsg + "comma #" + Trim(CStr(i)) + " in ""PRODUCTVERSION""."

                Exit Sub

            End If

        Next

        ActiveDocument.Selection.CharRight

        ActiveDocument.Selection.WordRight dsExtend

        ActiveDocument.Selection = strHiWord

        If ActiveDocument.Selection.FindText(",") = False Then

            MsgBox strMsg + "comma #3" + " in ""PRODUCTVERSION""."

            Exit Sub

        End If

        ActiveDocument.Selection.CharRight

        ActiveDocument.Selection.WordRight dsExtend

        ActiveDocument.Selection = strLoWord

    End If

    If bStringFileInfo Then

        ' Now seek out FileVersion in the string block and synchronize

        If ActiveDocument.Selection.FindText("FileVersion", dsMatchWord + dsMatchCase) = False Then

            MsgBox strMsg + """FileVersion"""

            Exit Sub

        End If

        For i = 1 To 3

            If ActiveDocument.Selection.FindText(",") = False Then

                MsgBox strMsg + "comma #" + Trim(CStr(i)) + " in ""PRODUCTVERSION""."

                Exit Sub

            End If

        Next

        ActiveDocument.Selection.WordRight

        ActiveDocument.Selection.WordRight dsExtend

        ActiveDocument.Selection = strHiWord

        If ActiveDocument.Selection.FindText(",") = False Then

            MsgBox strMsg + "comma #4" + " in ""PRODUCTVERSION""."

            Exit Sub

        End If

        ActiveDocument.Selection.WordRight

        ActiveDocument.Selection.WordRight dsExtend

        ActiveDocument.Selection = strLoWord

        If bPRODUCTVERSION Then

            ' Now seek out ProductVersion in the string block and synchronize

            If ActiveDocument.Selection.FindText("ProductVersion", dsMatchWord + dsMatchCase) = False Then

                MsgBox strMsg + """ProductVersion"""

                Exit Sub

            End If

            For i = 1 To 3

                If ActiveDocument.Selection.FindText(",") = False Then

                    MsgBox strMsg + "comma #" + Trim(CStr(i)) + " in ""ProductVersion""."

                    Exit Sub

                End If

            Next

            ActiveDocument.Selection.WordRight

            ActiveDocument.Selection.WordRight dsExtend

            ActiveDocument.Selection = strHiWord

            If ActiveDocument.Selection.FindText(",") = False Then

                MsgBox strMsg + "comma #4" + " in ""ProductVersion""."

                Exit Sub

            End If

            ActiveDocument.Selection.WordRight

            ActiveDocument.Selection.WordRight dsExtend

            ActiveDocument.Selection = strLoWord

        End If  ' bPRODUCTVERSION

    End If ' bStringFileInfo

    'close RC file

    ActiveDocument.Close

    If Err.Number <> 0 Then

        MsgBox ("Error 0x" & Hex(Err.Number) & ":  " & Err.Description)

        Exit Sub

    End If

    'build active project

    ExecuteCommand "BuildToggleBuild"

End Sub

Date Posted: February 23, 1999