BuildStamp : Stamp Build information into an .exe or .dll after it's built

Download buildstamp.zip

Synopsis:

buildstamp.cpp


buildstamp.cpp

Synopsis
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <windows.h>

using std::cout;
using std::endl;

int main(int argc, char** argv)
  {
 
  if (argc < 4)
    {
    cout << "Usage: BuildStamp <fname> <item> <fill> <buildnum>\n"
         << "  where fname   is the file name to modify\n"
         << "        item    is the item name in the Version info area\n"
         << "        fill    is the fill pattern in the item area\n"
         << "        buildnum is the build number to insert into the item area\n"
         << "\n"
         << "in the executables .rc file put a string in the VersionInfo string table\n"
         << "that looks like something like'VALUE \"PrivateBuild\", \"fillfillfill\"\n"
         << "where 'PrivateBuild' is the item name\n"
         << "and 'fill' is a fill pattern repeated as often as necessary\n"
         << "to make the item area the length desired\n";

    exit(1);
    }

  char* fname = argv[1];

  size_t mbstowcs( wchar_t *wcstr, const char *mbstr, size_t count );
  wchar_t* prefix;
  prefix = new wchar_t[strlen(argv[2]) + 1];
  mbstowcs(prefix, argv[2], strlen(argv[2])+1);

  wchar_t* pattern;
  pattern = new wchar_t[strlen(argv[3]) + 1];
  mbstowcs(pattern, argv[3], strlen(argv[3]) + 1);

  long  buildnumber = atol(argv[4]);

  size_t prefixlength  = (wcslen(prefix)+1) * sizeof(wchar_t);
  size_t patternlength = wcslen(pattern) * sizeof(wchar_t);

  FILE* fh = fopen(fname, "r+b");
  if (!fh)
    {
    cout << "Failed to open " << fname << endl;
    exit(1);
    }

  char* buf = new char[8096];
  int bufsize;
    
  //search for prefix
  bool foundprefix = false;
  long prefixposn = 0;
  int i;
  int offset = 0;
  for (;;)
    {
    bufsize = fread(buf, 1, 8096, fh);
    offset += bufsize;
    if (offset > 0x02F867)
      offset = 0;
    int bufsize2;
    bufsize2 = bufsize - prefixlength;
    for (i = 0; i < bufsize2; i++)
      {
      if (memcmp(&buf[i], prefix, prefixlength) == 0)
        {
        foundprefix = true;
        //points to the posn between the prefix and the first pattern
        prefixposn = ftell(fh) - bufsize + i + prefixlength;
        if (fseek(fh, prefixposn, SEEK_SET) != 0)
          {
          cout << "fseek failed!\n";
          goto byebye;
          }
        break;
        }
      }
    if (bufsize != 8096 || foundprefix)
      break;
    int jumpback;
    jumpback = prefixlength;
    if (fseek(fh, -jumpback, SEEK_CUR) != 0)
      {
      cout << "fseek failed!\n";
      goto byebye;
      }
    }

  delete [] buf;

  if (!foundprefix)
    {
    cout << "Could not find prefix 'PrivateBuild' in file" << endl;
    goto byebye;
    }

  //found the prefix, now count the number of patterns we find...
  buf = new char[patternlength];
  int numpatterns;
  for (numpatterns = 0;;numpatterns++)
    {
    if (fread(buf, 1, patternlength, fh) != patternlength)
      break;
    if (memcmp(buf, pattern, patternlength) != 0)
      break;
    }
  
  delete buf;
  if (numpatterns == 0)
    {
    cout << "There were no patterns after the prefix!" << endl;
    goto byebye;
    }

  //total buffer size is number of patterns found * the pattern length
  size_t totalbuflength;
  totalbuflength = numpatterns * patternlength;
  buf = new char[totalbuflength];

  SYSTEMTIME st;
  GetLocalTime(&st);

  //put the date, time, build number &  computer name in the buffer
  wchar_t dtstr[512];
  swprintf(dtstr, L"%.2d/%.2d/%.2d %.2d:%.2d:%.2d Build#%.6d", 
       st.wMonth, st.wDay, st.wYear, st.wHour, st.wMinute, st.wSecond, buildnumber);

  //get the computername
  wchar_t cname[MAX_COMPUTERNAME_LENGTH + 1];
  DWORD cnamelen;
  cname[0] = L' ';
  cname[1] = 0;
  cnamelen = sizeof(cname)/sizeof(wchar_t) - 1;
  if (!GetComputerNameW(&cname[1], &cnamelen))
    {
    wcscat(cname, L"UnknownComputer");
    cout << "GetComputerName failed (gle="<< GetLastError() << "), using UnknownComputer\n";
    }

  wchar_t volume[265];
  volume[0] = L' ';
  volume[1] = 0;
  DWORD   volumelen;
  volumelen = 265 - 1;
  if (!GetVolumeInformationW(
    0,              // address of root directory; 0=> use current drive
    &volume[1],     // address of name of the volume 
    volumelen,      // length of lpVolumeNameBuffer 
    0,              // address of volume serial number 
    0,              // address of system’s maximum filename length 
    0,              // address of file system flags 
    0,              // address of name of file system 
    0               // length of lpFileSystemNameBuffer 
    ))
    {
    wcscat(volume, L"UnknownVolume");
    cout << "GetVolumeInformation failed (gle="<< GetLastError() << "), using UnknownVolume\n";
    }
 
  int remaining;
  remaining = (sizeof(dtstr)/sizeof(wchar_t)) - (wcslen(dtstr) + wcslen(cname) + 1);
  if (remaining > 0)
    wcsncat(dtstr, cname, remaining);

  remaining = (sizeof(dtstr)/sizeof(wchar_t)) - (wcslen(dtstr) + wcslen(volume) + 1);
  if (remaining > 0)
    wcsncat(dtstr, volume, remaining);

  memset(buf, 0x00, totalbuflength);
  size_t len;
  len = totalbuflength - sizeof(wchar_t);
  if (len > wcslen(dtstr) * sizeof(wchar_t))
    len = wcslen(dtstr) * sizeof(wchar_t);
  memcpy(buf, dtstr, len);

  //go back to just after the prefix, and replace the data there...
  if (fseek(fh, prefixposn, SEEK_SET) != 0)
    {
    cout << "fseek failed!\n";
    goto byebye;
    }
  if (fwrite(buf, 1, totalbuflength, fh) != totalbuflength)
    {
    cout << "fwrite failed!\n";
    goto byebye;
    }

byebye:
  fclose(fh);
  return 0;
  }






Contact me about content on this page using john_web-at-arrizza-dot-com
For Web Master or site problems contact: webadmin-at-arrizza-dot-com
Copyright John Arrizza (c) 2001-2010