#define _UNICODE #define UNICODE #include #include #include #include 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; }