; Based on script from w0xx0m at http://www.autohotkey.com/forum/topic8618.html

; changelog :
; modified to extract ALL infos and put them in an object
; modified to support AutoHotkey_L (Unicode version)
; numeric info modified to extract X.X.X.X even if last is null
; added FileVersion and ProductVersion extraction formatted as X,X,X,X (for direct use in RC file)
; added BuildID extraction

oldErrMode:=DllCall("SetErrorMode", UInt,1) ; SEM_FAILCRITICALERRORS=0x0001

FileGetFullVer(_file, _FTypeRet="")
{
; Valid values for _FTypeRet are WC for "Windows Constants" and HR for "Human Readable", other values output values as BYTES

; *** Constants definitions ***
	; arrays for FileType as Windows Constants (WC) and Human Readable (HR)
	Static _dwFileTypeWC := Array()
		_dwFileTypeWC.Insert(0,"VFT_UNKNOWN","VFT_APP","VFT_DLL","VFT_DRV","VFT_FONT","VFT_VXD","N/A","VFT_STATIC_LIB")
	Static _dwFileTypeHR := Array()
		_dwFileTypeHR.Insert(0,"Unknown","Application","DLL","Driver","Font","VxD driver","N/A","Static Lib")

	; arrays for FileSubType for FONT as WC & HR
	Static _dwFileSubtypeWCFONT := ["VFT2_FONT_RASTER","VFT2_FONT_VECTOR","VFT2_FONT_TRUETYPE"]
	Static _dwFileSubtypeHRFONT := ["Raster","Vector","TrueType"]

	; arrays for FileSubType for DRV as WC & HR
	Static _dwFileSubtypeWCDRV := Array()
		_dwFileSubtypeWCDRV.Insert(0,"VFT2_UNKNOWN","VFT2_DRV_PRINTER","VFT2_DRV_KEYBOARD","VFT2_DRV_LANGUAGE","VFT2_DRV_DISPLAY","VFT2_DRV_MOUSE","VFT2_DRV_NETWORK","VFT2_DRV_SYSTEM","VFT2_DRV_INSTALLABLE","VFT2_DRV_SOUND","VFT2_DRV_COMM")
	Static _dwFileSubtypeHRDRV := Array()
		_dwFileSubtypeHRDRV.Insert(0,"Unknown","Printer","Keyboard","Language","Display","Mouse","Network","System","Installable","Sound","Comm.")

	; array of predefined version infos
	Static _PredefinedVI := ["Comments","CompanyName","FileDescription","FileVersion","InternalName","LegalCopyright","LegalTrademarks","OriginalFilename","ProductName","ProductVersion","PrivateBuild","SpecialBuild","LegalTrademarks1","LegalTrademarks2","BuildID"]

	Static _wordlength := 2* (A_IsUnicode ? 2 : 1)
	Static _dwordlength := 4* (A_IsUnicode ? 2 : 1)
; *** End of Constants definitions ***

	o_Version := Object()
	_fiSize:=DllCall("version\GetFileVersionInfoSize", Str, _file, UInt, 0)
	varSetCapacity(_fi,_fiSize,0)

	if !DllCall("version\GetFileVersionInfo", Str, _file, Int,0, UInt,_fiSize, UInt, &_fi)
		Return False
	if !DllCall("version\VerQueryValue", UInt,&_fi, Str,"\", UIntp,_fiFFI#, UIntp,0)
		Return False

	varSetCapacity(_fiFFI,13*_dwordlength)
	DllCall("RtlMoveMemory", UInt,&_fiFFI, UInt,_fiFFI#, UInt,13*4)
	o_Version.CommaFileVersion := NumGet(_fiFFI,10, "UShort") "," NumGet(_fiFFI,8, "UShort") "," NumGet(_fiFFI,14, "UShort") "," NumGet(_fiFFI,12, "UShort")
	o_Version.CommaProductVersion := NumGet(_fiFFI,18, "UShort") "," NumGet(_fiFFI,16, "UShort") "," NumGet(_fiFFI,22, "UShort") "," NumGet(_fiFFI,20, "UShort")
	o_Version.FileOS := Int2HexaPadding(NumGet(_fiFFI,32, "UInt"), 8) ; FileOS is always returned as DWORD
	o_Version.FileType := NumGet(_fiFFI,36, "UInt")
	o_Version.FileSubType := NumGet(_fiFFI,40, "UInt")

	If _FTypeRet
		{
		If o_Version.FileType = 3
			_suffix := "DRV"
		Else If o_Version.FileType = 4
			_suffix := "FONT"
		If _suffix
			o_Version.FileSubType := _dwFileSubtype%_FTypeRet%%_suffix%[o_Version.FileSubType]
		o_Version.FileType := _dwFileType%_FTypeRet%[o_Version.FileType]
		}
	Else
		{
		; returns values as BYTES
		o_Version.FileSubType := Int2HexaPadding(o_Version.FileSubType,2)
		o_Version.FileType := Int2HexaPadding(o_Version.FileType,2)
		}

	if !DllCall("version\VerQueryValue", UInt,&_fi, Str,"\StringFileInfo\", UIntp,_fiFFI#, UIntp,0)
		Return o_Version

	varSetCapacity(_fiLANGCODE,4*_wordlength)
	varSetCapacity(_fiCHARSET,4*_wordlength)
	DllCall("RtlMoveMemory", Ptr,&_fiLANGCODE, Ptr,_fiFFI#+6, Ptr,8)
	o_Version.LangCode := StrGet(&_fiLANGCODE)
	DllCall("RtlMoveMemory", Ptr,&_fiCHARSET, Ptr,_fiFFI#+14, Ptr,8)
	o_Version.CharSet := StrGet(&_fiCHARSET)

	_sSubBlock := "\StringFileInfo\" o_Version.LangCode o_Version.CharSet "\"

	For each, _item in _PredefinedVI
		{
		If !DllCall("version\VerQueryValue", UInt,&_fi, Str,_sSubBlock _item, UIntP,_fiValue#, UIntP,0)
			Continue
		o_Version.Insert(_item, StrGet( _fiValue#))
		}

	DllCall("version\VerQueryValue", UInt,&_fi, Str,"\VarFileInfo\Translation\", UIntp,_fiFFI#, UIntp,0)
	o_Version.VarFileInfoLangCode := Int2HexaPadding(NumGet(_fiFFI#+0, "UShort"),4)
	o_Version.VarFileInfoCharSet := Int2HexaPadding(NumGet(_fiFFI#+2, "UShort"),4)

Return o_Version
}

Int2HexaPadding(_integer, _pad)
{
	_result := ""
	SetFormat, IntegerFast, H
	_result .= _integer
	SetFormat, IntegerFast, D
Return HexaPadding(_result, _pad)
}

HexaPadding(_string, _pad)
{
	_padding := ""
	Loop % _pad - StrLen(_string) + 2
		_padding .= "0"
Return "0x" _padding SubStr(_string, 3)
}