Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

FileGetVersionInfo_AW()


  • Please log in to reply
22 replies to this topic
SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005
FileGetVersionInfo_AW()

Extracts and returns a single or multiple strings of version information from an executable file.
AHK's FileGetVersion command will fetch you the FileVersion, but there are more - that this function would fetch.
Following are Windows Standard Names:
[*:1giapuu9]Comments
[*:1giapuu9]CompanyName
[*:1giapuu9]FileDescription
[*:1giapuu9]FileVersion
[*:1giapuu9]InternalName
[*:1giapuu9]LegalCopyright
[*:1giapuu9]LegalTrademarks
[*:1giapuu9]OriginalFilename
[*:1giapuu9]PrivateBuild
[*:1giapuu9]ProductName
[*:1giapuu9]ProductVersion
[*:1giapuu9]SpecialBuild



Credit : [::script::GetFullVersionInfo] string version info for execs by wOxxOm
Reference : How to extract version information using the Windows API Peter D Johnson

VersionInfo of AutoHotkey_L as seen from ResHacker
Posted Image



Function along with example calls:
FileGetVersionInfo_AW( peFile="", StringFileInfo="", Delimiter="|") {    ; Written by SKAN
 ; www.autohotkey.com/forum/viewtopic.php?t=64128          CD:24-Nov-2008 / LM:28-May-2010
 Static CS, HexVal, Sps="                        ", DLL="Version\"
 If ( CS = "" )
  CS := A_IsUnicode ? "W" : "A", HexVal := "msvcrt\s" (A_IsUnicode ? "w": "" ) "printf"
 If ! FSz := DllCall( DLL "GetFileVersionInfoSize" CS , Str,peFile, UInt,0 )
   Return "", DllCall( "SetLastError", UInt,1 )
 VarSetCapacity( FVI, FSz, 0 ), VarSetCapacity( Trans,8 * ( A_IsUnicode ? 2 : 1 ) )
 DllCall( DLL "GetFileVersionInfo" CS, Str,peFile, Int,0, UInt,FSz, UInt,&FVI )
 If ! DllCall( DLL "VerQueryValue" CS
    , UInt,&FVI, Str,"\VarFileInfo\Translation", UIntP,Translation, UInt,0 )
   Return "", DllCall( "SetLastError", UInt,2 )
 If ! DllCall( HexVal, Str,Trans, Str,"%08X", UInt,NumGet(Translation+0) )
   Return "", DllCall( "SetLastError", UInt,3 )
 Loop, Parse, StringFileInfo, %Delimiter%
 { subBlock := "\StringFileInfo\" SubStr(Trans,-3) SubStr(Trans,1,4) "\" A_LoopField
  If ! DllCall( DLL "VerQueryValue" CS, UInt,&FVI, Str,SubBlock, UIntP,InfoPtr, UInt,0 )
    Continue
  Value := DllCall( "MulDiv", UInt,InfoPtr, Int,1, Int,1, "Str"  )
  Info  .= Value ? ( ( InStr( StringFileInfo,Delimiter ) ? SubStr( A_LoopField Sps,1,24 )
        .  A_Tab : "" ) . Value . Delimiter ) : ""
} StringTrimRight, Info, Info, 1
Return Info
}

#SingleInstance, Force
SetBatchLines -1

Loop, %A_WinDir%\System32\*.??l
  Files .= "`n" A_LoopFileLongPath
Files := A_AhkPath . Files

StringFileInfo=
( LTrim
[color=#800000]  FileDescription
  FileVersion
  InternalName
  LegalCopyright
  OriginalFilename
  ProductName
  ProductVersion
  CompanyName
  PrivateBuild
  SpecialBuild
  LegalTrademarks
[/color])

Loop, Parse, Files, "`n"
  If VI := FileGetVersionInfo_AW( A_LoopField, StringFileInfo, "`n"  )
    MsgBox, 64, %A_LoopField%, %VI%



Code Update:

Edit: 28-May-2011
[*:1giapuu9]When a single item is requested, the function returns the value directly instead of a table row. Thanks to HotKeyIt for the suggestion.
[*:1giapuu9]StrGet() has been removed. MulDiv() retrieves text properly in both ( ansi/unicode ) versions properly.



SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005
* Bump *

Function Split from AHK Functions
Function updated to return multiple strings from a single call.
Function updated to run in 32bit Unicode build.
Previous code below ( for comparison )

FileGetVersionInfo( peFile="", StringFileInfo="" ) { ; [color=red]OBSOLETE[/color]
 FSz := DllCall( "Version\GetFileVersionInfoSizeA",Str,peFile,UInt,0 )
 IfLess, FSz,1, Return -1
 VarSetCapacity( FVI, FSz, 0 ), VarSetCapacity( Trans,8 )
 DllCall( "Version\GetFileVersionInfoA", Str,peFile, Int,0, Int,FSz, UInt,&FVI )
 If ! DllCall( "Version\VerQueryValueA", UInt,&FVI, Str,"\VarFileInfo\Translation"
                                       , UIntP,Translation, UInt,0 )
   Return -2
 If ! DllCall( "msvcrt.dll\sprintf", Str,Trans, Str,"%08X", UInt,NumGet(Translation+0) )
   Return -4
 subBlock := "\StringFileInfo\" SubStr(Trans,-3) SubStr(Trans,1,4) "\" StringFileInfo
 If ! DllCall( "Version\VerQueryValueA", UInt,&FVI, Str,SubBlock, UIntP,InfoPtr, UInt,0 )
   Return
 VarSetCapacity( Info, DllCall( "lstrlen", UInt,InfoPtr ) )
 DllCall( "lstrcpy", Str,Info, UInt,InfoPtr )
Return Info
}

SetBatchLines -1
Loop, %A_WinDir%\System32\*.??l
  Files .= "|" A_LoopFileLongPath
Files := A_AhkPath . Files

Loop, Parse, Files, |
  MsgBox, 0, % (PeFile:=A_LoopField)
  , % "FileDescription      `t:`t" FileGetVersionInfo( PeFile, "FileDescription"  ) "`n"
    . "FileVersion          `t:`t" FileGetVersionInfo( PeFile, "FileVersion"      ) "`n"
    . "InternalName         `t:`t" FileGetVersionInfo( PeFile, "InternalName"     ) "`n"
    . "LegalCopyright       `t:`t" FileGetVersionInfo( PeFile, "LegalCopyright"   ) "`n"
    . "OriginalFilename     `t:`t" FileGetVersionInfo( PeFile, "OriginalFilename" ) "`n"
    . "ProductName          `t:`t" FileGetVersionInfo( PeFile, "ProductName"      ) "`n"
    . "ProductVersion       `t:`t" FileGetVersionInfo( PeFile, "ProductVersion"   ) "`n`n`n"
    . "CompanyName          `t:`t" FileGetVersionInfo( PeFile, "CompanyName"      ) "`n"
    . "PrivateBuild         `t:`t" FileGetVersionInfo( PeFile, "PrivateBuild"     ) "`n"
    . "SpecialBuild         `t:`t" FileGetVersionInfo( PeFile, "SpecialBuild"     ) "`n"
    . "LegalTrademarks      `t:`t" FileGetVersionInfo( PeFile, "LegalTrademarks"  ) "`n"


Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
I didn't realise until seeing your post (today) that the format of the FileVersion/ProductVersion string isn't restricted. They could well include the AutoHotkey_L revision or similar. Unfortunately this realisation is too late as I intend to change the version numbering in the next L release.

Experimenting with alternative formats, I noticed an oddity in Windows 7: the "Details" tab of the executable shows File version as "1.0.48.5" (apparently a standard format based on the version number stored in the PE header) and Product version as "1, 0, 48, 05" (clearly the string defined in the StringFileInfo block). I wonder why they didn't use the FileVersion string.

Anyway, one benefit of your function over FileGetVersion is that it returns the version number in the format intended by the developer of the application; e.g. "1.0.48.05" vs "1.0.48.5". (Or it would if Chris hadn't used the odd format "1, 0, 48, 05".)

blondedude092
  • Members
  • 77 posts
  • Last active: Oct 15 2015 04:51 PM
  • Joined: 07 Jan 2009
This script works great!, but seems i can't adapt it to my needs, lack of my knowledge...

How would i be able to specify only one file?
How would i be able to get this info from any file type?
and how would i save each one into its own variable?

if possible an example would be great!

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005

How would i be able to specify only one file?


Note: A_AhkPath contains the full path to AutoHotkey.exe

[color=indigo]StringFileInfo[/color]=
( LTrim
  FileDescription
  FileVersion
  InternalName
  LegalCopyright
  OriginalFilename
  ProductName
  ProductVersion
  CompanyName
  PrivateBuild
  SpecialBuild
  LegalTrademarks
)

[color=darkred]VI[/color] := FileGetVersionInfo_AW( A_AhkPath, [color=indigo]StringFileInfo[/color], "`n"  )
MsgBox, 64, %A_AhkPath%, %[color=darkred]VI[/color]%

Note that I have used linefeed ( `n ) as the delimiter.
The function returns the result as a TABLE, like below:
FileDescription         	AutoHotkey
FileVersion             	1, 0, 48, 05
InternalName            	AutoHotkey
LegalCopyright          	Copyright (C) 2009
OriginalFilename        	AutoHotkey.rc
ProductName             	AutoHotkey
ProductVersion          	1, 0, 48, 05

The left column is Variable name and the Right Column is the Value.
The columns are always separated with TAB ( `t )
The rows are separated with a delimiter character you specify, which in this example is linefeed.
To read the values from the table, you can either use RegEx or StrX()

would i be able to get this info from any file type?


Unfortunately, the answer is no.
The function is limited to Portable Executables, shortly know as PE.

how would i save each one into its own variable?


I have already suggested RegEx/StrX(), above... But the following is my favorite method.
Note that I am using Pipe ( | ) to delimit the rows

PEFile := "[color=red]C:\Windows\Explorer.exe[/color]"

VI := FileGetVersionInfo_AW( PEFile, "FileVersion[color=red]|[/color]FileDescription[color=red]|[/color]LegalCopyRight", "[color=red]|[/color]" )

; Parse the table and assign Variables ( From Column 1 ) with values ( From Column 2 )
Loop, Parse, VI, [color=red]|[/color]%A_Tab%, %A_Space%
 A_Index & 1 ? ( _ := A_LoopField ) : ( %_% := A_LoopField )

MsgBox, % FileVersion
MsgBox, % FileDescription
MsgBox, % LegalCopyRight


pacifika
  • Members
  • 26 posts
  • Last active: Dec 19 2012 12:01 AM
  • Joined: 27 Oct 2008
Hi SKAN great piece of code and just what I need. I'm writing my own patcher using diff/bsdiff and autohotkey.

However, when I take your last example code and point it to my ahk compiled exe I get empty message boxes (it works for explorer.exe). the details pane of the file properties in windows show entries for fileversion productversion etc so I know they're there, I just can't retrieve them.

I noticed my locale is 2057
Resource Hacker Screendump

How to debug this?
Cheers

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005

when I take your last example code


Which one exactly?. Please show me your call in a single line of code.
Also, did you check A_LastError? What is the error value?

pacifika
  • Members
  • 26 posts
  • Last active: Dec 19 2012 12:01 AM
  • Joined: 27 Oct 2008
FileSelectFile,PEFile
VI := FileGetVersionInfo_AW( PEFile, "FileVersion|FileDescription|LegalCopyRight", "|" )
A_LastError returns 1813

msdn says

]ERROR_RESOURCE_TYPE_NOT_FOUND
1813 (0x715)
The specified resource type cannot be found in the image file.


Windows 7 64bit. It seems to have something to do with the Compile_AHK option I use, as when I compile my script regularly there is no problem.
Cheers

pacifika
  • Members
  • 26 posts
  • Last active: Dec 19 2012 12:01 AM
  • Joined: 27 Oct 2008
Downloaded latest version of Compile_AHK but the problem remains although it might be a combination of two bugs.

When I set the locale to English_UnitedStates (which is locale 1033) the locale of the executable produced by Compile_AHK according to ResourceHacker still uses locale 2057.

It looks like Locale 2057 does not get picked up correctly by some call in your script and therefore returns the error 1813.

When I manually replace 2057 with 1033 in Resource Hacker then your script picks up all information fine. However because of the bug above I can't automate it.
Cheers

pacifika
  • Members
  • 26 posts
  • Last active: Dec 19 2012 12:01 AM
  • Joined: 27 Oct 2008

Adding:

VersionInfo = 1033
to line 2122 (just before -addoverwrite of the version info) of Compile_AHK_0.9.0.58 sourcecode does not change the versionInfo reported by ResourceHacker but makes the executables work correctly with FileGetVersionInfo_AW()


Cheers

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005
I need a sample. Can you please compile a one-liner like:
MsgBox
and provide me the executable?.

sinkfaze
  • Moderators
  • 6367 posts
  • Last active: Nov 30 2018 08:50 PM
  • Joined: 18 Mar 2008
Just saw this now, SKAN, very nice. I did a couple of rewrites of the function for a little extra convenience for AHK_L users. The first omits the last two parameters and retrieves all attributes of a requested file to an object, where you can choose which attribute(s) you would like to see:

Loop, %A_WinDir%\System32\*.??l
	MsgBox %	FileGetVersionInfo_AW(A_LoopFileLongPath).InternalName

FileGetVersionInfo_AW( peFile="" ) {	; Written by SKAN
	; www.autohotkey.com/forum/viewtopic.php?p=233188#233188  CD:24-Nov-2008 / LM:27-Oct-2010
	Static	CS, HexVal, Sps="                        ", DLL="Version\", StrGet="StrGet"
	Static	fInfo :=	["FileDescription","FileVersion","InternalName","LegalCopyright","OriginalFilename","ProductName","ProductVersion"
		 ,"CompanyName","PrivateBuild","SpecialBuild","LegalTrademarks"]
	If	!CS
		CS :=	A_IsUnicode ? "W" : "A", HexVal :=	"msvcrt\s" (A_IsUnicode ? "w": "" ) "printf"

	If	!FSz :=	DllCall( DLL "GetFileVersionInfoSize" CS , Str,peFile, UInt,0 )
		Return	"", DllCall( "SetLastError", UInt,1 )

	VarSetCapacity( FVI, FSz, 0 ), VarSetCapacity( Trans,8 * ( A_IsUnicode ? 2 : 1 ) )
	DllCall( DLL "GetFileVersionInfo" CS, Str,peFile, Int,0, UInt,FSz, UInt,&FVI )
	If	!DllCall( DLL "VerQueryValue" CS, UInt,&FVI, Str,"\VarFileInfo\Translation", UIntP,Translation, UInt,0 )
		Return	"", DllCall( "SetLastError", UInt,2 )

	If	!DllCall( HexVal, Str,Trans, Str,"%08X", UInt,NumGet(Translation+0) )
		Return	"", DllCall( "SetLastError", UInt,3 )
	res :=	{}
	For each, attr in fInfo
	{
		subBlock :=	"\StringFileInfo\" SubStr(Trans,-3) SubStr(Trans,1,4) "\" attr
		If	!DllCall( DLL "VerQueryValue" CS, UInt,&FVI, Str,SubBlock, UIntP,InfoPtr, UInt,0 )
			Continue

		if	Value :=	( A_IsUnicode ? %StrGet%( InfoPtr, DllCall( "lstrlen" CS, UInt,InfoPtr ) )
		 :  DllCall( "MulDiv", UInt,InfoPtr, Int,1, Int,1, "Str"  ) )
			res.Insert(attr,Value)
	}
	Return	res
}

The other leaves the stringfileinfo parameter as variadic and omits the delimiter parameter, so the user can select the file attributes they'd like to see, which are returned in an object:

Loop, %A_WinDir%\System32\*.??l
{
	f :=	FileGetVersionInfo_AW(A_LoopFileLongPath,"FileVersion","InternalName","LegalCopyRight")
	MsgBox %	f.FileVersion "`n" f.InternalName "`n" f.LegalCopyRight
}

FileGetVersionInfo_AW( peFile="", params*) {	; Written by SKAN
	; www.autohotkey.com/forum/viewtopic.php?p=233188#233188  CD:24-Nov-2008 / LM:27-Oct-2010
	Static	CS, HexVal, Sps="                        ", DLL="Version\", StrGet="StrGet"
	If	!CS
		CS :=	A_IsUnicode ? "W" : "A", HexVal :=	"msvcrt\s" (A_IsUnicode ? "w": "" ) "printf"

	If	!FSz :=	DllCall( DLL "GetFileVersionInfoSize" CS , Str,peFile, UInt,0 )
		Return	"", DllCall( "SetLastError", UInt,1 )

	VarSetCapacity( FVI, FSz, 0 ), VarSetCapacity( Trans,8 * ( A_IsUnicode ? 2 : 1 ) )
	DllCall( DLL "GetFileVersionInfo" CS, Str,peFile, Int,0, UInt,FSz, UInt,&FVI )
	If	!DllCall( DLL "VerQueryValue" CS, UInt,&FVI, Str,"\VarFileInfo\Translation", UIntP,Translation, UInt,0 )
		Return	"", DllCall( "SetLastError", UInt,2 )

	If	!DllCall( HexVal, Str,Trans, Str,"%08X", UInt,NumGet(Translation+0) )
		Return	"", DllCall( "SetLastError", UInt,3 )
	res :=	{}
	For each, attr in params
	{
		subBlock :=	"\StringFileInfo\" SubStr(Trans,-3) SubStr(Trans,1,4) "\" attr
		If	!DllCall( DLL "VerQueryValue" CS, UInt,&FVI, Str,SubBlock, UIntP,InfoPtr, UInt,0 )
			Continue

		if	Value :=	( A_IsUnicode ? %StrGet%( InfoPtr, DllCall( "lstrlen" CS, UInt,InfoPtr ) )
		 :  DllCall( "MulDiv", UInt,InfoPtr, Int,1, Int,1, "Str"  ) )
			res.Insert(attr,Value)
	}
	Return	res
}

Hopefully someone finds them useful, and if you see any errors in the implementation please let me know.

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005
Thanks sinkfaze. I like the latter version. :)

pacifika
  • Members
  • 26 posts
  • Last active: Dec 19 2012 12:01 AM
  • Joined: 27 Oct 2008

I need a sample. Can you please compile a one-liner like:

MsgBox
and provide me the executable?.

Sorry, of course:
getversion-test.zip
Cheers

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005
The is no bug in my code.
StringFileInfo and VarFileInfo should agree on Translation.
In your case, you need to alter BLOCK "StringFileInfo",
That is: BLOCK "040904b0" to BLOCK "080904b0"

Posted Image

Note:
ResHacker parses a PE file in RAW mode, i.e., it does not depend on MS API, so it can display broken info.
For example: You can add a named resource in lowercase with ResHacker, but FindResource API will not be able to find it.
Likewise, if you UPX compress a PE, LoadResource will load the data, but ResHacker cannot.