 |
|
 |
Hi,
I used this tool for a while but now I've to change some of my rc files to unicode. Now every time I use rcstamp on unicode rc files, it destroys them.
Is there an easy solution (sure, I can look at the source and do the modifications myself but perhaps somebody else has done this - or somebody knows a tool which is doing the same but also on unicode rc file).
Thanks
|
|
|
|
 |
|
 |
Truly a nice and lean tool, Peterchen! But there is probably a little non-compliance of standards (actually a very tiny one):
The four numbers after the FILEVERSION statement are separated by commas, because internally they are regarded as separate parameters and are transformed into 32/16 bit integers. The "FileVersion" string, on the other hand is an entity. Such entities are usually separated by decimal points, not commas. Further in the .rc file generated by VS there are no blanks. Take the improved version of RCStamp posted below, if you like an .rc file like this:
FILEVERSION 1,0,9,0
...
VALUE "FileVersion", "1.0.9.0"
It also has another little improvement: It increments not only the FileVersion, but the ProductVersion, too.
And here it comes…
// RCStamp.cpp : Defines the entry point for the console application.
// Minor changes (comma / decimal point) by PI 12/2005
#include "stdafx.h"
#include
#include
#include
#include
using namespace std;
char const * strUsage =
" RCSTAMP command line:\r\n"
"\r\n"
" rcstamp file <format> [options...]\r\n"
" rcstamp @file [<format>] [options...]\r\n"
"\r\n"
" file : resource script to modify\r\n"
" @file : file containing a list of file (see below)\r\n"
"\r\n"
" format: specifies version number changes, e.g. *.*.33.+\r\n"
" * : don't modify position\r\n"
" + : increment position by one\r\n"
" number : set position to this value\r\n"
" \r\n"
" options: \r\n"
" -n : don't update the FileVersion string \r\n"
" (default: set to match the FILEVERSION value)\r\n"
" \r\n"
" -rRESNAME: update only version resource with resource id RESNAME\r\n"
" (default: update all Version resources)\r\n"
"\r\n"
" -l the specified file(s) are file list files\r\n"
"\r\n"
" -v Verbose\r\n"
" \r\n"
" file list files:\r\n"
"\r\n"
" can specify one file on each line, e.g.\r\n"
" d:\\sources\\project1\\project1.rc\r\n"
" d:\\sources\\project2\\project2.rc=*.*.*.+\r\n"
"\r\n"
" a format in the list file overrides the format from the command line\r\n"
" using the -l option, list files itself can be modified\r\n"
"\r\n";
// command line options:
//
// rcstamp file <format> [options...]
// rcstamp @file [<format>] [options...]
//
// file : resource script to modify
// @file : file containing a list of file (see below)
//
//
// format: specifies verison numbr changes, e.g. *.*.33.+
// * : don't modify
// + : increment by one
// number : set to this value
// P : take from product version
//
// options:
// -n : don't update the FileVersion string
// (by default, set to match the FILEVERSION value)
//
// -rRESNAME: replace version resource with resource id RESNAME
// (default: replace
//
// -l the specified file (or files in the file list) are file list files
//
// -v Verbose
//
// file list files:
//
// can specify one file on each line, e.g.
// d:\sources\project1\project1.rc
// d:\sources\project2\project2.rc=*.*.*.+
//
// specifying a format overrides the format specified on the command line
// using the -l option, list files itself can be modified
//
//
// Command Line Information
char const * scriptFile = NULL; // single resource script (.rc), or name of list file
bool scriptFileList = false; // script file is a file list file
char const * format = NULL; // format specifier
char const * resName = NULL;
bool processListFile = false; // the file(s) specified are list files, not resource files
bool noReplaceString = false; // don't replace fileversion string
bool verbose = false;
bool ParseArg(char const * arg)
{
if (*arg!='-' && *arg!='/') {
if (scriptFile == NULL) {
scriptFile = arg;
if (*scriptFile=='@') {
scriptFileList = true;
++scriptFile;
}
}
else if (format == NULL) {
format = arg;
}
else {
cerr << "Unexpected argument\"" << arg << "\"\r\n";
return false;
}
return true;
}
++arg;
char c = tolower(*arg);
if (c=='n') noReplaceString = true;
else if (c=='r') resName = arg+1;
else if (c=='l') processListFile = true;
else if (c=='v') verbose = true;
else {
cerr << "Unknown option\"" << arg << "\"\r\n";
return false;
}
return true;
}
bool CalcNewVersion(char const * oldVersion, char const * fmtstr, char * version)
{
if (!fmtstr) fmtstr = format;
char const * fmt[4];
char * fmtDup = strdup(fmtstr);
fmt[0] = strtok(fmtDup, " .,");
fmt[1] = strtok(NULL, " .,");
fmt[2] = strtok(NULL, " .,");
fmt[3] = strtok(NULL, " .,");
if (fmt[3] == 0)
{
cerr << "Invalid Format\r\n";
return false;
}
char * outHead = version;
char * verDup = strdup(oldVersion);
char * verStr = strtok(verDup, " ,.");
*version = 0;
for(int i=0; i<4; ++i)
{
int oldVersion = atoi(verStr);
int newVersion = oldVersion;
char c = fmt[i][0];
if (strcmp(fmt[i], "*")==0)
newVersion = oldVersion;
else if (isdigit(c))
newVersion = atoi(fmt[i]);
else if (c=='+' || c=='-')
{
if (isdigit(fmt[i][1]))
newVersion = oldVersion + atoi(fmt[i]);
else
newVersion = oldVersion + ((c=='+') ? 1 : -1);
}
itoa(newVersion, outHead, 10);
outHead += strlen(outHead);
if (i != 3) {
strcpy(outHead, ",");
outHead += 1;
verStr = strtok(NULL, ",");
}
}
free(fmtDup);
free(verDup);
return true;
}
bool ProcessFile(char const * fileName, char const * fmt = NULL)
{
const int MAXLINELEN = 2048;
ifstream is(fileName);
if (is.fail()) {
cerr << "cannot open " << fileName << "\r\n";
return false;
}
string result;
char line[MAXLINELEN];
char fileversion[64] = { 0 }; // "final" version string
char productversion[64] = { 0 }; // "final" version string
bool inReplaceBlock = false;
while (!is.eof()) {
is.getline(line, MAXLINELEN);
if (is.bad()) {
cerr << "Error reading " << fileName << "\r\n";
return false;
}
if (processListFile)
{
char * pos = strchr(line, '=');
if (pos) {
CalcNewVersion(pos+1, fmt, pos+1); // in-place replace
}
}
else
{
char * dupl = strdup(line);
char * word1 = strtok(dupl, " \t");
char * word2 = strtok(NULL, " \t,"); // allow comma for [VALUE "FileVersion",] entry
if (word1 && word2 && strcmpi(word2, "VERSIONINFO") == 0 && strnicmp(word1, "//",2)!=0)
{
if (resName==NULL || strcmpi(resName, word1)==0)
inReplaceBlock = true;
else
inReplaceBlock = false;
}
if (inReplaceBlock) {
if ( word1 && (strcmpi(word1, "FILEVERSION") == 0 || strcmpi(word1, "PRODUCTVERSION") == 0) )
{
//int offset;
if (strcmpi(word1, "FILEVERSION") == 0)
{
int offset = strlen("FILEVERSION") + word1 - dupl + 1;
CalcNewVersion(line+offset, fmt, fileversion);
strcpy(line+offset, fileversion);
}
else
{
int offset = strlen("PRODUCTVERSION") + word1 - dupl + 1;
CalcNewVersion(line+offset, fmt, productversion);
strcpy(line+offset, productversion);
}
if (verbose) cout << line << "\r\n";
}
if (!noReplaceString && word1 && word2 && strcmpi(word1, "VALUE")==0)
{
if( strcmpi(word2, "\"FileVersion\"")==0 )
{
if (!*fileversion)
{
cerr << "Error: VALUE \"FileVersion\" encountered before FILEVERSION\r\n";
}
else
{
for (unsigned int j=0; j |
|
|
|
 |
|
 |
Unfortunately, when you pasted the code, some of it disappeared (besides the formatting) and made it unusable. On a quick glance, two "for" loops are incomplete, maybe other things are missing too.
Example: look under main, the line:
for(int i=1; i if (!ParseArg(argv[i]))
should not look like that. Please upload the code as a file...
Thanks!
-=Athos=-
|
|
|
|
 |
|
 |
Just wanted to drop a line and say thanks for sharing your efforts. Saved me some time.
|
|
|
|
 |
|
 |
Further you can look at build manager at http://republika.pl/dailybuilds
|
|
|
|
 |
|
 |
To make it a little more prominent
Beau Skinner has an article on using RCStamp for:
(quote)
"1. Increment the build number, so that each time the project is compiled, the executable's version information is updated.
2. Archive a copy of each build executable in a separate location and name the files based on their respective versions.
3. Allow the application itself to easily retrieve its own version information, such as through preprocessor constants in C++. "
You find the article Here[^].
Enjoy!
Pandoras Gift #44: Hope. The one that keeps you on suffering. aber.. "Wie gesagt, der Scheiss is' Therapie" boost your code || Fold With Us! || sighist | doxygen
|
|
|
|
 |
|
 |
In my post-build step I would like something like:
if not exist ..\Build mkdir ..\Build
copy "$(TargetPath)" "..\build\$(TargetName)-$(FILEVERSION).exe"
..\rcstamp.exe "$(ProjectDir)\$(ProjectName).rc" *.*.*.+
So that the build executable is copied into a separate folder and renamed according to the version and build numbers. However FILEVERSION is not defined as an environment variable. What would be the best way to accomplish this?
(I know that FILEVERSION has commas and spaces in it; I will clean that up later. I just want to know how to access the values in the .rc file from the post-build batch commands.)
Thanks.
|
|
|
|
 |
|
 |
Beau Skinner wrote:
I just want to know how to access the values in the .rc file from the post-build batch commands.)
not possible (sorry)
Funny that you ask this right now, I've been trying to solve something similar in the last hours.
The best suggestion I can think of is: writing a custom copy.exe, that reads the source files version info
Pandoras Gift #44: Hope. The one that keeps you on suffering. aber.. "Wie gesagt, der Scheiss is' Therapie" boost your code || Fold With Us! || sighist | doxygen
|
|
|
|
 |
|
 |
Ah yes that is a good idea and I could use it on any file I wanted.
I was hoping that I could modify rcstamp to set the environment variables when it is parsing the file, but that appears to be a different environment from the one that VC++ uses. Do you know if it is possible to define environment variables in other environments?
Thanks.
|
|
|
|
 |
|
 |
Beau Skinner wrote:
Do you know if it is possible to define environment variables in other environments?
Again bad news :-/
Each child process gets a copy of the environment block of he process that started it, changign the environment will only affect that copy.
To my knowledge, there is no documented (nor robust, nor "sometimes working") way to modify the Root Environment block, or the one of a parent process.
The System Environment variables (that initialize the root environment block at startup) are stored somewhere in the registry, and there is even a predefined message you should broadcast when changing them. Problem is, I don't know any program that listens to that message...
Pandoras Gift #44: Hope. The one that keeps you on suffering. aber.. "Wie gesagt, der Scheiss is' Therapie" boost your code || Fold With Us! || sighist | doxygen
|
|
|
|
 |
|
 |
I'm considering writing an article about my final solution and would like to request your permission to link to this article, if I do write one.
|
|
|
|
 |
|
 |
Beau Skinner wrote:
and would like to request your permission to link to this article, if I do write one.
sure!
Beau Skinner wrote:
I'm considering writing an article about my final solution
I was going to ask you to
Pandoras Gift #44: Hope. The one that keeps you on suffering. aber.. "Wie gesagt, der Scheiss is' Therapie" boost your code || Fold With Us! || sighist | doxygen
|
|
|
|
 |
|
|
 |
|
 |
I finally got this working exactly how I want it.
As you suggested, I wrote a VerCopy.exe that copies a file to another location and renames it according to version and build.
I then wrote a VerHeader.exe that takes a .rc file and outputs a header file with #defines for the major and minor version, and build number.
So now my pre-build command line is:
..\VerHeader "$(ProjectName).rc" version.info
And my post-build command line is:
..\VerCopy "$(TargetPath)" "..\Builds\$(ConfigurationName)"
..\RCStamp "$(ProjectName).rc" *.*.+.*
And finally, I add this to my application:
#include "version.info"
Now I have a copy of all of my incremental builds and the application knows what version/build it is.
Thanks for not making me write the RCStamp part myself.
|
|
|
|
 |
|
 |
Nice tool, got my 5.
I noticed that the format for the FILEVERSION on 'rc' files generated by VC7.1 (VC .Net 2003) differ from the one the tool generates:
- VC generates a 4 number string comma sepparated without spaces.
- Your tool adds spaces on it.
The same happen for the Version.
- VC generates a 4 number string dot sepparated without spaces.(VALUE "FileVersion", "1.1.0.7")
- Your tool puts the same number as before, appending a '\0'.(VALUE "FileVersion", "1, 1, 0, 7\0")
Is there a problem with this?
(I guess that VC version differences caused this problem..)
Also, I was thinking to add some support for persistent command line options via an INI file. (e.g. add also support to fix the PRODUCTVERSION and its companion VALUE.
Any comment?
-- Ricky Marek (AKA: rbid)
-- "Things are only impossible until they are not" --- Jean-Luc Picard
|
|
|
|
 |
|
 |
Hi,
thanks for your feedback!
The parser is tolerant, it acceps with spaces or without, and dot or comma. One could use the format found in the input, or use a separate option for that.
As for the changes... I short, I don't think I find the time.
First, the project this was used for now uses a different mechanism (an include file with #defines which is modified in a similar way, a bit more to set up, but was necessary).
Second, I have other updates pending (firstly, someone submitted some code snippets, and I feel oblieged to publish them), that I think the community benefits more from. If there's something else I would modify about this article, I'd throw in your suggestions.
Having said that - the source code is there waiting for you
Feel free to add your changes. you can publish them on your own, or send them to me and I do it.
we are here to help each other get through this thing, whatever it is Vonnegut jr. boost your code || Fold With Us! || sighist | doxygen
|
|
|
|
 |
|
 |
Nice tool. One small problem: Each time the tool runs it adds a new empty line at the end of the target file. So after some time of using rcstamp you get tons of empty lines at the end of the rc file.
Thomas Haase
|
|
|
|
 |
|
 |
change line 268 of rcstamp.cpp from
result += "\n";
to
if (!is.eof()) result += "\n";
this isn't perfect, but should do the job.
(I've got this problem every time when writing parsers like this.. )
Flirt harder, I'm a Coder
mlog || Agile Programming | doxygen
|
|
|
|
 |
|
 |
When I use Rcstamp, VC6 always says that the .rc file was changed outside of VC. So I changed to source of Rcstamp to remember the write time of the .rc file before the file is processed. After processing the file it is set back to the previous write time. This works.
|
|
|
|
 |
|
 |
Hey, i have a question. When you ran the rcstamp program. What where the parameters and how did you type them in. Because i'm trying to compare this code to mine, but i can't get the rcstamp code to update the .rc file. I need some help.
visioneer
|
|
|
|
 |
|
 |
Would you mind sharing the source for that improvement? VS 2005 does the same thing, and it's rather annoying.
|
|
|
|
 |
|
 |
This does not work in Visual Studio 2008.
What I did, in the ProcessFile() function:
Added after "const int MAXLINELEN = 2048;":
bool fSetFileTimes = true;
HANDLE hFile = CreateFile(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
FILETIME ftCreate, ftAccess, ftWrite;
if (hFile == INVALID_HANDLE_VALUE)
{
fSetFileTimes = false;
}
else
{
// Retrieve the file times for the file.
if (!GetFileTime(hFile, &ftCreate, &ftAccess, &ftWrite))
{
fSetFileTimes = false;
}
else
{
printf("Good!\n");
}
CloseHandle(hFile);
}
Added before "return true":
if (fSetFileTimes == true)
{
HANDLE hFile = CreateFile(fileName, FILE_WRITE_ATTRIBUTES | GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
SetFileTime(hFile, &ftCreate, &ftAccess, &ftWrite);
CloseHandle(hFile);
}
}
else
{
printf("Bad!\n");
}
|
|
|
|
 |
|
 |
I always wanted to do the following
Increment the last number with every build automatically. ie, as a pre-build step, it increments the last number in the a.b.c.d. Do you have any idea how I can do this?
Thomas
|
|
|
|
 |
|
 |
You can add the following to your project:
Add
rcstamp.exe $(ProjDir)\$(TargetName).rc *.*.*.+
to your post-build step
(In Detail:
- menu: Project/Settings,
- Select the configurations you want to affect
(e.g. you can have one oly for release builds)
- Select "Post-build step" Tab
- add
rcstamp.exe $(ProjDir)\.rc *.*.*.+
)
the programmers just set it so that when in doubt, the women still think they're involved with you. It's much less frustrating that way - BGII Quest Directory
|
|
|
|
 |
|
 |
Hey, i have a question. When you ran the rcstamp program. What were the parameters and how did you type them in. Because i'm trying to compare this code to mine, but i can't get the rcstamp code to update the .rc file. I need some help.
|
|
|
|
 |