Win32 Version Control 
by Ping Ni and Mark Nelson

Listing One 
############################################################################
# This program will change the FileVersion and FILEVERSION accordingly and 
#  set them consistently across all the directories.
# To use this program, run as
#     IncVersion.pl major project-1 project-2 ...
#  or
#     IncVersion.pl minor project-1 project-2 ...
# The version info will be used to generate label.bat that can be call to 
#       label files in SourceSafe.
#############################################################################
  $maxMajor = 0;
  $maxMinor = 0;
  $count = 1;
# find the max of major and minor version number
  while ($count <= $#ARGV) {
    $file1 = $ARGV[$count]."\\".$ARGV[$count].".rc";
    eval { SetMax() };
    print $@;
    $count++;
  }
  if ($ARGV[0] eq "major") {
    $maxMajor++;
    $maxMinor = 0;
  } else {
    $maxMinor++;
  }
# write a batch file to be called later to set labels in SourceSafe
  $labelFile = "label.bat";
  open(OutLabel, ">$labelFile") || die 
                              "Failed to open $labelFile to write\n\t$!\n";
  print OutLabel "ss label -C- -L\"PROJECT $maxMajor\.$maxMinor\"\n";
  close(OutLabel);

  $count = 1;
  while ($count <= $#ARGV) {
    $file1 = $ARGV[$count]."\\".$ARGV[$count].".rc";
    $file2 = $ARGV[$count]."\\".$ARGV[$count].".tmp";
    eval { ProcessFile()};
    if ($@) { 
      print $@;
    } else {
      eval { CopyFile()};
      print $@;
    }
    $count++;
  }
sub SetMax {
  print "Find the max in $file1\n";
  open(InRC, "$file1") || die "Failed to open $file1 to read\n\t$!\n";
  while ($_ = <InRC>)   {
    if ($_ =~/FILEVERSION/) {
      @currentLine = split(' ', $_);
      @oldVersion  = split(',', $currentLine[1]);
      if ($oldVersion[0] > $maxMajor) {
        $maxMajor = $oldVersion[0];
      }
      if ($oldVersion[2] > $maxMinor) {
        $maxMinor = $oldVersion[2];
      }
    } elsif ($_  =~/FileVersion/) {
      @currentLine = split(' ', $_);
      @item        = split('"', @currentLine[2]);
      @vItem       = split(/\\/, @item[1]);
      @version     = split('\.', $vItem[0]);
      if ($version[0] > $maxMajor) {
        $maxMajor = $version[0];
      }
      if ($version[1] > $maxMinor) {
        $maxMinor = $version[1];
      }
    }
  }
  close (InRC);
}
sub ProcessFile {
  print "Converting $file1 to $file2\n";
  open(InRC, "$file1") || die "Failed to open $file1 to read\n\t$!\n";
  open(OutRC, ">$file2") || die "Failed to open $file2 to write\n\t$!\n";
  while ($_ = <InRC>)   {
    if ($_ =~/FILEVERSION/)
      {
      print OutRC " FILEVERSION $maxMajor,0,$maxMinor,0\n";
      }
    elsif ($_  =~/FileVersion/)
      {
      print OutRC "\t    VALUE \"FileVersion\", \"$maxMajor.$maxMinor\\0\"\n";
      }
    else
      {
      print OutRC "$_";
      }
  }
  close (InRC);
  close (outRC);
}
sub CopyFile {
  print "Copying $file2 to $file1\n";
  unlink($file1);
  open(InRC, "$file2") || die "Failed to open $file2 to read\n\t$!\n";
  open(OutRC, ">$file1") || die "Failed to open $file1 to write\n\t$!\n";
  while ($_ = <InRC>)   {
    print OutRC "$_";
    }
  close (InRC);
  close (outRC);
}

Listing Two
#include <string>
#include <vector>
using namespace std;
// Extracts the fixed-info version information from the version resource in 
// the RC file for the current module. The four integers that make up the 
// fixed-info version information are formatted into a string and returned 
// to the caller.
string FixedModuleVersion()
{
    char file_name[ MAX_PATH ];
    GetModuleFileName( ::GetModuleHandle( NULL ), file_name, MAX_PATH );
    DWORD dwDummyHandle; 
    DWORD len = GetFileVersionInfoSize( file_name, &dwDummyHandle );
    vector<BYTE> buf( len );
    ::GetFileVersionInfo( file_name, 0, len, buf.begin() );
    unsigned int ver_length;
    LPVOID lpvi;
    ::VerQueryValue( buf.begin(), "\\", &lpvi, &ver_length );
    VS_FIXEDFILEINFO fileInfo;
    fileInfo = *(VS_FIXEDFILEINFO*)lpvi;
    stringstream s;
    s << HIWORD(fileInfo.dwFileVersionMS) << "."
      << LOWORD(fileInfo.dwFileVersionMS) << "."
      << HIWORD(fileInfo.dwFileVersionLS) << "."
      << LOWORD(fileInfo.dwFileVersionLS);
    return s.str();
}
// This routine will extract the version string from the string version 
// resource in the RC file for the current module. You must add version.lib 
// to your project to link to the Win32 versioning API calls. The actual call
// VerQueryValue() uses a value of 040904B0 for the language and character set.
// This value is equivalent to English language text encoded using Unicode.
//
string StringModuleVersion()
{
    char file_name[ MAX_PATH ];
    GetModuleFileName( ::GetModuleHandle( NULL ), file_name, MAX_PATH );
    DWORD dwDummyHandle; 
    DWORD len = GetFileVersionInfoSize( file_name, &dwDummyHandle );
    vector<BYTE> buf( len );
    ::GetFileVersionInfo( file_name, 0, len, buf.begin() );
    char *version;
    unsigned int ver_length;
    ::VerQueryValue( buf.begin(), "\\StringFileInfo\\040904B0\\FileVersion",
                                          (void **) &version, &ver_length );
    return string( version, ver_length );
}





3


