Found this right here on Stackoverflow:
How to determine the version of a Delphi application
I already knew how to determine the version of the application, but @StijnSanders suggested the "best" way, precisely for the reasons why I hit:
I highly recommend not using GetFileVersion if you want to know the version of the executable that is currently running! I have two good reasons for this:
- The executable file may be unavailable (mapped drive / share) or modified (.exe renamed to .bak and replaced with a new .exe without stopping the running process).
- The version information that you are trying to read is already loaded into memory and available to you when you load this resource, which is always better than performing additional (relatively slow) disk operations.
Which I adapted in:
function GetModuleVersion(Instance: THandle; out iMajor, iMinor, iRelease, iBuild: Integer): Boolean; var fileInformation: PVSFIXEDFILEINFO; verlen: Cardinal; rs: TResourceStream; m: TMemoryStream; resource: HRSRC; begin //You said zero, but you mean "us" if Instance = 0 then Instance := HInstance; //UPDATE: Workaround bug in Delphi if resource doesn't exist resource := FindResource(Instance, 1, RT_VERSION); if resource = 0 then begin iMajor := 0; iMinor := 0; iRelease := 0; iBuild := 0; Result := False; Exit; end; m := TMemoryStream.Create; try rs := TResourceStream.CreateFromID(Instance, 1, RT_VERSION); try m.CopyFrom(rs, rs.Size); finally rs.Free; end; m.Position:=0; if not VerQueryValue(m.Memory, '\', (*var*)Pointer(fileInformation), (*var*)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;
Warning : the above code sometimes fails due to an error in Delphi:
rs := TResourceStream.CreateFromID(Instance, 1, RT_VERSION);
If version information is missing, Delphi tries to throw an exception:
procedure TResourceStream.Initialize(Instance: THandle; Name, ResType: PChar); procedure Error; begin raise EResNotFound.CreateFmt(SResNotFound, [Name]); end; begin HResInfo := FindResource(Instance, Name, ResType); if HResInfo = 0 then Error; ... end;
The error, of course, is that PChar
not always a pointer to an ANSI character. With unnamed resources, they are integer constants cast to PChar
. In this case:
Name: PChar = PChar(1);
When Delphi tries to create an exception string and dereferences the 0x00000001
pointer, this also causes an access violation.
The fix is to FindResource(Instance, 1, RT_VERSION)
manually call FindResource(Instance, 1, RT_VERSION)
:
var ... resource: HRSRC; begin ... resource := FindResource(Instance, 1, RT_VERSION); if (resource = 0) begin iMajor := 0; iMinor := 0; iRelease := 0; iBuild := 0; Result := False; Exit; end; m := TMemoryStream.Create; ...
Note Any code is published in the public domain. Attribution is not required.