1

In the Project Options, there are some informations that can be set for the compiled file, like:

  • CompanyName
  • FileDescription
  • FileVersion
  • InternalName
  • LegalCopyright
  • LegalTrademarks
  • OriginalFilename
  • ProductName
  • ProductVersion
  • Comments

I know how to extract the file version from the compiled file (exe/bpl) at runtime, but I don't know how to extract these extra informations.

In particular, I would like to get the ProductVersion value

Project Options window displaying the Version Info settings

Fabrizio
  • 7,603
  • 6
  • 44
  • 104
  • Did you see this question ? : https://stackoverflow.com/questions/1717844/how-to-determine-delphi-application-version – Charles-Henri Oct 27 '20 at 10:09
  • @Charles-Henri: It's a different question but I've noticed that Jiri Krivanek's answer fit also for my question – Fabrizio Oct 27 '20 at 10:21
  • 1
    There can be up to **two** product version records stored in [`VERSIONINFO`](https://learn.microsoft.com/en-us/windows/win32/menurc/versioninfo-resource). The first one is stored as number (2 DWORDs) in `TVSFixedFileInfo` and the second one is stored as string in `StringFileInfo` under `ProductVersion` key. The latter can is not limited to numbers and can contain other information like *BETA* or *RC1*. – Peter Wolf Oct 27 '20 at 10:35
  • @PeterWolf: I'm looking for the one stored in `StringFileInfo` for the Locale ID `$0409` (I suppose...) but where can I find some information about these two kind of file informations? – Fabrizio Oct 27 '20 at 11:38
  • 1
    See official MS documentation for [`VERSIONINFO`](https://learn.microsoft.com/en-us/windows/win32/menurc/versioninfo-resource) resource that I already posted. See also sub-category [`StringFileInfo`](https://learn.microsoft.com/en-us/windows/win32/menurc/stringfileinfo-block) block in the navigation pane of the documentation. – Peter Wolf Oct 27 '20 at 11:42

1 Answers1

2

Here after is the code to get the ProductVersion out of the executable file (Or any file given his file name):

type
    TLangAndCodePage = record
        wLanguage : WORD;
        wCodePage : WORD;
    end;
    PLangAndCodePage = ^TLangAndCodePage;

    procedure TForm1.Button1Click(Sender: TObject);
    var
        InfoSize        : Integer;
        ValueSize       : DWORD;
        Dummy           : DWORD;
        VerInfo         : Pointer;
        LangAndCodePage : PLangAndCodePage;
        Ptr             : PLangAndCodePage;
        TranslateBytes  : UINT;
        I               : Integer;
        SubBlock        : String;
        SubBlockBuffer  : PChar;
    begin
        InfoSize := GetFileVersionInfoSize(PChar(Application.ExeName), Dummy);
        if InfoSize <> 0 then begin
            GetMem(VerInfo, InfoSize);
            try
                if GetFileVersionInfo(PChar(Application.ExeName), 0,
                                      InfoSize, VerInfo) then begin
    
                    VerQueryValue(VerInfo,
                                  '\VarFileInfo\Translation',
                                  Pointer(LangAndCodePage),
                                  TranslateBytes);
    
                    Ptr := LangAndCodePage;
                    for I := 0 to (TranslateBytes div SizeOf(TLangAndCodePage)) - 1 do begin
                        SubBlock := Format('\StringFileInfo\%04.4X%04.4X\ProductVersion',
                                           [Ptr.wLanguage, Ptr.wCodePage]);
                        Memo1.Lines.Add(SubBlock);
    
                        VerQueryValue(VerInfo,
                                      PChar(SubBlock),
                                      Pointer(SubBlockBuffer),
                                      ValueSize);
                        Memo1.Lines.Add('  ProductVersion="' + SubBlockBuffer + '"');
    
                        Inc(Ptr);
                    end;
                end;
            finally
                FreeMem(VerInfo, InfoSize);
            end;
        end;
    end;

The code first by querying the languages available and then iterate thru all languages available.

SubBlock is actually a kind of path for the value to query. Here you see I included ProductVersion you asked for. There are other predefined values. See Microsoft documentation.

You should add error testing that I omitted for example simplicity.

fpiette
  • 11,983
  • 1
  • 24
  • 46
  • Note: this is almost trivial stuff compared to wanting to enumerate all values in a block **without** knowing their names. Likewise `ProductVersion` is not guaranteed to exist in a `StringFileInfo` block at all. – AmigoJack Oct 27 '20 at 22:32