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 '[email protected] ' 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