Jump to content
dummzeuch

How do get all strings from a version resource

Recommended Posts

The usual way to get version information for a file goes like this:

 

type
  TEXEVersionData = record
    CompanyName,
      FileDescription,
      FileVersion,
      InternalName,
      LegalCopyRight,
      LegalTradeMarks,
      OriginalFilename,
      ProductName,
      ProductVersion,
      Comments,
      PrivateBuild,
      SpecialBuild: string;
  end;

function TCustomFileInfo.ReadVersionData: TEXEVersionData;
type
  PLandCodepage = ^TLandCodepage;
  TLandCodepage = record
    wLanguage,
      wCodePage: Word;
  end;
var
  Dummy: Cardinal;
  Len: Cardinal;
  Buf: array of Byte;
  pntr: Pointer;
  lang: string;
begin
  Len := GetFileVersionInfoSize(PChar(Filename), Dummy);
  if Len = 0 then
    RaiseLastOSError;
  SetLength(Buf, Len);

  if not GetFileVersionInfo(PChar(Filename), 0, Len, @Buf[0]) then
    RaiseLastOSError;

  if not VerQueryValue(Buf, '\VarFileInfo\Translation\', pntr, Len) then
    RaiseLastOSError;

  lang := Format('%.4x%.4x', [PLandCodepage(pntr)^.wLanguage, PLandCodepage(pntr)^.wCodePage]);

  if VerQueryValue(@Buf[0], PChar('\StringFileInfo\' + lang + '\CompanyName'), pntr, Len) { and (@len <> nil)} then
    Result.CompanyName := PChar(pntr);
  if VerQueryValue(@Buf[0], PChar('\StringFileInfo\' + lang + '\FileDescription'), pntr, Len) { and (@len <> nil)} then
    Result.FileDescription := PChar(pntr);
  if VerQueryValue(@Buf[0], PChar('\StringFileInfo\' + lang + '\FileVersion'), pntr, Len) { and (@len <> nil)} then
    Result.FileVersion := PChar(pntr);
  if VerQueryValue(@Buf[0], PChar('\StringFileInfo\' + lang + '\InternalName'), pntr, Len) { and (@len <> nil)} then
    Result.InternalName := PChar(pntr);
  if VerQueryValue(@Buf[0], PChar('\StringFileInfo\' + lang + '\LegalCopyright'), pntr, Len) { and (@len <> nil)} then
    Result.LegalCopyRight := PChar(pntr);
  if VerQueryValue(@Buf[0], PChar('\StringFileInfo\' + lang + '\LegalTrademarks'), pntr, Len) { and (@len <> nil)} then
    Result.LegalTradeMarks := PChar(pntr);
  if VerQueryValue(@Buf[0], PChar('\StringFileInfo\' + lang + '\OriginalFileName'), pntr, Len) { and (@len <> nil)} then
    Result.OriginalFilename := PChar(pntr);
  if VerQueryValue(@Buf[0], PChar('\StringFileInfo\' + lang + '\ProductName'), pntr, Len) { and (@len <> nil)} then
    Result.ProductName := PChar(pntr);
  if VerQueryValue(@Buf[0], PChar('\StringFileInfo\' + lang + '\ProductVersion'), pntr, Len) { and (@len <> nil)} then
    Result.ProductVersion := PChar(pntr);
  if VerQueryValue(@Buf[0], PChar('\StringFileInfo\' + lang + '\Comments'), pntr, Len) { and (@len <> nil)} then
    Result.Comments := PChar(pntr);
  if VerQueryValue(@Buf[0], PChar('\StringFileInfo\' + lang + '\PrivateBuild'), pntr, Len) { and (@len <> nil)} then
    Result.PrivateBuild := PChar(pntr);
  if VerQueryValue(@Buf[0], PChar('\StringFileInfo\' + lang + '\SpecialBuild'), pntr, Len) { and (@len <> nil)} then
    Result.SpecialBuild := PChar(pntr);
end;

(This is based on http://stackoverflow.com/a/5539411/49925 )

 

This only handles the case of known strings. But you can put any additional strings into it, e.g.:

LANGUAGE LANG_ENGLISH,SUBLANG_ENGLISH_US

1 VERSIONINFO LOADONCALL MOVEABLE DISCARDABLE IMPURE
FILEVERSION 1, 3, 6, 577
PRODUCTVERSION 1, 3, 6, 577
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
FILEOS VOS__WINDOWS32
FILETYPE VFT_APP
{
 BLOCK "StringFileInfo"
 {
  BLOCK "040904E4"
  {
   VALUE "CompanyName", "dummzeuch.de\000"
   VALUE "FileDescription", "dzlib PrepBuild commandline and IDE prebuild tool\000"
   VALUE "FileVersion", "1.3.6.577\000"
   VALUE "InternalName", "PrepBuild\000"
   VALUE "LegalCopyright", "Copyright 2002-2020 by Thomas Mueller\000"
   VALUE "LegalTrademarks", "\000"
   VALUE "OriginalFilename", "PrepBuild.exe\000"
   VALUE "ProductName", "PrepBuild\000"
   VALUE "ProductVersion", "2020-05-31\000"
   VALUE "Comments", "<svn range=\"87:92\" modified=\"yes\" url=\"https://svn.osdn.net/svnroot/dzprepbuild/trunk\" />\000"
   VALUE "Revision", "\000"
   VALUE "BuildDateTime", "2020-05-31\000"
  }
 }
 BLOCK "VarFileInfo"
 {
  VALUE "Translation", 1033, 1252
 }
}

This contains two additional strings: Revision (which is empty) and BuildDateTime.

 

There could be many more. But, how do I get these strings without knowing their name? VerQueryValue requires me to pass it the name of the string I want to read, so that's not a solution.

Share this post


Link to post

As far as I remember there's no API for that. I think you will have to parse the resource block yourself. If you're lucky the memory layout is the same as the RES layout but even then many of the structures in the VERSIONINFO RES format are undocumented or very poorly documented.

The only suggestion I have is to have a look at the source of Collin Willson's old resource editor and see how he did it (with regard to the RES format).

  • Thanks 1

Share this post


Link to post

There apparently is an official documentation for the VS_VERSIONINFO structure.

Which then references the

Not simple but doable.

 

I also found some C++ code to read the VS_VERSIONINFO structure on StackOverflow https://stackoverflow.com/a/43229358/49925

 

I'll see where it that gets me.

Share this post


Link to post

Got it to work. Now the GExperts PEInfo tool also shows all strings in all languages stored in the version info resource which includes the non-standard once like BuildDateTime:

 

image.thumb.png.4039de69dd7387dd37602dfa626d2370.png

  • Like 1

Share this post


Link to post
7 minutes ago, dummzeuch said:

Got it to work. Now the GExperts PEInfo tool also shows all strings in all languages stored in the version info resource which includes the non-standard once like BuildDateTime:

Nice job. I think soon I'll need something like this in one of my applications - at least I'll know where to look 🙂

Share this post


Link to post

There are some odd entries in the resources of BDS.EXE and BDS.fr:

 

image.thumb.png.86304ad51b939008770994cac8faf72a.png

 

image.thumb.png.3a67572a83fbd7ff9d39e5f4e5907b00.png

 

And some other executables do not have proper version info:

image.thumb.png.b4851f76307b384983a32de90e4955e6.png

So they do not display anything when read the usual way (like e.g. in the Properties dialog in Windows).

 

Older Delphi versions still refer to Appmethod in the translations (this is XE8):

image.thumb.png.9aa8c549cc550f38d6dde9b78dc1c200.png

 

(I definitely had too much time on my hands today. 😉

  • Like 1
  • Thanks 1

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×