1

I need to read the VersionInfo from a file (exe or dll) using a TSream.

I cannot use the windows API GetFileVersionInfo, because my file is at memory (TMemoryStream) and I don't want to write the file to disk for get this information, I have some performance restrictions.

Someone can help me?

Beto Neto
  • 3,962
  • 7
  • 47
  • 81
  • 2
    Use the Windows API to do this. No point writing your own PE file parser. – David Heffernan May 07 '13 at 17:20
  • 4
    If you're not going to use the API, then you need to specify exactly *how much* of the API you're not going to use. Will you also not use the resource-locating functions that TResourceStream relies on? You're going to have to write this code yourself, so go do that. Which part are you having trouble with? – Rob Kennedy May 07 '13 at 17:35

2 Answers2

3

If the raw file data is in memory, then the Win32 API cannot help you locate the file's version resource. You will have to manually read and interpret the file's PE header to locate the file's resources table and then loop through the table looking for the desired version resource. Once you have located it, you can use the Win32 API VerQueryValue() function to access some (but not all) of the values inside of the resource. I say some because VerQueryValue() internally relies on lookups that GetFileVersionInfo() establishes at runtime. However, accessing the VS_FIXEDFILEINFO structure, for instance, works fine without calling GetFileVersionInfo() first.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 1
    I dislike the term *to parse*, *parser*, etc when it applied to the **binary** file. Meanwhile, JCL provides a class for accessing PE internals – OnTheFly May 08 '13 at 03:18
  • @user539484 whatabout EBML or XML/Binary ? Why is thet NOT parsing ? – Arioch 'The May 08 '13 at 12:52
  • @Arioch 'The, EBML is a result of *parsing* already. If you feel a desire *to parse* it again, or parse PE header and/or its tables - you are using a wrong word *parse*. Please consider studying a [definition](http://en.wikipedia.org/wiki/Parsing) and the [theory](http://en.wikipedia.org/wiki/Compilers:_Principles,_Techniques,_and_Tools) – OnTheFly May 08 '13 at 13:13
  • @user539484 symbol is something of an alphabet... Taken extended ASCII, we are gettying byte for symbol and start parsing chains of symbols aka EBML – Arioch 'The May 08 '13 at 16:02
  • @Arioch 'The, very bad example. EBML consists a bit strings, not byte strings as you tried to demonstrate. OK, back to simple and specific: what exactly has to be "*parsed*" in the PE header? – OnTheFly May 08 '13 at 17:00
  • Are you guys seriously debating the semantics of terminology?? That does not belong here. And why was my answer downvoted? It better not have been because I used the word "parse". – Remy Lebeau May 08 '13 at 17:33
  • +1 FWIW, parsing seems to be fine here. We all understand the meaning. I personally dislike it when terminology is criticised, but the critic does not offer what the critic considers to be the correct term. – David Heffernan May 08 '13 at 17:38
0

It can be archived using the technique below.

Use the HInstance value of modules already loaded in .EXE memory space to get the RT_VERSION resource using TResourceStream.

By example, to get the MainModule hInstace and respective Version:

var module: HMODULE;
    version: String;
...
module := GetModuleHandle(nil);
version := FileVersion(base);

If you can't load as a resource from memory like I did below, you can parse the .EXE using PE HEADERS and find RT_VERSION resource using a TMemoryStream.

unit Version;

interface

implementation

uses
  Winapi.Windows, System.SysUtils, System.Classes, Math;

function FileVersion(Module: HINST = 0): String;
var
  verblock:PVSFIXEDFILEINFO;
  versionMS,versionLS:cardinal;
  verlen:cardinal;
  rs:TResourceStream;
  m:TMemoryStream;
  p:pointer;
  s:cardinal;
begin
  m:=TMemoryStream.Create;
  try
    if Module = 0 then
      Module := HInstance;

    rs:=TResourceStream.CreateFromID(Module,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;
        Result:=
          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
          Result:=PChar(p)+' '+Result;
  finally
    m.Free;
  end;
end;

end.
Community
  • 1
  • 1
Luiz Vaz
  • 1,669
  • 1
  • 19
  • 32