views:

489

answers:

4

How to retrieve at runtime the version info stored in a Windows exe/dll? This info is manually set using a resource file.

+3  A: 

Here is a C++ way of doing it, using the standard Windows API functions:

try
{
 TCHAR szFileName[ MAX_PATH ];
 if( !::GetModuleFileName( 0, szFileName, MAX_PATH ) )
  throw __LINE__;

 DWORD nParam;
 DWORD nVersionSize = ::GetFileVersionInfoSize( szFileName, &nParam );
 if( !nVersionSize )
  throw __LINE__;

 HANDLE hMem = ::GetProcessHeap();
 if( !hMem )
  throw __LINE__;

 LPVOID lpVersionData = ::HeapAlloc( hMem, 0, nVersionSize );
 if( !lpVersionData )
  throw __LINE__;

 if( !::GetFileVersionInfo( szFileName, 0, nVersionSize, lpVersionData ) )
  throw __LINE__;

 LPVOID pVersionInfo;
 UINT nSize;
 if( !::VerQueryValue( lpVersionData, _T("\\"), &pVersionInfo, &nSize ) )
  throw __LINE__;

 VS_FIXEDFILEINFO *pVSInfo = (VS_FIXEDFILEINFO *)pVersionInfo;
 CString strVersion;
 strVersion.Format( _T(" version %i.%i.%i.%i"),
  pVSInfo->dwProductVersionMS >> 16,
  pVSInfo->dwProductVersionMS & 0xFFFF,
  pVSInfo->dwProductVersionLS >> 16,
  pVSInfo->dwProductVersionLS & 0xFFFF
  );
 GetDlgItem( IDC_ABOUT_VERSION )->SetWindowText( strAppName + strVersion );

 if( !HeapFree( hMem, 0, lpVersionData ) )
  throw __LINE__;
}
catch( int err )
{
 ASSERT( !err ); // always break on debug builds to inspect error codes and such

 DWORD dwErr = ::GetLastError();
}

Note that the catch part is purely educational - in a real situation you would properly cleanup after the memory allocation and actually use the error code!

Valentin Galea
If there is a throw after your call to ::HeapAlloc, this will leak memory.
plinth
+1  A: 

Valentin's answer is correct, but note commenter plinth's warning about the possibility of a memory leak.

I'm also not sure why you'd use ::HeapAlloc in this day and age.

Here is a snippet that uses new and boost::shared_array to do the same thing in what IMHO is a safer and cleaner way.

#include <boost/shared_array.hpp> 

//.....

DWORD   dwHandle;
DWORD   dwFileVersionInfoSize = GetFileVersionInfoSize((LPTSTR)lpszFileName, &dwHandle);

if (!dwFileVersionInfoSize)
         return FALSE;

// ensure our data will be deleted
boost::shared_array<BYTE> data(new BYTE[dwFileVersionInfoSize]); 
LPVOID const lpData = data.get(); 

//party on with lpData....
Jim In Texas
+2  A: 

Here's a Delphi 7 version:

uses Windows, SysUtils;

function GetEXEVersion(exename: string; const Fmt : string = '%d.%d.%d.%d'): string;
{
    credit to [email protected] 
  ( http://martinstoeckli.ch/delphi/delphi.html#AppVersion )
}
var
  iBufferSize, iDummy : dword;
  pBuffer, pFileInfo : Pointer;
  iVer : array[1..4] of word;
begin
  Result := '';
  iBufferSize := GetFileVersionInfoSize(PChar(exename), iDummy);
  if iBufferSize > 0 then begin
    GetMem(pBuffer, iBufferSize);
    try
      GetFileVersionInfo(PChar(exename), 0, iBufferSize, pBuffer);
      VerQueryValue(pBuffer, '\', pFileInfo, iDummy);
      iVer[1] := HiWord(PVSFixedFileInfo(pFileInfo)^.dwFileVersionMS);
      iVer[2] := LoWord(PVSFixedFileInfo(pFileInfo)^.dwFileVersionMS);
      iVer[3] := HiWord(PVSFixedFileInfo(pFileInfo)^.dwFileVersionLS);
      iVer[4] := LoWord(PVSFixedFileInfo(pFileInfo)^.dwFileVersionLS);
      finally FreeMem(pBuffer);
    end;
    Result := Format(Fmt, [iVer[1],iVer[2],iVer[3],iVer[4]] );
  end;
end;
Blorgbeard
A: 

To check .NET assemblies, in C#:

System.Reflection.Assembly.LoadFile(@"c:\windows\Microsoft.NET\Framework\v2.0.50727\system.data.dll").GetName().Version.ToString();

K.Kong