///////////////////////////////////////////////////////////////////////////// /* DESCRIPTION: CFileVersionInfo - Class for getting file version information http://www.codeproject.com/file/VersionInfo.asp NOTES: Copyright(C) Armen Hakobyan, 2003 mailto:armen.h@web.am VERSION HISTORY: 25 Jul 2003 - Posted the article 27 Jul 2003 - Added DLLVERSIONINFO2 support to DllGetVersion 21 Jan 2004 - Added GetFileVersionMajor, GetFileVersionMinor, GetFileVersionBuild, GetFileVersionQFE functions 29 Jan 2004 - Added GetProductVersionMajor, GetProductVersionMinor, GetProductVersionBuild, GetProductVersionQFE functions */ ///////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "verinfo.h" ///////////////////////////////////////////////////////////////////////////// HRESULT STDAPICALLTYPE DllGetVersion( IN HMODULE hModule, OUT DLLVERSIONINFO* lpDVI ) { if( hModule == NULL || ::IsBadReadPtr( lpDVI, sizeof( DLLVERSIONINFO* ) ) ) { ASSERT_RETURN( S_FALSE ); } CONST DWORD cbSize = lpDVI->cbSize; if( #ifdef DLLVERSIONINFO2 ( #endif cbSize != sizeof( DLLVERSIONINFO ) #ifdef DLLVERSIONINFO2 && cbSize != sizeof( DLLVERSIONINFO2 ) ) #endif || ::IsBadWritePtr( lpDVI, cbSize ) ) { ASSERT_RETURN( S_FALSE ); } ::ZeroMemory( lpDVI, cbSize ); lpDVI->cbSize = cbSize; CFileVersionInfo fvi; if( fvi.Open( hModule ) ) { VS_FIXEDFILEINFO vsffi = fvi.GetVSFFI(); if( vsffi.dwFileType == VFT_DLL || vsffi.dwFileType == VFT_STATIC_LIB ) { switch( vsffi.dwFileOS ) { case VOS__WINDOWS32: case VOS_NT_WINDOWS32: lpDVI->dwPlatformID = DLLVER_PLATFORM_WINDOWS; break; case VOS_NT: lpDVI->dwPlatformID = DLLVER_PLATFORM_NT; break; default: return ( S_FALSE ); } lpDVI->dwMajorVersion = HIWORD( vsffi.dwFileVersionMS ); lpDVI->dwMinorVersion = LOWORD( vsffi.dwFileVersionMS ); lpDVI->dwBuildNumber = HIWORD( vsffi.dwFileVersionLS ); #ifdef DLLVERSIONINFO2 if( cbSize == sizeof( DLLVERSIONINFO2 ) ) { DLLVERSIONINFO2* lpDVI2 = (DLLVERSIONINFO2*)lpDVI; lpDVI2->ullVersion = MAKEDLLVERULL( lpDVI->dwMajorVersion, lpDVI->dwMinorVersion, lpDVI->dwBuildNumber , LOWORD( vsffi.dwFileVersionLS ) ); } #endif return ( S_OK ); } #ifdef _DEBUG else ASSERT( 0 ); #endif fvi.Close(); } return ( S_FALSE ); } ///////////////////////////////////////////////////////////////////////////// // HIWORD( ffi.dwFileVersionMS ) - major // LOWORD( ffi.dwFileVersionMS ) - minor // HIWORD( ffi.dwFileVersionLS ) - build // LOWORD( ffi.dwFileVersionLS ) - QFE ///////////////////////////////////////////////////////////////////////////// CFileVersionInfo::CFileVersionInfo( void ) : m_lpbyVIB( NULL ) { Close(); } CFileVersionInfo::~CFileVersionInfo( void ) { Close(); } LPCTSTR CFileVersionInfo::s_ppszStr[] = { _T( "Comments" ), _T( "CompanyName" ), _T( "FileDescription" ), _T( "FileVersion" ), _T( "InternalName" ), _T( "LegalCopyright" ), _T( "LegalTrademarks" ), _T( "OriginalFilename" ), _T( "PrivateBuild" ), _T( "ProductName" ), _T( "ProductVersion" ), _T( "SpecialBuild" ), _T( "OLESelfRegister" ) }; //////////////////////////////////////////////////////////////////////////////// // Implementation BOOL CFileVersionInfo::Open( IN HINSTANCE hInstance ) { if( hInstance == NULL ) ASSERT_RETURN( FALSE ); TCHAR szFileName[ MAX_PATH ] = { 0 }; if( ::GetModuleFileName( hInstance, szFileName, MAX_PATH ) ) return Open( szFileName ); return FALSE; }; BOOL CFileVersionInfo::Open( IN LPCTSTR lpszFileName ) { if( lpszFileName == NULL ) ASSERT_RETURN( FALSE ); Close(); if( !GetVersionInfo( lpszFileName ) || !QueryVersionTrans() ) Close(); return m_bValid; }; BOOL CFileVersionInfo::GetVersionInfo( IN LPCTSTR lpszFileName ) { DWORD dwDummy = 0; DWORD dwSize = ::GetFileVersionInfoSize( const_cast< LPTSTR >( lpszFileName ), &dwDummy // Set to 0 ); if ( dwSize > 0 ) { m_lpbyVIB = (LPBYTE)malloc( dwSize ); if ( m_lpbyVIB != NULL && ::GetFileVersionInfo( const_cast< LPTSTR >( lpszFileName ), 0, dwSize, m_lpbyVIB ) ) { UINT uLen = 0; LPVOID lpVSFFI = NULL; if ( ::VerQueryValue( m_lpbyVIB, _T( "\\" ), (LPVOID*)&lpVSFFI, &uLen ) ) { ::CopyMemory( &m_vsffi, lpVSFFI, sizeof( VS_FIXEDFILEINFO ) ); m_bValid = ( m_vsffi.dwSignature == VS_FFI_SIGNATURE ); } } } return m_bValid; } BOOL CFileVersionInfo::QueryVersionTrans( void ) { if( m_bValid == FALSE ) ASSERT_RETURN( FALSE ); UINT uLen = 0; LPVOID lpBuf = NULL; if( ::VerQueryValue( m_lpbyVIB, _T( "\\VarFileInfo\\Translation" ), (LPVOID*)&lpBuf, &uLen ) ) { m_lpdwTrans = (LPDWORD)lpBuf; m_nTransCnt = ( uLen / sizeof( DWORD ) ); } return (BOOL)( m_lpdwTrans != NULL ); } void CFileVersionInfo::Close( void ) { m_nTransCnt = 0; m_nTransCur = 0; m_bValid = FALSE; m_lpdwTrans = NULL; ::ZeroMemory( &m_vsffi, sizeof( VS_FIXEDFILEINFO ) ); _free( m_lpbyVIB ); } BOOL CFileVersionInfo::QueryStringValue( IN LPCTSTR lpszItem, OUT LPTSTR lpszValue, IN INT nBuf ) const { if( m_bValid == FALSE || lpszItem == NULL ) ASSERT_RETURN( FALSE ); if( lpszValue != NULL && nBuf <= 0 ) ASSERT_RETURN( FALSE ); ::ZeroMemory( lpszValue, nBuf * sizeof( TCHAR ) ); TCHAR szSFI[ MAX_PATH ] = { 0 }; ::wsprintf( szSFI, _T( "\\StringFileInfo\\%04X%04X\\%s" ), GetCurLID(), GetCurCP(), lpszItem ); BOOL bRes = FALSE; UINT uLen = 0; LPTSTR lpszBuf = NULL; if( ::VerQueryValue( m_lpbyVIB, (LPTSTR)szSFI, (LPVOID*)&lpszBuf, &uLen ) ) { if( lpszValue != NULL && nBuf > 0 ) bRes = (BOOL)( ::lstrcpyn( lpszValue, lpszBuf, nBuf ) != NULL ); else bRes = TRUE; } return ( bRes ); } BOOL CFileVersionInfo::QueryStringValue( IN INT nIndex, OUT LPTSTR lpszValue, IN INT nBuf ) const { if( nIndex < VI_STR_COMMENTS || nIndex > VI_STR_OLESELFREGISTER ) { ASSERT_RETURN( FALSE ); } return QueryStringValue( s_ppszStr[ nIndex ], lpszValue, nBuf ); } LPCTSTR CFileVersionInfo::GetVerStringName( IN INT nIndex ) { if( nIndex < VI_STR_COMMENTS || nIndex > VI_STR_OLESELFREGISTER ) { ASSERT_RETURN( FALSE ); } return (LPCTSTR)s_ppszStr[ nIndex ]; } INT CFileVersionInfo::FindTrans( IN LANGID wLID, IN WORD wCP ) const { if( m_bValid == FALSE ) ASSERT_RETURN( -1 ); for( UINT n = 0; n < m_nTransCnt; n++ ) { if( LOWORD( m_lpdwTrans[ n ] ) == wLID && HIWORD( m_lpdwTrans[ n ] ) == wCP ) { return n; } } return -1; } BOOL CFileVersionInfo::SetTrans( IN LANGID wLID /*LANG_NEUTRAL*/, IN WORD wCP /*WSLVI_CP_UNICODE*/ ) { if( m_bValid == FALSE ) ASSERT_RETURN( FALSE ); if( GetCurLID() == wLID && GetCurCP() == wCP ) return TRUE; INT nPos = FindTrans( wLID, wCP ); if( nPos != -1 ) m_nTransCur = nPos; return ( m_nTransCur == (UINT)nPos ); } DWORD CFileVersionInfo::GetTransByIndex( IN UINT nIndex ) const { if( m_bValid == FALSE || nIndex < 0 || nIndex > m_nTransCnt ) ASSERT_RETURN( 0 ); return m_lpdwTrans[ nIndex ]; } BOOL CFileVersionInfo::SetTransIndex( IN UINT nIndex /*0*/ ) { if( m_bValid == FALSE ) ASSERT_RETURN( FALSE ); if( m_nTransCur == nIndex ) return TRUE; if( nIndex >= 0 && nIndex <= m_nTransCnt ) m_nTransCur = nIndex; return ( m_nTransCur == nIndex ); } ///////////////////////////////////////////////////////////////////////////// // Static members // If the LID identifier is unknown, it returns a // default string ("Language Neutral"): BOOL CFileVersionInfo::GetLIDName( IN WORD wLID, OUT LPTSTR lpszName, IN INT nBuf ) { if( lpszName == NULL || nBuf <= 0 ) ASSERT_RETURN( FALSE ); return (BOOL)::VerLanguageName( wLID, lpszName, nBuf ); } // If the CP identifier is unknown, it returns a // default string ("Unknown"): BOOL CFileVersionInfo::GetCPName( IN WORD wCP, OUT LPCTSTR* ppszName ) { if( ppszName == NULL ) ASSERT_RETURN( FALSE ); BOOL bRes = TRUE; *ppszName = NULL; switch ( wCP ) { case VI_CP_ASCII: *ppszName = _T( "7-bit ASCII" ); break; case VI_CP_JAPAN: *ppszName = _T( "Japan (Shift ?JIS X-0208)" );break; case VI_CP_KOREA: *ppszName = _T( "Korea (Shift ?KSC 5601)" ); break; case VI_CP_TAIWAN: *ppszName = _T( "Taiwan (Big5)" ); break; case VI_CP_UNICODE: *ppszName = _T( "Unicode" ); break; case VI_CP_LATIN2: *ppszName = _T( "Latin-2 (Eastern European)" );break; case VI_CP_CYRILLIC: *ppszName = _T( "Cyrillic" ); break; case VI_CP_MULTILNG: *ppszName = _T( "Multilingual" ); break; case VI_CP_GREEK: *ppszName = _T( "Greek" ); break; case VI_CP_TURKISH: *ppszName = _T( "Turkish" ); break; case VI_CP_HEBREW: *ppszName = _T( "Hebrew" ); break; case VI_CP_ARABIC: *ppszName = _T( "Arabic" ); break; default: *ppszName = _T( "Unknown" ); bRes = FALSE; break; } return bRes; } /////////////////////////////////////////////////////////////////////////////