Click here to Skip to main content
15,894,017 members
Articles / Desktop Programming / Win32

Simple Version Resource Tool for Windows

Rate me:
Please Sign up or sign in to vote.
4.83/5 (65 votes)
3 Sep 2012CPOL9 min read 371.9K   27.4K   169  
A utility for creating version info on executable files without Resource Compiler
//
// 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.
    char cFileVerTailSeparator; // separator of version suffix: '-' or none or space
    char cProductVerTailSeparator; // 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
	);

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions