You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
485 lines
20 KiB
485 lines
20 KiB
//***************************************************************************
|
|
//* 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;
|
|
}
|
|
|