Want to obtain Delphi Application build number and post into title bar
-
4I see most proposed answers make use of GetFileVersion. There are issues with this option, I've posted the details in an answer of my own. – Stijn Sanders Nov 12 '09 at 07:25
7 Answers
Here is how I do it. I put this in almost all of my small utilities:
procedure GetBuildInfo(var V1, V2, V3, V4: word);
var
VerInfoSize, VerValueSize, Dummy: DWORD;
VerInfo: Pointer;
VerValue: PVSFixedFileInfo;
begin
VerInfoSize := GetFileVersionInfoSize(PChar(ParamStr(0)), Dummy);
if VerInfoSize > 0 then
begin
GetMem(VerInfo, VerInfoSize);
try
if GetFileVersionInfo(PChar(ParamStr(0)), 0, VerInfoSize, VerInfo) then
begin
VerQueryValue(VerInfo, '\', Pointer(VerValue), VerValueSize);
with VerValue^ do
begin
V1 := dwFileVersionMS shr 16;
V2 := dwFileVersionMS and $FFFF;
V3 := dwFileVersionLS shr 16;
V4 := dwFileVersionLS and $FFFF;
end;
end;
finally
FreeMem(VerInfo, VerInfoSize);
end;
end;
end;
function GetBuildInfoAsString: string;
var
V1, V2, V3, V4: word;
begin
GetBuildInfo(V1, V2, V3, V4);
Result := IntToStr(V1) + '.' + IntToStr(V2) + '.' +
IntToStr(V3) + '.' + IntToStr(V4);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Form1.Caption := Form1.Caption + ' - v' + GetBuildInfoAsString;
end;
-
1Mick, you should check the returned VerInfoSize like Joseph, and only pursue if > 0, as it may not have file version informastion. – Francesca Nov 11 '09 at 23:07
-
1
-
-
Mick, as i can see your code will not compile since there was no declaration of sFileName variable in your snippet. – Konstantin Apr 21 '11 at 07:06
Thanks to the posts above, I made my own library for this purpose.
I believe that it is a little bit more correct than all other solutions here, so I share it - feel free to reuse it...
unit KkVersion;
interface
function FileDescription: String;
function LegalCopyright: String;
function DateOfRelease: String; // Proprietary
function ProductVersion: String;
function FileVersion: String;
implementation
uses
Winapi.Windows, System.SysUtils, System.Classes, Math;
(*
function GetHeader(out AHdr: TVSFixedFileInfo): Boolean;
var
BFixedFileInfo: PVSFixedFileInfo;
RM: TMemoryStream;
RS: TResourceStream;
BL: Cardinal;
begin
Result := False;
RM := TMemoryStream.Create;
try
RS := TResourceStream.CreateFromID(HInstance, 1, RT_VERSION);
try
RM.CopyFrom(RS, RS.Size);
finally
FreeAndNil(RS);
end;
// Extract header
if not VerQueryValue(RM.Memory, '\\', Pointer(BFixedFileInfo), BL) then
Exit;
// Prepare result
CopyMemory(@AHdr, BFixedFileInfo, Math.Min(sizeof(AHdr), BL));
Result := True;
finally
FreeAndNil(RM);
end;
end;
*)
function GetVersionInfo(AIdent: String): String;
type
TLang = packed record
Lng, Page: WORD;
end;
TLangs = array [0 .. 10000] of TLang;
PLangs = ^TLangs;
var
BLngs: PLangs;
BLngsCnt: Cardinal;
BLangId: String;
RM: TMemoryStream;
RS: TResourceStream;
BP: PChar;
BL: Cardinal;
BId: String;
begin
// Assume error
Result := '';
RM := TMemoryStream.Create;
try
// Load the version resource into memory
RS := TResourceStream.CreateFromID(HInstance, 1, RT_VERSION);
try
RM.CopyFrom(RS, RS.Size);
finally
FreeAndNil(RS);
end;
// Extract the translations list
if not VerQueryValue(RM.Memory, '\\VarFileInfo\\Translation', Pointer(BLngs), BL) then
Exit; // Failed to parse the translations table
BLngsCnt := BL div sizeof(TLang);
if BLngsCnt <= 0 then
Exit; // No translations available
// Use the first translation from the table (in most cases will be OK)
with BLngs[0] do
BLangId := IntToHex(Lng, 4) + IntToHex(Page, 4);
// Extract field by parameter
BId := '\\StringFileInfo\\' + BLangId + '\\' + AIdent;
if not VerQueryValue(RM.Memory, PChar(BId), Pointer(BP), BL) then
Exit; // No such field
// Prepare result
Result := BP;
finally
FreeAndNil(RM);
end;
end;
function FileDescription: String;
begin
Result := GetVersionInfo('FileDescription');
end;
function LegalCopyright: String;
begin
Result := GetVersionInfo('LegalCopyright');
end;
function DateOfRelease: String;
begin
Result := GetVersionInfo('DateOfRelease');
end;
function ProductVersion: String;
begin
Result := GetVersionInfo('ProductVersion');
end;
function FileVersion: String;
begin
Result := GetVersionInfo('FileVersion');
end;
end.

- 221
- 2
- 2
-
3Thank you - I have voted you up because you did not fall into the trap of assuming the locale is $0409 (Unites States English). It isn't on my Delphi XE7, and I'm sure for the majority of Delphi developers – Reversed Engineer Mar 19 '15 at 09:42
-
@DaveBoltman Yeah, mine is always set to South African English for all of my Delphi projects. – Shaun Roselt Jul 01 '19 at 08:15
-
1@ShaunRoselt - "South African English" - yea mine too!! Greetings from Krugersdorp – Reversed Engineer Jul 01 '19 at 10:00
I most strongly recommend not to use GetFileVersion when you want to know the version of the executable that is currently running! I have two pretty good reasons to do this:
- The executable may be unaccessible (disconnected drive/share), or changed (.exe renamed to .bak and replaced by a new .exe without the running process being stopped).
- The version data you're trying to read has actually already been loaded into memory, and is available to you by loading this resource, which is always better than to perform extra (relatively slow) disk operations.
To load the version resource in Delphi I use code like this:
uses Windows,Classes,SysUtils;
var
verblock:PVSFIXEDFILEINFO;
versionMS,versionLS:cardinal;
verlen:cardinal;
rs:TResourceStream;
m:TMemoryStream;
p:pointer;
s:cardinal;
begin
m:=TMemoryStream.Create;
try
rs:=TResourceStream.CreateFromID(HInstance,1,RT_VERSION);
try
m.CopyFrom(rs,rs.Size);
finally
rs.Free;
end;
m.Position:=0;
if VerQueryValue(m.Memory,'\',pointer(verblock),verlen) then
begin
VersionMS:=verblock.dwFileVersionMS;
VersionLS:=verblock.dwFileVersionLS;
AppVersionString:=Application.Title+' '+
IntToStr(versionMS shr 16)+'.'+
IntToStr(versionMS and $FFFF)+'.'+
IntToStr(VersionLS shr 16)+'.'+
IntToStr(VersionLS and $FFFF);
end;
if VerQueryValue(m.Memory,PChar('\\StringFileInfo\\'+
IntToHex(GetThreadLocale,4)+IntToHex(GetACP,4)+'\\FileDescription'),p,s) or
VerQueryValue(m.Memory,'\\StringFileInfo\\040904E4\\FileDescription',p,s) then //en-us
AppVersionString:=PChar(p)+' '+AppVersionString;
finally
m.Free;
end;
end;

- 35,982
- 11
- 45
- 67
-
I'll vote this up if you make the code exception-safe with regard to memory and resource allocations. – mghie Nov 12 '09 at 08:09
-
1I've added try-finally's, should do the trick. In case you were wondering what the TMemoryStream's for: VerQueryValue had trouble reading from rs.Memory directory... – Stijn Sanders Nov 12 '09 at 08:41
-
If resources are anything like they were in my API days, they aren't already in memory, but are fetched from the file. Also, it would be unusual to be doing a swap underneath the app, so I don't think it worth worrying about for most people. – mj2008 Nov 12 '09 at 10:04
-
2There's nothing unusual about renaming a running executable, it's the accepted workaround for Windows' inability to overwrite it. Update routines often rename the running executable to be able to copy the new version to the original location. – mghie Nov 12 '09 at 10:13
-
This is an awful lot of code just to extract version info. There should be something simple, like just calling a function (for example get **majorversion**) in the RTTI.... – Yogi Yang 007 Nov 12 '09 at 11:21
-
He wants to post the build # into the title bar. To me, that means the application is looking at it's own version information. So part of your scenario #1 is not valid; the EXE will always have access to itself. It might have been renamed though; that is a good point. – JosephStyons Nov 13 '09 at 14:14
-
2There's a bug in Delphi that is exposed by the code. The `Name` of the resource you're asking for is `PChar(1)`. If the resource is not found (i.e. there is no version information), Delphi will try to throw an `EResNotFound` (*Resource %s not found*). When it tries to build a string using `PChar` `0x00000001` it will trigger an access violation, as there are no ansi chars to be read at address $00000001. – Ian Boyd Jul 09 '12 at 15:34
-
1Also doesn't work unless the locale is $0409 (Unites States English) - Not a reliable assumption over the world's population of Delphi developers - otherwise good answer – Reversed Engineer Mar 19 '15 at 09:38
-
@DaveBoltman, the Delphi Project Properties Dialog has a Locale selection drop-down for which to add the version data. It's indeed required to select 'English (United States)' there, or use the selected Locale ID in above code. – Stijn Sanders Mar 19 '15 at 10:20
Pass the full file name of your EXE to this function, and it will return a string like: 2.1.5.9, or whatever your version # is.
function GetFileVersion(exeName : string): string;
const
c_StringInfo = 'StringFileInfo\040904E4\FileVersion';
var
n, Len : cardinal;
Buf, Value : PChar;
begin
Result := '';
n := GetFileVersionInfoSize(PChar(exeName),n);
if n > 0 then begin
Buf := AllocMem(n);
try
GetFileVersionInfo(PChar(exeName),0,n,Buf);
if VerQueryValue(Buf,PChar(c_StringInfo),Pointer(Value),Len) then begin
Result := Trim(Value);
end;
finally
FreeMem(Buf,n);
end;
end;
end;
After defining that, you can use it to set your form's caption like so:
procedure TForm1.FormShow(Sender: TObject);
begin
//ParamStr(0) is the full path and file name of the current application
Form1.Caption := Form1.Caption + ' version ' + GetFileVersion(ParamStr(0));
end;

- 57,317
- 63
- 160
- 234
-
2
-
@Wodzu: it works for me in D2007. Does your project have the "Include version information in project" option checked under Project->Options->Version Info? What does Windows Explorer tell you the file version is? – JosephStyons Dec 03 '09 at 13:38
-
2This doesn't work unless your locale is $0409 (United States English) - not a reliable assumption – Reversed Engineer Mar 19 '15 at 09:35
-
-
@DaveBoltman, you are right, thanks for pointing that out. User Jiri Krivanek has another version below, which claims to avoid the locale issue. I haven't tried it, but it might be a better fit for you. Alternatively, you could just change the "040904E4" in my code to the locale you need. – JosephStyons Mar 07 '17 at 14:43
We do this for all our apps but we use a Raize component RzVersioninfo. works quite well just need to use the following code
on form create
Caption := RzVersioninfo1.filedescripion + ': ' + RzVersionInfo1.FileVersion;
obviously if you don't want any of the other components from raize use one of the options above as there is a cost to the raize components.

- 326
- 3
- 8
From http://www.martinstoeckli.ch/delphi/delphi.html#AppVersion
With this function you can get the version of a file, which contains a version resource. This way you can display the version number of your application in an information dialog. To include a version resource to your Delphi application, set the "Versioninfo" in the project options.

- 7,958
- 8
- 39
- 51

- 4,197
- 5
- 40
- 53
-
10Good link, but lousy answer. Please summarize the contents of the link: What kind of solution should we expect to find by following the link, and what are the important functions to pay attention to? – Rob Kennedy Nov 11 '09 at 22:01
My code:
uses unit Winapi.Windows;
function GetModuleVersion(Instance: THandle; out iMajor, iMinor, iRelease, iBuild: Integer): Boolean;
var
fileInformation: PVSFIXEDFILEINFO;
verlen: Cardinal;
rs: TResourceStream;
m: TMemoryStream;
begin
result := false;
m := TMemoryStream.Create;
try
try
rs := TResourceStream.CreateFromID(Instance, 1, RT_VERSION);
try
m.CopyFrom(rs, rs.Size);
finally
rs.Free;
end;
except
exit;
end;
m.Position:=0;
if not VerQueryValue(m.Memory, '\', Pointer(fileInformation), verlen) then
begin
iMajor := 0;
iMinor := 0;
iRelease := 0;
iBuild := 0;
Exit;
end;
iMajor := fileInformation.dwFileVersionMS shr 16;
iMinor := fileInformation.dwFileVersionMS and $FFFF;
iRelease := fileInformation.dwFileVersionLS shr 16;
iBuild := fileInformation.dwFileVersionLS and $FFFF;
finally
m.Free;
end;
Result := True;
end;
Usage:
if GetModuleVersion(HInstance, iMajor, iMinor, iRelease, iBuild) then
ProgramVersion := inttostr(iMajor)+'.'+inttostr(iMinor)+'.'+inttostr(iRelease)+'.'+inttostr(iBuild);

- 381
- 4
- 12