Skip to content
⌘ NSIS Forum Archive

Reading PE version info strings: Need another pair of eyes..

10 posts

sbabineau#edited

Reading PE version info strings: Need another pair of eyes..

I've been racking my brain for a couple days on this one function.

The purpose is to read the file info strings from an executable (or dll, ocx, etc). Not all executables have them, but if you right-click on an executable, select Properties, then click the Version tab (if it's there) you will see the information I am trying to retrieve.

Once fixed, I think it makes a good candidate for a NSIS Example wiki page..

Anyway, I'm messing with the stack and the System plugin and it is wreaking havoc on my program throwing spurious "invalid opcode" and "Access denied" (while reading a memory address). Can someone else take a look at this with me and help me figure it out?

I think the key to fixing the problem is near the "System::Call "*$4(&t$5 .r7)"" line. When I comment that out all my problems seem to disappear. What am I doing wrong? 😠


Var PROGRAMINFO

; Call like this:
; !insertmacro GetInfoFromExeHeader "ProductName" "c:\programs\myexecutable.exe"
!macro GetInfoFromExeHeader DataType ProgramExe
Push ${DataType}
Push ${ProgramExe}
Call GetInfoFromExeHeader
Pop $PROGRAMINFO
!macroend

Function GetInfoFromExeHeader
; Valid Data Types:
; ProductName
; InternalName
; ProductVersion
; SpecialBuild
; PrivateBuild
; LegalCopyright
; LegalTrademarks
; Comments
; CompanyName
; FileVersion
; FileDescription

Exch $R0 ; path to EXE
IfFileExists $R0 +4 0
MessageBox MB_OK "$R0 doesn't exist! Please correct this."
Exch $R0 ; Put the path back on the stack
Return

Exch
Exch $R1 ; Data type

Push $1
Push $2
Push $3
Push $4
Push $5
Push $6
Push $7

StrCpy $1 "0"
StrCpy $3 "0"
StrCpy $7 ""
StrCpy $2 "0"

System::Call "version::GetFileVersionInfoSize(t '$R0', t /NUL) i .r2" ;returns size of version block in header
IntCmp $2 0 Done Done 0 ; $2 needs to be greater than 0
System::Call "version::GetFileVersionInfo(t '$R0', i 0, i r2, *i .r3) i .r1" ; returns 1 or 0 with r3 being the fileinfo struct
IntCmp $1 0 Done 0 0 ; $1 needs to be <> 0
System::Call "version::VerQueryValue(*i r3, t '\StringFileInfo\040904E4\$R1', *i .r4, *i .r5) i .r6"
IntCmp $6 0 Done 0 0 ; $6 needs to be <> 0
IntCmp $5 0 Done Done 0
;IntOp $5 $5 + 1
System::Call "*$4(&t$5 .r7)"

Done:
System::Free $R0
System::Free $R1
System::Free $1
System::Free $2
System::Free $3
System::Free $4
System::Free $5
System::Free $6

;SetPluginUnload "manual"
Push $7

Exch 9 ; put $7 on the bottom
Pop $7
Pop $6
Pop $5
Pop $4
Pop $3
Pop $2
Pop $1
Pop $R0
Pop $R1
FunctionEnd
Documentation on the three version functions:

GetFileVersionInfoSize: http://msdn2.microsoft.com/en-us/library/ms647005.aspx

GetFileVersionInfo: http://msdn2.microsoft.com/en-us/library/ms647003.aspx

VerQueryValue: http://msdn2.microsoft.com/en-us/library/ms647464.aspx

-Steve.
Red Wine#
Have you seen the MoreInfo plugin? It's so easy 😉

kichik#
  1. File path may contain quotes, so it's better to use R0 instead of '$R0' when passing it to System::Call.
  2. /NUL is not a valid parameter. Either use n for NULL or pass a real pointer.
  3. You must allocate a buffer for GetFileVersionInfo with System::Alloc. You just passed it pointer to an integer which it fills and then some. That's corrupting the heap.

Once fixed, I think it makes a good candidate for a NSIS Example wiki page..
Everything makes a good candidate for that. Feel free to create Wiki pages with what you'd consider useful information.
stass#
Why MoreInfo plugin gives false information in the file vcredist_x86.exe (MicrosoftVisual C ++ 2012 Redistributable) ?
Распространяемый пакет VC; Visual Studio; распространяемый пакет Visual C++ для Visual Studio 2012; VC ++


!addplugindir .
OutFile 'MoreInfo-test.exe'
var a
var b
Section
strcpy $1 "$EXEDIR\vcredist_x86.exe"
  MoreInfo::GetProductName "$1"
  Pop $a
  StrCpy $0 "$0$\r$\n - ProductName: $a"
  
  MoreInfo::GetFileVersion "$1"
  Pop $b
  StrCpy $0 "$0$\r$\n - GetFileVersion: $b"
MessageBox MB_OK|MB_ICONINFORMATION "$0"
SectionEnd 


How to fix it?

question 2 :

How to "make friends" "FileVerInfo.nsh" and "FileFunc.nsh" ?
When used together, getting the error ...
Anders#
Originally Posted by stass View Post
Why MoreInfo plugin gives false information in the file vcredist_x86.exe (MicrosoftVisual C ++ 2012 Redistributable) ?
How to fix it?

question 2 :

How to "make friends" "FileVerInfo.nsh" and "FileFunc.nsh" ?
When used together, getting the error ...
Are you mixing Unicode and Ansi installers and plugins? Are you using NSIS 3? Please provide more information when asking questions.

What is FileVerInfo.nsh and where did you download it?
stass#
Anders
I thought it was your product ...

Incompatibility disappeared when commenting line 97
Problem with MoreInfo plugin - remained. NSIS 3b. With encodings - all right.
Anders#
I'm guessing there can be 4 reasons why it is not working:

1) The version info in that .exe is broken/corrupt (Unlikely)
2) A bug in the plugin
3) A bug in NSIS
4) Programmer error; mixing Ansi/Unicode compiler/plugins (This is my guess)

To find the answer I would have to be able to reproduce the problem and since I don't want to waste too much time on this I would need some extra pieces of information:

1) Exact NSIS version (And is it a special build?)
2) Exact plugin version (The wiki has two zip files, one of the zip files has both Ansi and Unicode builds)
3) The value of the Unicode and RequestExecutionLevel attributes used in your script

And as a side note; a Unicode string displayed by Ansi code/functions will end up as a single character most of the time and this is a strong indication that you are mixing Ansi/Unicode stubs/plugins. Ansi strings displayed by Unicode code/functions often looks like Chinese (CJK codepoints).
stass#edited
Anders

I have installed the regular NSIS - nsis-3.0b1-setup.exe

I work with ANSI.

Accordingly, use ansi version MoreInfo plugin: \ Program Files \ NSIS \ Plugins \ x86-ansi \ MoreInfo.dll
( http://www.fdos.org/win32/nsis/plugi...fo.1.0.1.2.zip )

Script - simple as in Example .

In determining the version of the file vcredist_x86.exe - getting only the first character .

FileVerInfo.nsh determines the version of the file vcredist_x86.exe - right ! ( But only version right ... Everything else is nonsense ... )



It turns out that we have no means to obtain information about a file? Because the available plug-ins do not work with all the files ... This is very bad ...
Anders#
There might be something strange with the version info in that .exe after all!

Outfile "$%Temp%\Test.exe"
Unicode false
RequestExecutionLevel user

!include LogicLib.nsh

Function GetFileVerFirstLangProductVersion
... http://pastebin.com/f5e461c4d
FunctionEnd


!define THEFILE "$Temp\X\vcredist_x86.exe" #"$windir\explorer.exe"

Section

!if "${NSIS_CHAR_SIZE}" <= 1
!addplugindir "$%Temp%\X\MoreInfo\Plugins\Ansi"
MoreInfo::GetProductName "${THEFILE}"
Pop $0
DetailPrint |$0|
!endif

Push "${THEFILE}"
Call GetFileVerFirstLangProductVersion
Pop $0
DetailPrint |$0|

SectionEnd
This fails to read correctly when compiling as Ansi but works correctly when compiled as Unicode! Reading the version info from %windir%\explorer.exe works in both.

I'm not sure if VerQueryValueA is broken or if the .exe is not following the PE spec. Either way, this bug is not in NSIS...

You can work around it like this:
Function GetFileVerFirstNamedLangEntryOnWindowsNT
System::Store S
pop $3
pop $4
push "" ;failed ret
System::Call 'version::GetFileVersionInfoSizeW(w"$3",i.r2)i.r0'
${If} $0 <> 0
System::Alloc $0
System::Call 'version::GetFileVersionInfoW(w"$3",ir2,ir0,isr1)i.r0 ? e'
pop $2
${If} $0 <> 0
${AndIf} $2 = 0 ;a user comment on MSDN said you should check GLE to avoid crash
System::Call 'version::VerQueryValueW(i r1,w "\VarFileInfo\Translation",*i0r2,*i0)i.r0'
${If} $0 <> 0
System::Call '*$2(&i2.r2,&i2.r3)'
IntFmt $2 %04x $2
IntFmt $3 %04x $3
System::Call 'version::VerQueryValueW(i r1,w "\StringFileInfo\$2$3\$4",*i0r2,*i0r3)i.r0'
${If} $0 <> 0
pop $0
System::Call *$2(&w$3.s)
${EndIf}
${EndIf}
${EndIf}
System::Free $1
${EndIf}
System::Store L
FunctionEnd

Section
Push LegalCopyright
Push "${THEFILE}"
Call GetFileVerFirstNamedLangEntryOnWindowsNT
Pop $0
DetailPrint |$0|

Push FileDescription
Push "${THEFILE}"
Call GetFileVerFirstNamedLangEntryOnWindowsNT
Pop $0
DetailPrint |$0|
SectionEnd
...will not work on Win9x and is probably not a good idea in general, it would be better if we could figure out why the Ansi version fails...

If someone has a VM with Win95/98 it would be fun to see what Windows does there...
stass#
Anders, thank you very much for your help !
Now Function GetFileVerFirstNamedLangEntryOnWindowsNT works !