AutoHotkey Community

It is currently May 27th, 2012, 11:57 am

All times are UTC [ DST ]




Post new topic Reply to topic  [ 23 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: FileGetVersionInfo_AW()
PostPosted: November 23rd, 2008, 7:56 pm 
Offline
User avatar

Joined: December 26th, 2005, 4:40 pm
Posts: 8776
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:
Code:
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
  FileDescription
  FileVersion
  InternalName
  LegalCopyright
  OriginalFilename
  ProductName
  ProductVersion
  CompanyName
  PrivateBuild
  SpecialBuild
  LegalTrademarks
)

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



    Code Update:

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


Last edited by SKAN on May 28th, 2011, 6:32 pm, edited 1 time in total.

Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: October 27th, 2010, 5:28 am 
Offline
User avatar

Joined: December 26th, 2005, 4:40 pm
Posts: 8776
* 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 )

Code:
FileGetVersionInfo( peFile="", StringFileInfo="" ) { ; OBSOLETE
 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"


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: October 28th, 2010, 1:39 pm 
Offline

Joined: October 17th, 2006, 4:15 pm
Posts: 7503
Location: Australia
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".)


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 4th, 2011, 6:14 am 
Offline

Joined: January 7th, 2009, 1:08 am
Posts: 66
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!


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 4th, 2011, 8:22 am 
Offline
User avatar

Joined: December 26th, 2005, 4:40 pm
Posts: 8776
Quote:
How would i be able to specify only one file?


Note: A_AhkPath contains the full path to AutoHotkey.exe

Code:
StringFileInfo=
( LTrim
  FileDescription
  FileVersion
  InternalName
  LegalCopyright
  OriginalFilename
  ProductName
  ProductVersion
  CompanyName
  PrivateBuild
  SpecialBuild
  LegalTrademarks
)

VI := FileGetVersionInfo_AW( A_AhkPath, StringFileInfo, "`n"  )
MsgBox, 64, %A_AhkPath%, %VI%


Note that I have used linefeed ( `n ) as the delimiter.
The function returns the result as a TABLE, like below:
Code:
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()

Quote:
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.

Quote:
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

Code:
PEFile := "C:\Windows\Explorer.exe"

VI := FileGetVersionInfo_AW( PEFile, "FileVersion|FileDescription|LegalCopyRight", "|" )

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

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


Report this post
Top
 Profile  
Reply with quote  
PostPosted: May 24th, 2011, 4:23 pm 
Offline

Joined: October 27th, 2008, 12:25 pm
Posts: 24
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


Report this post
Top
 Profile  
Reply with quote  
PostPosted: May 24th, 2011, 8:47 pm 
Offline
User avatar

Joined: December 26th, 2005, 4:40 pm
Posts: 8776
pacifika wrote:
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?


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 25th, 2011, 12:51 pm 
Offline

Joined: October 27th, 2008, 12:25 pm
Posts: 24
Code:
FileSelectFile,PEFile
VI := FileGetVersionInfo_AW( PEFile, "FileVersion|FileDescription|LegalCopyRight", "|" )

A_LastError returns 1813

msdn says
Quote:
]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


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 25th, 2011, 1:11 pm 
Offline

Joined: October 27th, 2008, 12:25 pm
Posts: 24
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


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 25th, 2011, 1:36 pm 
Offline

Joined: October 27th, 2008, 12:25 pm
Posts: 24
pacifika wrote:
Adding:
Code:
   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


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 25th, 2011, 8:13 pm 
Offline
User avatar

Joined: December 26th, 2005, 4:40 pm
Posts: 8776
I need a sample. Can you please compile a one-liner like:
Code:
MsgBox

and provide me the executable?.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 25th, 2011, 9:11 pm 
Offline
User avatar

Joined: March 19th, 2008, 12:43 am
Posts: 5482
Location: the tunnel(?=light)
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:

Code:
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:

Code:
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.

_________________
Image
Try Quick Search for Autohotkey or see the tutorial for newbies.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 26th, 2011, 10:27 am 
Offline
User avatar

Joined: December 26th, 2005, 4:40 pm
Posts: 8776
Thanks sinkfaze. I like the latter version. :)


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 26th, 2011, 12:44 pm 
Offline

Joined: October 27th, 2008, 12:25 pm
Posts: 24
SKAN wrote:
I need a sample. Can you please compile a one-liner like:
Code:
MsgBox

and provide me the executable?.

Sorry, of course:
getversion-test.zip

_________________
Cheers


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 27th, 2011, 11:20 am 
Offline
User avatar

Joined: December 26th, 2005, 4:40 pm
Posts: 8776
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"

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.


Report this post
Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 23 posts ]  Go to page 1, 2  Next

All times are UTC [ DST ]


Who is online

Users browsing this forum: notsoobvious, rrhuffy and 23 guests


You can post new topics in this forum
You can reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Powered by phpBB® Forum Software © phpBB Group