Unit uversion; {$mode objfpc} Interface (* Building on the excellent vinfo.pas supplied by Paul Ishenin and available elsewhere on these Lazarus Forums - I hid the TVersionInfo class from the end user to simplify their (mine) number of required Uses... - Added defensive code to TVersionInfo if no build info is compiled into the exe - Deduced GetResourceStrings - works under Linux 64/GTK2 with Lazarus 0.9.30, but fails under Win XP 32bit/Lazarus 0.9.29 - suspecting my install as the lazresexplorer example also fails for me under Lazarus 0.9.29, but works with Lazarus 0.9.30 Trawled through IDE source code, FPC source code and Lazarus supplied example program lasresexplorer to find the other defines and lookups... End user only needs to use uVersion - no other units necessary for their project. Jedi CodeFormatter seems to fail on the {$I %VARIABLE%} references, so sticking them all in here means end user code can be neatly formatted using Jedi CodeFormatter Other interesting includes I picked up in my travels are... // {$I %HOME%} = User Home Directory // {$I %FILE%} = Current pas file // {$I %LINE%} = current line number Mike Thompson - mike.cornflake@gmail.com July 24 2011 *) Uses Classes, SysUtils,FPCAdds; Function GetFileVersion: String; Function GetProductVersion: String; Function GetMajorProductVersion: Cardinal; Function GetMinorProductVersion: Cardinal; Function GetRevisionProductVersion: Cardinal; Function GetBuildProductVersion: Cardinal; Function GetCompiledDate: String; Function GetCompilerInfo: String; Function GetTargetInfo: String; Function GetOS: String; Function GetResourceStrings(oStringList : TStringList) : Boolean; Function GetLCLVersion: String; function GetWidgetSet: string; function Reload(Inst:THandle):Boolean; function GetLocalizedBuildDate(): string; Const WIDGETSET_GTK = 'GTK widget set'; WIDGETSET_GTK2 = 'GTK 2 widget set'; WIDGETSET_WIN = 'Win32/Win64 widget set'; WIDGETSET_WINCE = 'WinCE widget set'; WIDGETSET_CARBON = 'Carbon widget set'; WIDGETSET_QT = 'QT widget set'; WIDGETSET_fpGUI = 'fpGUI widget set'; WIDGETSET_OTHER = 'Other gui'; Implementation Uses resource, versiontypes, versionresource, LCLVersion, InterfaceBase; Type TVersionInfo = Class private FBuildInfoAvailable: Boolean; FVersResource: TVersionResource; Function GetFixedInfo: TVersionFixedInfo; Function GetStringFileInfo: TVersionStringFileInfo; Function GetVarFileInfo: TVersionVarFileInfo; public Constructor Create; Destructor Destroy; override; Procedure Load(Instance: THandle); Property BuildInfoAvailable: Boolean Read FBuildInfoAvailable; Property FixedInfo: TVersionFixedInfo Read GetFixedInfo; Property StringFileInfo: TVersionStringFileInfo Read GetStringFileInfo; Property VarFileInfo: TVersionVarFileInfo Read GetVarFileInfo; End; {The compiler generated date string is always of the form y/m/d. This function gives it a string respresentation according to the shortdateformat} function GetLocalizedBuildDate(): string; var BuildDate: string; SlashPos1, SlashPos2: integer; Date: TDateTime; begin BuildDate := {$I %date%}; SlashPos1 := Pos('/',BuildDate); SlashPos2 := SlashPos1 + Pos('/', Copy(BuildDate, SlashPos1+1, Length(BuildDate)-SlashPos1)); Date := EncodeDate(StrToWord(Copy(BuildDate,1,SlashPos1-1)), StrToWord(Copy(BuildDate,SlashPos1+1,SlashPos2-SlashPos1-1)), StrToWord(Copy(BuildDate,SlashPos2+1,Length(BuildDate)-SlashPos2))); Result := FormatDateTime('yyyy-mm-dd', Date); end; function GetWidgetSet: string; begin case WidgetSet.LCLPlatform of lpGtk: Result := WIDGETSET_GTK; lpGtk2: Result := WIDGETSET_GTK2; lpWin32: Result := WIDGETSET_WIN; lpWinCE: Result := WIDGETSET_WINCE; lpCarbon:Result := WIDGETSET_CARBON; lpQT: Result := WIDGETSET_QT; lpfpGUI: Result := WIDGETSET_fpGUI; else Result:=WIDGETSET_OTHER; end; end; Function GetCompilerInfo: String; begin Result := 'FPC '+{$I %FPCVERSION%}; end; Function GetTargetInfo: String; begin Result := {$I %FPCTARGETCPU%}+' - '+{$I %FPCTARGETOS%}; end; Function GetOS: String; Begin Result := {$I %FPCTARGETOS%}; End; Function GetLCLVersion: String; begin Result := 'LCL '+ lcl_version; end; Function GetCompiledDate: String; Var sDate, sTime: String; Begin sDate := GetLocalizedBuildDate; //{$I %DATE%}; sTime := {$I %TIME%}; Result := sDate + ' at ' + sTime; End; { Routines to expose TVersionInfo data } Var FInfo: TVersionInfo; Procedure CreateInfo; Begin If Not Assigned(FInfo) Then Begin FInfo := TVersionInfo.Create; FInfo.Load(HINSTANCE); End; End; Function GetResourceStrings(oStringList: TStringList): Boolean; Var i, j : Integer; oTable : TVersionStringTable; begin CreateInfo; oStringList.Clear; Result := False; If FInfo.BuildInfoAvailable Then Begin Result := True; For i := 0 To FInfo.StringFileInfo.Count-1 Do Begin oTable := FInfo.StringFileInfo.Items[i]; For j := 0 To oTable.Count-1 Do If Trim(oTable.ValuesByIndex[j])<>'' Then oStringList.Values[oTable.Keys[j]] := oTable.ValuesByIndex[j]; end; end; end; Function ProductVersionToString(PV: TFileProductVersion): String; Begin Result := Format('%d.%d.%d.%d', [PV[0], PV[1], PV[2], PV[3]]); End; Function GetMajorProductVersion: Cardinal; Begin CreateInfo; If FInfo.BuildInfoAvailable Then Result := FInfo.FixedInfo.ProductVersion[0] Else Result := 0; End; Function GetMinorProductVersion: Cardinal; Begin CreateInfo; If FInfo.BuildInfoAvailable Then Result := FInfo.FixedInfo.ProductVersion[1] Else Result := 0; End; Function GetRevisionProductVersion: Cardinal; Begin CreateInfo; If FInfo.BuildInfoAvailable Then Result := FInfo.FixedInfo.ProductVersion[2] Else Result := 0; End; Function GetBuildProductVersion: Cardinal; Begin CreateInfo; If FInfo.BuildInfoAvailable Then Result := FInfo.FixedInfo.ProductVersion[3] Else Result := 0; End; Function GetProductVersion: String; Begin CreateInfo; If FInfo.BuildInfoAvailable Then Result := ProductVersionToString(FInfo.FixedInfo.ProductVersion) Else Result := 'No build information available'; End; {%H-}Function Reload(Inst:THandle):Boolean; begin FreeAndNil(FInfo); If Not Assigned(FInfo) Then Begin FInfo := TVersionInfo.Create; FInfo.Load(Inst); End; end; Function GetFileVersion: String; Begin CreateInfo; If FInfo.BuildInfoAvailable Then Result := ProductVersionToString(FInfo.FixedInfo.FileVersion) Else Result := 'No build information available'; End; { TVersionInfo } Function TVersionInfo.GetFixedInfo: TVersionFixedInfo; Begin Result := FVersResource.FixedInfo; End; Function TVersionInfo.GetStringFileInfo: TVersionStringFileInfo; Begin Result := FVersResource.StringFileInfo; End; Function TVersionInfo.GetVarFileInfo: TVersionVarFileInfo; Begin Result := FVersResource.VarFileInfo; End; Constructor TVersionInfo.Create; Begin Inherited Create; FVersResource := TVersionResource.Create; FBuildInfoAvailable := False; End; Destructor TVersionInfo.Destroy; Begin FVersResource.Free; Inherited Destroy; End; Procedure TVersionInfo.Load(Instance: THandle); Var Stream: TResourceStream; ResID: Integer; Res: TFPResourceHandle; Begin FBuildInfoAvailable := False; ResID := 1; // Defensive code to prevent failure if no resource available... Res := FindResource(Instance, {%H-}PChar(PtrInt(ResID)), {%H-}PChar(RT_VERSION)); If Res = 0 Then Exit; Stream := TResourceStream.CreateFromID(Instance, ResID, PChar(RT_VERSION)); Try FVersResource.SetCustomRawDataStream(Stream); // access some property to load from the stream FVersResource.FixedInfo; // clear the stream FVersResource.SetCustomRawDataStream(nil); FBuildInfoAvailable := True; Finally Stream.Free; End; End; Initialization FInfo := nil; Finalization If Assigned(FInfo) Then FInfo.Free; End.