Lazarus

Free Pascal => FPC development => Topic started by: OLLI_S on July 11, 2018, 09:45:08 pm

Title: [Solved] Exception in TFileVersionInfo.ReadFileInfo
Post by: OLLI_S on July 11, 2018, 09:45:08 pm
Hello,

I get some exceptions when I read the file info from some applications and the method I am using is provided by Lazarus.
I use Lazarus 1.8.4 in Windows 10 Pro German.
My programming skills I would estimate between beginner and intermediate.

I programmed a little tool that reads all information (like copyright, version info, etc) from applications.
I used the following code example: http://wiki.freepascal.org/Show_Application_Title,_Version,_and_Company

I created a new project in Lazarus, added a button and for the Button1Click event I pasted some code from the WIKI article above.
Here is the complete source of my unit:

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
  9.  
  10.   fileinfo,
  11.   winpeimagereader; {need this for reading exe info}
  12.  
  13. type
  14.  
  15.   { TForm1 }
  16.  
  17.   TForm1 = class(TForm)
  18.     Button1: TButton;
  19.     procedure Button1Click(Sender: TObject);
  20.   private
  21.  
  22.   public
  23.  
  24.   end;
  25.  
  26. var
  27.   Form1: TForm1;
  28.  
  29. implementation
  30.  
  31. {$R *.lfm}
  32.  
  33. { TForm1 }
  34.  
  35. procedure TForm1.Button1Click(Sender: TObject);
  36.  
  37. var
  38.   FileVerInfo: TFileVersionInfo;
  39.  
  40. begin
  41.  
  42.   FileVerInfo := TFileVersionInfo.Create(nil);
  43.  
  44.   try
  45.  
  46.     FileVerInfo.FileName := 'C:\lazarus\lazarus.exe';
  47.     FileVerInfo.ReadFileInfo;
  48.     ShowMessage(FileVerInfo.VersionStrings.Text);
  49.  
  50.   finally
  51.     FileVerInfo.Free;
  52.   end;
  53.  
  54. end;
  55.  
  56. end.

This works perfectly for most applications.
But some applications raise exceptions at the line FileVerInfo.ReadFileInfo; (line 47).

Examples:
As I wrote above I am not a good programmer.
But in FileVerInfo.ReadFileInfo I see no exception handling (the block "except").
Could that be the problem?
Is this a problem that can be fixed from your side (Lazarus Developers) or do I have to do anything?

I also can not set a breakpoint in FileVerInfo.ReadFileInfo to see what line causes the problems (the debugger does not stop, no matter where I set the breakpoint).
If you have any tips how I can make the breakpoint working, this would be fantastic.

I have in Lazarus the two additional packages anchordockingdsgn and sparta_DockedFormEditor.
I hope that this is not the reason for my problems (but I think not).

Thank you for your help!

Best regards

OLLI
Title: Re: Exception in TFileVersionInfo.ReadFileInfo
Post by: ASerge on July 12, 2018, 06:34:19 am
I get some exceptions when I read the file info from some applications and the method I am using is provided by Lazarus.
Please, give a link to the "some applications" exe file (without setup), for testing.
Title: Re: Exception in TFileVersionInfo.ReadFileInfo
Post by: OLLI_S on July 12, 2018, 11:04:18 am
Hello ASerge,

Please, give a link to the "some applications" exe file (without setup), for testing.

I posted the names of some of these applications (Firefox, System Explorer, etc) in my first posting.
I suppose you have Linux because you asked for the EXE without installers?
So I will upload the EXE files to OneDrive tonight and send the links.

OLLI
Title: Re: Exception in TFileVersionInfo.ReadFileInfo
Post by: ASerge on July 12, 2018, 04:29:15 pm
I posted the names of some of these applications (Firefox, System Explorer, etc) in my first posting.
Oops, missed FireFox.
Yeah, it looks like a bug. If you need only under Windows, here's a quick made module (attached), it seems to work.
Code: Pascal  [Select][+][-]
  1. uses VerInfo;
  2.  
  3. procedure TForm1.Button1Click(Sender: TObject);
  4. var
  5.   VerInfo: TVersionInfo;
  6.  
  7.   procedure AppendInfo(const Value: string);
  8.   begin
  9.     Memo1.Append(Value + '=' + VerInfo[Value]);
  10.   end;
  11.  
  12. var
  13.   LV: TLongVersion;
  14. begin
  15.   VerInfo := TVersionInfo.Create('c:\Program Files (x86)\Mozilla Firefox\firefox.exe');
  16.   try
  17.     Memo1.Clear;
  18.     AppendInfo(CviFileVersion);
  19.     AppendInfo(CviFileDescription);
  20.     AppendInfo(CviLegalCopyright);
  21.     AppendInfo(CviOriginalFilename);
  22.     AppendInfo(CviProductName);
  23.     AppendInfo('BuildID');
  24.     LV := VerInfo.FileLongVersion;
  25.     Memo1.Append(Format('FileLongVersion=%d.%d.%d.%d', [LV.Words[3], LV.Words[2], LV.Words[1], LV.Words[0]]));
  26.   finally
  27.     VerInfo.Free;
  28.   end;
  29. end;
Title: Re: Exception in TFileVersionInfo.ReadFileInfo
Post by: OLLI_S on July 12, 2018, 05:47:33 pm
Hello,

I copied your VerInfo.pp into the folder of my project and use it like you did in your source code.
It is working, all apps are now shown without any exceptions!
Fantastic work!

Is this a fix that will be included in Lazarus in the future?
So I don't need your code in my project folder?

Thank you again and best regards

OLLI
Title: Re: Exception in TFileVersionInfo.ReadFileInfo
Post by: ASerge on July 12, 2018, 06:02:10 pm
Is this a fix that will be included in Lazarus in the future?
Given the cross-platform nature of the FPC, it is very doubtful. Just use it in your project.
Title: Re: Exception in TFileVersionInfo.ReadFileInfo
Post by: OLLI_S on July 12, 2018, 07:47:42 pm
Given the cross-platform nature of the FPC, it is very doubtful. Just use it in your project.

OK, but thank you for providing the code, I will use it in my project.
Title: Re: Exception in TFileVersionInfo.ReadFileInfo
Post by: OLLI_S on July 15, 2018, 11:40:00 pm
@ASerge
I added your code and it is working perfectly (I tested many apps).

But one user reported that he gets no information when he tries to read them from the application Acronis True Image Home.
I downloaded and installed this app (in a VM) and I also get no information from this app (the memo is empty, just the following lines get a result:

Code: Pascal  [Select][+][-]
  1.     LV := VerInfo.FileLongVersion;
  2.     Memo1.Append(Format('FileLongVersion=%d.%d.%d.%d', [LV.Words[3], LV.Words[2], LV.Words[1], LV.Words[0]]));
  3.  
  4.     LV := VerInfo.ProductLongVersion;
  5.     Memo1.Append(Format('FileLongVersion=%d.%d.%d.%d', [LV.Words[3], LV.Words[2], LV.Words[1], LV.Words[0]]));


A trial version of Acronis True Image Home can be downloaded here: https://www.acronis.com/en-us/personal/computer-backup/

I uploaded the EXE to my OneDrive (https://1drv.ms/f/s!AsiLVok82IpQg_97o977pmEQx4gi9A)
I have protected the folder with a password (because I fear legal issues when I upload and share an application) and send you the password via a PM.

Can you please try your code with this EXE and tell me where the problem might be?
Thank you!

Best regards

OLLI
Title: Re: Exception in TFileVersionInfo.ReadFileInfo
Post by: ASerge on July 16, 2018, 07:20:03 am
@ASerge
But one user reported that he gets no information when he tries to read them from the application Acronis True Image Home.
Can you please try your code with this EXE and tell me where the problem might be?
Problem in Acronis True Image.
There is it's VersionInfo block:
Code: C  [Select][+][-]
  1. 1 VERSIONINFO
  2. FILEVERSION 22,5,1,12510
  3. PRODUCTVERSION 22,5,1,12510
  4. FILEOS 0x4
  5. FILETYPE 0x1
  6. {
  7. BLOCK "StringFileInfo"
  8. {
  9.         BLOCK "040904b0"
  10.         {
  11.                 VALUE "Comments", "http://www.acronis.com/"
  12.                 VALUE "CompanyName", "Acronis International GmbH"
  13.                 VALUE "FileDescription", "Acronis True Image 2018"
  14.                 VALUE "FileVersion", "22,5,1,12510"
  15.                 VALUE "InternalName", "ti_console"
  16.                 VALUE "LegalCopyright", "Copyright (C) Acronis International GmbH, 2002-2017."
  17.                 VALUE "LegalTrademarks", "Acronis International GmbH. All rights reserved."
  18.                 VALUE "OriginalFilename", "ti_console.exe"
  19.                 VALUE "ProductName", "Acronis True Image 2018"
  20.                 VALUE "ProductVersion", "22,5,1,12510"
  21.         }
  22. }
  23.  
  24. BLOCK "VarFileInfo"
  25. {
  26.         VALUE "Translation", 0x0000 0x04B0
  27. }
  28. }
As you can see VarFileInfo=0x0000 0x04B0, but StringFileInfo contains only "040904b0", and the block "000004b0" does not. This is a mistake of developers of acronis.

You can tweak the programs to account for this particular error so:
Code: Pascal  [Select][+][-]
  1. function TVersionInfo.GetVerValue(const Name: string): string;
  2. type
  3.   TTranslateInfo = packed record
  4.     LangId, CodePage: Word;
  5.   end;
  6. const
  7.   Default: TTranslateInfo = (LangId:$0409; CodePage:1252);
  8. var
  9.   PLang: ^TTranslateInfo;
  10.   PValue: PChar;
  11.   BlockName: string;
  12. begin
  13.   Result := '';
  14.   PLang := VerGetValue(FBuffer, '\VarFileInfo\Translation');
  15.   if PLang = nil then
  16.     PLang := @Default;
  17.   FmtStr(BlockName, '\StringFileInfo\%.4x%.4x\%s',
  18.     [PLang^.LangId, PLang^.CodePage, Name]);
  19.   PValue := VerGetValue(FBuffer, BlockName);
  20.   if PValue = nil then
  21.   begin
  22.     // Error in resource, like TrueImage.exe, where no LangId.
  23.     // Lets try with default langId
  24.     FmtStr(BlockName, '\StringFileInfo\%.4x%.4x\%s',
  25.       [Default.LangId, PLang^.CodePage, Name]);
  26.     PValue := VerGetValue(FBuffer, BlockName);
  27.   end;
  28.   if PValue <> nil then
  29.     Result := UTF8Encode(WideString(PWideChar(PValue)));
  30. end;
Added line [20..27].
Although of course more correct to correct the error. But it is necessary to write to acronis developers.
Title: Re: Exception in TFileVersionInfo.ReadFileInfo
Post by: OLLI_S on July 16, 2018, 10:36:59 am
Hello ASerge,

Problem in Acronis True Image.
There is it's VersionInfo block:
Code: C  [Select][+][-]
  1. 1 VERSIONINFO
  2. FILEVERSION 22,5,1,12510
  3. PRODUCTVERSION 22,5,1,12510
  4. FILEOS 0x4
  5. FILETYPE 0x1
  6. {
  7. BLOCK "StringFileInfo"
  8. {
  9.         BLOCK "040904b0"
  10.         {
  11.                 VALUE "Comments", "http://www.acronis.com/"
  12.                 VALUE "CompanyName", "Acronis International GmbH"
  13.                 VALUE "FileDescription", "Acronis True Image 2018"
  14.                 VALUE "FileVersion", "22,5,1,12510"
  15.                 VALUE "InternalName", "ti_console"
  16.                 VALUE "LegalCopyright", "Copyright (C) Acronis International GmbH, 2002-2017."
  17.                 VALUE "LegalTrademarks", "Acronis International GmbH. All rights reserved."
  18.                 VALUE "OriginalFilename", "ti_console.exe"
  19.                 VALUE "ProductName", "Acronis True Image 2018"
  20.                 VALUE "ProductVersion", "22,5,1,12510"
  21.         }
  22. }
  23.  
  24. BLOCK "VarFileInfo"
  25. {
  26.         VALUE "Translation", 0x0000 0x04B0
  27. }
  28. }
As you can see VarFileInfo=0x0000 0x04B0, but StringFileInfo contains only "040904b0", and the block "000004b0" does not. This is a mistake of developers of acronis.

I am really a noob (beginner) about this topic.
Where can this version-information that you postet be found?
What do thoese Hex-Values (like 0x0000) mean?

I will check the code tonight.
But is this a universal working code (for all applications that have the same issues in the version info)?
Or is this just a fix for Acronis True Image?

Thank you for helping me!

Best regards

OLLI
Title: Re: Exception in TFileVersionInfo.ReadFileInfo
Post by: ASerge on July 16, 2018, 04:02:41 pm
Where can this version-information that you postet be found?
What do thoese Hex-Values (like 0x0000) mean?
From documentation https://docs.microsoft.com/en-us/windows/desktop/menurc/versioninfo-resource (https://docs.microsoft.com/en-us/windows/desktop/menurc/versioninfo-resource)

Quote
But is this a universal working code (for all applications that have the same issues in the version info)?
Or is this just a fix for Acronis True Image?
This is a fix for errors such as those in Acronis True Image. In normal cases, this is not necessary. Applications are usually developed without taking into account other developers errors, but if the percentage of such errors is large, then add code that takes this into account.
Title: Re: Exception in TFileVersionInfo.ReadFileInfo
Post by: OLLI_S on July 16, 2018, 10:45:13 pm
Thank you ASerge for the source code, it is working perfectly.
And also thank you for providing the link with additional info.
Title: Re: Exception in TFileVersionInfo.ReadFileInfo
Post by: OLLI_S on July 17, 2018, 09:04:26 pm
Hello ASerge,

I have one more question about the File Version and the Product Version:

With the following lines I get the File Version and Product Version stings:

Code: Pascal  [Select][+][-]
  1.     AppendInfo(CviFileVersion);
  2.     AppendInfo(CviProductVersion);

But there are also the following lines:

Code: Pascal  [Select][+][-]
  1.     LV := VerInfo.FileLongVersion;
  2.     Memo1.Append(Format('FileLongVersion=%d.%d.%d.%d', [LV.Words[3], LV.Words[2], LV.Words[1], LV.Words[0]]));
  3.  
  4.     LV := VerInfo.ProductLongVersion;
  5.     Memo1.Append(Format('FileLongVersion=%d.%d.%d.%d', [LV.Words[3], LV.Words[2], LV.Words[1], LV.Words[0]]));

There are sometimes differences between File Version (from first code) and File Version (from second code).
Is it correct that the first file info is the File Version String from the version info and the second is the file version from the application (outside the version info block)?

I am asking because I am looking for a perfect label (caption) for those fields.
The text I write before the values.
Maybe "File Version" and "File Version String" (but I need something better).

Best regards

OLLI
Title: Re: Exception in TFileVersionInfo.ReadFileInfo
Post by: ASerge on July 18, 2018, 12:07:07 am
There are sometimes differences between File Version (from first code) and File Version (from second code).
The link above already contains information. These fields are different. A string is a general way of storing data for a version resource, and a numeric one is a dedicated way of storing data for quick access (For example the SysUtils.GetFileVersion reads it). Usually developers make them the same.
Example of *.rc file:
Code: Text  [Select][+][-]
  1. #define FileVersionInt 2018,7,18,1
  2. #define FileVersionStr "2018.07.18.1\0"
  3.  
  4. #pragma code_page(1252)
  5. LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
  6. 1 VERSIONINFO
  7. FILEVERSION FileVersionInt
  8. FILEOS VOS_NT_WINDOWS32
  9. FILETYPE VFT_APP
  10. BEGIN
  11.   BLOCK "StringFileInfo"
  12.   BEGIN
  13.     BLOCK "040904E4"
  14.     BEGIN
  15.       VALUE "FileVersion", FileVersionStr
  16. #...
  17.  
Title: Re: Exception in TFileVersionInfo.ReadFileInfo
Post by: OLLI_S on November 12, 2018, 04:10:05 pm
Hello @ASerge,

a user sent me some additional files where the current version of VerInfo crashes:
Code: Pascal  [Select][+][-]
  1. procedure TVersionInfo.ReadVersionInfo;
  2. var
  3.   Size: DWORD;
  4.   Dummy: DWORD;
  5.   FileNameW: UnicodeString;
  6. begin
  7.   FileNameW := UTF8Decode(FFileName);
  8.   Size := GetFileVersionInfoSizeW(Pointer(FileNameW), @Dummy);
  9.   if Size = 0 then
  10.     RaiseLastOSError; // <------- Here the application crashes
  11.   GetMem(FBuffer, Size);
  12.   if not GetFileVersionInfoW(Pointer(FileNameW), 0, Size, FBuffer) then
  13.     RaiseLastOSError;
  14. end;  

The variable "Size" has the value 0 for these files.
So I am not sure if these EXE-File that users try to analyze simply have no version info stored or if something is missing in the code.
I have the EXE files, so I can send them by PM.

Best regards

OLLI
Title: Re: Exception in TFileVersionInfo.ReadFileInfo
Post by: Thaddy on November 12, 2018, 04:36:01 pm
Indeed, a filesize of 0 is a legal filesize and will not immediately set an Os Error. This is actually a bit undefined I guess.
Title: Re: Exception in TFileVersionInfo.ReadFileInfo
Post by: lucamar on November 12, 2018, 04:57:57 pm
According to Microsoft's doc (https://docs.microsoft.com/en-us/windows/desktop/api/winver/nf-winver-getfileversioninfosizew):

Quote
If the function fails, the return value is zero. To get extended error information, call GetLastError.

The most probable cause for the function to fail is that the executable has no version info but there are others: You should check the error info and act in consequence.
Title: Re: Exception in TFileVersionInfo.ReadFileInfo
Post by: Thaddy on November 12, 2018, 05:05:21 pm
The most probable cause for the function to fail is that the executable has no version info but there are others: You should check the error info and act in consequence.
Which it doesn't properly. Right? It should only raise if there is actually an error.
Title: Re: Exception in TFileVersionInfo.ReadFileInfo
Post by: OLLI_S on November 17, 2018, 05:30:45 pm
Hello,

According to Microsoft's doc (https://docs.microsoft.com/en-us/windows/desktop/api/winver/nf-winver-getfileversioninfosizew):

Quote
If the function fails, the return value is zero. To get extended error information, call GetLastError.

The most probable cause for the function to fail is that the executable has no version info but there are others: You should check the error info and act in consequence.

I am not a good programmer, what must be changed in ReadVersionInfo?

Best regards

OLLI
Title: Re: Exception in TFileVersionInfo.ReadFileInfo
Post by: ASerge on November 17, 2018, 05:57:06 pm
I am not a good programmer, what must be changed in ReadVersionInfo?
Nothing.
There may be several reasons why version information is not available. Simply does not exist, or no access, etc. So that generation of errors and transfer to a higher code - the right thing. If you do not want to show this to the user - catch, output to the log, and show the user that there is no data.
Title: Re: Exception in TFileVersionInfo.ReadFileInfo
Post by: OLLI_S on November 17, 2018, 06:30:48 pm
Hello Aserge,

I added a try ... except block:

Code: Pascal  [Select][+][-]
  1.     try
  2.       // Create the Version Information
  3.       VerInfo := TVersionInfo.Create(AppFileName);
  4.     except
  5.       // will only be executed in case of an exception
  6.       on E: Exception do begin
  7.         ShowMessage( 'Error: '+ E.ClassName + #13#10 + E.Message );
  8.         VerInfo.Free;
  9.         exit;
  10.       end;
  11.     end;  

When I select an application that causes problems I see the error message (as expected).
But I also get an Access-Violation message.

So maybe I have to add the try block in TVersionInfo.ReadVersionInfo ?
Or should I modify TVersionInfo.ReadVersionInfo so when the size is zero, that I don't call RaiseLastOSError but show a message and exit?

OLLI
Title: Re: Exception in TFileVersionInfo.ReadFileInfo
Post by: lucamar on November 17, 2018, 07:29:35 pm
When I select an application that causes problems I see the error message (as expected).
But I also get an Access-Violation message.

So maybe I have to add the try block in TVersionInfo.ReadVersionInfo ?
Or should I modify TVersionInfo.ReadVersionInfo so when the size is zero, that I don't call RaiseLastOSError but show a message and exit?

The access violation probably comes from the fact that since, indirectly, ReadVersionInfo is called from the constructor, an exception will prevent the constructor from finalizing, so your call to VerInfo.Free catchs the object at an ... "incovenient" moment :)

To avoid it, you have some options but the best, IMHO, is to modify how TVersionInfo works. The needed changes are rather few and small:


The basic effect of the changes is to defer doing anything until it is actually needed, instead of proactively doing it as soon as possible; which incidentally prevents raising exceptions until we are actually ready to catch them. Just a question of tastes ;D

ETA Incidentally, if I were you I wouldn't call that class TVersionInfo but TAltVersionInfo or something like that, since there is already a class TVersionInfo in the Fileinfo unit.
Title: Re: Exception in TFileVersionInfo.ReadFileInfo
Post by: ASerge on November 17, 2018, 08:39:16 pm
I added a try ... except block:
Code: Pascal  [Select][+][-]
  1.     try
  2.       // Create the Version Information
  3.       VerInfo := TVersionInfo.Create(AppFileName);
  4.     except
  5.       // will only be executed in case of an exception
  6.       on E: Exception do begin
  7.         ShowMessage( 'Error: '+ E.ClassName + #13#10 + E.Message );
  8.         VerInfo.Free;
  9.         exit;
  10.       end;
  11.     end;  

When I select an application that causes problems I see the error message (as expected).
But I also get an Access-Violation message.
Bad code: release object not in finally, but in the except block. When an exception occurs, the constructor calls the destructor and then you use Free again.
Use right construction:
Code: Pascal  [Select][+][-]
  1. try
  2.   VerInfo := TVersionInfo.Create(AppFileName);
  3.   try
  4.     // Do something with version
  5.   finally
  6.     VerInfo.Free;
  7.   end;
  8. except
  9.   on E: Exception do
  10.     ShowMessage( 'Error: '+ E.Message);
  11. end;
Title: Re: Exception in TFileVersionInfo.ReadFileInfo
Post by: OLLI_S on February 10, 2019, 02:37:28 pm
Hello ASerge,

Bad code: release object not in finally, but in the except block. When an exception occurs, the constructor calls the destructor and then you use Free again.
Use right construction:
Code: Pascal  [Select][+][-]
  1. try
  2.   VerInfo := TVersionInfo.Create(AppFileName);
  3.   try
  4.     // Do something with version
  5.   finally
  6.     VerInfo.Free;
  7.   end;
  8. except
  9.   on E: Exception do
  10.     ShowMessage( 'Error: '+ E.Message);
  11. end;

Thank you for your correction of the code.
I now catch the exception and show an Information-Message.
So the problem is solved.

Best regards

OLLI
TinyPortal © 2005-2018