354 lines
11 KiB
C
354 lines
11 KiB
C
|
#define _UNICODE
|
||
|
#define UNICODE
|
||
|
#include <tchar.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <windows.h>
|
||
|
|
||
|
void Usage();
|
||
|
BOOL ParseCmdLine(int argc, TCHAR **argv);
|
||
|
void SetVersionOfImage(IN LPTSTR lpszFileName);
|
||
|
void VersionDwordLong2String(IN DWORDLONG Version, OUT LPTSTR lpszVerString);
|
||
|
void VersionString2DwordLong(IN LPCTSTR lpszVerString, OUT PDWORDLONG pVersion);
|
||
|
void DirWalker(LPCTSTR lpszPath, BOOL fRecursive, void (*pFunc)());
|
||
|
|
||
|
BOOL g_fRecursive = FALSE;
|
||
|
BOOL g_fForceTrue = FALSE;
|
||
|
|
||
|
TCHAR g_szPath[MAX_PATH]; // contains the DirName/FileName we're interested
|
||
|
BOOL g_fSet = FALSE; // TRUE if set new version, FALSE to only display version info
|
||
|
DWORDLONG g_dwlNewVersion = 0;
|
||
|
|
||
|
void __cdecl _tmain(int argc, TCHAR **argv)
|
||
|
{
|
||
|
void (*pFunc)();
|
||
|
|
||
|
if (ParseCmdLine(argc, argv) == FALSE) {
|
||
|
Usage();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
pFunc = SetVersionOfImage;
|
||
|
|
||
|
DirWalker(g_szPath, g_fRecursive, pFunc);
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
void SetVersionOfImage(IN LPTSTR lpszFileName)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Get/Set file version.
|
||
|
|
||
|
The version is specified in the dwFileVersionMS and dwFileVersionLS fields
|
||
|
of a VS_FIXEDFILEINFO, as filled in by the win32 version APIs.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
lpszFileName - supplies the full path of the file whose version data is desired.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
DWORD d;
|
||
|
PVOID pVersionBlock;
|
||
|
VS_FIXEDFILEINFO *pFixedVersionInfo;
|
||
|
UINT iDataLength;
|
||
|
BOOL b = FALSE;
|
||
|
DWORD dwIgnored;
|
||
|
HANDLE hHandle = NULL;
|
||
|
PVOID pTranslation;
|
||
|
PWORD pLangId, pCodePage;
|
||
|
TCHAR szQueryString[MAX_PATH];
|
||
|
TCHAR *lpszVerString;
|
||
|
TCHAR szOldVerString[MAX_PATH], szNewVerString[MAX_PATH];
|
||
|
DWORDLONG dwlOldVersion = 0;
|
||
|
|
||
|
VersionDwordLong2String(g_dwlNewVersion, szNewVerString);
|
||
|
|
||
|
//
|
||
|
// Get the size of version info block.
|
||
|
//
|
||
|
if(d = GetFileVersionInfoSize((LPTSTR)lpszFileName,&dwIgnored)) {
|
||
|
//
|
||
|
// Allocate memory block of sufficient size to hold version info block
|
||
|
//
|
||
|
pVersionBlock = LocalAlloc(LPTR, d);
|
||
|
if(pVersionBlock) {
|
||
|
|
||
|
//
|
||
|
// Get the version block from the file.
|
||
|
//
|
||
|
if(GetFileVersionInfo((LPTSTR)lpszFileName,0,d,pVersionBlock)) {
|
||
|
|
||
|
//
|
||
|
// Get fixed version info.
|
||
|
//
|
||
|
if(VerQueryValue(pVersionBlock,TEXT("\\"),(LPVOID *)&pFixedVersionInfo,&iDataLength)) {
|
||
|
|
||
|
//
|
||
|
// display the verions info first
|
||
|
//
|
||
|
dwlOldVersion = (((DWORDLONG)pFixedVersionInfo->dwFileVersionMS) << 32)
|
||
|
+ pFixedVersionInfo->dwFileVersionLS;
|
||
|
VersionDwordLong2String(dwlOldVersion, szOldVerString);
|
||
|
|
||
|
_tprintf(TEXT("%s: %s"), lpszFileName, szOldVerString);
|
||
|
|
||
|
if (g_fSet == FALSE) {
|
||
|
_tprintf(TEXT("\n"));
|
||
|
} else {
|
||
|
//
|
||
|
// set to the new version
|
||
|
//
|
||
|
pFixedVersionInfo->dwFileVersionMS = (DWORD)((g_dwlNewVersion >> 32) & 0xffffffff);
|
||
|
pFixedVersionInfo->dwFileVersionLS = (DWORD)(g_dwlNewVersion & 0xffffffff);
|
||
|
|
||
|
// Attempt to get language of file. We'll simply ask for the
|
||
|
// translation table and use the first language id we find in there
|
||
|
// as *the* language of the file.
|
||
|
//
|
||
|
// The translation table consists of LANGID/Codepage pairs.
|
||
|
//
|
||
|
|
||
|
if(VerQueryValue(pVersionBlock,TEXT("\\VarFileInfo\\Translation"),&pTranslation,&iDataLength)) {
|
||
|
pLangId = (PWORD)pTranslation;
|
||
|
pCodePage = (PWORD)pTranslation + 1;
|
||
|
|
||
|
// update FileVersion string
|
||
|
_stprintf(szQueryString, TEXT("\\StringFileInfo\\%04x%04x\\FileVersion"), *pLangId, *pCodePage);
|
||
|
if (VerQueryValue(pVersionBlock,szQueryString,(LPVOID *)&lpszVerString,&iDataLength)) {
|
||
|
memset(lpszVerString, 0, iDataLength * sizeof(TCHAR));
|
||
|
_tcsncpy(lpszVerString, szNewVerString, iDataLength - 1);
|
||
|
}
|
||
|
|
||
|
// update ProductVersion string
|
||
|
_stprintf(szQueryString, TEXT("\\StringFileInfo\\%04x%04x\\ProductVersion"), *pLangId, *pCodePage);
|
||
|
if (VerQueryValue(pVersionBlock,szQueryString,(LPVOID *)&lpszVerString,&iDataLength)) {
|
||
|
memset(lpszVerString, 0, iDataLength * sizeof(TCHAR));
|
||
|
_tcsncpy(lpszVerString, szNewVerString, iDataLength - 1);
|
||
|
}
|
||
|
|
||
|
} // Language-related String Update
|
||
|
|
||
|
//
|
||
|
// Update the version resource
|
||
|
//
|
||
|
b = FALSE;
|
||
|
hHandle = BeginUpdateResource((LPTSTR) lpszFileName, FALSE);
|
||
|
if (hHandle) {
|
||
|
b = UpdateResource(
|
||
|
hHandle,
|
||
|
RT_VERSION,
|
||
|
MAKEINTRESOURCE(VS_VERSION_INFO),
|
||
|
MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
|
||
|
pVersionBlock,
|
||
|
d);
|
||
|
|
||
|
if (b)
|
||
|
b = EndUpdateResource(hHandle, FALSE);
|
||
|
|
||
|
}
|
||
|
|
||
|
if (b)
|
||
|
_tprintf(TEXT(" ==> %s\n"), szNewVerString);
|
||
|
else
|
||
|
_tprintf(TEXT("\nError = %d\n"), GetLastError());
|
||
|
|
||
|
} // g_fGet
|
||
|
} // VerQueryValue() on FixedVersionInfo
|
||
|
} // GetFileVersionInfo()
|
||
|
|
||
|
LocalFree(pVersionBlock);
|
||
|
} // malloc()
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
void DirWalker(LPCTSTR lpszPath, BOOL fRecursive, void (*pFunc)())
|
||
|
{
|
||
|
DWORD retCode;
|
||
|
WIN32_FIND_DATA FindFileData;
|
||
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
||
|
TCHAR szSubDir[MAX_PATH] = _T("");
|
||
|
TCHAR szDirName[MAX_PATH] = _T("");
|
||
|
int len = 0;
|
||
|
|
||
|
retCode = GetFileAttributes(lpszPath);
|
||
|
|
||
|
if (retCode == 0xFFFFFFFF) {
|
||
|
// lpszPath is not a valid DirName or FileName
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( !(retCode & FILE_ATTRIBUTE_DIRECTORY) ) {
|
||
|
// apply the function call
|
||
|
(*pFunc)(lpszPath);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
len = _tcslen(lpszPath);
|
||
|
if (lpszPath[len - 1] == _T('\\'))
|
||
|
_stprintf(szDirName, _T("%s*"), lpszPath);
|
||
|
else
|
||
|
_stprintf(szDirName, _T("%s\\*"), lpszPath);
|
||
|
|
||
|
hFile = FindFirstFile(szDirName, &FindFileData);
|
||
|
|
||
|
if (hFile != INVALID_HANDLE_VALUE) {
|
||
|
do {
|
||
|
if (_tcsicmp(FindFileData.cFileName, _T(".")) != 0 &&
|
||
|
_tcsicmp(FindFileData.cFileName, _T("..")) != 0 ) {
|
||
|
|
||
|
if (lpszPath[len - 1] == _T('\\'))
|
||
|
_stprintf(szSubDir, _T("%s%s"), lpszPath, FindFileData.cFileName);
|
||
|
else
|
||
|
_stprintf(szSubDir, _T("%s\\%s"), lpszPath, FindFileData.cFileName);
|
||
|
|
||
|
if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||
|
if (fRecursive)
|
||
|
DirWalker(szSubDir, fRecursive, pFunc);
|
||
|
} else {
|
||
|
(*pFunc)(szSubDir);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!FindNextFile(hFile, &FindFileData)) {
|
||
|
FindClose(hFile);
|
||
|
break;
|
||
|
}
|
||
|
} while (TRUE);
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
void VersionDwordLong2String(IN DWORDLONG Version, OUT LPTSTR lpszVerString)
|
||
|
{
|
||
|
WORD a, b, c, d;
|
||
|
|
||
|
a = (WORD)(Version >> 48);
|
||
|
b = (WORD)((Version >> 32) & 0xffff);
|
||
|
c = (WORD)((Version >> 16) & 0xffff);
|
||
|
d = (WORD)(Version & 0xffff);
|
||
|
|
||
|
_stprintf(lpszVerString, TEXT("%d.%d.%d.%d"), a, b, c, d);
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
void VersionString2DwordLong(IN LPCTSTR lpszVerString, OUT PDWORDLONG pVersion)
|
||
|
{
|
||
|
WORD a=0, b=0, c=0, d=0;
|
||
|
|
||
|
_stscanf(lpszVerString, TEXT("%d.%d.%d.%d"), &a, &b, &c, &d);
|
||
|
|
||
|
*pVersion = (((DWORDLONG)a) << 48) +
|
||
|
(((DWORDLONG)b) << 32) +
|
||
|
(((DWORDLONG)c) << 16) +
|
||
|
(DWORDLONG)d;
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
BOOL ParseCmdLine(int argc, TCHAR **argv)
|
||
|
{
|
||
|
BOOL fReturn = FALSE;
|
||
|
TCHAR szVerString[MAX_PATH];
|
||
|
TCHAR c;
|
||
|
int i = 0;
|
||
|
|
||
|
if (argc < 2 || argc > 4 ||
|
||
|
_tcsicmp(argv[1], TEXT("/?")) == 0 ||
|
||
|
_tcsicmp(argv[1], TEXT("-?")) == 0) {
|
||
|
fReturn = FALSE;
|
||
|
} else {
|
||
|
|
||
|
i = 1;
|
||
|
if (_tcsicmp(argv[1], TEXT("/r")) == 0 || _tcsicmp(argv[1], TEXT("-r")) == 0) {
|
||
|
g_fRecursive = TRUE;
|
||
|
i = 2;
|
||
|
}
|
||
|
|
||
|
if (_tcsicmp(argv[1], TEXT("/rf")) == 0 || _tcsicmp(argv[1], TEXT("-rf")) == 0) {
|
||
|
g_fRecursive = TRUE;
|
||
|
g_fForceTrue = TRUE;
|
||
|
i = 2;
|
||
|
}
|
||
|
|
||
|
if (_tcsicmp(argv[1], TEXT("/f")) == 0 || _tcsicmp(argv[1], TEXT("-f")) == 0) {
|
||
|
g_fForceTrue = TRUE;
|
||
|
i = 2;
|
||
|
}
|
||
|
|
||
|
|
||
|
if (argv[i]) {
|
||
|
if (GetFileAttributes(argv[i]) != 0xFFFFFFFF) {
|
||
|
// argv[i] is pointing to a valid DirName or FileName
|
||
|
_tcscpy(g_szPath, argv[i]);
|
||
|
|
||
|
i++;
|
||
|
if (argv[i] == NULL) {
|
||
|
// display version info only
|
||
|
// parse is done
|
||
|
g_fSet = FALSE;
|
||
|
fReturn = TRUE;
|
||
|
} else {
|
||
|
// set version info
|
||
|
g_fSet = TRUE;
|
||
|
// make sure it's a valid version string
|
||
|
VersionString2DwordLong(argv[i], &g_dwlNewVersion);
|
||
|
VersionDwordLong2String(g_dwlNewVersion, szVerString);
|
||
|
_tprintf(TEXT("Stamp files with new version %s ? (y/n) "), szVerString);
|
||
|
if (g_fForceTrue)
|
||
|
{
|
||
|
_tprintf(TEXT("Yes\n"));
|
||
|
fReturn = TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_tscanf(TEXT("%c"), &c);
|
||
|
if (c == TEXT('y') || c == TEXT('Y')) {
|
||
|
fReturn = TRUE;
|
||
|
} else {
|
||
|
fReturn = FALSE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
_tprintf(TEXT("Error: no such file called %s!\n"), argv[i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
return fReturn;
|
||
|
}
|
||
|
|
||
|
void Usage()
|
||
|
{
|
||
|
_tprintf(TEXT("\n"));
|
||
|
_tprintf(TEXT("Display or change version info of files.\n"));
|
||
|
_tprintf(TEXT("\n"));
|
||
|
_tprintf(TEXT("Usage:\n"));
|
||
|
_tprintf(TEXT("\n"));
|
||
|
_tprintf(TEXT("verstamp [-rf] Path [newVersion]\n"));
|
||
|
_tprintf(TEXT("\t-r\t\trecursively apply the Path\n"));
|
||
|
_tprintf(TEXT("\t-rf\t\trecursively apply the Path and force to Yes.\n"));
|
||
|
_tprintf(TEXT("\tPath\t\ta file name or directory name\n"));
|
||
|
_tprintf(TEXT("\tnewVersion\tstamp file with this new version\n"));
|
||
|
_tprintf(TEXT("\n"));
|
||
|
_tprintf(TEXT("verstamp [-?]\t\tprint out this usage info.\n"));
|
||
|
_tprintf(TEXT("\n"));
|
||
|
_tprintf(TEXT("\n"));
|
||
|
_tprintf(TEXT("Example:\n"));
|
||
|
_tprintf(TEXT("verstamp iis.dll 5.0.1780.0\n"));
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|