How to get version of running executable?

delphi, delphi-5, resources, versioning, winapi

Solution

Found it, right here on Stackoverflow:

How to determine Delphi Application Version

i already knew how to determine an application version, but @StijnSanders suggested the "better" way, for exactly the reasons i was hitting:

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.

Which i adapted into:

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 crashes sometimes due to a bug in Delphi:

rs := TResourceStream.CreateFromID(Instance, 1, RT_VERSION);

If there is no version information, Delphi tries to raise 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 bug, of course, is that `PChar` is not always a pointer to an ansi char. With non-named resources they are integer constants, cast to a `PChar`. In this case:

Name: PChar = PChar(1);

When Delphi tries to build the exception string, and dereferences the pointer `0x00000001` it triggers and access violation.

The fix is to manually call `FindResource(Instance, 1, RT_VERSION)` first:

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 released into the public domain. No attribution required.

Problem

How can i get the version of my running application? i have been using `GetFileVersionInfo(ParamStr(0), ...)`: ``` filename := PChar(ExtractShortPathName(ParamStr(0))); //Get the number of bytes he have to allocate for the file information structure dwInfoLength := GetFileVersionInfoSize(lptstrFilename, {var}dwHandle); //Get version info GetMem(pInfoData, dwInfoLength); GetFileVersionInfo(lptstrFilename, dwHandle, dwInfoLength, pInfoData); //Set what information we want to extract from pInfoData lpSubBlock := PChar(Chr(92)+Chr(0)); //Extract the desired data from pInfoData into the FileInformation structure VerQueryValue(pInfoData, lpSubBlock, PFileInformation, LengthOfReturned); ``` The problem with this technique is that it requires the Windows loader to load the image before the resources can be read. i build my applications with the `IMAGE_FILE_NET_RUN_FROM_SWAP` image flag (in order to avoid in-page exceptions on a fiddly network). This causes the Windows loader to load the entire image across the network again, rather than just looking at "me". Since i check, and save, my own version at startup, a 6 second application startup turns into a 10 second application startup. How can i read the version of me, my running application? i would assume Windows has no API to read the version of a running process, only the file that i loaded from (and if the file no longer exists, then it cannot read any version info). But i also assume that it might be possible to read version resources out of my processes own memory (without being a member of the Administrators or Debuggers group of course). Can i read the version of my process? Associated Bonus Question: How can i load `PE Image` resources from me rather than across the network?

Original source

Related problems