Very often, you have to check some information contained in the VERSIONINFO section of your application and most of the examples you can find on the InterWebz are doing it by using this WinAPI routine: GetFileVersionInfo
.
It certainly works and can be used to get the VersionInfo from any file you want, much like in Windows Explorer, because the routine takes a FileName as its main parameter and reads it from its header.
But if you need it from inside the running application, why would you go and read the file on disk (or at least ask the OS for it) when you have the information directly available as a resource.
The main difference is that instead of calling GetFileVersionInfo, you have to find, load and lock the resource. Then you can query the value all the same. (Warning: see Arthur’s comment below and read Raymond Chen’s blog post he references)
procedure GetResourceVersionNumbers(out AMajor, AMinor, ARelease, ABuild: Integer);
{ Get the module version information from the embedded resource.
Called upon Initialization }
var
HResource: TResourceHandle;
HResData: THandle;
PRes: Pointer;
InfoSize: DWORD;
FileInfo: PVSFixedFileInfo;
FileInfoSize: DWORD;
begin
HResource := FindResource(HInstance, MakeIntResource(VS_VERSION_INFO), RT_VERSION);
if HResource <> 0 then begin
HResData:=LoadResource(HInstance, HResource);
if HResData <> 0 then begin
PRes:=LockResource(HResData);
if Assigned(PRes) then begin
InfoSize := SizeofResource(HInstance, HResource);
if InfoSize = 0 then
raise Exception.Create('Can''t get version information.');
// Query the information for the version
VerQueryValue(PRes, '\', Pointer(FileInfo), FileInfoSize);
// Now fill in the version information
AMajor := FileInfo.dwFileVersionMS shr 16;
AMinor := FileInfo.dwFileVersionMS and $FFFF;
ARelease := FileInfo.dwFileVersionLS shr 16;
ABuild := FileInfo.dwFileVersionLS and $FFFF;
end;
// UnlockResource(HResData); // unnecessary per MSDN
// FreeResource(HResData); // unnecessary per MSDN
end;
end
else
RaiseLastOSError;
end;
procedure GetFileVersionNumbers(out AMajor, AMinor, ARelease, ABuild: Integer);
{ Get the file version information. Called upon Initialization }
var
PInfo: Pointer;
InfoSize: DWORD;
FileInfo: PVSFixedFileInfo;
FileInfoSize: DWORD;
FileName: string;
Tmp: DWORD;
begin
FileName := ParamStr(0);
// Get the size of the FileVersionInformatioin
InfoSize := GetFileVersionInfoSize(PChar(FileName), Tmp);
// If InfoSize = 0, then the file may not exist, or
// it may not have file version information in it.
if InfoSize = 0 then
raise Exception.Create('Can''t get file version information for '+ FileName);
// Allocate memory for the file version information
GetMem(PInfo, InfoSize);
try
// Get the information
GetFileVersionInfo(PChar(FileName), 0, InfoSize, PInfo);
// Query the information for the version
VerQueryValue(PInfo, '\', Pointer(FileInfo), FileInfoSize);
// Now fill in the version information
AMajor := FileInfo.dwFileVersionMS shr 16;
AMinor := FileInfo.dwFileVersionMS and $FFFF;
ARelease := FileInfo.dwFileVersionLS shr 16;
ABuild := FileInfo.dwFileVersionLS and $FFFF;
finally
FreeMem(PInfo);
end;
end;
var
gMajorVersion: Integer = 0;
gMinorVersion: Integer = 0;
gRelease: Integer = 0;
gBuild: Integer = 0;
initialization
GetResourceVersionNumbers(gMajorVersion, gMinorVersion, gRelease, gBuild);
// GetFileVersionNumbers(gMajorVersion, gMinorVersion, gRelease, gBuild);
ShowMessage(Format('major: %d, minor: %d, release: %d, build: %d',
[gMajorVersion, gMinorVersion, gRelease, gBuild]));
end.
Nice, I think in Delphi the code should be:
var
Ver : String;
begin
Ver := GetVersionNumber;
end;
The reason I think this is because Delphi is awesome and everything should be very simple to do in Delphi.
The main reason I’ve seen for getting the version numbers is to test and check for some special behavior/features.
And even for just displaying the version in an “About” box, you may want to display it differently depending on the circumstances. So, getting the individual values gives more flexibility.
You can always easily make a GetStringVersion function available publicly and use it as much as you like.
Hello François,
This solution doesn’t work in Windows Server 2003, we just found out. It gives access violations in VerQueryValue. See also http://blogs.msdn.com/b/oldnewthing/archive/2006/12/26/1365215.aspx
Regards, Arthur Pijpers
Thanks for the warning Arthur.
Never had this problem (but I’m not sure I used it with Win Server 2003).
I’m guessing it falls into this Ansi/Unicode mismatch pointed out by Raymond Chen in the blog entry you referenced.
What version of Delphi are you using to compile the app that misbehave in Win Server 2003?
How does the Version info from the exe appear in a resource editor?