Question

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

Was it helpful?

Solution

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();

    // handle memory cleanup...
}

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!

OTHER TIPS

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.... 

Here's a Delphi 7 version:

uses Windows, SysUtils;

function GetEXEVersion(exename: string; const Fmt : string = '%d.%d.%d.%d'): string;
{
    credit to martinstoeckli@gmx.ch 
  ( 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;

To check .NET assemblies, in C#:

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top