Show a useful product name and description in the UAC prompt under Windows Vista/7

Add a VERSION_INFO section to the executable's resource file specifying
the product name and description.

The organization is currently set as 'Mendeley Ltd' - other projects
will want to customize this.

When the produced executable is digitally signed, the product name
and description are shown in the UAC prompt asking for admin
permissions.
This commit is contained in:
Robert Knight 2011-09-02 12:53:44 +01:00
parent ea7fc49002
commit 2f89332824
23 changed files with 3099 additions and 0 deletions

225
external/verpatch/src/ReadMe.txt vendored Normal file
View file

@ -0,0 +1,225 @@
Verpatch - a tool to patch win32 version resources on .exe or .dll files,
Version: 01-Nov-2009 (rev. #7 for CodeProject)
Verpatch is a command line tool for adding and editing the version information
of Windows executable files (applications, DLLs, kernel drivers)
without rebuilding the executable.
It can also add or replace Win32 (native) resources, and do some other
modifications of executable files.
Verpatch sets ERRORLEVEL 0 on success, otherwise errorlevel is non-zero.
Verpatch modifies files in place, so please make copies of precious files.
Command line syntax
===================
verpatch filename [version] [/options]
The filename is the executable file (exe, dll, sys, ocx...) to edit.
NOTE: the file will be modified; please make backup copies of precious files.
The version argument is one to four decimal numbers, separated by dots.
The version argument can also have additional text after the numbers; see examples below.
Common Options:
/s name "value" - add any version resource string attribute
The name can be either a full attribute name or alias; see below.
/sc "comment" - add or replace Comments string (same as /s Comments "comment")
/pv <version> - specify Product version
where <version> arg has same form as the file version (1.2.3.4 or "1.2.3.4 text")
/vft2 num - specify driver subtype (VFT2_xxx value, see winver.h)
The application type (VFT_xxx) is retained from the existing version resource of the file,
or filled automatically, based on the filename extension.
/fn - preserves Original filename, Internal name in the existing version resource of the file.
/va - creates a version resource. Use when the file has no version resource at all.
If this option not specified, verpatch will read and parse the version resourse from the file.
/vo - outputs the version info in RC format to stdout.
This can be used with /xi to import a version resource from another file.
Output of /vo (text from #ifdef RC_INVOKED to #endif) can be saved to a .rc file and compiled with rc.
Other options:
/xi- test mode. does all operations but does not modify the file
/xlb - test mode. Re-parses the version resource after modification.
/rpdb - remove path of the .pdb file in debug information.
/rf #id file - add or replace a raw binary resource from file (see below)
/noed - do not check for extra data appended to exe file
Examples
========
verpatch d:\foo.dll 1.2.33.44
- replaces only the file version, all 4 numbers,
the Original file name and Internal name strings are set to "foo.dll".
verpatch d:\foo.dll 33.44 /sc "a comment"
- replaces only two last numbers of the file version and adds a comment.
verpatch d:\foo.dll "33.44 special release" /sc "a comment"
- same as previous, with additional text in the version argument.
verpatch d:\foo.dll "1.2.33.44" /va /s FileDescription "foo.dll"
/s CompanyName "company" /s LegalCopyright "(c) 2009"
- adds a version resource to foo.dll, specify several string values.
( this should be one line)
verpatch d:\foo.dll /vo /xi
- dumps the version resource in RC format, does not update the file.
Remarks
=======
Verpatch replaces the version number in existing file version info resource
with the values given on the command line.
In "patch" mode (no /va option), the PE file should have a version resource,
which is parsed, and then parameters specified on the command line are applied.
If the file has no version resource, use /va to create it.
All nesessary strings can be specified with the /s option.
The command line can become very long, so you may want to use
a batch file or script.
See the example batch files, how to create a version resource and
specify all parameters with /va.
Verpatch can be run on same PE file any number of times.
The Version argument can be specified as 1 to 4 dot separated decimal numbers,
or as quoted string containing additional text after the numbers.
If less than 4 numbers are given, they are considered as lower numbers.
The higher version parts are retained from existing version resource.
For example, if the existing version info has version number 1.2.3.4
and 55.66 specified on the command line, the result will be 1.2.55.66.
The quotes surrounding string arguments are needed for the command line shell (cmd.exe),
for any argument that contains spaces.
The program ensures that the version numbers in the binary part
of the version structure and in the string part (as text) are same.
By default, Original File Name and Internal File Name are replaced to the actual filename.
Use /fn to preserve existing values in the version resource.
For option /s, specify *invariant* string names, not translations
( example: PrivateBuild, not "Private Build Description" ).
See below for the list of known string names and aliases.
Null values can be specified as "".
Strings for File version and Product version parameters are handled in a special way,
the /s switch can not be used to set them:
- File version can be specified as the 2nd positional argument of the command
- Product version can be specified using /pv switch
The /rf switch adds a resource from a file, or replaces a resource with same type and id.
The file is added as opaque binary chunk; the size is rounded up to 4 bytes.
The argument "#id" is a 32-bit hex number, prefixed with #.
Low 16 bits of this value are resource id; can not be 0.
Next 8 bits are resource type: one of RT_xxx symbols in winuser.h, or user defined.
High 8 bits of the #id arg are reserved0.
If the type value is 0, RT_RCDATA (10) is assumed.
The language code of resources added by this switch is 0 (Neutral).
Named resource types and ids are not implemented.
The program detects extra data appended to executable files, saves it and appends
again after modifying resources.
Such extra data is used by some installers, self-extracting archives and other applications.
However, the way we restore the data may be not compatible with these applications.
Please, verify that executable files that contain extra data work correctly after modification.
Command switch /noed disables checking for extra data.
====================================================================
Known string keys in VS_VERSION_INFO resource
====================================================================
The aliases are for use with the /s switch (case insensitive).
-------------------+----+-------------------------------+------------
Invariant name |note| English translation | Alias
-------------------+----+-------------------------------+------------
Comments Comments comment
CompanyName E Company company
FileDescription E Description description, desc
FileVersion *1 File version
InternalName Internal Name title
*2 Language
LegalCopyright E Copyright copyright, (c)
LegalTrademarks E Legal Trademarks tm, (tm)
OriginalFilename Original File Name
ProductName Product Name product
ProductVersion *1 Product Version productver, prodver
PrivateBuild Private Build Description pb
SpecialBuild Special Build Description sb, build
OleSelfRegister A -
AssemblyVersion N
Notes
*1: FileVersion, ProductVersion values:
Can be any text. Windows Explorer displays the version numbers from the binary header.
To avoid confusion, should begin with same v1.v2.v3.v4 version number as in the binary header.
*2: The "Language" value is the name of the language code specified in the header of the string block
of VS_VERSION_INFO resource. (or taken from VarFileInfo block?)
It is not a string in the string version data.
E: Displayed by Windows Explorer in Vista+
A: Intended for some API (OleSelfRegister is used in COM object registration)
N: Added by some .NET compilers. This version number is not contained in the
binary part of the version struct and can differ from the file version.
To change it, just use switch /s AssemblyVersion [value]
====================================================================
Known issues and TO DO's:
=========================
- Currenly implemented only US English and Language Neutral Unicode version resources.
MUI resource configuration manifests not checked.
New version resource will be created as Language Neutral.
Version info in other languages will be erroneously rewritten as English or Language Neutral- TO DO
A second (language neutral) version resource may be added to a file
that already has a version resource in other language. Switch /va won't help.
TO DO: ensure that a file has only one version resource!
- When verpatch is invoked from command prompt, or batch file, the string
arguments can contain only ANSI characters, because cmd.exe batch files cannot be
in Uncode format. If you need to include characters not in current locale,
use other shell languages that fully support Unicode (PowerShell, vbs, js).
- TO DO: In RC source output (/vo), special characters in strings are not quoted;
so /vo may produce invalid RC input
- The parser of binary version resources handles only the most common type of structure.
If the parser breaks because of unhandled structure format, try /va switch to
skip reading existing version resource and re-create it from scratch.
Consider using WINE or other open source implementations?
- option to add extra 0 after version strings : "string\0"
(tentative, requiested by a reader for some old VB code)
- For files with extra data, checksum is not re-calculated. Revise.
Source code
============
The source is provided as a Visual C++ 2005 project, it can be compiled with VC 2008 Express.
It demonstrates use of the UpdateResource and imagehlp.dll API.
It does not demonstrate use of c++, good coding manners or anything else.
Dependencies on C++ libraries available only with the full Visual C 2008 have been removed.
LICENSE TERMS: CPOL (CodeProject Open License)
http://www.codeproject.com/info/licenses.aspx
~~

100
external/verpatch/src/peExtras.cpp vendored Normal file
View file

@ -0,0 +1,100 @@
//
// PE image related utils
//
#include "stdafx.h"
#include "peExtras.h"
#include "relstamp.h"
#include <imagehlp.h>
#pragma comment(lib, "imagehlp")
#if UNICODE
// only non-unicode form of MapAndLoad exists in imagehlp. Grr.
BOOL MapAndLoadW(
__in PCWSTR ImageName,
__in_opt PCWSTR DllPath,
__out PLOADED_IMAGE LoadedImage,
__in BOOL DotDll,
__in BOOL ReadOnly
);
#define MapAndLoadT MapAndLoadW
#else
#define MapAndLoadT MapAndLoad
#endif
BOOL getFileExtraData(LPCTSTR fname, PVOID *extraData, LPDWORD dwSize)
{
*dwSize = 0;
*extraData = NULL;
BOOL r;
LOADED_IMAGE im;
r = ::MapAndLoadT(
fname,
_T("\\no-implicit-paths"),
&im,
FALSE, // .exe by default
TRUE // readonly
);
if (!r) {
dprint("err open file for reading extra data %d\n", GetLastError() );
return FALSE;
}
ULONG endOfImage = 0;
for (ULONG i = 0; i < im.NumberOfSections; i++)
{
if (endOfImage < im.Sections[i].PointerToRawData + im.Sections[i].SizeOfRawData)
endOfImage = im.Sections[i].PointerToRawData + im.Sections[i].SizeOfRawData;
}
if (im.SizeOfImage > endOfImage)
{
*dwSize = im.SizeOfImage - endOfImage;
*extraData = malloc(*dwSize);
ASSERT(*extraData);
memcpy(*extraData, &im.MappedAddress[endOfImage], *dwSize);
}
r = ::UnMapAndLoad( &im );
if (!r) {
dprint("err unloading file %d\n", GetLastError() );
return FALSE;
}
return TRUE;
}
BOOL appendFileExtraData( PCTSTR fname, PVOID extraData, DWORD dwSize)
{
DWORD bytesWritten = 0;
if (extraData == NULL)
return TRUE;
HANDLE fh = CreateFile(fname, GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, NULL);
if (INVALID_HANDLE_VALUE == fh ) {
dtprint(_T("Error opening executable %s err=%d\n"), fname, GetLastError());
return false;
}
DWORD pos = SetFilePointer(fh, 0, NULL, FILE_END);
if ( INVALID_SET_FILE_POINTER == pos ) {
dtprint(_T("Error seeking executable %s err=%d\n"), fname, GetLastError());
CloseHandle(fh);
return false;
}
if ( !WriteFile(fh, extraData, dwSize, &bytesWritten, NULL) || (bytesWritten != dwSize) ) {
dtprint(_T("Error writing extra data %s err=%d\n"), fname, GetLastError());
CloseHandle(fh);
return false;
}
CloseHandle(fh);
return TRUE;
}

4
external/verpatch/src/peExtras.h vendored Normal file
View file

@ -0,0 +1,4 @@
#pragma once
extern BOOL getFileExtraData(PCTSTR fname, PVOID *extraData, PDWORD dwSize);
extern BOOL appendFileExtraData(PCTSTR fname, PVOID extraData, DWORD size);

234
external/verpatch/src/peutils.cpp vendored Normal file
View file

@ -0,0 +1,234 @@
//
// PE image related utils
//
#include "stdafx.h"
#include "relstamp.h"
#include <imagehlp.h>
#pragma comment(lib, "imagehlp")
#if UNICODE
// only non-unicode form of MapAndLoad exists in imagehlp. Grr.
BOOL MapAndLoadW(
__in PCWSTR ImageName,
__in_opt PCWSTR DllPath,
__out PLOADED_IMAGE LoadedImage,
__in BOOL DotDll,
__in BOOL ReadOnly
);
#define MapAndLoadT MapAndLoadW
#else
#define MapAndLoadT MapAndLoad
#endif
bool clearPdbPath( PLOADED_IMAGE pim )
{
bool ret = false;
__try {
PIMAGE_NT_HEADERS pnth = pim->FileHeader;
PIMAGE_FILE_HEADER pfh = &(pnth->FileHeader);
WORD mtype = pfh->Machine;
PIMAGE_DEBUG_DIRECTORY pdebug;
PIMAGE_DATA_DIRECTORY pd;
switch (mtype) {
case IMAGE_FILE_MACHINE_I386: {
PIMAGE_OPTIONAL_HEADER32 pih =
&(((PIMAGE_NT_HEADERS32)pnth)->OptionalHeader);
pd = pih->DataDirectory;
break;
}
case IMAGE_FILE_MACHINE_AMD64: {
// EXPERIMENTAL for x64 image mapped on x86 host:
PIMAGE_OPTIONAL_HEADER64 pih =
&(((PIMAGE_NT_HEADERS64)pnth)->OptionalHeader);
pd = pih->DataDirectory;
break;
}
case IMAGE_FILE_MACHINE_IA64:
dprint("not tested for IA64\n"); return false;
default:
dprint("Unsupported arch\n"); return false;
}
ULONG32 adebug = pd[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
ULONG i;
for ( i = 0; i < pim->NumberOfSections; i++ ) {
PIMAGE_SECTION_HEADER sh = &pim->Sections[i];
if ( (sh->VirtualAddress <= adebug) &&
(sh->SizeOfRawData + sh->VirtualAddress > adebug) ) {
adebug -= sh->VirtualAddress;
adebug += sh->PointerToRawData;
break;
}
}
if ( i >= pim->NumberOfSections ) {
d2print("virt address %#x not found\n", adebug);
__leave;
}
pdebug = (PIMAGE_DEBUG_DIRECTORY)( adebug + (PUCHAR)(pim->MappedAddress) );
if ( pdebug->Type != IMAGE_DEBUG_TYPE_CODEVIEW /*2*/ )
__leave;
char *pdd = pdebug->PointerToRawData + (char*)(pim->MappedAddress);
if ( *((PULONG32)pdd) != 'SDSR' )
__leave;
unsigned dsize = pdebug->SizeOfData;
if ( dsize <= 0x18 ) //?
__leave;
pdd += 0x18; //skip header to the pdb name string
dsize -= 0x18;
unsigned dsize2 = strnlen( pdd, dsize );
if ( dsize2 == 0 || dsize2 >= dsize )
__leave;
d2print("PDB path in image:[%hs]\n", pdd);
char * pdbname = strrchr( pdd, '\\' );
if ( !pdbname || (pdbname - pdd) <= 4 || strchr( pdd, '\\' ) == pdbname ) {
d2print("PDB path too short, not changing\n");
ret = true; __leave;
}
memcpy( pdd, "\\x\\", 3 );
strcpy_s( pdd + 3, dsize2 - 3, pdbname );
char *t = pdd + strlen(pdd);
SecureZeroMemory( t, dsize2 - (t - pdd) );
ret = true;
} __except( EXCEPTION_EXECUTE_HANDLER ) {
ret = false;
dprint("%s - exception\n", __FUNCTION__);
}
return ret;
}
BOOL updFileChecksum( LPCTSTR fname, bool fRemovePdbPath )
{
BOOL r;
LOADED_IMAGE im;
r = ::MapAndLoadT(
fname,
_T("\\no-implicit-paths"),
&im,
FALSE, // .exe by default
FALSE // readonly
);
if (!r) {
dprint("err open file for rechecksum %d\n", GetLastError() );
return FALSE;
}
if ( !(im.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE) ) {
dprint("error: the file not marked as executable (build errors?)\n");
::UnMapAndLoad( &im );
return FALSE;
}
// begin image patches
if ( fRemovePdbPath && !clearPdbPath( &im ) ) {
dprint("error while editing pdb path\n");
::UnMapAndLoad( &im );
return FALSE;
}
// end image patches
// Checksum offset is same for IMAGE_OPTIONAL_HEADER32 and 64.
DWORD old_sum, new_sum;
PIMAGE_NT_HEADERS pimh = ::CheckSumMappedFile( im.MappedAddress, im.SizeOfImage, &old_sum, &new_sum );
if (!pimh ) {
dprint( "err CheckSumMappedFile %d\n", GetLastError() );
::UnMapAndLoad( &im );
return FALSE;
}
// d3print( "old cksm=%4.4X new=%4.4X\n", old_sum, new_sum );
pimh->OptionalHeader.CheckSum = new_sum;
r = ::UnMapAndLoad( &im );
if (!r) {
dprint("err writing file back after patching %d\n", GetLastError() );
return FALSE;
}
return TRUE;
}
#if UNICODE
PSTR strFilePathDeUnicode( PCWSTR tstr );
//---------------------------------------------------------------------------------
// Only a non-unicode form of MapAndLoad exists in imagehlp. Grr.
//---------------------------------------------------------------------------------
BOOL
MapAndLoadW(
__in PCWSTR fname,
__in_opt PCWSTR path,
__out PLOADED_IMAGE LoadedImage,
BOOL DotDll,
BOOL Readonly )
{
BOOL r;
UNREFERENCED_PARAMETER(path); // fake this arg to not search, and not deunicode
// Convert PWSTR to something acceptable for CreateFileA
// Another way: Require the file *name* only be ascii, then path can be gibberish.
// => cd to the path and use ./filename form.
PSTR a_fname = strFilePathDeUnicode(fname);
if (!a_fname) {
dprint("err opening file for rechecksum (unicode path)\n");
return FALSE;
}
r = MapAndLoad( a_fname, "\\dont-search-path", LoadedImage, DotDll, Readonly );
free( a_fname );
return r;
}
//---------------------------------------------------------------------------------
// Convert unicode file path to something acceptable for non-unicode file APIs.
// Caller should free returned pointer with free()
//---------------------------------------------------------------------------------
PSTR strFilePathDeUnicode( __in PCWSTR tstr)
{
//- dprint("orig. path [%ws]\n", tstr );
DWORD r;
PWSTR p = (PWSTR)calloc( MAX_PATH + 1, sizeof(WCHAR) );
if ( !p )
return NULL;
r = ::GetShortPathName( tstr, p, MAX_PATH );
if ( r == 0 || r >= MAX_PATH ) {
dprint("err GetShortPathName, gle=%d\n", GetLastError() );
free(p);
return NULL;
}
// now convert to single bytes
for (int i = 0; i < MAX_PATH; i++ ) {
WCHAR w = p[i];
if ( !w )
break;
if ( w > (WCHAR)0xFF ) {
dprint("error: GetShortPathName returned unicode??\n");
free(p);
SetLastError(ERROR_INVALID_NAME);
return NULL;
}
p[i] = 0;
((PUCHAR)p)[i] = w & 0xFF;
}
d3print("de-unicoded name [%ws]=[%hs]\n", tstr, p );
return (PSTR)p;
}
#endif //UNICODE

850
external/verpatch/src/relstamp.cpp vendored Normal file
View file

@ -0,0 +1,850 @@
// relstamp.cpp
//
// pa04 fixed bug in productversion aliases (/pv)
// pa03 fixed up for VC2008 Express
#include "stdafx.h"
#include "relstamp.h"
#include "vs_version.h"
#pragma comment(lib, "version")
#include "peExtras.h"
// Options
struct cmd_params {
bool PatchMode; // true: patch version descr. of the input file. false: create and replace.
bool DbgImportOnly; // debug: test import only, don't modify file
bool DbgLoopbackTest; // reparse generated blob
// For import & patch mode
bool DumpImportVerAsRC; // dump input version info in RC format
bool PreserveOriginalFilename; // false: set to actual filename
bool PreserveInternalFilename; // false: set to actual filename
bool PreserveStringVersionTail; // true: save appendix of fileversion, productversion strings
bool fClearPdbPath;
bool fNoPreserveExtraAppendedData; // true = do not check for extra data appended to end of file
bool cmd_arg_parse( int argc, _TCHAR *argv[], PCTSTR *fname, struct file_ver_data_s *fvd );
// cmd_params() {} // ctor
} g_params;
// Struct to hold a resource:
struct ResDesc
{
PUCHAR m_data;
DWORD m_cbdata;
ULONG_PTR m_type; // string ptr or numeric id
ULONG_PTR m_name; // string ptr or numeric id
WORD m_language;
ResDesc() : m_data(0), m_cbdata(0) {}
ResDesc( void *data, unsigned cbData, ULONG_PTR typeId,
ULONG_PTR name_id, WORD langId = LANG_NEUTRAL )
: m_data((PUCHAR)data), m_cbdata(cbData), m_type(typeId),
m_name(name_id), m_language(langId) {}
friend bool addResourceFromFile( PCTSTR resfile, UINT32 id_flags );
};
static
ResDesc *aRes[_A_MAX_N_RES + 1];
static
void addUpdRes( ResDesc *rd )
{
for (int i = 0; i < ARRAYSIZE(aRes) - 1; i++) {
if ( NULL == aRes[i] ) {
aRes[i] = rd;
return;
}
}
dprint("ERROR: Too many resources added\n");
throw ":TooManyRes";
return;
}
BOOL fillCompanyInfo( __out file_ver_data_s *fvd )
{
// default
fvd->addTwostr( L"CompanyName", DEF_COMPANY_NAME );
fvd->addTwostr( L"LegalCopyright", DEF_COPYRGT ); //"Copyright (c) 2009"
return TRUE;
}
BOOL fillProductInfo( __out file_ver_data_s *fvd )
{
// default
fvd->addTwostr( L"ProductName", DEF_PRODUCT_NAME );
fvd->pv_1 = 1;
fvd->pv_2 = 0;
fvd->pv_3 = 0;
fvd->pv_4 = 0;
return TRUE;
}
// callback class for ParseBinaryVersionResource()
class VerCallback1 : public IParseVerStrCallback
{
file_ver_data_s *m_vd;
int m_numcalled;
public:
VerCallback1(file_ver_data_s *fvd) : m_numcalled(0), m_vd(fvd) {};
void callback( PCWSTR name, PCWSTR value ) override;
};
// Callback to import version strings
void VerCallback1::callback( PCWSTR name, PCWSTR value )
{
d3print( "callback #%d [%ws]= [%ws]\n", m_numcalled, name, value );
++m_numcalled;
// filter off FileVersion, Product Version. always take from FIXED_INFO.
if ( 0 == _wcsicmp(name, L"FileVersion") ) {
// Optional tail: "1.2.3.4 tail"
PCTSTR tail = wcschr(value, _T(' '));
if ( tail ) {
while( *tail == _T(' ') ) tail++;
if (*tail) m_vd->sFileVerTail = tail;
}
return;
}
if ( 0 == _wcsicmp(name, L"ProductVersion") ) {
// Optional tail: "1.2.3.4 tail"
PCTSTR tail = wcschr(value, _T(' '));
if ( tail ) {
while( *tail == _T(' ') ) tail++;
if (*tail) m_vd->sProductVerTail = tail;
}
return;
}
if ( 0 == _wcsicmp(name, L"@LANG") ) {
static const WCHAR sigLangNeutral[] = L"000004b0"; /* LANG_NEUTRAL */
static const WCHAR sigLangEng[] = L"040904B0"; /* LANG_ENGLISH/SUBLANG_ENGLISH_US, Unicode CP */
if ( 0 == _wcsicmp( value, sigLangEng ) )
m_vd->langid = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
else if ( 0 == _wcsicmp( value, sigLangNeutral ) )
m_vd->langid = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
else {
dprint("Resource languages not supported yet! id=%ws", value);
m_vd->langid = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
}
return;
}
m_vd->addTwostr( name, value );
}
BOOL fillFileInfo( __out file_ver_data_s *fvd, PCTSTR fpath )
{
UINT xname, xdot_ext;
if ( !fileGetNameExtFromPath( fpath, &xname, &xdot_ext) ) {
dtprint(_T("Error parsing the file name\n") );
return FALSE;
}
PCTSTR pfilename = (PCTSTR)( (PUCHAR)fpath + xname );
PCTSTR pdot_ext = (PCTSTR)( (PUCHAR)fpath + xdot_ext );
if ( g_params.PatchMode )
{
// Import version blob from the file
PUCHAR verinfo = (PUCHAR)calloc( _MAX_VERS_SIZE_CB, 1 );
ASSERT(verinfo);
if ( !fileReadVersionInfo( fpath, verinfo, _MAX_VERS_SIZE_CB ) ) {
dprint("error reading version info from the file, err=%d\n", GetLastError());
if (ERROR_RESOURCE_TYPE_NOT_FOUND == GetLastError() )
dprint("The file does not have a version resource\n");
if (ERROR_RESOURCE_DATA_NOT_FOUND == GetLastError() )
dprint("The file could not be found or is not executable/dll\n");
free(verinfo);
return FALSE;
}
VS_FIXEDFILEINFO *fxi = NULL;
VerCallback1 mycb(fvd);
if ( !ParseBinaryVersionResource( verinfo, _MAX_VERS_SIZE_CB,
&fxi, &mycb, g_params.DumpImportVerAsRC ) ) {
dprint("error parsing version info from the file\n");
free(verinfo);
return FALSE;
}
fvd->v_1 = HIWORD(fxi->dwFileVersionMS);
fvd->v_2 = LOWORD(fxi->dwFileVersionMS);
fvd->v_3 = HIWORD(fxi->dwFileVersionLS);
fvd->v_4 = LOWORD(fxi->dwFileVersionLS);
fvd->pv_1 = HIWORD(fxi->dwProductVersionMS);
fvd->pv_2 = LOWORD(fxi->dwProductVersionMS);
fvd->pv_3 = HIWORD(fxi->dwProductVersionLS);
fvd->pv_4 = LOWORD(fxi->dwProductVersionLS);
fvd->dwFileFlags = (fxi->dwFileFlags & fxi->dwFileFlagsMask);
fvd->dwFileType = fxi->dwFileType;
fvd->dwFileSubType = fxi->dwFileSubtype;
if (fxi->dwFileOS != VOS_NT_WINDOWS32 && fxi->dwFileOS != VOS__WINDOWS32 ) {
dprint( "warning: imported dwFileOS not WINDOWS32 (=%#x)\n", fxi->dwFileOS);
}
free(verinfo);
} // patch mode
if ( !g_params.PreserveOriginalFilename ) {
fvd->addTwostr(L"OriginalFilename", pfilename);
}
if ( !g_params.PreserveInternalFilename ) {
fvd->addTwostr(L"InternalName", pfilename);
}
// Fill in per file info:
if ( VFT_UNKNOWN == fvd->dwFileType ) {
d3tprint(_T("file ext=%s\n"), pdot_ext);
if ( 0 == _tcsicmp( pdot_ext, _T(".exe") ))
fvd->dwFileType = VFT_APP;
if ( 0 == _tcsicmp( pdot_ext, _T(".sys") ))
fvd->dwFileType = VFT_DRV;
if ( 0 == _tcsicmp( pdot_ext, _T(".dll") ))
fvd->dwFileType = VFT_DLL;
}
if ( !fvd->getValStr(L"FileDescription") ) {
// Use filename as default FileDescription
fvd->addTwostr( L"FileDescription", pfilename );
}
return TRUE;
}
BOOL parseFileVer( __out file_ver_data_s *fvd, PCTSTR arg )
{
unsigned n1=0,n2=0,n3=0,n4=0;
int nf = _stscanf_s( arg, _T("%d.%d.%d.%d"), &n1, &n2, &n3, &n4);
// if less than 4 numbers given, they are minor
switch( nf ) {
case 1:
fvd->v_4 = n1;
break;
case 2:
fvd->v_3 = n1; fvd->v_4 = n2;
break;
case 3:
fvd->v_2 = n1; fvd->v_3 = n2; fvd->v_4 = n3;
break;
case 4:
fvd->v_1 = n1; fvd->v_2 = n2; fvd->v_3 = n3; fvd->v_4 = n4;
break;
default:
dprint("error parsing version arg\n");
return FALSE;
}
// Optional tail: "1.2.3.4 tail"
// If no tail found, don't replace existing.
PCTSTR tail = _tcschr(arg, _T(' '));
if ( tail ) {
while( *tail == _T(' ') ) tail++;
fvd->sFileVerTail = (*tail) ? strUnEscape(tail) : NULL;
}
return TRUE;
}
BOOL parseProductVer( __out file_ver_data_s *fvd, PCTSTR arg )
{
unsigned n1=0,n2=0,n3=0,n4=0;
int nf = _stscanf_s( arg, _T("%d.%d.%d.%d"), &n1, &n2, &n3, &n4);
// if less than 4 numbers given, they are minor
switch( nf ) {
case 1:
fvd->pv_4 = n1;
break;
case 2:
fvd->pv_3 = n1; fvd->pv_4 = n2;
break;
case 3:
fvd->pv_2 = n1; fvd->pv_3 = n2; fvd->pv_4 = n3;
break;
case 4:
fvd->pv_1 = n1; fvd->pv_2 = n2; fvd->pv_3 = n3; fvd->pv_4 = n4;
break;
default:
dprint("error parsing version arg\n");
return FALSE;
}
// Optional tail: "1.2.3.4 tail"
// If no tail found, don't replace existing.
PCTSTR tail = _tcschr(arg, _T(' '));
if ( tail ) {
while( *tail == _T(' ') ) tail++;
fvd->sProductVerTail = (*tail) ? strUnEscape(tail) : NULL;
}
return TRUE;
}
BOOL updFileResources( LPCTSTR fname, __in ResDesc *ard[] )
{
if ( !ard[0] ) {
d2print("No resources to update\n");
return TRUE;
}
// open file, start update
BOOL bDeleteExistingResources = FALSE;
BOOL ok = FALSE;
int n_updated = 0;
HANDLE rhandle = ::BeginUpdateResource( fname, bDeleteExistingResources );
if ( !rhandle ) {
dprint( "Error opening file for update resources, err=%d\n", GetLastError() );
return FALSE;
}
for (int i = 0; ; i++) {
ResDesc *rd = ard[i];
if ( !rd )
break;
ok = ::UpdateResource(
rhandle,
(LPCTSTR)rd->m_type,
(LPCTSTR)rd->m_name,
rd->m_language,
(LPVOID)rd->m_data,
rd->m_cbdata
);
if (!ok) {
dprint("UpdateResource #%d err=%d\n", i, GetLastError());
break;
}
n_updated++;
}
d2print("Resources updated: %d\n", n_updated);
BOOL r2 = ::EndUpdateResource( rhandle, !ok );
if (!r2) {
dprint("EndUpdateResource err=%d\n", GetLastError());
ok = FALSE;
}
return ok;
}
long parseFileSubType( PCTSTR ap )
{
if ( isdigit(*ap) )
return _tcstol( ap, NULL, 16 );
if ( 0 == _tcsicmp(ap, _T("DRV_SYSTEM")) )
return VFT2_DRV_SYSTEM;
if ( 0 == _tcsicmp(ap, _T("DRV_NETWORK")) )
return VFT2_DRV_NETWORK;
if ( 0 == _tcsicmp(ap, _T("DRV_COMM")) )
return VFT2_DRV_COMM;
return 0;
}
////////////////////////////////////////////////////////////////////////
// main
////////////////////////////////////////////////////////////////////////
int _tmain(int argc, _TCHAR* argv[])
{
static file_ver_data_s file_ver_data;
PCTSTR fname;
BOOL r;
SetErrorMode( SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX );
r = SetThreadLocale( LOCALE_INVARIANT );
if ( !g_params.cmd_arg_parse( argc, argv, &fname, NULL ) ) {
return 1;
}
d2tprint(_T("relstamp file=[%s]\n"), fname );
r &= fillFileInfo( &file_ver_data, fname );
if ( !g_params.PatchMode ) { // if not patch mode, read from INI
r &= fillCompanyInfo( &file_ver_data );
r &= fillProductInfo( &file_ver_data );
}
// Parse again, put the args into the data
if ( !g_params.cmd_arg_parse( argc, argv, &fname, &file_ver_data ) ) {
dprint("Error parsing command args 2 pass!\n");
r = FALSE;
}
if ( !r ) {
dprint("Some of actions failed, exiting\n");
return 1;
}
PUCHAR verdata = NULL;
r = makeVersionResource( &file_ver_data, &verdata );
if ( !r ) return 1;
if ( g_params.DbgLoopbackTest )
{ // loopback test for vers res parser
VS_FIXEDFILEINFO *fxi = NULL;
VerCallback1 mycb(&file_ver_data);
dprint("dbg: begin reparse\n");
r = ParseBinaryVersionResource( verdata, _MAX_VERS_SIZE_CB, &fxi, &mycb, g_params.DumpImportVerAsRC );
dprint("dbg: end reparse\n");
}
// Save extra data possibly appended to the exe file
DWORD extrasSize = 0;
void *extras;
if ( !g_params.fNoPreserveExtraAppendedData ) {
r = getFileExtraData(fname, &extras, &extrasSize);
if ( !r ) {
dprint("Failed to read extra data\n");
return 2;
}
if ( extrasSize ) {
d2print("Found extra %d bytes appended to the file\n", extrasSize);
}
}
if( g_params.DbgImportOnly ) {
dprint("read only, exiting\n");
return 0;
}
// Add the version to resources update:
addUpdRes(
new ResDesc(
(PVOID)verdata,
*((PWORD)verdata),
(ULONG_PTR)RT_VERSION,
(ULONG_PTR)MAKEINTRESOURCE(VS_VERSION_INFO),
file_ver_data.langid
)
);
r = updFileResources( fname, &aRes[0] );
if ( !r ) {
dprint("Update file resources failed, the file may be damaged\n");
return 2;
}
if ( extrasSize ) {
// Restore extra data appended to end of exec
r = appendFileExtraData(fname, extras, extrasSize);
if ( !r ) {
dprint("Failed to restore extra data, the file may be damaged\n");
return 3;
}
} else {
// Re-checksum
r = updFileChecksum( fname, g_params.fClearPdbPath );
if ( !r ) {
dprint("Update file checksum failed, the file may be damaged\n");
return 3;
}
}
d2print("ok\n");
return 0;
}
// Check if the string key name is alias, return the correct invariant key name:
// See ver-res.txt
LPCTSTR aliasToStringKey( LPCTSTR key )
{
if ( 0 == _tcsicmp(key, _T("comment"))) return _T("Comments");
if ( 0 == _tcsicmp(key, _T("company"))) return _T("CompanyName");
if ( 0 == _tcsicmp(key, _T("description"))) return _T("FileDescription");
if ( 0 == _tcsicmp(key, _T("desc"))) return _T("FileDescription");
if ( 0 == _tcsicmp(key, _T("title"))) return _T("InternalName");
if ( 0 == _tcsicmp(key, _T("copyright"))) return _T("LegalCopyright");
if ( 0 == _tcsicmp(key, _T("(c)"))) return _T("LegalCopyright");
if ( 0 == _tcsicmp(key, _T("trademarks"))) return _T("LegalTrademarks");
if ( 0 == _tcsicmp(key, _T("(tm)"))) return _T("LegalTrademarks");
if ( 0 == _tcsicmp(key, _T("tm"))) return _T("LegalTrademarks");
if ( 0 == _tcsicmp(key, _T("product"))) return _T("ProductName");
if ( 0 == _tcsicmp(key, _T("pb"))) return _T("PrivateBuild");
if ( 0 == _tcsicmp(key, _T("sb"))) return _T("SpecialBuild");
if ( 0 == _tcsicmp(key, _T("build"))) return _T("SpecialBuild");
if ( 0 == _tcsicmp(key, _T("fileversion"))) {
dprint("do NOT use FileVersion with /s option, see usage!\n");
}
if ( 0 == _tcsicmp(key, _T("language"))) {
dprint("do NOT use Language with /s option, this won't work.\n");
}
//d3tprint(_T("alias not found %s\n"), key);
return key;
}
///////////////////////////////////////////////////////////////////////
// Command args
//
//TODO: handle escapes in strings passed on command line
// mode without version res: only bin patches
//
///////////////////////////////////////////////////////////////////////
bool cmd_params::cmd_arg_parse( int argc, _TCHAR *argv[], PCTSTR *fname,
__out_opt struct file_ver_data_s *fvd )
{
bool firstPass = false;
static file_ver_data_s my_fvd;
if ( !fvd ) {
firstPass = true;
fvd = &my_fvd;
}
int pos_args = 0;
int patch_actions = 0;
PatchMode = true; //default mode -pa04
for( int i = 1; i < argc; i++ ) {
PCTSTR ap = argv[i];
if (!ap)
break; //done
if ( *ap == _T('/') || *ap == _T('-') ) {
ap++;
if ( argmatch(_T("?"), ap ) )
{ showUsage(); return false; }
else if ( argmatch(_T("fn"), ap) ) // keep original filename
PreserveInternalFilename = PreserveOriginalFilename = true;
else if ( argmatch(_T("vo"), ap) ) // dump input res. desc. as RC source
DumpImportVerAsRC = true;
else if ( argmatch(_T("xi"), ap) ) { // read only, don't patch
DbgImportOnly = true; // dbg
}
else if ( argmatch(_T("xlb"), ap) ) // reparse created res.desc. (self test)
DbgLoopbackTest = true; //dbg
else if ( argmatch(_T("sc"), ap) ) { // /sc "comment"
ap = argv[++i]; ASSERT(ap);
patch_actions++;
if( !fvd->addTwostr( _T("Comments"), strUnEscape(ap) ) ) {
dtprint(_T("Error adding string:[%s]\n"), ap);
return false;
}
}
else if ( argmatch(_T("s"), ap) ) {
//Add a string to version res: /s name "comment"
PCTSTR ns = argv[++i]; ASSERT(ns);
ap = argv[++i]; ASSERT(ap);
patch_actions++;
if( !fvd->addTwostr( aliasToStringKey(ns), strUnEscape(ap) ) ) {
dtprint(_T("Error adding string:[%s]\n"), ap);
return false;
}
}
else if( argmatch(_T("pv"), ap) ||
argmatch(_T("prodver"), ap) ||
argmatch(_T("productver"), ap) ||
argmatch(_T("productversion"), ap) ) {
// product version string has same form as the file version arg (positional)
ap = argv[++i]; ASSERT(ap);
if ( !parseProductVer( fvd, ap ) ) {
dprint("bad product version arg, see usage (/?)\n");
return false;
}
patch_actions++;
}
else if (argmatch(_T("vft2"), ap) ) { // version subtype
ap = argv[++i]; ASSERT(ap);
long n = parseFileSubType(ap);
if ( !(n >= 0 && n <= 0xFFFF) ) {
dtprint(_T("Bad subtype \"%s\". For usage: /?\n"), ap);
return false;
}
fvd->dwFileSubType = (USHORT)n;
patch_actions++;
}
else if ( argmatch(_T("rpdb"), ap) ) {
g_params.fClearPdbPath = true;
patch_actions++;
}
else if ( argmatch(_T("va"), ap) ) { // auto generate version desc.
PatchMode = false;
patch_actions++;
}
else if ( argmatch(_T("rf"), ap) ) {
// Add a resource from file: /rf #<number> filename
// For now, use numeric ids only. High 16 bits can be used for type, etc.
ULONG res_id = 0;
ap = argv[++i]; ASSERT(ap);
if ( *ap++ != _T('#') || 0 == (res_id = _tcstol( ap, NULL, 16 )) ) {
dtprint(_T("Resource id must be #hex_number\n"), ap);
return false;
}
ap = argv[++i]; ASSERT( ap && *ap != _T('/') && *ap != _T('-') );
// only during 2nd pass:
if ( !firstPass && !addResourceFromFile( ap, res_id ) ) {
dtprint(_T("Error adding resource file [%s] id=%#X\n"), ap, res_id);
return false;
}
}
else if ( argmatch(_T("noed"), ap) ) {
// Do not check for extra data appended to exe file
g_params.fNoPreserveExtraAppendedData = true;
}
else {
dtprint(_T("Unknown option \"%s\". For usage: /?\n"), ap);
return false;
}
} else {
// position args:
switch(pos_args++) {
case 0:
*fname = ap; //ex d:/stuff/foo.sys
break;
case 1:
if ( parseFileVer( fvd, ap ) ) {
patch_actions++;
break;
}
dprint("bad version arg, see usage (/?)\n");
return false;
default:
dprint("Too many args, see usage (/?)\n");
return false;
}
}
}
if ( 0 == pos_args ) {
showUsage();
return false;
}
if ( 0 == patch_actions )
DbgImportOnly = true; //dbg
// flags: SpecialBuild fVerBeta fVerDebug?
return true;
}
////////////////////////////////////////////////////////////////////////
// misc. utils
////////////////////////////////////////////////////////////////////////
LPWSTR stralloc( __in PCSTR s )
{
ASSERT(s);
size_t n = strlen(s);
ASSERT( n < (256));
LPWSTR p = (LPWSTR)malloc( (n + 1) * sizeof(WCHAR) );
ASSERT( p );
for ( size_t i = 0; i <= n; i++ )
p[i] = s[i];
return p;
}
LPWSTR stralloc( __in PCWSTR s )
{
ASSERT(s);
PWSTR p = _wcsdup(s);
ASSERT(p);
return p;
}
// return byte offset to name and .ext parts of filename
BOOL fileGetNameExtFromPath( __in PCTSTR path, __out PUINT pname, __out PUINT pext )
{
ASSERT(path);
PTSTR name = (PTSTR)calloc( 2*MAX_PATH, sizeof(TCHAR));
ASSERT(name);
PTSTR ext = name + MAX_PATH;
errno_t e;
e = _tsplitpath_s(path, NULL, 0 , NULL, 0, name, MAX_PATH, ext, 10);
if ( e == ERROR_SUCCESS ) {
size_t lname = _tcslen(name);
size_t lext = _tcslen(ext);
*pname = (UINT)(_tcslen(path) - lname - lext) * sizeof(TCHAR);
*pext = *pname + (lname * sizeof(TCHAR));
} else {
dtprint(_T("Error parsing filename: err=%d path=[%s]\n"), e, path);
}
if ( name ) free( name );
return e == ERROR_SUCCESS;
}
// Read VS_VERSION resource blob from a file by name
BOOL fileReadVersionInfo( __in PCTSTR fname, __out PUCHAR buf, __in unsigned size)
{
BOOL r;
r = GetFileVersionInfo( fname, NULL /*reserved*/, (DWORD)size, (LPVOID)buf );
return r;
}
// Format a string escaped for RC: quotes, symbols (R) (C) and so on
PCWSTR strEscape( __in PCWSTR ws )
{
if ( !ws || !*ws ) {
return (PCWSTR)L"\\0";
}
return ws; //$$$ TODO. for now, unescaped text will be printed, edit manually
}
// Unescape a string escaped for RC: quotes, symbols (R) (C) and so on
PCWSTR strUnEscape( __in PCWSTR ws )
{
return ws; //$$$ TODO
}
bool argmatch(PCTSTR sw, PCTSTR cmp )
{
if ( 0 == _tcsicmp(sw, cmp) ) return true;
return false;
}
//////////////////////////////////////////////////////////////////////////
// Add raw binary resource from file.
// Low 16 bit of id_flags = resource ID. Bitmask FF0000 = type (0=RCDATA). High byte reserved.
//////////////////////////////////////////////////////////////////////////
bool addResourceFromFile( PCTSTR resfile, UINT32 id_flags )
{
UINT64 xFileSize;
HANDLE fh = CreateFile(resfile, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, NULL);
if (INVALID_HANDLE_VALUE == fh ) {
dtprint(_T("Error opening res. file %s err=%d\n"), resfile, GetLastError());
return false;
}
if ( !GetFileSizeEx( fh, (PLARGE_INTEGER)&xFileSize ) ) {
dtprint(_T("Error get file size %s\n"), resfile);
CloseHandle(fh);
return false;
}
if ( xFileSize > _A_MAX_RES_CB ) {
dtprint(_T("Error: file size too large %s %I64d K\n"), resfile, (xFileSize/1024));
CloseHandle(fh);
return false;
}
DWORD dwFileSize = (DWORD)xFileSize;
PUCHAR dp = (PUCHAR)calloc( dwFileSize + 4, 1 ); // round up to 4 bytes
ASSERT(dp);
DWORD cbread;
if ( !ReadFile( fh, (LPVOID)dp, dwFileSize, &cbread, NULL ) || cbread != dwFileSize ) {
dtprint(_T("Error reading file %s\n"), resfile);
CloseHandle(fh);
free(dp);
return false;
}
CloseHandle(fh);
// round up to 4 bytes
dwFileSize = (dwFileSize + 3) & ~3;
ULONG restype = (id_flags >> 16) & 0xFF;
if ( 0 == restype ) restype = (ULONG)RT_RCDATA;
addUpdRes( new ResDesc( dp, dwFileSize, restype, id_flags & 0xFFFF ) );
return true;
}
#if 1
// Get a resource (pointer and size)
// hm: module handle. 0 is the exe file
bool getResource( HMODULE hm, DWORD rtype, DWORD rid, __out LPCVOID *p, __out PDWORD size )
{
HRSRC hrs = FindResourceEx( hm, (LPCTSTR)rtype, (LPCTSTR)rid, 0 /*MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)*/ );
if ( !hrs ) {
d2print("cannot find res. %#x, err=%d\n", rid, GetLastError());
return false;
}
DWORD rsize = SizeofResource(hm, hrs);
if ( rsize < sizeof (DWORD) ) {
d2print("res. size = 0??\n");
return false;
}
HGLOBAL hg = LoadResource( hm, hrs );
if ( !hg ) {
d2print("err LoadResource %d\n", GetLastError() );
return false;
}
*p = LockResource( hg );
*size = rsize;
return !!(*p) ;
}
////////////////////////////////////////////////////////////////////////////////
// USAGE
// How to add the help text:
// - Put the text in a file (ex. usage.txt)
// - make a copy of this exe file
// - use /rf #64 <file.txt> to attach this text file to the copy of the program
// See ver-self.cmd for a working example (this is also a kind of unit test :)
////////////////////////////////////////////////////////////////////////////////
void showUsage()
{
const ULONG usage_txt_id = 0x64; //100
const ULONG usage_txt_type = (ULONG)(ULONG_PTR)RT_RCDATA;
const void *p;
DWORD len;
if ( !getResource( 0, usage_txt_type, usage_txt_id, &p, &len ) ) {
dprint("No usage text! See readme.txt for instructions how to add it.\n");
return;
}
fwrite(p, len, 1, stderr);
}
#else
void showUsage()
{
dprint("verpatch r2 (2009/05/31)\n");
dprint("Usage: verpatch filename [version] [/options]\n");
dprint("\nOptions:\n");
dprint(" /sc \"comment\"\t- add Comments string\n"); // todo: escapes
dprint(" /s name \"value\"\t- add/replace any version resource string\n"); // todo: escapes
dprint(" /va\t- create a default version resource\n");
dprint(" /vft2 num\t- specify driver type (VFT2_xxx, see winver.h)\n");
dprint(" /fn\t- preserve Original filename, Internal name in the file version info\n");
dprint(" /vo\t- output the file version info in RC format\n");
dprint(" /xi\t- test mode, do not patch the file\n");
dprint(" /rpdb\t- remove path to .pdb in debug information\n");
dprint(" /rf #hex-id file - add a resource from file (see readme)\n");
dprint("\n\nExamples:\n");
dprint(" verpatch d:\\foo.dll 1.2.33.44 /sc \"holy cow, it works!\"\n");
dprint(" verpatch d:\\foo.sys \"33.44 release\" /fn\n");
dprint(" verpatch d:\\foo.exe 1.2.3.4 /rf #9 driver.sys\n");
}
#endif

64
external/verpatch/src/relstamp.h vendored Normal file
View file

@ -0,0 +1,64 @@
// relstamp R2
#pragma once
// Defs for Windows version resource
// http://msdn.microsoft.com/en-us/library/ms646997(VS.85).aspx
#define _MAX_VERS_SIZE_CB 4096
#define _MAX_VER_STRING_LEN_CCH 255
#define _MAX_VER_CUSTOM_STRINGS 16
#define _A_MAX_N_RES 8
#define _A_MAX_RES_CB (500*1024)
#if ( 1 && !defined(DEF_COMPANY_NAME) )
#define DEF_COMPANY_NAME _T(" ")
#define DEF_COPYRGT _T("Copyright (c) 2009")
#define DEF_PRODUCT_NAME _T(" ")
#endif
#define dprint(fmt, ...) printf(fmt, __VA_ARGS__)
#define dtprint(tfmt, ...) _tprintf(tfmt, __VA_ARGS__)
#ifdef NDEBUG
#undef NDEBUG
#endif
#define ASSERT assert
#ifndef _A_NOISE_DBG
#define _A_NOISE_DBG 1
#endif
#if _A_NOISE_DBG
#define d2print(fmt, ...) dprint(fmt, __VA_ARGS__)
#define d2tprint(tfmt, ...) dtprint(tfmt, __VA_ARGS__)
#else
#define d2print(fmt, ...) __noop(fmt, __VA_ARGS__)
#define d2tprint(tfmt, ...) __noop(tfmt, __VA_ARGS__)
#endif //_A_NOISE_DBG
#if ( _A_NOISE_DBG > 1 )
#define d3print d2print
#define d3tprint d2tprint
#else
#define d3print(fmt, ...) __noop(fmt, __VA_ARGS__)
#define d3tprint(tfmt, ...) __noop(tfmt, __VA_ARGS__)
#endif //_A_NOISE_DBG
// Format a string escaped for RC: quotes, (R), (C) and so on
PCWSTR strEscape( __in PCWSTR ws );
PCWSTR strUnEscape( __in PCWSTR ws );
// strdup likes:
LPWSTR stralloc( __in PCSTR s );
LPWSTR stralloc( __in PCWSTR s );
// Get name, ext from full filename
BOOL fileGetNameExtFromPath( __in PCTSTR path, __out PUINT pname, __out PUINT pext );
BOOL fileReadVersionInfo( __in PCTSTR fname, __out PUCHAR buf, __in unsigned size);
// 3state flag:
enum f3state { F3NOTSET, F3FALSE, F3TRUE };
void showUsage();
bool argmatch(__in PCTSTR sw, __in PCTSTR cmp );
BOOL updFileChecksum( LPCTSTR fname, bool fRemovePdbPath = false );

8
external/verpatch/src/stdafx.cpp vendored Normal file
View file

@ -0,0 +1,8 @@
// stdafx.cpp : source file that includes just the standard includes
// relstamp.pch will be the pre-compiled header
// stdafx.obj will contain the pre-compiled type information
#include "stdafx.h"
// TODO: reference any additional headers you need in STDAFX.H
// and not in this file

22
external/verpatch/src/stdafx.h vendored Normal file
View file

@ -0,0 +1,22 @@
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//
#pragma once
#include "targetver.h"
#define WIN32_LEAN_AND_MEAN 1
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <tchar.h>
#include <malloc.h>
#include <string.h>
#include <assert.h>
#include <strsafe.h>
// TODO: reference additional headers your program requires here

13
external/verpatch/src/targetver.h vendored Normal file
View file

@ -0,0 +1,13 @@
#pragma once
// The following macros define the minimum required platform. The minimum required platform
// is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run
// your application. The macros work by enabling all features available on platform versions up to and
// including the version specified.
// Modify the following defines if you have to target a platform prior to the ones specified below.
// Refer to MSDN for the latest info on corresponding values for different platforms.
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501 //XP
#endif

22
external/verpatch/src/usage.txt vendored Normal file
View file

@ -0,0 +1,22 @@
verpatch r8 (2011-04-25)
Usage: verpatch filename [version] [/options]
Options:
/s name "value" - add or replace any version resource string.
name: a string attribute. See readme.txt for reference.
/sc "comment" - add Comments string (same as /s comment "comment")
/va - create the version resource (use when the file has no version info, or to replace it)
/pv <version> - specify Product version
/prodver, /productversion - same as /pv
/fn - preserve Original Filename, Internal Name in the file version info
/vo - output the file version info in RC format
/xi - test mode, does not modify the file
/vft2 num - specify driver type (one of VFT2_ values defined in winver.h)
/rpdb - remove path to .pdb in debug information
/rf #hex-id file - add a raw binary resource from file (see readme)
/noed - discard any extra data attached at the end of file
Examples:
verpatch d:\foo.dll 1.2.33.44 /s comment "fixed this and that"
verpatch d:\foo.dll "33.44 released on %date%"
verpatch d:\foo.exe /va 1.2.3.4 /s desc "my program"
verpatch d:\foo.exe 1.2.3.4 /rf #9 embedded-dll.dll

19
external/verpatch/src/ver-self.cmd vendored Normal file
View file

@ -0,0 +1,19 @@
: Add version info resource with some strings and a resource file to verpatch.exe
: (also kind of self test)
: Run this in Release or Debug dir
:
set _ver="1.0.1.9 [%date%]"
set _s1=/s desc "Version patcher tool" /s copyright "(C) 1998-2011, pavel_a"
set _s1=%_s1% /s pb "pa"
set _s1=%_s1% /pv "1.0.0.1 (free)"
set _resfile=/rf #64 ..\usage.txt
: Run a copy of verpatch on itself:
copy verpatch.exe v.exe || exit /b 1
v.exe verpatch.exe /va %_ver% %_s1% %_s2% %_resfile%
@echo Errorlevel=%errorlevel%

View file

@ -0,0 +1,234 @@
Verpatch - a tool to patch win32 version resources on .exe or .dll files,
Version: 01-Nov-2009 (rev. #7 for CodeProject) edited
Verpatch is a command line tool for adding and editing the version information
of Windows executable files (applications, DLLs, kernel drivers)
without rebuilding the executable.
It can also add or replace Win32 (native) resources, and do some other
modifications of executable files.
Verpatch sets ERRORLEVEL 0 on success, otherwise errorlevel is non-zero.
Verpatch modifies files in place, so please make copies of precious files.
Command line syntax
===================
verpatch filename [version] [/options]
Where
- filename : any Windows PE file (exe, dll, sys, ocx...) that can have native resources
- version : one to four decimal numbers, separated by dots, ex.: 1.2.3.4
Additional text can follow the numbers; see examples below. Ex.: "1.2.3.4 extra text"
- options: see below
Common Options:
/va - creates a version resource. Use when the file has no version resource at all,
or existing version resource should be replaced.
If this option not specified, verpatch will patch the version resourse found in the file.
/s name "value" - add a version resource string attribute
The name can be either a full attribute name or alias; see below.
/sc "comment" - add or replace Comments string (shortcut for /s Comments "comment")
/pv <version> - specify Product version
where <version> arg has same form as the file version (1.2.3.4 or "1.2.3.4 text")
/fn - preserves Original filename, Internal name in the existing version resource of the file.
Other options:
/vo - outputs the version info in RC format to stdout.
This can be used with /xi to import a version resource from another file.
Output of /vo (text from #ifdef RC_INVOKED to #endif) can be saved to a .rc file and compiled with rc.
/xi- test mode. does all operations but does not modify the file
/xlb - test mode. Re-parses the version resource after modification.
/rpdb - removes path to the .pdb file in debug information; leaves only file name.
/rf #id file - add or replace a raw binary resource from file (see below)
/noed - do not check for extra data appended to exe file
/vft2 num - specify driver subtype (VFT2_xxx value, see winver.h)
The application type (VFT_xxx) is retained from the existing version resource of the file,
or filled automatically, based on the filename extension (exe->app, sys->driver, anything else->dll)
Examples
========
verpatch d:\foo.dll 1.2.33.44
- replaces only the file version, all 4 numbers,
the Original file name and Internal name strings are set to "foo.dll".
File foo.dll should already have a version resource.
verpatch d:\foo.dll 33.44 /s comment "a comment"
- replaces only two last numbers of the file version and adds a comment.
File foo.dll should already have a version resource.
verpatch d:\foo.dll "33.44 special release" /sc "a comment"
- same as previous, with additional text in the version argument.
verpatch d:\foo.dll "1.2.33.44" /va /s description "foo.dll"
/s company "My Company" /s copyright "(c) 2009"
- adds or replaces version resource to foo.dll, with several string values.
( all options should be one line)
verpatch d:\foo.dll /vo /xi
- dumps the version resource in RC format, does not update the file.
Remarks
=======
Verpatch replaces the version number in existing file version info resource
with the values given on the command line.
In "patch" mode (no /va option), the PE file should have a version resource,
which is parsed, and then parameters specified on the command line are applied.
If the file has no version resource, or you want to discard the existing resource, use /va switch.
All nesessary strings can be specified with the /s option.
The command line can become very long, so you may want to use a batch file or script.
See the example batch files, how to create a version resource and
specify all parameters with /va.
Verpatch can be run on same PE file any number of times.
The Version argument can be specified as 1 to 4 dot separated decimal numbers,
or as quoted string containing additional text after the numbers.
If less than 4 numbers are given, they are considered as minor numbers.
The higher version parts are retained from existing version resource.
For example, if the existing version info has version number 1.2.3.4
and 55.66 specified on the command line, the result will be 1.2.55.66.
The quotes surrounding string arguments are needed for the command shell (cmd.exe),
for any argument that contains spaces.
The program ensures that the version numbers in the binary part
of the version structure and in the string part (as text) are same.
By default, Original File Name and Internal File Name are replaced to the actual filename.
Use /fn to preserve existing values in the version resource.
For option /s, specify language-neutral string names, not translations
( example: PrivateBuild, not "Private Build Description" ).
Null values can be specified as empty string ("").
See below for the list of known string keys names and their aliases.
The examples above use the aliases.
Strings for File version and Product version parameters are handled in a special way,
the /s switch can not be used to set them:
- File version can be specified only as the 2nd positional argument
- Product version can be specified using /pv switch
The /rf switch adds a resource from a file, or replaces a resource with same type and id.
The argument "#id" is a 32-bit hex number, prefixed with #.
Low 16 bits of this value are resource id; can not be 0.
Next 8 bits are resource type: one of RT_xxx symbols in winuser.h, or user defined.
If the type value is 0, RT_RCDATA (10) is assumed.
High 8 bits of the #id arg are reserved0.
The language code of resources added by this switch is 0 (Neutral).
Named resource types and ids are not implemented.
The file is added as opaque binary chunk; the resource size is rounded up to 4 bytes
and padded with zero bytes.
The program detects extra data appended to executable files, saves it and appends
again after modifying resources.
Such extra data is used by some installers, self-extracting archives and other applications.
However, the way we restore the data may be not compatible with these applications.
Please, verify that executable files that contain extra data work correctly after modification.
Command switch /noed disables checking for extra data.
====================================================================
Known string keys in VS_VERSION_INFO resource
====================================================================
The aliases are for use with the /s switch, and are not case sensitive.
-------------------+----+-------------------------------+------------
Invariant(LN) name |note| English translation | Alias
-------------------+----+-------------------------------+------------
Comments Comments comment
CompanyName E Company company
FileDescription E Description description, desc
FileVersion *1 File version
InternalName Internal Name title
*2 Language
LegalCopyright E Copyright copyright, (c)
LegalTrademarks E Legal Trademarks tm, (tm)
OriginalFilename Original File Name
ProductName Product Name product
ProductVersion *1 Product Version productver, prodver
PrivateBuild Private Build Description pb
SpecialBuild Special Build Description sb, build
OleSelfRegister A -
AssemblyVersion N
Notes
*1: FileVersion, ProductVersion values should begin with same 1.2.3.4 version number as in the binary header.
Can be any text. Windows Explorer displays the version numbers from the binary header.
*2: The "Language" value is the name of the language code specified in the header of the string block of VS_VERSION_INFO resource.
(or taken from VarFileInfo block?)
It is displayed by Windows Explorer, but is not contained in the version data.
E: Displayed by Windows Explorer in Vista+
A: Intended for some API (OleSelfRegister is used in COM object registration)
N: Added by some .NET compilers. This version number is not contained in the
binary part of the version struct and can differ from the file version.
To change it, just use switch /s AssemblyVersion [value]
====================================================================
Known issues and TO DO's:
=========================
- Does not work on old PE files that have link version 5.x (before VC6?)
No known workaround; this seems to be limitation of Windows UpdateResource API.
- Does not work on signed files (TO DO)
- Currenly implemented only US English and Language Neutral Unicode version resources.
MUI resource configuration manifests not checked.
New version resource will be created as Language Neutral.
Version info in other languages will be erroneously rewritten as English or Language Neutral- TO DO
A second (language neutral) version resource may be added to a file
that already has a version resource in other language. Switch /va won't help.
TO DO: ensure that a file has only one version resource!
- When verpatch is invoked from command prompt, or batch file, the string
arguments can contain only ANSI characters, because cmd.exe batch files cannot be
in Uncode format. If you need to use arbitrary character sets,
use other shells that fully support Unicode (PowerShell, vbs, js).
- TO DO: In RC source output (/vo), special characters in strings are not quoted;
so /vo may produce invalid RC input
- The parser of binary version resources handles only the most common type of structure.
If the parser breaks because of unhandled structure format, try /va switch to
skip reading existing version resource and re-create it from scratch.
TODO: Consider using WINE or other open source implementations?
- option to add extra 0 after version strings : "string\0"
(requiested by one reader for some old VB code)
Source code
============
The source is provided as a Visual C++ 2005 project, it can be compiled with VC 2008 Express.
It demonstrates use of the UpdateResource and imagehlp.dll API.
It does not demonstrate use of c++, good coding manners or anything else.
Dependencies on C++ libraries available only with the full Visual C 2008 have been removed.
LICENSE TERMS: CPOL (CodeProject Open License)
http://www.codeproject.com/info/licenses.aspx
~~

BIN
external/verpatch/src/verpatch.ncb vendored Normal file

Binary file not shown.

24
external/verpatch/src/verpatch.sln vendored Normal file
View file

@ -0,0 +1,24 @@

Microsoft Visual Studio Solution File, Format Version 10.00
# Visual C++ Express 2008
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "verpatch", "verpatch.vcproj", "{E08F11BD-35BC-4496-82BB-1CFD56BB044C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{E08F11BD-35BC-4496-82BB-1CFD56BB044C}.Debug|Win32.ActiveCfg = Debug|Win32
{E08F11BD-35BC-4496-82BB-1CFD56BB044C}.Debug|Win32.Build.0 = Debug|Win32
{E08F11BD-35BC-4496-82BB-1CFD56BB044C}.Release|Win32.ActiveCfg = Release|Win32
{E08F11BD-35BC-4496-82BB-1CFD56BB044C}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(SubversionScc) = preSolution
Svn-Managed = True
Manager = AnkhSVN - Subversion Support for Visual Studio
EndGlobalSection
EndGlobal

BIN
external/verpatch/src/verpatch.suo vendored Normal file

Binary file not shown.

336
external/verpatch/src/verpatch.vcproj vendored Normal file
View file

@ -0,0 +1,336 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="9.00"
Name="verpatch"
ProjectGUID="{E08F11BD-35BC-4496-82BB-1CFD56BB044C}"
RootNamespace="relstamp"
Keyword="Win32Proj"
TargetFrameworkVersion="131072"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="2"
WarningLevel="3"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
OutputFile="$(OutDir)\verpatch.exe"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
LargeAddressAware="1"
TerminalServerAware="1"
RandomizedBaseAddress="1"
DataExecutionPrevention="0"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="2"
EnableIntrinsicFunctions="true"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;_A_NOISE_DBG=0"
RuntimeLibrary="2"
EnableFunctionLevelLinking="true"
UsePrecompiledHeader="2"
WarningLevel="3"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
OutputFile="$(OutDir)\verpatch.exe"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
LargeAddressAware="1"
TerminalServerAware="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
RandomizedBaseAddress="1"
DataExecutionPrevention="0"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
RelativePath=".\peExtras.cpp"
>
</File>
<File
RelativePath=".\peutils.cpp"
>
</File>
<File
RelativePath=".\relstamp.cpp"
>
</File>
<File
RelativePath=".\stdafx.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
</File>
<File
RelativePath=".\vs_version.cpp"
>
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
<File
RelativePath=".\peExtras.h"
>
</File>
<File
RelativePath=".\relstamp.h"
>
</File>
<File
RelativePath=".\stdafx.h"
>
</File>
<File
RelativePath=".\targetver.h"
>
</File>
<File
RelativePath=".\vs_version.h"
>
</File>
</Filter>
<Filter
Name="Etc"
UniqueIdentifier="DOCUM"
ParseFiles="false"
>
<File
RelativePath=".\ReadMe.txt"
>
<FileConfiguration
Name="Debug|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCustomBuildTool"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCResourceCompilerTool"
/>
</FileConfiguration>
</File>
<File
RelativePath=".\usage.txt"
>
<FileConfiguration
Name="Debug|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCustomBuildTool"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCustomBuildTool"
/>
</FileConfiguration>
</File>
<File
RelativePath=".\ver-self.cmd"
>
<FileConfiguration
Name="Debug|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCustomBuildTool"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCustomBuildTool"
/>
</FileConfiguration>
</File>
</Filter>
<Filter
Name="CodeProjectFiles"
ParseFiles="false"
>
<File
RelativePath=".\codeproj\codeproject_vp.html"
>
</File>
<File
RelativePath=".\codeproj\ver-vista.png"
>
</File>
<File
RelativePath=".\codeproj\ver-winxp.png"
>
</File>
<File
RelativePath=".\codeproj\verpatch-update.html"
>
</File>
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View file

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioUserFile
ProjectType="Visual C++"
Version="9.00"
ShowAllFiles="false"
>
<Configurations>
<Configuration
Name="Debug|Win32"
>
<DebugSettings
Command="$(TargetPath)"
WorkingDirectory=""
CommandArguments=""
Attach="false"
DebuggerType="3"
Remote="1"
RemoteMachine="BUILD-WINDOWS"
RemoteCommand=""
HttpUrl=""
PDBPath=""
SQLDebugging=""
Environment=""
EnvironmentMerge="true"
DebuggerFlavor=""
MPIRunCommand=""
MPIRunArguments=""
MPIRunWorkingDirectory=""
ApplicationCommand=""
ApplicationArguments=""
ShimCommand=""
MPIAcceptMode=""
MPIAcceptFilter=""
/>
</Configuration>
<Configuration
Name="Release|Win32"
>
<DebugSettings
Command="$(TargetPath)"
WorkingDirectory=""
CommandArguments=""
Attach="false"
DebuggerType="3"
Remote="1"
RemoteMachine="BUILD-WINDOWS"
RemoteCommand=""
HttpUrl=""
PDBPath=""
SQLDebugging=""
Environment=""
EnvironmentMerge="true"
DebuggerFlavor=""
MPIRunCommand=""
MPIRunArguments=""
MPIRunWorkingDirectory=""
ApplicationCommand=""
ApplicationArguments=""
ShimCommand=""
MPIAcceptMode=""
MPIAcceptFilter=""
/>
</Configuration>
</Configurations>
</VisualStudioUserFile>

295
external/verpatch/src/vs_version.cpp vendored Normal file
View file

@ -0,0 +1,295 @@
//
// Code for VS_VERSION resource
//
#include "stdafx.h"
#include "relstamp.h"
#include "vs_version.h"
/// Make version resource
BOOL makeVersionResource( __in file_ver_data_s const * fvd, __out PUCHAR *retp )
{
PUCHAR palloc = (PUCHAR)calloc(_MAX_VERS_SIZE_CB, 1);
yybuf vbuf( palloc, _MAX_VERS_SIZE_CB );
unsigned cbWritten = 0;
BOOL ok = FALSE;
PCWSTR sigLang = L"040904B0"; /* LANG_ENGLISH/SUBLANG_ENGLISH_US, Unicode CP */
WORD vtransl[2] = {0x0409, 0x04B0};
WCHAR temps[_MAX_VER_STRING_LEN_CCH + 1];
if ( fvd->langid == LANG_NEUTRAL ) {
//TODO support any language. currently tested only with en or neutral
vtransl[0] = LANG_NEUTRAL;
sigLang = L"000004b0";
}
try {
// Fill the res header
// struct VS_VERSIONINFO {
// WORD wLength;
// WORD wValueLength;
// WORD wType;
// WCHAR szKey[];
// WORD Padding1[];
// VS_FIXEDFILEINFO Value;
// WORD Padding2[];
// WORD Children[];
// };
PWORD pTotalLen = vbuf.marksize(); // FIXUP LATER
vbuf.pushw(~0); // FIXUP LATER
vbuf.pushw( sizeof(VS_FIXEDFILEINFO) ); //0x34
vbuf.pushw( 0 ); //type
vbuf.pushstr( L"VS_VERSION_INFO" );
// Fixed info
VS_FIXEDFILEINFO *fxi = (VS_FIXEDFILEINFO *)vbuf.getptr();
fxi->dwSignature = 0xfeef04bd; // magic
fxi->dwStrucVersion = VS_FFI_STRUCVERSION; //0x00010000
fxi->dwFileVersionMS = MAKELONG( fvd->v_2, fvd->v_1 );
fxi->dwFileVersionLS = MAKELONG( fvd->v_4, fvd->v_3 );
fxi->dwProductVersionMS = MAKELONG( fvd->pv_2, fvd->pv_1 );
fxi->dwProductVersionLS = MAKELONG( fvd->pv_4, fvd->pv_3 );
fxi->dwFileFlagsMask = VS_FFI_FILEFLAGSMASK; //0x3F;
fxi->dwFileFlags = fvd->dwFileFlags;
fxi->dwFileOS = VOS_NT_WINDOWS32;
fxi->dwFileType = fvd->dwFileType;
fxi->dwFileSubtype = fvd->dwFileSubType;
if ( 0 == fxi->dwFileType && 0 != fxi->dwFileSubtype )
fxi->dwFileType = VFT_DRV;
fxi->dwFileDateLS = 0; //unused?
fxi->dwFileDateMS = 0; //
vbuf.incptr( sizeof(VS_FIXEDFILEINFO) );
vbuf.align4();
// String File Info
PWORD stringStart = vbuf.marksize();
vbuf.pushw(~0); //wLength FIXUP LATER
vbuf.pushw(0); //wValueLength
vbuf.pushw(1); //wType
vbuf.pushstr( L"StringFileInfo" );
PWORD stringTableStart = vbuf.marksize();
vbuf.pushw(~0); //wLength FIXUP LATER
vbuf.pushw(0); // ?
vbuf.pushw(1); //wType
vbuf.pushstr( sigLang );
// File version as string. Not shown by Vista, Win7.
HRESULT hr = ::StringCbPrintf( &temps[0], sizeof(temps), _T("%d.%d.%d.%d"),
fvd->v_1, fvd->v_2, fvd->v_3, fvd->v_4 );
if ( !SUCCEEDED(hr) ) temps[0] = 0;
if ( fvd->sFileVerTail ) {
hr = ::StringCbCatW(&temps[0], sizeof(temps), L" ");
hr = ::StringCbCatW(&temps[0], sizeof(temps), fvd->sFileVerTail);
if ( !SUCCEEDED(hr) ) temps[0] = 0;
}
vbuf.pushTwostr( L"FileVersion", &temps[0] );
hr = ::StringCbPrintf( &temps[0], sizeof(temps), _T("%d.%d.%d.%d"),
fvd->pv_1, fvd->pv_2, fvd->pv_3, fvd->pv_4 );
if ( !SUCCEEDED(hr) ) temps[0] = 0;
if ( fvd->sProductVerTail ) {
hr = ::StringCbCatW(&temps[0], sizeof(temps), L" ");
hr = ::StringCbCatW(&temps[0], sizeof(temps), fvd->sProductVerTail);
if ( !SUCCEEDED(hr) ) temps[0] = 0;
}
vbuf.pushTwostr( L"ProductVersion", &temps[0] );
// Strings
for ( int k = 0; k < ARRAYSIZE(fvd->CustomStrNames); k++ ) {
if ( fvd->CustomStrNames[k] != NULL ) {
vbuf.pushTwostr( fvd->CustomStrNames[k], fvd->CustomStrVals[k] );
if ( 0 == _wcsicmp( L"SpecialBuild", fvd->CustomStrNames[k] ) )
fxi->dwFileFlags |= VS_FF_SPECIALBUILD;
if ( 0 == _wcsicmp( L"PrivateBuild",fvd->CustomStrNames[k] ) )
fxi->dwFileFlags |= VS_FF_PRIVATEBUILD;
}
}
vbuf.patchsize( stringTableStart );
vbuf.patchsize( stringStart );
vbuf.align4();
// Var info
//struct VarFileInfo {
// WORD wLength;
// WORD wValueLength;
// WORD wType;
// WCHAR szKey[];
// WORD Padding[];
// Var Children[];
PWORD varStart = vbuf.marksize();
vbuf.pushw(~0); // size, patch
vbuf.pushw(0);
vbuf.pushw(1);
vbuf.pushstr( L"VarFileInfo" );
vbuf.pushw(0x24);
vbuf.pushw(0x04);
vbuf.pushw(0x00);
vbuf.pushstr( L"Translation" );
vbuf.pushw( vtransl[0] );
vbuf.pushw( vtransl[1] );
vbuf.patchsize( varStart );
/////////////////////////////
vbuf.patchsize(pTotalLen);
vbuf.checkspace();
ok = TRUE;
} catch(...) {
ok = FALSE;
}
if (ok) {
d3print("ver size= %d\n", vbuf.cbwritten() );
*retp = palloc;
} else {
dprint("error in %s\n", __FUNCTION__);
free( palloc );
}
return ok;
}
///////////////////////////////////////////////////////////////////////////
// Simple parser for binary version resource
///////////////////////////////////////////////////////////////////////////
BOOL ParseBinaryVersionResource(
__in const PUCHAR verres,
unsigned size,
__out VS_FIXEDFILEINFO **pfxi,
IParseVerStrCallback *strCallback,
bool b_dump_rc
)
{
BOOL ok = FALSE;
xybuf vbuf( verres, size );
WCHAR sigLang[8+1];
try {
// Res header
PWORD pTotalLen = vbuf.marksize();
vbuf.chkword( sizeof(VS_FIXEDFILEINFO) ); //0x34
vbuf.chkword(0); //type
vbuf.chkstr(L"VS_VERSION_INFO");
// Fixed info
VS_FIXEDFILEINFO *fxi = (VS_FIXEDFILEINFO *)vbuf.getptr();
if ( fxi->dwSignature != 0xfeef04bd )
throw ":fxi.sig";
if ( fxi->dwStrucVersion > 0x00010000 || (fxi->dwStrucVersion == 0) )
throw ":fxi.version";
*pfxi = fxi;
if (b_dump_rc) {
// Dump in RC format:
dtprint( _T("#ifdef RC_INVOKED\n\n"));
dtprint( _T("1\tVERSIONINFO\n"));
dtprint( _T("FILEVERSION\t%u,%u,%u,%u\n"),
HIWORD(fxi->dwFileVersionMS), LOWORD(fxi->dwFileVersionMS), HIWORD(fxi->dwFileVersionLS), LOWORD(fxi->dwFileVersionLS) );
dtprint( _T("PRODUCTVERSION\t%u,%u,%u,%u\n"),
HIWORD(fxi->dwProductVersionMS), LOWORD(fxi->dwProductVersionMS), HIWORD(fxi->dwProductVersionLS), LOWORD(fxi->dwProductVersionLS) );
dtprint( _T("FILEFLAGSMASK\t%#XL\n"), fxi->dwFileFlagsMask);
dtprint( _T("FILEFLAGS\t%#XL\n"), fxi->dwFileFlags);
dtprint( _T("FILEOS\t\t%#XL\n"), fxi->dwFileOS);
dtprint( _T("FILETYPE\t%#X\n"), fxi->dwFileType);
dtprint( _T("FILESUBTYPE\t%#X\n"), fxi->dwFileSubtype);
}
vbuf.incptr( sizeof(VS_FIXEDFILEINFO) );
vbuf.align4();
// String File Info
PWORD stringStart = vbuf.marksize();
vbuf.chkword(0); //wValueLength
vbuf.chkword(1); //wType
try {
vbuf.chkstr(L"StringFileInfo");
} catch( char *exs ) {
// !!! VarFileInfo can go before StringFileInfo!
vbuf.chkstr(L"VarFileInfo");
// ok so here is "VarFileInfo". Skip it and resync at StringFileInfo
vbuf.checkspace(0x40);
PUCHAR q = (PUCHAR)memchr( vbuf.getptr(), 'S', 0x30 );
if ( !q ) throw(":parse_err2");
vbuf.incptr( q - vbuf.getptr() - 3 *sizeof(WORD));
// Retry:
stringStart = vbuf.marksize();
vbuf.chkword(0); //wValueLength
vbuf.chkword(1); //wType
vbuf.chkstr(L"StringFileInfo");
}
PWORD stringTableStart = vbuf.marksize();
vbuf.chkword(0); // ?
vbuf.chkword(1); //wType
// Language string: ex. "040904B0"
vbuf.checkspace( 10 * sizeof(WCHAR) );
WORD n = wcslen( (PCWSTR)vbuf.getptr() );
if (n != 8)
throw(":bad_lang_str");
memcpy( sigLang, vbuf.getptr(), 9*sizeof(WCHAR) ); //incl term. 0
vbuf.incptr( (n + 1) * sizeof(WCHAR) );
vbuf.align4();
strCallback->callback( L"@LANG", sigLang ); // revise
if (b_dump_rc) {
// Dump in RC format:
dtprint(_T("BEGIN\n"));
dtprint(_T("\tBLOCK \"StringFileInfo\"\n"));
dtprint(_T("\tBEGIN\n"));
dtprint(_T("\t\tBLOCK \"%ws\"\n"), sigLang);
dtprint(_T("\t\tBEGIN\n"));
}
// Loop for strings:
int cntstrings = 0;
do {
PCWSTR wsname, wsval;
vbuf.pullTwoStr( &wsname, &wsval );
cntstrings++;
//- dprint(" str#%d [%ws]=[%ws]\n", cntstrings, wsname, wsval);
if (b_dump_rc) {
dtprint(_T("\t\t\tVALUE \"%ws\", \"%ws\"\n"), wsname, strEscape(wsval));
}
strCallback->callback( wsname, wsval );
} while( vbuf.getptr() < ((PUCHAR)stringStart + *stringStart) );
vbuf.align4();
d3print("strings counted:%d\n", cntstrings);
if (b_dump_rc) {
dtprint(_T("\t\tEND\n"));
dtprint(_T("\tEND\n"));
// VarFileInfo.....
dtprint(_T("\tBLOCK \"VarFileInfo\"\n"));
dtprint(_T("\tBEGIN\n"));
dtprint(_T("\t\tVALUE \"Translation\", 0x%4.4ws, 0x%4.4ws\n"), &sigLang[0], &sigLang[4]);
dtprint(_T("\tEND\n"));
dtprint(_T("END\n\n"));
dtprint( _T("#endif /*RC_INVOKED*/\n\n"));
}
ok = TRUE;
} catch(...) {
dprint("Exception in %s\n", __FUNCTION__);
}
if (!ok) dprint("Error in %s\n", __FUNCTION__);
return ok;
}

313
external/verpatch/src/vs_version.h vendored Normal file
View file

@ -0,0 +1,313 @@
//
// Code for VS_VERSION resource
//
#pragma once
// Stupid helper classes for the version struct
class yybuf
{
PUCHAR m_startptr;
PUCHAR m_curptr;
int m_inisize;
public:
yybuf( PUCHAR start, unsigned size )
: m_startptr(start), m_inisize(size), m_curptr(start)
{
ASSERT(((ULONG_PTR)start & 3) == 0); // must be aligned on 4
}
void align4() {
PULONG_PTR x = (PULONG_PTR)&m_curptr;
*x += 3;
*x &= ~(ULONG_PTR)3;
}
int cbwritten(void) { return m_curptr - m_startptr; }
void checkspace( int n = 8 ) {
if ( cbwritten() + n > m_inisize )
__debugbreak();
}
void pushw( WORD v ) {
*(PWORD)m_curptr = v;
m_curptr += sizeof(WORD);
}
void pushd( DWORD v ) {
*(PDWORD)m_curptr = v;
m_curptr += sizeof(DWORD);
}
void pushstr( __in LPCWSTR ws, bool b_align = TRUE ) {
if ( !ws ) return;
WORD n = wcslen( ws );
ASSERT( n < _MAX_VER_STRING_LEN_CCH );
n = (n + 1) * sizeof(WCHAR);
checkspace(n + sizeof(DWORD));
memcpy( m_curptr, ws, n );
m_curptr += n;
if (b_align)
align4();
}
PUCHAR getptr() { return m_curptr;}
void incptr( int n ) { checkspace(n); m_curptr += n; }
PWORD marksize() { return (PWORD)m_curptr; }
void patchsize ( PWORD mp ) {
WORD cb = getptr() - (PUCHAR)mp;
*mp = cb;
};
void yybuf::pushTwostr( __in LPCWSTR name, __in LPCWSTR val )
{
//struct String {
// WORD wLength;
// WORD wValueLength;
// WORD wType;
// WCHAR szKey[];
// WORD Padding[];
// WORD Value[];
//};
WORD wValueLength = val ? (WORD)wcslen(val) : 0;
if (wValueLength)
wValueLength = (wValueLength + 1) * sizeof(WCHAR);
WORD wNameLength = (WORD)((wcslen(name) + 1 ) * sizeof(WCHAR));
ASSERT(wNameLength > sizeof(WCHAR));
checkspace( wValueLength + wNameLength + 5*sizeof(WORD));
PUCHAR porig = m_curptr;
pushw(-1); //length, patch
pushw( wValueLength );
pushw( 1 ); //type
pushstr( name ); // with align
if ( wValueLength )
pushstr( val, false ); // don't align yet
*(PWORD)porig = (WORD)(m_curptr - porig);
align4();
}
}; // class
class xybuf
{
PUCHAR m_startptr;
PUCHAR m_curptr;
int m_inisize;
public:
xybuf( PUCHAR start, unsigned size )
: m_startptr(start), m_inisize(size), m_curptr(start)
{
ASSERT(((ULONG_PTR)start & 3) == 0); // must be aligned on 4
}
void align4() {
PULONG_PTR x = (PULONG_PTR)&m_curptr;
*x += 3;
*x &= ~(ULONG_PTR)3;
}
int cbread(void) { return m_curptr - m_startptr; }
void checkspace( int n = 8 ) {
if ( cbread() + n > m_inisize )
throw ":overrun read";
}
PUCHAR getptr() { return m_curptr;}
void incptr( int n ) { checkspace(n); m_curptr += n; }
PWORD marksize() { PWORD p = (PWORD)m_curptr; m_curptr += sizeof(WORD); return p; }
BOOL chksize( PWORD mp, bool b_nothrow = false ) {
// check size of block is correct
WORD cb = getptr() - (PUCHAR)mp;
if (*mp != cb ) {
if ( !b_nothrow ) throw ":chksize";
return FALSE;
}
return TRUE;
};
void chkword( WORD v ) {
if (*(PWORD)m_curptr != v)
throw ":chkword";
m_curptr += sizeof(WORD);
}
void chkdword( DWORD v ) {
if (*(PDWORD)m_curptr != v)
throw ":chkdword";
m_curptr += sizeof(DWORD);
}
void chkstr( __in LPCWSTR ws, bool b_align = TRUE ) {
WORD n = wcslen( ws );
ASSERT ( n );
ASSERT( n < _MAX_VER_STRING_LEN_CCH );
checkspace((n + 1) * sizeof(WCHAR) + sizeof(DWORD));
for (int i = 0; i <= n; i++ ) { // incl. term. 0
if ( *(PWCHAR)m_curptr != *ws &&
*(PWCHAR)m_curptr != (*ws ^ 0x20) )
throw ":chkstr";
m_curptr += sizeof(WCHAR);
ws++;
}
if (b_align)
align4();
}
void pullTwoStr( __out LPCWSTR *wsname, __out LPCWSTR *wsval )
{
//struct String {
// WORD wLength;
// WORD wValueLength;
// WORD wType;
// WCHAR szKey[];
// WORD Padding[];
// WORD Value[];
//};
checkspace(5*sizeof(WORD));
PWORD porig = marksize();
WORD wLength = *porig;
if ( wLength > 1024 || wLength < 5*sizeof(WORD))
throw ":string desc size bad";
checkspace(5*sizeof(WORD) + wLength);
WORD wValueLength = *((PWORD)m_curptr);
incptr(2);
chkword(1); //type
size_t nLength = wcsnlen( (LPWSTR)( getptr() ), wLength/sizeof(WCHAR) );
if (nLength == 0 || nLength == (wLength/sizeof(WCHAR)) )
throw ":string name len bad";
*wsname = (LPCWSTR)getptr(); //should point to name
unsigned bLength = (nLength + 1)*sizeof(WCHAR);
incptr( bLength );
align4(); //padding
if ( getptr() >= (PUCHAR)porig + *porig ) {
// null value
*wsval = L"";
return;
}
wLength -= bLength;
nLength = wcsnlen( LPWSTR( getptr() ), wLength/sizeof(WCHAR) );
if ( nLength == 0 || nLength == (wLength/sizeof(WCHAR)) )
throw ":string val name len bad";
*wsval = (LPCWSTR)getptr(); //should point to value
bLength = (nLength + 1)*sizeof(WCHAR);
// can be padded after 0 term
m_curptr = (PUCHAR)porig + *porig;
align4(); //padding
}
}; //class
#if 1
// a stupid string helper class.
// this doesn't run 24*7, so allow memory leaks...
class _xpwstr {
PWSTR m_str;
public:
void operator = (PCTSTR s ) {
if ( m_str )
free( m_str );
m_str = NULL;
if (s)
m_str = stralloc( s );
}
operator PCWSTR() const { return (PCWSTR)m_str; }
_xpwstr() : m_str(NULL) { }
~_xpwstr() { if (m_str) free( m_str ); }
};
typedef _xpwstr ASTR;
#else
typedef CString ASTR;
#endif
// Data for a vs_version resource
struct file_ver_data_s {
USHORT v_1, v_2, v_3, v_4; // Version components 1-4
USHORT pv_1, pv_2, pv_3, pv_4; // Product Version components 1-4
UINT32 dwFileType, dwFileSubType;
UINT32 dwFileFlags;
WORD langid; // language
ASTR sFileVerTail; // sometimes used - ex. WDK samples, common.ver
ASTR sProductVerTail; // same for product ver.
// Strings
ASTR CustomStrNames[_MAX_VER_CUSTOM_STRINGS];
ASTR CustomStrVals[_MAX_VER_CUSTOM_STRINGS];
bool addTwostr( __in_opt PCWSTR name, __in_opt PCWSTR val )
{
if ( !name )
return false;
int index = -1;
for (int i = 0; i < ARRAYSIZE(CustomStrNames); i++) {
if ( !CustomStrNames[i] ) {
if (index == -1) index = i;
continue;
}
if ( 0 == _wcsicmp( name, CustomStrNames[i] ) ) {
index = i;
d3print("replacing dup string in ver resource: %ws\n", (PCWSTR)name);
break;
}
}
if ( index != -1 ) {
CustomStrNames[index] = name;
CustomStrVals[index] = val; // can be 0
return true;
}
dprint("Too many strings in ver resource! not added %ws\n", name);
return false;
}
PCWSTR getValStr( __in PCWSTR name )
{
for (int i = 0; i < ARRAYSIZE(CustomStrNames); i++) {
PCWSTR s = CustomStrNames[i];
if ( s && (0 == _wcsicmp(name, s) ) )
return CustomStrVals[i];
}
return NULL;
}
};
// Interface for ParseBinaryVersionResource
class IParseVerStrCallback
{
public:
virtual void callback( __in PCWSTR name, __in_opt PCWSTR value ) = 0;
};
BOOL makeVersionResource( __in file_ver_data_s const * fvd, __out PUCHAR *retp );
BOOL ParseBinaryVersionResource(
__in const PUCHAR verres,
unsigned size,
__out VS_FIXEDFILEINFO **pfxi,
IParseVerStrCallback *strCallback,
bool b_dump_rc = false
);

234
external/verpatch/verpatch-ReadMe.txt vendored Normal file
View file

@ -0,0 +1,234 @@
Verpatch - a tool to patch win32 version resources on .exe or .dll files,
Version: 01-Nov-2009 (rev. #7 for CodeProject) edited
Verpatch is a command line tool for adding and editing the version information
of Windows executable files (applications, DLLs, kernel drivers)
without rebuilding the executable.
It can also add or replace Win32 (native) resources, and do some other
modifications of executable files.
Verpatch sets ERRORLEVEL 0 on success, otherwise errorlevel is non-zero.
Verpatch modifies files in place, so please make copies of precious files.
Command line syntax
===================
verpatch filename [version] [/options]
Where
- filename : any Windows PE file (exe, dll, sys, ocx...) that can have native resources
- version : one to four decimal numbers, separated by dots, ex.: 1.2.3.4
Additional text can follow the numbers; see examples below. Ex.: "1.2.3.4 extra text"
- options: see below
Common Options:
/va - creates a version resource. Use when the file has no version resource at all,
or existing version resource should be replaced.
If this option not specified, verpatch will patch the version resourse found in the file.
/s name "value" - add a version resource string attribute
The name can be either a full attribute name or alias; see below.
/sc "comment" - add or replace Comments string (shortcut for /s Comments "comment")
/pv <version> - specify Product version
where <version> arg has same form as the file version (1.2.3.4 or "1.2.3.4 text")
/fn - preserves Original filename, Internal name in the existing version resource of the file.
Other options:
/vo - outputs the version info in RC format to stdout.
This can be used with /xi to import a version resource from another file.
Output of /vo (text from #ifdef RC_INVOKED to #endif) can be saved to a .rc file and compiled with rc.
/xi- test mode. does all operations but does not modify the file
/xlb - test mode. Re-parses the version resource after modification.
/rpdb - removes path to the .pdb file in debug information; leaves only file name.
/rf #id file - add or replace a raw binary resource from file (see below)
/noed - do not check for extra data appended to exe file
/vft2 num - specify driver subtype (VFT2_xxx value, see winver.h)
The application type (VFT_xxx) is retained from the existing version resource of the file,
or filled automatically, based on the filename extension (exe->app, sys->driver, anything else->dll)
Examples
========
verpatch d:\foo.dll 1.2.33.44
- replaces only the file version, all 4 numbers,
the Original file name and Internal name strings are set to "foo.dll".
File foo.dll should already have a version resource.
verpatch d:\foo.dll 33.44 /s comment "a comment"
- replaces only two last numbers of the file version and adds a comment.
File foo.dll should already have a version resource.
verpatch d:\foo.dll "33.44 special release" /sc "a comment"
- same as previous, with additional text in the version argument.
verpatch d:\foo.dll "1.2.33.44" /va /s description "foo.dll"
/s company "My Company" /s copyright "(c) 2009"
- adds or replaces version resource to foo.dll, with several string values.
( all options should be one line)
verpatch d:\foo.dll /vo /xi
- dumps the version resource in RC format, does not update the file.
Remarks
=======
Verpatch replaces the version number in existing file version info resource
with the values given on the command line.
In "patch" mode (no /va option), the PE file should have a version resource,
which is parsed, and then parameters specified on the command line are applied.
If the file has no version resource, or you want to discard the existing resource, use /va switch.
All nesessary strings can be specified with the /s option.
The command line can become very long, so you may want to use a batch file or script.
See the example batch files, how to create a version resource and
specify all parameters with /va.
Verpatch can be run on same PE file any number of times.
The Version argument can be specified as 1 to 4 dot separated decimal numbers,
or as quoted string containing additional text after the numbers.
If less than 4 numbers are given, they are considered as minor numbers.
The higher version parts are retained from existing version resource.
For example, if the existing version info has version number 1.2.3.4
and 55.66 specified on the command line, the result will be 1.2.55.66.
The quotes surrounding string arguments are needed for the command shell (cmd.exe),
for any argument that contains spaces.
The program ensures that the version numbers in the binary part
of the version structure and in the string part (as text) are same.
By default, Original File Name and Internal File Name are replaced to the actual filename.
Use /fn to preserve existing values in the version resource.
For option /s, specify language-neutral string names, not translations
( example: PrivateBuild, not "Private Build Description" ).
Null values can be specified as empty string ("").
See below for the list of known string keys names and their aliases.
The examples above use the aliases.
Strings for File version and Product version parameters are handled in a special way,
the /s switch can not be used to set them:
- File version can be specified only as the 2nd positional argument
- Product version can be specified using /pv switch
The /rf switch adds a resource from a file, or replaces a resource with same type and id.
The argument "#id" is a 32-bit hex number, prefixed with #.
Low 16 bits of this value are resource id; can not be 0.
Next 8 bits are resource type: one of RT_xxx symbols in winuser.h, or user defined.
If the type value is 0, RT_RCDATA (10) is assumed.
High 8 bits of the #id arg are reserved0.
The language code of resources added by this switch is 0 (Neutral).
Named resource types and ids are not implemented.
The file is added as opaque binary chunk; the resource size is rounded up to 4 bytes
and padded with zero bytes.
The program detects extra data appended to executable files, saves it and appends
again after modifying resources.
Such extra data is used by some installers, self-extracting archives and other applications.
However, the way we restore the data may be not compatible with these applications.
Please, verify that executable files that contain extra data work correctly after modification.
Command switch /noed disables checking for extra data.
====================================================================
Known string keys in VS_VERSION_INFO resource
====================================================================
The aliases are for use with the /s switch, and are not case sensitive.
-------------------+----+-------------------------------+------------
Invariant(LN) name |note| English translation | Alias
-------------------+----+-------------------------------+------------
Comments Comments comment
CompanyName E Company company
FileDescription E Description description, desc
FileVersion *1 File version
InternalName Internal Name title
*2 Language
LegalCopyright E Copyright copyright, (c)
LegalTrademarks E Legal Trademarks tm, (tm)
OriginalFilename Original File Name
ProductName Product Name product
ProductVersion *1 Product Version productver, prodver
PrivateBuild Private Build Description pb
SpecialBuild Special Build Description sb, build
OleSelfRegister A -
AssemblyVersion N
Notes
*1: FileVersion, ProductVersion values should begin with same 1.2.3.4 version number as in the binary header.
Can be any text. Windows Explorer displays the version numbers from the binary header.
*2: The "Language" value is the name of the language code specified in the header of the string block of VS_VERSION_INFO resource.
(or taken from VarFileInfo block?)
It is displayed by Windows Explorer, but is not contained in the version data.
E: Displayed by Windows Explorer in Vista+
A: Intended for some API (OleSelfRegister is used in COM object registration)
N: Added by some .NET compilers. This version number is not contained in the
binary part of the version struct and can differ from the file version.
To change it, just use switch /s AssemblyVersion [value]
====================================================================
Known issues and TO DO's:
=========================
- Does not work on old PE files that have link version 5.x (before VC6?)
No known workaround; this seems to be limitation of Windows UpdateResource API.
- Does not work on signed files (TO DO)
- Currenly implemented only US English and Language Neutral Unicode version resources.
MUI resource configuration manifests not checked.
New version resource will be created as Language Neutral.
Version info in other languages will be erroneously rewritten as English or Language Neutral- TO DO
A second (language neutral) version resource may be added to a file
that already has a version resource in other language. Switch /va won't help.
TO DO: ensure that a file has only one version resource!
- When verpatch is invoked from command prompt, or batch file, the string
arguments can contain only ANSI characters, because cmd.exe batch files cannot be
in Uncode format. If you need to use arbitrary character sets,
use other shells that fully support Unicode (PowerShell, vbs, js).
- TO DO: In RC source output (/vo), special characters in strings are not quoted;
so /vo may produce invalid RC input
- The parser of binary version resources handles only the most common type of structure.
If the parser breaks because of unhandled structure format, try /va switch to
skip reading existing version resource and re-create it from scratch.
TODO: Consider using WINE or other open source implementations?
- option to add extra 0 after version strings : "string\0"
(requiested by one reader for some old VB code)
Source code
============
The source is provided as a Visual C++ 2005 project, it can be compiled with VC 2008 Express.
It demonstrates use of the UpdateResource and imagehlp.dll API.
It does not demonstrate use of c++, good coding manners or anything else.
Dependencies on C++ libraries available only with the full Visual C 2008 have been removed.
LICENSE TERMS: CPOL (CodeProject Open License)
http://www.codeproject.com/info/licenses.aspx
~~

BIN
external/verpatch/verpatch.exe vendored Normal file

Binary file not shown.

View file

@ -0,0 +1,7 @@
set VERSION="1.0"
set FILEDESCR=/s desc "Software update utility."
set BUILDINFO=/s pb "Built by Robert Knight"
set COMPINFO=/s company "Mendeley Ltd." /s (c) "(C) Mendeley Ltd. 2011"
set PRODINFO=/s product "Mendeley Software Updater" /pv "1.0"
verpatch /vo /xi updater.exe %VERSION% %FILEDESCR% %COMPINFO% %PRODINFO% %BUILDINFO%

View file

@ -1 +1,31 @@
IDI_APPICON ICON DISCARDABLE "updater.ico" IDI_APPICON ICON DISCARDABLE "updater.ico"
1 VERSIONINFO
FILEVERSION 0,0,1,0
PRODUCTVERSION 1,0,1,0
FILEFLAGSMASK 0X3FL
FILEFLAGS 0X8L
FILEOS 0X40004L
FILETYPE 0X1
FILESUBTYPE 0
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "000004b0"
BEGIN
VALUE "FileVersion", "0.0.1.0"
VALUE "ProductVersion", "1.0.1.0"
VALUE "OriginalFilename", "updater.exe"
VALUE "InternalName", "updater.exe"
VALUE "FileDescription", "Software Update Tool"
VALUE "CompanyName", "Mendeley Ltd."
VALUE "LegalCopyright", "(C) Mendeley Ltd. 2011"
VALUE "ProductName", "Mendeley Software Updater"
VALUE "PrivateBuild", "Built by Robert Knight"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x0000, 0x04b0
END
END