|
|
//***************************************************************************
//* Copyright (c) Microsoft Corporation 1996. All rights reserved. *
//***************************************************************************
//* *
//* VERSION.C - Function to overwrite the versin information from *
//* wextract.exe *
//* *
//***************************************************************************
//***************************************************************************
//* INCLUDE FILES *
//***************************************************************************
#include "pch.h"
#pragma hdrstop
#include "cabpack.h"
#include <memory.h>
extern CDF g_CDF; extern TCHAR g_szOverideCDF[MAX_PATH]; extern TCHAR g_szOverideSec[SMALL_BUF_LEN];
// Function prototypes
BOOL UpdateVersionInfo(LPBYTE lpOldVersionInfo, LPBYTE *lplpNewVersionInfo, WORD *pwSize); BOOL FindVerValue( WCHAR *lpKey, WCHAR *lpszData, WORD *pwLen); BOOL CALLBACK MyEnumLangsFunc(HANDLE hModule, LPSTR lpType, LPSTR lpName, WORD languages, LONG lParam);
// External function and variables
DWORD MyGetPrivateProfileString( LPCTSTR lpSec, LPCTSTR lpKey, LPCTSTR lpDefault, LPTSTR lpBuf, UINT uSize, LPCTSTR lpOverSec ); void MyWritePrivateProfileString( LPCTSTR lpSec, LPCTSTR lpKey, LPTSTR lpBuf, UINT uSize );
//////////////////////////////////////////////////////////////////////////////
//// Version information overwrite functions and data types
#define KEY_FROMFILE "FromFile"
#define COMPANYNAME "CompanyName"
#define INTERNALNAME "InternalName"
#define ORIGINALFILENAME "OriginalFilename"
#define PRODUCTNAME "ProductName"
#define PRODUCTVERSION "ProductVersion"
#define FILEVERSION "FileVersion"
#define FILEDESCRIPTION "FileDescription"
#define LEGALCOPYRIGHT "LegalCopyright"
#define MAX_VALUE 256
// What language is the version information in?
WORD wVerLang = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL); // Structure to save the keys and values for the Version info
typedef struct _VERINFO { LPSTR lpszName; CHAR szValue[MAX_VALUE]; } VERINFO;
// Array of keys and values which can be changed.
VERINFO Verinfo_Array[] = { { COMPANYNAME, ""}, { INTERNALNAME, ""}, { ORIGINALFILENAME, ""}, { PRODUCTNAME, ""}, { PRODUCTVERSION, ""}, { FILEVERSION, ""}, { FILEDESCRIPTION, ""}, { LEGALCOPYRIGHT, ""} };
#define ARRAYSIZE(a) (sizeof(a) / sizeof(a[0]))
UINT VerInfoElem = ARRAYSIZE(Verinfo_Array);
//... Decrement WORD at *pw by given amount w
#define DECWORDBY( pw,w) if (pw) { *(pw) = (*(pw) > (w)) ? *(pw) - (w) : 0;}
//... Increment WORD at *pw by given amount w
#define INCWORDBY( pw,w) if (pw) { *(pw) += (w);}
#define MEMSIZE( x ) ((x) * 2)
// was sizeof( TCHAR))
#define STRINGFILEINFOLEN 15
#define LANGSTRINGLEN 8 //... # WCHARs in string denoting language
//... and code page in a Version resource.
#define VERTYPESTRING 1 //... Version data value is a string
#pragma pack(1)
typedef struct VERBLOCK { WORD wLength; // Length of this block
WORD wValueLength; // Length of the valuedata
WORD wType; // Type of data (1=string, 0=binary)
WCHAR szKey[1]; // data
} VERBLOCK ;
typedef VERBLOCK * PVERBLOCK;
typedef struct VERHEAD { WORD wTotLen; WORD wValLen; WORD wType; TCHAR szKey[( sizeof( TEXT("VS_VERSION_INFO" )) +3 )&~03]; VS_FIXEDFILEINFO vsf;
} VERHEAD ; #pragma pack()
// Do the version info update
//
// szFile is the file we want to update the version info from
// hUpdate is the handle to the resource info which will be used to update all resources
//
BOOL DoVersionInfo(HWND hDlg, LPSTR szFile, HANDLE hUpdate) { HINSTANCE hModule; HRSRC hrsrc; HGLOBAL hgbl; LPBYTE lp; LPBYTE lpCopy; WORD wSize;
if (GetVersionInfoFromFile()) { // Get the current version info from the file
hModule = LoadLibraryEx(szFile, NULL,LOAD_LIBRARY_AS_DATAFILE| DONT_RESOLVE_DLL_REFERENCES); if (hModule == NULL) return FALSE; // Should not happen, we loaded the module before
// Determine the language of the version information
EnumResourceLanguages(hModule, RT_VERSION, MAKEINTRESOURCE(VS_VERSION_INFO), (ENUMRESLANGPROC)MyEnumLangsFunc, 0L); hrsrc = FindResourceEx (hModule, RT_VERSION, MAKEINTRESOURCE(VS_VERSION_INFO), wVerLang); if (hrsrc == NULL) { FreeLibrary(hModule); return FALSE; // Should we continue???
} if ((hgbl = LoadResource(hModule, hrsrc)) == NULL) { FreeResource(hrsrc); FreeLibrary(hModule); return FALSE; // Should we continue???
}
if ((lp = LockResource(hgbl)) == NULL) { FreeResource(hrsrc); FreeLibrary(hModule); return FALSE; // Should we continue???
}
// UPdate the version information, If success, lpCopy has the pointer to the update info
UpdateVersionInfo(lp, &lpCopy, &wSize); UnlockResource(hgbl); FreeResource(hrsrc); FreeLibrary(hModule);
if (lpCopy != NULL) { // Now update the resource for the file
if ( LocalUpdateResource( hUpdate, RT_VERSION, MAKEINTRESOURCE(VS_VERSION_INFO), wVerLang, //MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
lpCopy, wSize) == FALSE ) { free (lpCopy); ErrorMsg( hDlg, IDS_ERR_UPDATE_RESOURCE ); return FALSE; } free (lpCopy); }
return TRUE; } return TRUE; }
// Get the version information we use to overwrite from the CDF file
BOOL GetVersionInfoFromFile() { char szFilename[MAX_STRING]; HLOCAL hInfoBuffer; LPSTR lpValueBuffer; char szQuery[128]; DWORD dwBytes; DWORD dwLangCharset; DWORD dwInfoBuffer; DWORD dwDummy; UINT i;
if ( MyGetPrivateProfileString(SEC_OPTIONS, KEY_VERSIONINFO, "", g_CDF.achVerInfo, sizeof(g_CDF.achVerInfo), g_szOverideSec ) > 0) { // We better zero the version info in our array.
for (i = 0; i < VerInfoElem; i++) { Verinfo_Array[i].szValue[0] = '\0'; }
if ( MyGetPrivateProfileString( g_CDF.achVerInfo, KEY_FROMFILE, "", szFilename, sizeof(szFilename), g_CDF.achVerInfo) > 0) { // Fill the version info from the file version info
// determine if the file contains version information
// and get the size of the information if so
dwInfoBuffer = GetFileVersionInfoSize(szFilename, &dwDummy);
if (dwInfoBuffer != 0) {
// allocate memory to hold the version information
hInfoBuffer = LocalAlloc(LMEM_FIXED, dwInfoBuffer);
if (hInfoBuffer != NULL) {
// read version information into our memory
if (GetFileVersionInfo(szFilename, 0, dwInfoBuffer, (LPVOID)hInfoBuffer) != 0) { // get language and character set information
if (VerQueryValue((LPVOID)hInfoBuffer, "\\VarFileInfo\\Translation", &lpValueBuffer, &dwBytes)) dwLangCharset = *(LPDWORD)lpValueBuffer; else dwLangCharset = 0x04E40409; // If we don't have any default to US. Should never happen
// Now get the version info from the file
for (i = 0; i < VerInfoElem; i++) { // get version information string
wsprintf(szQuery, "\\StringFileInfo\\%4.4X%4.4X\\%s", LOWORD(dwLangCharset), HIWORD(dwLangCharset), Verinfo_Array[i].lpszName);
if (VerQueryValue((LPVOID)hInfoBuffer, szQuery, (LPVOID)&lpValueBuffer, &dwBytes) != 0) lstrcpyn(Verinfo_Array[i].szValue,lpValueBuffer, MAX_VALUE-1); // Found one, take it
} } LocalFree(hInfoBuffer); } } } // Got version info from file
// Now see if we have to overwrite some info from the batch file.
for (i = 0; i < VerInfoElem; i++) { if (MyGetPrivateProfileString(g_CDF.achVerInfo, Verinfo_Array[i].lpszName, "", szFilename, MAX_VALUE, g_CDF.achVerInfo) > 0) { lstrcpyn(Verinfo_Array[i].szValue, szFilename, MAX_VALUE-1); } } return TRUE; } return FALSE; }
// Update the lpOldVersionInfo with the overwritable data.
// lpOldVersionInfo: pointer to the old version info data block
// lplpNewVersionInfo: Will get the pointer to the updated version info data,
// the caller has to free the buffer if the pointer is not NULL,
// pwSize: pointer to a word which will return the size of the new version info block
//
// Note: This code assumes that there is only one language data block in the version info data.
//
BOOL UpdateVersionInfo(LPBYTE lpOldVersionInfo, LPBYTE *lplpNewVersionInfo, WORD *pwSize) { WCHAR szData[MAX_STRING]; // Will hold the data to put into the versin info
WORD wDataLen = 0; //... Length of old resource data
WORD wVerHeadSize; //... Sizeof of the VERHEAD struct
int nNewVerBlockSize = 0; // Size of the new version info data block
PVERBLOCK pNewVerStamp = NULL; // Pointer to the new version info data block
PVERBLOCK pNewBlk = NULL; // Pointer to the currently worked on data in new verblock
VERHEAD *pVerHdr = (VERHEAD*)lpOldVersionInfo; // Pointer to old verinfo
VERBLOCK *pVerBlk; // Pointer to the currently worked on data in old verblock
LPBYTE lp; // Pointer to the data area to copy (overwrite)
WORD wStringTableLen = 0; // Bytes (left) in the language data block
PVERBLOCK pNewStringTblBlk; // Pointer to the language part for the version info
WORD wStringInfoLen = 0; //... # of bytes in StringFileInfo
PVERBLOCK pNewStringInfoBlk; //... Start of this StringFileInfo blk
WORD wLen = 0;
*lplpNewVersionInfo = NULL; *pwSize = 0; wVerHeadSize = (WORD)(3 * sizeof(WORD) + MEMSIZE(lstrlen("VS_FIXEDFILEINFO") + 1) + sizeof(VS_FIXEDFILEINFO)); wVerHeadSize = ROUNDUP(wVerHeadSize, 4);
// Total length of the version information
wDataLen = pVerHdr->wTotLen;
if ( wDataLen == 0 || wDataLen == (WORD)-1 ) { return(FALSE); //... No resource data
}
//... Allocate buffer to hold New Version
//... Stamping Block (make the buffer large to
//... account for expansion of strings
pVerBlk = (PVERBLOCK)((PBYTE)pVerHdr + wVerHeadSize); // point into version block of the old info
// we potentialy replace 8 (VerInfoElem=8) string in the version info
// I alloc 9 * 2 * 256 + size of the current version info. This should give us plenty of space
// I need to multiply by 2 because we work with unicode strings. One character = 2 bytes.
nNewVerBlockSize = wDataLen + (2 * (VerInfoElem+1) * MAX_VALUE); pNewVerStamp = (PVERBLOCK)malloc( nNewVerBlockSize ); //... Fill new memory block with zeros
memset((void *)pNewVerStamp, 0, nNewVerBlockSize);
//... Copy version info header into new version buffer
memcpy((void *)pNewVerStamp, (void *)pVerHdr, wVerHeadSize); pNewVerStamp->wLength = wVerHeadSize; //... Move after version info header
pNewBlk = (PVERBLOCK)((PBYTE)pNewVerStamp + wVerHeadSize);
wDataLen -= wVerHeadSize;
if (wDataLen > 0) { //... Start of a StringFileInfo block?
pNewStringInfoBlk = pNewBlk; //... Get # of bytes in this StringFileInfo
//... (Length of value is always 0 here)
wStringInfoLen = pVerBlk->wLength;
//... Move to start of first StringTable blk.
// -2 is for the starting WCHAR part of the VERBLOCK
wLen = ROUNDUP(sizeof(VERBLOCK) - 2 + MEMSIZE( STRINGFILEINFOLEN),4);
// Copy StringFileVersion header
CopyMemory( pNewBlk, pVerBlk, wLen); pNewStringInfoBlk->wLength = 0; // Set length, will be updated dynamicly
// Go to the language ID block
pVerBlk = (PVERBLOCK)((PBYTE)pVerBlk + wLen); pNewBlk = (PVERBLOCK)((PBYTE)pNewBlk + wLen);
// Decrement byte counter
DECWORDBY(&wDataLen, wLen); DECWORDBY(&wStringInfoLen, wLen);
// Update the size values
INCWORDBY(&pNewVerStamp->wLength, wLen); INCWORDBY(&pNewStringInfoBlk->wLength, wLen);
// We should be now at the language codepage ID string
if (wStringInfoLen > 0) { //... Get # of bytes in this StringTable
wStringTableLen = pVerBlk->wLength;
pNewStringTblBlk = pNewBlk;
//... Move to start of first String.
// -2 is for the starting WCHAR part of the VERBLOCK
wLen = ROUNDUP( sizeof(VERBLOCK) - 2 + MEMSIZE( LANGSTRINGLEN),4); // Copy language/codepage header
CopyMemory( pNewBlk, pVerBlk, wLen); pNewStringTblBlk->wLength = 0; // Set length, will be updated dynamicly
// Go to the first data block
pVerBlk = (PVERBLOCK)((PBYTE)pVerBlk + wLen); pNewBlk = (PVERBLOCK)((PBYTE)pNewBlk + wLen);
DECWORDBY(&wDataLen, wLen); DECWORDBY(&wStringInfoLen, wLen); DECWORDBY(&wStringTableLen, wLen);
// Update the size values
INCWORDBY(&pNewVerStamp->wLength, wLen); INCWORDBY(&pNewStringInfoBlk->wLength, wLen); INCWORDBY(&pNewStringTblBlk->wLength, wLen);
while ( wStringTableLen > 0 ) { // Copy the old data
CopyMemory( pNewBlk, pVerBlk, ROUNDUP(pVerBlk->wLength,4));
wLen = pVerBlk->wLength; //... Is value a string?
if (pVerBlk->wType == VERTYPESTRING) { //... See if we need to replace the value for this data
wLen = sizeof(szData); if (FindVerValue( pVerBlk->szKey, szData, &wLen)) { // Update the length values
pNewBlk->wValueLength = wLen; // Find the start of the data
lp = (LPBYTE) ((PBYTE)pNewBlk + ROUNDUP(pVerBlk->wLength,4) - ROUNDUP(MEMSIZE(pVerBlk->wValueLength),4));
// Get the size of the new data
wLen = ROUNDUP(MEMSIZE(pNewBlk->wValueLength),4); // Overwrite the old data
CopyMemory(lp, szData, wLen);
// calculate the size of this data and set it.
wLen = MEMSIZE(pNewBlk->wValueLength); pNewBlk->wLength += (wLen - MEMSIZE(pVerBlk->wValueLength)); } }
// Update the size values
wLen = ROUNDUP(pNewBlk->wLength,4); INCWORDBY(&pNewVerStamp->wLength, wLen); INCWORDBY(&pNewStringInfoBlk->wLength, wLen); INCWORDBY(&pNewStringTblBlk->wLength, wLen);
// Go to the next data block in the old version info
wLen = ROUNDUP(pVerBlk->wLength,4); pVerBlk = (PVERBLOCK)((PBYTE)pVerBlk + wLen);
DECWORDBY(&wDataLen, wLen); DECWORDBY(&wStringInfoLen, wLen); DECWORDBY(&wStringTableLen, wLen);
// Go to where the next data block in the new version info would be.
pNewBlk = (PVERBLOCK)((PBYTE)pNewBlk + ROUNDUP(pNewBlk->wLength,4));
} //... END while wStringTableLen
// Copy the rest of the VERBLOCK, this should be the VarFileInfo part.
if (wDataLen > 0) { // Update the most outer length info.
INCWORDBY(&pNewVerStamp->wLength, wDataLen); // Update length info
CopyMemory(pNewBlk, pVerBlk, wDataLen); } // Set the values to return to the caller.
*pwSize = pNewVerStamp->wLength; *lplpNewVersionInfo = (LPBYTE)pNewVerStamp;
} //... END if wStringInfoLen
}
// If some thing went wrong in finding the first language common part of the version info
// we did not update the version info, therefore we have to free the buffer we allocated
if (*pwSize == 0) free (pNewVerStamp);
return(TRUE); }
// Try to find the string in our array of version info we can overwrite
// lpKey: is a pointer to the value string in the old versin info block (UNICODE)
// lpszData: will contain the data string (UNICODE) if we found the value
// pwLen: pointer to a word which contains the size of the lpszData buffer on input
// if we found the value it contains the length the version info uses as ValueLength
// which is the size in single byte + zero termination
//
BOOL FindVerValue( WCHAR *lpKey, WCHAR *lpszData, WORD *pwLen) { char szValue[MAX_STRING]; UINT i = 0;
// Make it a SB character string
WideCharToMultiByte(CP_ACP, 0, lpKey, -1, szValue, sizeof(szValue), NULL, NULL);
// Zero out the buffer, I use so that the caller can over write more memory then the
// data in the string would take up. This is because the data is WORD aligned.
memset(lpszData, 0, *pwLen);
while (i < VerInfoElem) { if (lstrcmpi(Verinfo_Array[i].lpszName, szValue) == 0) { if ((Verinfo_Array[i].szValue[0] != '\0') && (*pwLen >= MEMSIZE(lstrlen(Verinfo_Array[i].szValue) + 1) ) ) { // Convert the ANSI data string into UNICODE
*pwLen = (WORD)MultiByteToWideChar(CP_ACP, 0, Verinfo_Array[i].szValue, -1 , lpszData, *pwLen); } i = VerInfoElem; // Stop searching
} i++; } // Return if we found the value and the array contained data.
return (*lpszData != '\0'); }
BOOL CALLBACK MyEnumLangsFunc(HANDLE hModule, LPSTR lpType, LPSTR lpName, WORD languages, LONG lParam) { // The first language we find is OK.
wVerLang = languages; return FALSE; }
|