BigDaddyO Posted July 18, 2006 Posted July 18, 2006 (edited) Hello,I have been searching for a while to find something that would give me the version information from microsoft patches, because I got burned too many times with Micro$ofts stupid way they release their patches. Some use Update.exe and some use msiexec.exe and since they use different switches for the silent install I have to know ahead of time and hard code the stupid switches during the install. This script would allow me to check the Installer Engine and automatically use the correct switches. I have tested the attached vbscript and modified it slightly to give me the Installer Engine from the Version tab in the file properties, but I can't figure out how to convert it to AutoIT.Could anybody give me a hand with this.PS. it uses Binary reads of the executable's header information to retrieve the data. File Props.txt is the VBscript.File Props.au3 is my feeble attempt at a conversion.edit: removed my feeble attempt since there is now a working AutoIT conversion.Thanks,Mike Edited July 19, 2006 by MikeOsdx
HardCopy Posted July 18, 2006 Posted July 18, 2006 Hello,I have been searching for a while to find something that would give me the version information from microsoft patches, because I got burned too many times with Micro$ofts stupid way they release their patches. Some use Update.exe and some use msiexec.exe and since they use different switches for the silent install I have to know ahead of time and hard code the stupid switches during the install. This script would allow me to check the Installer Engine and automatically use the correct switches. I have tested the attached vbscript and modified it slightly to give me the Installer Engine from the Version tab in the file properties, but I can't figure out how to convert it to AutoIT.Could anybody give me a hand with this.PS. it uses Binary reads of the executable's header information to retrieve the data. File Props.txt is the VBscript.File Props.au3 is my feeble attempt at a conversion.Thanks,MikeMikeHave you tried Simucals UDFin scripts n scrapsLinkworth a goHardCopy Contributions: UDF _DateYearFirstChildren are like Farts, you can just about stand your own.Why am I not a Vegetarian?...Well...my ancestors didn't fight & evolve to the Top of the food chain for me to survive on Salad
BigDaddyO Posted July 18, 2006 Author Posted July 18, 2006 (edited) MikeHave you tried Simucals UDFin scripts n scrapsLinkworth a goHardCopyI tried that first, but it only gives information from the General Tab. and I need info from the Version Tab. Unless I'm not seeing something? I upped the variable in his script to 70 outputs and it does not display the Installer Engine anywhere.Mike Edited July 18, 2006 by MikeOsdx
HardCopy Posted July 18, 2006 Posted July 18, 2006 (edited) I tried that first, but it only gives information from the General Tab. and I need info from the Version Tab. Unless I'm not seeing something? I upped the variable in his script to 70 outputs and it does not display the Installer Engine anywhere.MikeOk mike , Taken a look myselfSimucal parameters only go up to 34the info u seek is in 36 37 38 39so just use one of those numbers as the parameter and it should work.HardCopyEDIT *** ack...just reread your reply..... u tried thatI tested on the Autoit EXE and came back with version info Edited July 18, 2006 by HardCopy Contributions: UDF _DateYearFirstChildren are like Farts, you can just about stand your own.Why am I not a Vegetarian?...Well...my ancestors didn't fight & evolve to the Top of the food chain for me to survive on Salad
BigDaddyO Posted July 18, 2006 Author Posted July 18, 2006 I guess I edited my last post a little too late for you to read. I upped the variables in Simucals script to 70 and it does not display the Installer engine anywhere. 35 - 39 displays information but not what I need. Mike
HardCopy Posted July 18, 2006 Posted July 18, 2006 I guess I edited my last post a little too late for you to read. I upped the variables in Simucals script to 70 and it does not display the Installer engine anywhere. 35 - 39 displays information but not what I need.MikeThen thats me exhausted of ideas then.Hope you get the result you need from another forumner.RegardsHardCopy Contributions: UDF _DateYearFirstChildren are like Farts, you can just about stand your own.Why am I not a Vegetarian?...Well...my ancestors didn't fight & evolve to the Top of the food chain for me to survive on Salad
eltorro Posted July 18, 2006 Posted July 18, 2006 (edited) CMenu by mHz.CyberSlug did some work with Peid for Universal Silent Switch Finder.USSF Edited July 18, 2006 by eltorro Regards, [indent]ElTorro[/indent][font="Book"] Decide, Commit, Achieve[/font]_ConfigIO.au3Language Translation --uses Google(tm) MsgBox Move XML wrapper UDF XML2TreeView Zip functionality Split your GUI Save Print ScreenZipPluginEdit In Place listviewSome of my scripts on Google code
BigDaddyO Posted July 18, 2006 Author Posted July 18, 2006 CMenu by mHz.CyberSlug did some work with Peid for Universal Silent Switch Finder.USSFThanks, but I need to integrate this with my own code and that's already compiled.I will take a look at the PEiD thing that is mentioned in the CMenu details.The part I'm stuck at on the vbscript is line 152, the binary read where it skips 12 then reads 2, acording to AutoIT help file i can only read 512 at a time. Anybody have any ideas?Thanks,Mike
eltorro Posted July 18, 2006 Posted July 18, 2006 I am not sure where your at in the script but this might work. ;======= I am now lost ======================== ;~ $Boo = False ;~ $TS = FileOpen($sFilePath, 4) ;~ TS.Skip LocRes + 12 ;-- get number of names from bytes 13,14 in top level "Type" directory. ;We want to skip $LocRes+12 bytes, read 2 more , read 2 more. ;so read 16 and dump/ignore first 12 $s1a=FileRead($TS,$LocRes +16) FileClose($TS) ;~ s1 = TS.Read(2) ;-- Read bytes 13,14 to get number of named resource types. $s1=StringMid($s1a,13,2) ;not sure on the next 2 lines $aS1 = _GetArray($s1) ; get array from string ;~ iNum1 = Asc(s1) ;-- number of names. $iNum1 = _GetNumFromBytes($aS1) ;~ s1 = TS.Read(2) ;-- Read bytes 15,16 to get number of numbered resource types. $s2= StringMid($s1a,15,2) ;~ iNum2 = Asc(s1) ;-- number of nums. $aS2 =_GetArray($s2) $iNum2 = _GetNumFromBytes($aS2) ;~ If (iNum2 = 0) Then '-- no numbered entries. have to quit here. if $iNum2= 0 Then Return 3 ;~ TS.Close ;~ Set TS = Nothing ;~ GetVersionInfo = 3 'failed to find version info in resource table. ;~ Exit Function ;~ End If EndIf oÝ÷ Ø l£*.(!·§¶Ú©àzØ^~éÜuç%j·¢ë$^}ú+Ó~Eëvjëh×6Func _GetVersionInfo($sFilePath, ByRef $ARet2) I hope this helps. Regards, [indent]ElTorro[/indent][font="Book"] Decide, Commit, Achieve[/font]_ConfigIO.au3Language Translation --uses Google(tm) MsgBox Move XML wrapper UDF XML2TreeView Zip functionality Split your GUI Save Print ScreenZipPluginEdit In Place listviewSome of my scripts on Google code
BigDaddyO Posted July 18, 2006 Author Posted July 18, 2006 OK, I have given up on trying to convert the file to AU3. I was able to figure out enought to modify the vbscript to output only the info I need to a file. I am now launching the .vbs from an autoit script, waiting for it to finish and then reading the info from the temp file. I have attached both the updated File Props.vbs as a txt file. and the PatchProp.au3 file. Now I just have to integrate the PatchProp.au3 into my main script and I'm all set. Thanks to those who tried to help. MikeFile_Props.txtPatchProp.au3
eltorro Posted July 18, 2006 Posted July 18, 2006 (edited) OK,I have given up on trying to convert the file to AU3.I was able to figure out enought to modify the vbscript to output only the info I need to a file. I am now launching the .vbs from an autoit script, waiting for it to finish and then reading the info from the temp file.I have attached both the updated File Props.vbs as a txt file. and the PatchProp.au3 file.Now I just have to integrate the PatchProp.au3 into my main script and I'm all set.Thanks to those who tried to help.MikeYou can try this conversion.Edit 2 : Now working. fixed problem with offset calculation. Edit 3 : Updated code is in later post. Edited July 19, 2006 by eltorro Regards, [indent]ElTorro[/indent][font="Book"] Decide, Commit, Achieve[/font]_ConfigIO.au3Language Translation --uses Google(tm) MsgBox Move XML wrapper UDF XML2TreeView Zip functionality Split your GUI Save Print ScreenZipPluginEdit In Place listviewSome of my scripts on Google code
GaryFrost Posted July 18, 2006 Posted July 18, 2006 (edited) this should get you closer expandcollapse popup;-- This script includes a class and a demo. Drop any PE file onto the script (EXE, DLL, OCX) ;-- to get file properties. The class, ClsProps, is a complete set of all functions needed ;-- get file properties using only VBS and the Textstream object. ;-- The file properties returned are those that are found when a file is right-clicked, ;-- Properties menu is clicked, and Version tab is selected. This information ;-- is included in most PE (portable executable) files. ;-- Thank you to Ed Gruberman (http://www.rjump.com) for help with this code. ;-- He corrected a bug and also worked out the "aspack" variation whereby some ;-- PE files have been compressed and require a different method to find the version info. j. ;-- NOTE: This script is written to be compact for pasting into scripts. It uses simplified versions of some functions from ;-- the Textstream Binary Ops and Base 64 download, using only the functionality needed to get file version info., ;-- in order to keep this class as small as possible. If you want to use functions such as _GetArray or _GetByteString ;-- you may want to download the other package. It provides a fairly complete set of methods for working with binary files. ;-- Demo script -------------------- ;~ Dim $Arg, cPE, $i2, $AReturn ;~ If WScript.arguments.count = 0 Then ;~ MsgBox( 266288 , @ScriptName, "Drop A PE file [EXE-DLL-OCX] onto this script to get version information.") ;~ WScript.quit ;~ Else ;~ $Arg = WScript.arguments.item(0) ;~ EndIf Local $Arg, $i2, $AReturn $Arg = "WXP-KB914388-DHCP.exe" Global $FSO, $TS, $sAst, $ANums, $Char1 $sAst = "*" $Char1 = Chr(1) $FSO = ObjCreate("Scripting.FileSystemObject") $i2 = _GetVersionInfo($Arg, $AReturn) Switch $i2 Case 1 MsgBox(266288, @ScriptName, "File path not valid") Case 2 MsgBox(266288, @ScriptName, "Unable to find Resource table header in file.") Case 3 MsgBox(266288, @ScriptName, "Unable to find file version info. in file.") Case 4 MsgBox(266288, @ScriptName, "This is not a PE file.") Case 5 MsgBox(266288, @ScriptName, "This is a 16-bit executable. It is not a PE file.") Case 0 ; success $s = "Company: " & $AReturn (0) & @CRLF $s = $s & "File Description: " & $AReturn (1) & @CRLF $s = $s & "File Version: " & $AReturn (2) & @CRLF $s = $s & "Product Name: " & $AReturn (3) & @CRLF $s = $s & "Copyright: " & $AReturn (4) & @CRLF $s = $s & "Original file name: " & $AReturn (5) & @CRLF $s = $s & "Installer Engine: " & $AReturn (6) & @CRLF MsgBox(266288, @ScriptName, $Arg & @CRLF & @CRLF & $s) EndSwitch ;-- End demo script ------------------------------------------------------------ ;-- //////////////////////// Start Class: ClsProps /////////////////////////////////////////////// ; Functions in this class. (All functions are needed for getting file version information.) ; Public Function _GetVersionInfo($sFilePath, $ARet2) - returns version information For PE files. ; On success $ARet2 returns array(5) containing version info. strings for file. ; Function return error codes: 0 = success. 1 = invalid file path. 2 = no .rsrc table listed in section table. ; 3 = failed to find version info. 4 = not a PE file. 5 = file is a 16-bit executable. ("NE" file rather than "PE") ; Private (internal) functions: ; _GetArray(StringIn) - convert a string to an array of byte values. ; _GetByteString(StringIn, $SnipUnicode) - convert a string to a manageable version. If $SnipUnicode = True then get only every 2nd byte. ; _GetNumFromBytes(array) - takes array of ubound 1 or 3. return numeric value for 2 or 4 bytes. ;----------------//////////// --- BEGIN Class HERE --- /////////////////////------------------------------------------- ;-- The public function in this class: _GetVersionInfo ----------------------------------------- Func _GetVersionInfo($sFilePath, ByRef $ARet2) ;-- return array(5) Local $ARet, $s1, $sB, $Pt1, $sRes, $A1, $A4[3], $A2[1], $LocRes, $VLocRes, $SizeRes, $iOff, $Boo, $sVerString, $sMarker Local $iNum1, $iNum2, $iReadPt, $iNum3, $LocAspack, $VLocAspack, $VIOffset, $ReadOffset, $BooAspack ; On Error Resume Next If ($FSO.FileExists ($sFilePath) = False) Then Return 1 ;bad path. EndIf $sRes = ".rsrc" $sVerString = "VS_VER" $BooAspack = False $TS = $FSO.OpenTextFile ($sFilePath, 1) ;~ $s1 = String($TS.Read (2048)) ;-- Read first 2 KB. For $x = 1 To 2048 $s1 = $s1 & String($TS.Read(1)) ;-- Read first 2 KB. Next $TS.Close ConsoleWrite($s1 & @LF) $TS = 0 ConsoleWrite(StringMid($s1, 61, 2) & @LF) $A1 = _GetArray(StringMid($s1, 61, 2)) ;-- get number value at offset 60 that points to PE signature address. $iNum1 = (_GetNumFromBytes($A1) + 1) ;-- get offset of "PE00" $sB = _GetByteString($s1, False) ;-- get a workable string with Chr(0) replaced by "*". $sMarker = StringMid($sB, $iNum1, 4) If ($sMarker <> "PE**") Then If StringLeft($sMarker, 2) = "NE" Then Return 5 ;-- 16 bit. Else Return 4 ;-- no PE signature found. EndIf EndIf $Pt1 = StringInStr($sB, $sRes) ;-- find .rsrc table. If ($Pt1 = 0) Then Return 2 ;no resource table header found. EndIf $Pt1 = $Pt1 + 12 ;-- size of raw data is 4 bytes at offset of 16 into the .rsrc table. $A1 = _GetArray(StringMid($s1, $Pt1, 12)) ;-- get the same string as a numeric array to Read offset numbers. For $iOff = 0 To 3 $A4[$iOff] = $A1[$iOff] Next $VLocRes = _GetNumFromBytes($A4) For $iOff = 0 To 3 $A4[$iOff] = $A1 ($iOff + 4) Next $SizeRes = _GetNumFromBytes($A4) ;--size of resource section in bytes. For $iOff = 0 To 3 $A4[$iOff] = $A1[$iOff + 8] Next $LocRes = _GetNumFromBytes($A4) ;-- offset location of resource section. $Pt1 = StringInStr(1, $sB, ".aspack") ;-- find .rsrc table. If ($Pt1 > 0) Then $BooAspack = True $Pt1 = $Pt1 + 12 ;-- virtual offset is first 4 bytes; raw offset is bytes 9-12. $A1 = _GetArray(StringMid($s1, $Pt1, 12)) For $iOff = 0 To 3 $A4[$iOff] = $A1[$iOff] Next $VLocAspack = _GetNumFromBytes($A4) For $iOff = 0 To 3 $A4[$iOff] = $A1[$iOff + 8] Next $LocAspack = _GetNumFromBytes($A4) EndIf $Boo = False $TS = $FSO.OpenTextFile ($sFilePath, 1) $TS.Skip ($LocRes + 12) ;-- get number of names from bytes 13,14 in top level "Type" directory. $s1 = $TS.Read (2) ;-- Read bytes 13,14 to get number of named resource types. $iNum1 = Asc($s1) ;-- number of names. $s1 = $TS.Read (2) ;-- Read bytes 15,16 to get number of numbered resource types. $iNum2 = Asc($s1) ;-- number of nums. If ($iNum2 = 0) Then ;-- no numbered entries. have to quit here. $TS.Close $TS = 0 Return 3 ;failed to find version info in resource table. EndIf If ($iNum1 > 0) Then $TS.Skip ($iNum1 * 8) ;-- Skip past named entries. $iReadPt = $LocRes + 16 + ($iNum1 * 8) ;-- update file offset variable because this will be needed. $Boo = False For $iOff = 1 To $iNum2 $s1 = $TS.Read (8) $iReadPt = $iReadPt + 8 If (Asc($s1) = 16) Then ;-- this is version info. entry. $Boo = True ExitLoop EndIf Next If ($Boo = False) Then ;-- have to quit. no version info. entry found. $TS.Close $TS = 0 Return 3 ;failed to find version info in resource table. EndIf $A1 = _GetArray($s1) ;-- get a byte array for version info entry at top level. $iOff = 0 $iNum3 = 1 ;Do While 1 For $iNum1 = 0 To 2 ;-- get offset number to next level from 2nd 4 bytes of entry structure. $A4[$iNum1] = $A1[$iNum1 + 4] Next $A4[3] = 0 $iNum2 = _GetNumFromBytes($A4) If ($A1[7] > 127) Then ;-- high bit was set in entry offset value, so it;s just a pointer to another pointer. $iNum2 = $LocRes + $iNum2 + 16 $TS.Skip ($iNum2 - $iReadPt) ;- 1) $s1 = $TS.Read (8) $iReadPt = $iReadPt + (($iNum2 - $iReadPt) + 8) $A1 = _GetArray($s1) Else ;-- this is the offset of version info offset info.! $iOff = ($iNum2 + $LocRes) ;ExitLoop ExitLoop EndIf $iNum3 = $iNum3 + 1 If ($iNum3 > 10) Then ExitLoop WEnd ;Loop If ($iOff = 0) Then ;-- have to quit. no final offset found. $TS.Close $TS = 0 Return 3 ;failed to find version info in resource table. EndIf $TS.Skip ($iOff - $iReadPt) $s1 = $TS.Read (8) $iReadPt = $iReadPt + (($iOff - $iReadPt) + 8) $A1 = _GetArray($s1) For $iNum1 = 0 To 3 $A4[$iNum1] = $A1[$iNum1] Next $VIOffset = _GetNumFromBytes($A4) ;--offset of version info. given in .rsrc section. $ReadOffset = (($VIOffset - $VLocRes) + $LocRes) For $iNum1 = 0 To 3 $A4[$iNum1] = $A1[$iNum1 + 4] Next $SizeRes = _GetNumFromBytes($A4) $TS.Skip ($ReadOffset - $iReadPt) $s1 = $TS.Read ($SizeRes) ;-- read out the entire FileVersionInfo data area. $TS.Close $TS = 0 $sB = _GetByteString($s1, True) ;-- snip unicode. $Pt1 = StringInStr(1, $sB, $sVerString) If ($Pt1 > 0) Then ;-- "VS_VER" was found, so process the string and quit. $ARet = _ProcessRes($sB) $ARet2 = $ARet Return 0 ; ok ElseIf ($BooAspack = True) Then ;-- if "VS_VER" was not found but there is an "aspack" section then try that. $ReadOffset = (($VIOffset - $VLocAspack) + $LocAspack) ;-- calculate a new file version info data offset. $TS = $FSO.OpenTextFile ($sFilePath, 1) ;-- The file was closed and is now re-opened here. Keeping the file $TS.Skip ($ReadOffset) ;-- open "just in case" wouldn;t have helped because the file pointer $s1 = $TS.Read ($SizeRes) ;-- for this read may be further back thean the pointer was when the file $TS.Close ;-- was closed. So rather than try to sort out the read point, the file is just $TS = 0 ;-- opened fresh and Skip is used. $sB = _GetByteString($s1, True) $Pt1 = StringInStr($sB, $sVerString) If ($Pt1 > 0) Then $ARet = _ProcessRes($sB) $ARet2 = $ARet Return 0 ; ok Else Return 3 ;failed to find version info in resource table. EndIf Else Return 3 ;failed to find version info in resource table. EndIf EndFunc ;==>_GetVersionInfo Func _ProcessRes($sDat) Dim $AInfo[6] ; On Error Resume Next $AInfo[0] = _GetInfo($sDat, "CompanyName") $AInfo[1] = _GetInfo($sDat, "FileDescription") $AInfo[2] = _GetInfo($sDat, "FileVersion") $AInfo[3] = _GetInfo($sDat, "ProductName") $AInfo[4] = _GetInfo($sDat, "LegalCopyright") $AInfo[5] = _GetInfo($sDat, "OriginalFilename") $AInfo[6] = _GetInfo($sDat, "Installer Engine") Return $AInfo EndFunc ;==> _ProcessRes Func _GetInfo($sStr, $sVal) Dim $Pta, $Ptb, $LenVal, $s4 ; On Error Resume Next ; _GetInfo = "" $LenVal = StringLen($sVal) + 1 ;-- length of info string: "CompanyName" = 11 $Pta = StringInStr($sStr, $sVal) ;-- find string name. If ($Pta > 0) Then $Pta = $Pta + $LenVal ;$Ptb = InStr(($Pta + 1), $sStr, $sAst) ;-- look for next *. some properties are Name**value** and some are $Ptb = StringInStr(StringTrimLeft($sStr, ($Pta + 1)), $sAst) ;-- look for next *. some properties are Name**value** and some are If $Ptb > ($Pta + 2) Then ;-- Name*value**. So start looking at 3rd character after. If that $s4 = StringMid($sStr, $Pta, ($Ptb - $Pta)) ;-- character is * then it;s Name*** which means there;s $s4 = StringReplace($s4, $sAst, "") ;--no value for that specific property. If StringInStr($s4, $Char1, 0) = 0 Then Return $s4 ;-- check for Chr(1) which seems to be found EndIf ; between values. If it;s in the string that means there is no value for ; this property and function has actually read next property name. EndIf EndFunc ;==> _GetInfo ;-------------- simplified version of _GetByteString For this Class. --------------------- Func _GetByteString($sStr, $SnipUnicode) Local $sRet, $iLen, $iA, $iLen2, $A2[1] ; On Error Resume Next $iLen2 = 0 If ($SnipUnicode = False) Then ReDim $A2[StringLen($sStr) - 1] For $iLen = 1 To StringLen($sStr) $iA = Asc(StringMid($sStr, $iLen, 1)) If $iA = 0 Then $iA = 42 ;-- converts 0-byte to * $A2[$iLen - 1] = Chr($iA) Next Else ReDim $A2[ (StringLen($sStr) / 2) - 1] For $iLen = 1 To StringLen($sStr) Step 2 $iA = Asc(StringMid($sStr, $iLen, 1)) If $iA = 0 Then $iA = 42 ;-- converts 0-byte to * $A2[$iLen2] = Chr($iA) $iLen2 = $iLen2 + 1 Next EndIf Return _Join($A2, "") EndFunc ;==> _GetByteString ;-------------------------------- Simplified version of _GetArray. ----------------------- Func _GetArray($sStr) ConsoleWrite($sStr & @LF) Dim $iA, $Len1, $Len2, $AStr[1] ; On Error Resume Next $Len1 = StringLen($sStr) ReDim $AStr[$Len1 - 1] For $iA = 1 To $Len1 $AStr ($iA - 1) = Asc(StringMid($sStr, $iA, 1)) Next Return $AStr EndFunc ;==> _GetArray ;-------------------- return a number from 2 or 4 bytes. --------------- Func _GetNumFromBytes($ABytes) Dim $Num1 ; Err.Clear ; On Error Resume Next ;_GetNumFromBytes = -1 $Num1 = ($ABytes[0] + $ABytes[1] * 256) If (UBound($ABytes) = 3) Then $Num1 = $Num1 + ($ABytes[2] * 65536) + ($ABytes[3] * 16777216) EndIf Return $Num1 EndFunc ;==> _GetNumFromBytes Func _Join($aStrJ, $sDelim = "") Local $sTemp If IsArray($aStrJ) Then For $x = 0 To UBound($aStrJ) - 1 $sTemp = $sTemp & $aStrJ[$x] & $sDelim Next Return $sTemp EndIf SetError(1) Return "" EndFunc ;==>_Join Func OnAutoItStart() ;~ If $CmdLine[0] = 0 Then ;~ MsgBox (266288,@ScriptName,"Drop A PE file [EXE-DLL-OCX] onto this script to get version information.") ;~ Exit ;~ EndIf ;Global $Arg = $CmdLine[1] EndFunc ;==>OnAutoItStart Func OnAutoItExit() $TS = 0 ;-- just in case. $FSO = 0; Nothing EndFunc ;==>OnAutoItExit Edited July 18, 2006 by gafrost SciTE for AutoItDirections for Submitting Standard UDFs Don't argue with an idiot; people watching may not be able to tell the difference.
BigDaddyO Posted July 18, 2006 Author Posted July 18, 2006 this should get you closer OK, Using most of your code, I am now able to get to the point where it detects the PE header but gets stuck again. For some reason the sticking points have been the $FSO.OpenTextFile ($sFilePath, 1) it does not seem to read null characters which is important for figuring out the location of the PE. the RAW FileOpen is able to read the nulls so I am able to replace them with chr(42) and get the output to look exactly like VBScript and find PE** Here is what I have so far. it keeps erroring out on line 171 I think because the $FSO.OpenTextFile ($sFilePath, 1) on line 137 is not reading the binary data properly. expandcollapse popup;-- This script includes a class and a demo. Drop any PE file onto the script (EXE, DLL, OCX) ;-- to get file properties. The class, ClsProps, is a complete set of all functions needed ;-- get file properties using only VBS and the Textstream object. ;-- The file properties returned are those that are found when a file is right-clicked, ;-- Properties menu is clicked, and Version tab is selected. This information ;-- is included in most PE (portable executable) files. ;-- Thank you to Ed Gruberman (http://www.rjump.com) for help with this code. ;-- He corrected a bug and also worked out the "aspack" variation whereby some ;-- PE files have been compressed and require a different method to find the version info. j. ;-- NOTE: This script is written to be compact for pasting into scripts. It uses simplified versions of some functions from ;-- the Textstream Binary Ops and Base 64 download, using only the functionality needed to get file version info., ;-- in order to keep this class as small as possible. If you want to use functions such as _GetArray or _GetByteString ;-- you may want to download the other package. It provides a fairly complete set of methods for working with binary files. ;-- Demo script -------------------- ;~ Dim $Arg, cPE, $i2, $AReturn ;~ If WScript.arguments.count = 0 Then ;~ MsgBox( 266288 , @ScriptName, "Drop A PE file [EXE-DLL-OCX] onto this script to get version information.") ;~ WScript.quit ;~ Else ;~ $Arg = WScript.arguments.item(0) ;~ EndIf Local $Arg, $i2, $AReturn $Arg = "D:\Temp\WinXP.exe" Global $FSO, $TS, $sAst, $ANums, $Char1 $sAst = "*" $Char1 = Chr(1) $FSO = ObjCreate("Scripting.FileSystemObject") $i2 = _GetVersionInfo($Arg, $AReturn) Switch $i2 Case 1 MsgBox(266288, @ScriptName, "File path not valid") Case 2 MsgBox(266288, @ScriptName, "Unable to find Resource table header in file.") Case 3 MsgBox(266288, @ScriptName, "Unable to find file version info. in file.") Case 4 MsgBox(266288, @ScriptName, "This is not a PE file.") Case 5 MsgBox(266288, @ScriptName, "This is a 16-bit executable. It is not a PE file.") Case 0 ; success $s = "Company: " & $AReturn (0) & @CRLF $s = $s & "File Description: " & $AReturn (1) & @CRLF $s = $s & "File Version: " & $AReturn (2) & @CRLF $s = $s & "Product Name: " & $AReturn (3) & @CRLF $s = $s & "Copyright: " & $AReturn (4) & @CRLF $s = $s & "Original file name: " & $AReturn (5) & @CRLF $s = $s & "Installer Engine: " & $AReturn (6) & @CRLF MsgBox(266288, @ScriptName, $Arg & @CRLF & @CRLF & $s) EndSwitch ;-- End demo script ------------------------------------------------------------ ;-- //////////////////////// Start Class: ClsProps /////////////////////////////////////////////// ; Functions in this class. (All functions are needed for getting file version information.) ; Public Function _GetVersionInfo($sFilePath, $ARet2) - returns version information For PE files. ; On success $ARet2 returns array(5) containing version info. strings for file. ; Function return error codes: 0 = success. 1 = invalid file path. 2 = no .rsrc table listed in section table. ; 3 = failed to find version info. 4 = not a PE file. 5 = file is a 16-bit executable. ("NE" file rather than "PE") ; Private (internal) functions: ; _GetArray(StringIn) - convert a string to an array of byte values. ; _GetByteString(StringIn, $SnipUnicode) - convert a string to a manageable version. If $SnipUnicode = True then get only every 2nd byte. ; _GetNumFromBytes(array) - takes array of ubound 1 or 3. return numeric value for 2 or 4 bytes. ;----------------//////////// --- BEGIN Class HERE --- /////////////////////------------------------------------------- ;-- The public function in this class: _GetVersionInfo ----------------------------------------- Func _GetVersionInfo($sFilePath, ByRef $ARet2) ;-- return array(5) Local $ARet, $s1, $sB, $Pt1, $sRes, $A1, $A4[4], $A2[1], $LocRes, $VLocRes, $SizeRes, $iOff, $Boo, $sVerString, $sMarker Local $iNum1, $iNum2, $iReadPt, $iNum3, $LocAspack, $VLocAspack, $VIOffset, $ReadOffset, $BooAspack ; On Error Resume Next ;~ If ($FSO.FileExists ($sFilePath) = False) Then ;~ Return 1 ;bad path. ;~ EndIf $sRes = ".rsrc" $sVerString = "VS_VER" $BooAspack = False $TS = FileOpen($sFilePath, 4) $s1 = FileRead($TS, 2048) FileClose($TS) ;~ $TS = $FSO.OpenTextFile ($sFilePath, 1) ;~ $s1 = $TS.Read(2048) ;~ $s1 = String($TS.Read (2048)) ;-- Read first 2 KB. ;~ For $x = 1 To 2048 ;~ $s1 = $s1 & String($TS.Read(1)) ;-- Read first 2 KB. ;~ Next ;~ $TS.Close ConsoleWrite($s1 & @LF) ;~ $TS = 0 ConsoleWrite(StringMid($s1, 61, 2) & @LF) $A1 = _GetArray(StringMid($s1, 61, 2)) ;-- get number value at offset 60 that points to PE signature address. $iNum1 = (_GetNumFromBytes($A1) + 1) ;-- get offset of "PE00" $sB = _GetByteString($s1, False) ;-- get a workable string with Chr(0) replaced by "*". MsgBox(0, "_GetByteString 'AutoIT'", $sB) $sMarker = StringMid($sB, $iNum1, 4) MsgBox(0, "sMarker 'AutoIT'", $sMarker) If ($sMarker <> "PE**") Then If StringLeft($sMarker, 2) = "NE" Then Return 5 ;-- 16 bit. Else Return 4 ;-- no PE signature found. EndIf EndIf $Pt1 = StringInStr($sB, $sRes) ;-- find .rsrc table. If ($Pt1 = 0) Then Return 2 ;no resource table header found. EndIf $Pt1 = $Pt1 + 12 ;-- size of raw data is 4 bytes at offset of 16 into the .rsrc table. $A1 = _GetArray(StringMid($s1, $Pt1, 12)) ;-- get the same string as a numeric array to Read offset numbers. For $iOff = 0 To 3 $A4[$iOff] = $A1[$iOff] Next $VLocRes = _GetNumFromBytes($A4) For $iOff = 0 To 3 $A4[$iOff] = $A1[$iOff + 4] Next $SizeRes = _GetNumFromBytes($A4) ;--size of resource section in bytes. For $iOff = 0 To 3 $A4[$iOff] = $A1[$iOff + 8] Next $LocRes = _GetNumFromBytes($A4) ;-- offset location of resource section. $Pt1 = StringInStr(1, $sB, ".aspack") ;-- find .rsrc table. If ($Pt1 > 0) Then $BooAspack = True $Pt1 = $Pt1 + 12 ;-- virtual offset is first 4 bytes; raw offset is bytes 9-12. $A1 = _GetArray(StringMid($s1, $Pt1, 12)) For $iOff = 0 To 3 $A4[$iOff] = $A1[$iOff] Next $VLocAspack = _GetNumFromBytes($A4) For $iOff = 0 To 3 $A4[$iOff] = $A1[$iOff + 8] Next $LocAspack = _GetNumFromBytes($A4) EndIf $Boo = False $TS = $FSO.OpenTextFile ($sFilePath, 1) $TS.Skip ($LocRes + 12) ;-- get number of names from bytes 13,14 in top level "Type" directory. $s1 = $TS.Read (2) ;-- Read bytes 13,14 to get number of named resource types. $iNum1 = Asc($s1) ;-- number of names. $s1 = $TS.Read (2) ;-- Read bytes 15,16 to get number of numbered resource types. $iNum2 = Asc($s1) ;-- number of nums. If ($iNum2 = 0) Then ;-- no numbered entries. have to quit here. $TS.Close $TS = 0 Return 3 ;failed to find version info in resource table. EndIf If ($iNum1 > 0) Then $TS.Skip ($iNum1 * 8) ;-- Skip past named entries. $iReadPt = $LocRes + 16 + ($iNum1 * 8) ;-- update file offset variable because this will be needed. $Boo = False For $iOff = 1 To $iNum2 $s1 = $TS.Read (8) $iReadPt = $iReadPt + 8 If (Asc($s1) = 16) Then ;-- this is version info. entry. $Boo = True ExitLoop EndIf Next If ($Boo = False) Then ;-- have to quit. no version info. entry found. $TS.Close $TS = 0 Return 3 ;failed to find version info in resource table. EndIf ;~ MsgBox(0, "_GetArray()", "Moving into the Next Section") $A1 = _GetArray($s1) ;-- get a byte array for version info entry at top level. $iOff = 0 $iNum3 = 1 ;Do While 1 For $iNum1 = 0 To 2 ;-- get offset number to next level from 2nd 4 bytes of entry structure. $A4[$iNum1] = $A1[$iNum1 + 4] Next $A4[3] = 0 $iNum2 = _GetNumFromBytes($A4) If ($A1[7] > 127) Then ;-- high bit was set in entry offset value, so it;s just a pointer to another pointer. $iNum2 = $LocRes + $iNum2 + 16 $TS.Skip ($iNum2 - $iReadPt) ;- 1) $s1 = $TS.Read (8) $iReadPt = $iReadPt + (($iNum2 - $iReadPt) + 8) $A1 = _GetArray($s1) Else ;-- this is the offset of version info offset info.! $iOff = ($iNum2 + $LocRes) ;ExitLoop ExitLoop EndIf $iNum3 = $iNum3 + 1 If ($iNum3 > 10) Then ExitLoop WEnd ;Loop If ($iOff = 0) Then ;-- have to quit. no final offset found. $TS.Close $TS = 0 Return 3 ;failed to find version info in resource table. EndIf $TS.Skip ($iOff - $iReadPt) $s1 = $TS.Read (8) $iReadPt = $iReadPt + (($iOff - $iReadPt) + 8) $A1 = _GetArray($s1) For $iNum1 = 0 To 3 $A4[$iNum1] = $A1[$iNum1] Next $VIOffset = _GetNumFromBytes($A4) ;--offset of version info. given in .rsrc section. $ReadOffset = (($VIOffset - $VLocRes) + $LocRes) For $iNum1 = 0 To 3 $A4[$iNum1] = $A1[$iNum1 + 4] Next $SizeRes = _GetNumFromBytes($A4) $TS.Skip ($ReadOffset - $iReadPt) $s1 = $TS.Read ($SizeRes) ;-- read out the entire FileVersionInfo data area. $TS.Close $TS = 0 $sB = _GetByteString($s1, True) ;-- snip unicode. $Pt1 = StringInStr(1, $sB, $sVerString) If ($Pt1 > 0) Then ;-- "VS_VER" was found, so process the string and quit. $ARet = _ProcessRes($sB) $ARet2 = $ARet Return 0 ; ok ElseIf ($BooAspack = True) Then ;-- if "VS_VER" was not found but there is an "aspack" section then try that. $ReadOffset = (($VIOffset - $VLocAspack) + $LocAspack) ;-- calculate a new file version info data offset. $TS = $FSO.OpenTextFile ($sFilePath, 1) ;-- The file was closed and is now re-opened here. Keeping the file $TS.Skip ($ReadOffset) ;-- open "just in case" wouldn;t have helped because the file pointer $s1 = $TS.Read ($SizeRes) ;-- for this read may be further back thean the pointer was when the file $TS.Close ;-- was closed. So rather than try to sort out the read point, the file is just $TS = 0 ;-- opened fresh and Skip is used. $sB = _GetByteString($s1, True) $Pt1 = StringInStr($sB, $sVerString) If ($Pt1 > 0) Then $ARet = _ProcessRes($sB) $ARet2 = $ARet Return 0 ; ok Else Return 3 ;failed to find version info in resource table. EndIf Else Return 3 ;failed to find version info in resource table. EndIf EndFunc ;==>_GetVersionInfo Func _ProcessRes($sDat) Dim $AInfo[6] ; On Error Resume Next $AInfo[0] = _GetInfo($sDat, "CompanyName") $AInfo[1] = _GetInfo($sDat, "FileDescription") $AInfo[2] = _GetInfo($sDat, "FileVersion") $AInfo[3] = _GetInfo($sDat, "ProductName") $AInfo[4] = _GetInfo($sDat, "LegalCopyright") $AInfo[5] = _GetInfo($sDat, "OriginalFilename") $AInfo[6] = _GetInfo($sDat, "Installer Engine") Return $AInfo EndFunc ;==> _ProcessRes Func _GetInfo($sStr, $sVal) Dim $Pta, $Ptb, $LenVal, $s4 ; On Error Resume Next ; _GetInfo = "" $LenVal = StringLen($sVal) + 1 ;-- length of info string: "CompanyName" = 11 $Pta = StringInStr($sStr, $sVal) ;-- find string name. If ($Pta > 0) Then $Pta = $Pta + $LenVal ;$Ptb = InStr(($Pta + 1), $sStr, $sAst) ;-- look for next *. some properties are Name**value** and some are $Ptb = StringInStr(StringTrimLeft($sStr, ($Pta + 1)), $sAst) ;-- look for next *. some properties are Name**value** and some are If $Ptb > ($Pta + 2) Then ;-- Name*value**. So start looking at 3rd character after. If that $s4 = StringMid($sStr, $Pta, ($Ptb - $Pta)) ;-- character is * then it;s Name*** which means there;s $s4 = StringReplace($s4, $sAst, "") ;--no value for that specific property. If StringInStr($s4, $Char1, 0) = 0 Then Return $s4 ;-- check for Chr(1) which seems to be found EndIf ; between values. If it;s in the string that means there is no value for ; this property and function has actually read next property name. EndIf EndFunc ;==> _GetInfo ;-------------- simplified version of _GetByteString For this Class. --------------------- Func _GetByteString($sStr, $SnipUnicode) Local $sRet, $iLen, $iA, $iLen2, $A2[1] ; On Error Resume Next $iLen2 = 0 If ($SnipUnicode = False) Then ;~ MsgBox(0, "String Length", StringLen($sStr)) ReDim $A2[StringLen($sStr) + 1] ;~ MsgBox(0, "Ubound", UBound($A2)) For $iLen = 1 To StringLen($sStr) $iA = Asc(StringMid($sStr, $iLen, 1)) If $iA = 0 Then $iA = 42 ;-- converts 0-byte to * ;~ MsgBox(0, "Found a 0", $iLen) EndIf $A2[$iLen - 1] = Chr($iA) Next Else ReDim $A2[ (StringLen($sStr) / 2) - 1] For $iLen = 1 To StringLen($sStr) Step 2 $iA = Asc(StringMid($sStr, $iLen, 1)) If $iA = 0 Then $iA = 42 ;-- converts 0-byte to * $A2[$iLen2] = Chr($iA) $iLen2 = $iLen2 + 1 Next EndIf Return _Join($A2, "") EndFunc ;==> _GetByteString ;-------------------------------- Simplified version of _GetArray. ----------------------- Func _GetArray($sStr) ConsoleWrite($sStr & @LF) Dim $iA, $Len1, $Len2, $AStr[1] ; On Error Resume Next $Len1 = StringLen($sStr) ;~ MsgBox(0, "_GetArray()", "String Len: " & $Len1) ReDim $AStr[$Len1] For $iA = 1 To $Len1 $AStr[$iA - 1] = Asc(StringMid($sStr, $iA, 1)) Next Return $AStr EndFunc ;==> _GetArray ;-------------------- return a number from 2 or 4 bytes. --------------- Func _GetNumFromBytes($ABytes) Dim $Num1 ; Err.Clear ; On Error Resume Next ;_GetNumFromBytes = -1 $Num1 = $ABytes[0] + ($ABytes[1] * 256) If (UBound($ABytes) = 3) Then $Num1 = $Num1 + ($ABytes[2] * 65536) + ($ABytes[3] * 16777216) EndIf Return $Num1 EndFunc ;==> _GetNumFromBytes Func _Join($aStrJ, $sDelim = "") Local $sTemp If IsArray($aStrJ) Then For $x = 0 To UBound($aStrJ) - 1 $sTemp = $sTemp & $aStrJ[$x] & $sDelim Next ;~ MsgBox(0, "sTemp", $sTemp) Return $sTemp EndIf SetError(1) Return "" EndFunc ;==>_Join Func OnAutoItStart() ;~ If $CmdLine[0] = 0 Then ;~ MsgBox (266288,@ScriptName,"Drop A PE file [EXE-DLL-OCX] onto this script to get version information.") ;~ Exit ;~ EndIf ;Global $Arg = $CmdLine[1] EndFunc ;==>OnAutoItStart Func OnAutoItExit() $TS = 0 ;-- just in case. $FSO = 0; Nothing EndFunc ;==>OnAutoItExit
eltorro Posted July 19, 2006 Posted July 19, 2006 (edited) Here's the latest version expandcollapse popup;-- This script includes a class and a demo. Drop any PE file onto the script (EXE, DLL, OCX) ;-- to get file properties. The class, ClsProps, is a complete set of all functions needed ;-- get file properties using only VBS and the Textstream object. ;-- The file properties returned are those that are found when a file is right-clicked, ;-- Properties menu is clicked, and Version tab is selected. This information ;-- is included in most PE (portable executable) files. ;-- Thank you to Ed Gruberman (http://www.rjump.com) for help with this code. ;-- He corrected a bug and also worked out the "aspack" variation whereby some ;-- PE files have been compressed and require a different method to find the version info. j. ;-- NOTE: This script is written to be compact for pasting into scripts. It uses simplified versions of some functions from ;-- the Textstream Binary Ops and Base 64 download, using only the functionality needed to get file version info., ;-- in order to keep this class as small as possible. If you want to use functions such as _GetArray or _GetByteString ;-- you may want to download the other package. It provides a fairly complete set of methods for working with binary files. ;-- Demo script -------------------- ;Converted to AutoItV3 Steve Podhajecki [eltorro] steve@ocotillo.sytes.net Global $sAst, $ANums Global $Arg, $i2, $AReturn, $s ;See OnAutoItStart() ;$Arg = "Windows2000-KB914388-x86-ENU.EXE" ;$Arg= "C:\Windows\System32\cmdhere.dll" ;$Arg = "C:\Windows\System32\ZlibTool.ocx" ;$Arg = "C:\Windows\System32\Calc.exe" $i2 = _GetVersionInfo($Arg, $AReturn) Switch $i2 Case 1 MsgBox(266288, @ScriptName, "File path not valid") Case 2 MsgBox(266288, @ScriptName, "Unable to find Resource table header in file.") Case 3 MsgBox(266288, @ScriptName, "Unable to find file version info. in file.") Case 4 MsgBox(266288, @ScriptName, "This is not a PE file.") Case 5 MsgBox(266288, @ScriptName, "This is a 16-bit executable. It is not a PE file.") Case 0 ; success $s = "Company: " & $AReturn[0] & @CRLF $s = $s & "File Description: " & $AReturn[1] & @CRLF $s = $s & "File Version: " & $AReturn[2] & @CRLF $s = $s & "Product Name: " & $AReturn[3] & @CRLF $s = $s & "Copyright: " & $AReturn[4] & @CRLF $s = $s & "Original file name: " & $AReturn[5] & @CRLF $s = $s & "Installer Engine: " & $AReturn[6] & @CRLF MsgBox(266288, @ScriptName, $Arg & @CRLF & @CRLF & $s) EndSwitch ;-- End demo script ------------------------------------------------------------ ;-- //////////////////////// Start Class: ClsProps /////////////////////////////////////////////// ; ; Functions in this class. (All functions are needed for getting file version information.) ; ; Public Function _GetVersionInfo($sFilePath, $ARet2) - returns version information For PE files. ; On success $ARet2 returns array(5) containing version info. strings for file. ; Function return error codes: 0 = success. 1 = invalid file path. 2 = no .rsrc table listed in section table. ; 3 = failed to find version info. 4 = not a PE file. 5 = file is a 16-bit executable. ("NE" file rather than "PE") ; ; Private (internal) functions: ; _GetArray(StringIn) - convert a string to an array of byte values. ; _GetByteString(StringIn, $SnipUnicode) - convert a string to a manageable version. If $SnipUnicode = True then get only every 2nd byte. ; _GetNumFromBytes(array) - takes array of ubound 1 or 3. return numeric value for 2 or 4 bytes. ; ; ; ; ; ;----------------//////////// --- BEGIN Class HERE --- /////////////////////------------------------------------------- Func OnAutoItStart() ;uncomment this for drag to or commandline usage. If $CmdLine[0] = 0 Then MsgBox(266288, @ScriptName, "Drop A PE file [EXE-DLL-OCX] onto this script to get version information." & _ @CRLF & "Commad Prompt Usage:" & @CRLF & @ScriptName & " " & "myfile.[exe,dll,ocx]") Exit EndIf Global $Arg = $CmdLine[1] EndFunc ;==>OnAutoItStart ;-- The public function in this class: _GetVersionInfo ----------------------------------------- Func _GetVersionInfo($sFilePath, ByRef $ARet2) ;-- return array(5) Local $ARet, $s1, $sB, $Pt1, $sRes, $A1, $A4[4], $A2[4], $LocRes, $VLocRes, $SizeRes, $iOff, $Boo, $sVerString, $sMarker Local $iNum1, $iNum2, $iReadPt, $iNum3, $LocAspack, $VLocAspack, $VIOffset, $ReadOffset, $BooAspack ; On Error Resume Next If Not FileExists($sFilePath) Then Return 1 ;bad path. EndIf $sRes = ".rsrc" $sVerString = "VS_VER" $BooAspack = False $file = FileOpen($sFilePath, 0) $s1 = FileRead($sFilePath, 2048) FileClose($file) $file = 0 ;ConsoleWrite($s1) $A1 = _GetArray(StringMid($s1, 61, 2)) ;-- get number value at offset 60 that points to PE signature address. $iNum1 = (_GetNumFromBytes($A1) + 1) ;-- get offset of "PE00" $sB = _GetByteString($s1, False) ;-- get a workable string with Chr(0) replaced by "*". $sMarker = StringMid($sB, $iNum1, 4) ;ConsoleWrite("$sMarker ="&$sMarker&@LF) If ($sMarker <> "PE**") Then If StringLeft($sMarker, 2) = "NE" Then Return 5 ;-- 16 bit. Else Return 4 ;-- no PE signature found. EndIf EndIf $Pt1 = StringInStr($sB, $sRes) ;-- find .rsrc table. If ($Pt1 = 0) Then Return 2 ;no resource table header found. EndIf $Pt1 = $Pt1 + 12 ;-- size of raw data is 4 bytes at offset of 16 into the .rsrc table. $A1 = _GetArray(StringMid($s1, $Pt1, 12)) ;-- get the same string as a numeric array to Read offset numbers. For $iOff = 0 To 3 ;ConsoleWrite("$iOff ="&$iOff&@LF) $A4[$iOff] = $A1[$iOff] Next $VLocRes = _GetNumFromBytes($A4) For $iOff = 0 To 3 $A4[$iOff] = $A1[$iOff + 4] Next $SizeRes = _GetNumFromBytes($A4) ;--size of resource section in bytes. For $iOff = 0 To 3 $A4[$iOff] = $A1[$iOff + 8] Next $LocRes = _GetNumFromBytes($A4) ;-- offset location of resource section. $Pt1 = StringInStr(1, $sB, ".aspack") ;-- find .rsrc table. If ($Pt1 > 0) Then $BooAspack = True $Pt1 = $Pt1 + 12 ;-- virtual offset is first 4 bytes; raw offset is bytes 9-12. $A1 = _GetArray(StringMid($s1, $Pt1, 12)) For $iOff = 0 To 3 $A4[$iOff] = $A1[$iOff] Next $VLocAspack = _GetNumFromBytes($A4) For $iOff = 0 To 3 $A4[$iOff] = $A1[$iOff + 8] Next $LocAspack = _GetNumFromBytes($A4) EndIf $Boo = False $file = FileOpen($sFilePath, 0) ;MsgBox(0, "$LocRes", "$LocRes+12 =" & $LocRes + 12) ;~ ConsoleWrite("$LocRes+12 =" & $LocRes + 12 & @CRLF) _Skip($file, $LocRes + 12) $s1 = FileRead($file, 2) ;-- Read bytes 13,14 to get number of named resource types. $iNum1 = Asc($s1) ;-- number of names. ; ConsoleWrite("$iNum1 =" & $iNum1 & @LF) $s2 = FileRead($file, 2) ;-- Read bytes 15,16 to get number of numbered resource types. $iNum2 = Asc($s2);-- number of nums. ; ConsoleWrite("$iNum2 =" & $iNum2 & @LF) If $iNum2 = 0 Then ;-- no numbered entries. have to quit here. Return 3 EndIf If ($iNum1 > 0) Then _Skip($file, $iNum1 * 8) ;-- Skip past named entries. $iReadPt = $LocRes + 16 + ($iNum1 * 8) ;-- update file offset variable because this will be needed. ;ConsoleWrite("$iReadPt ="& $iReadPt&@LF) $Boo = False For $iOff = 1 To $iNum2 $s1 = FileRead($file, 8) $iReadPt = $iReadPt + 8 If (Asc($s1) = 16) Then ;-- this is version info. entry. $Boo = True ExitLoop EndIf Next If ($Boo = False) Then ;-- have to quit. no version info. entry found. ;ConsoleWrite("Return point 1" & @LF) Return 3 ;failed to find version info in resource table. EndIf $A1 = _GetArray($s1) ;-- get a byte array for version info entry at top level. ;ConsoleWrite("Ubound>>"&UBound($A1)) $iOff = 0 $iNum3 = 1 ;Do While 1 For $iNum1 = 0 To 2 ;-- get offset number to next level from 2nd 4 bytes of entry structure. $A4[$iNum1] = $A1[$iNum1 + 4] Next $A4[3] = 0 $iNum2 = _GetNumFromBytes($A4) If ($A1[7] > 127) Then ;-- high bit was set in entry offset value, so it;s just a pointer to another pointer. $iNum2 = $LocRes + $iNum2 + 16 _Skip($file, $iNum2 - $iReadPt) ;- 1) $s1 = FileRead($file, 8) $iReadPt = $iReadPt + (($iNum2 - $iReadPt) + 8) $A1 = _GetArray($s1) Else ;-- this is the offset of version info offset info.! $iOff = ($iNum2 + $LocRes) ;ExitLoop ExitLoop EndIf $iNum3 = $iNum3 + 1 If ($iNum3 > 10) Then ExitLoop WEnd ;Loop If ($iOff = 0) Then ;-- have to quit. no final offset found. ;ConsoleWrite("Return point 2" & @LF) Return 3 ;failed to find version info in resource table. EndIf _Skip($file, $iOff - $iReadPt) $s1 = FileRead($file, 8) $iReadPt = $iReadPt + (($iOff - $iReadPt) + 8) $A1 = _GetArray($s1) For $iNum1 = 0 To 3 $A4[$iNum1] = $A1[$iNum1] Next $VIOffset = _GetNumFromBytes($A4) ;--offset of version info. given in .rsrc section. $ReadOffset = (($VIOffset - $VLocRes) + $LocRes) For $iNum1 = 0 To 3 $A4[$iNum1] = $A1[$iNum1 + 4] Next $SizeRes = _GetNumFromBytes($A4) _Skip($file, $ReadOffset - $iReadPt) $s1 = FileRead($file, $SizeRes) ;-- read out the entire FileVersionInfo data area. FileClose($file) $file = 0 $sB = _GetByteString($s1, True) ;-- snip unicode. $Pt1 = StringInStr($sB, $sVerString) If ($Pt1 > 0) Then ;-- "VS_VER" was found, so process the string and quit. $ARet = _ProcessRes($sB) $ARet2 = $ARet Return 0 ; ok ElseIf ($BooAspack = True) Then ;-- if "VS_VER" was not found but there is an "aspack" section then try that. $ReadOffset = (($VIOffset - $VLocAspack) + $LocAspack) ;-- calculate a new file version info data offset. $file = FileOpen($sFilePath, 0) ;-- The file was closed and is now re-opened here. Keeping the file _Skip($file, $ReadOffset) ;-- open "just in case" wouldn;t have helped because the file pointer $s1 = FileRead($file, $SizeRes) ;-- for this read may be further back thean the pointer was when the file FileClose($file) ;-- was closed. So rather than try to sort out the read point, the file is just $file = 0;-- opened fresh and Skip is used. $sB = _GetByteString($s1, True) $Pt1 = StringInStr($sB, $sVerString) If ($Pt1 > 0) Then $ARet = _ProcessRes($sB) $ARet2 = $ARet Return 0 ; ok Else ;ConsoleWrite("Return point 3" & @LF) Return 3 ;failed to find version info in resource table. EndIf Else ;ConsoleWrite("Return point 4" & @LF) Return 3 ;failed to find version info in resource table. EndIf EndFunc ;==>_GetVersionInfo Func _ProcessRes($sDat) Dim $AInfo[7] ; On Error Resume Next $AInfo[0] = _GetInfo($sDat, "CompanyName") $AInfo[1] = _GetInfo($sDat, "FileDescription") $AInfo[2] = _GetInfo($sDat, "FileVersion") $AInfo[3] = _GetInfo($sDat, "ProductName") $AInfo[4] = _GetInfo($sDat, "LegalCopyright") $AInfo[5] = _GetInfo($sDat, "OriginalFilename") $AInfo[6] = _GetInfo($sDat, "Installer Engine") Return $AInfo EndFunc ;==>_ProcessRes Func _GetInfo($sStr, $sVal) ; had to rewrite this function because of incompatiblities ;between AutoIt3 and Vb string functions. Dim $Pta, $Ptb, $LenVal, $s4, $occurance = 1 $LenVal = StringLen($sVal) + 1 ;-- length of info string: "CompanyName" = 11 $Pta = StringInStr($sStr, $sVal) ;-- find string name. If ($Pta > 0) Then $Pta = $Pta + $LenVal $Ptb = $Pta + StringInStr(StringMid($sStr, $Pta + 1), "*") If $Ptb > ($Pta + 2) Then ;-- Name*value**. So start looking at 3rd character after. If that $s4 = StringMid($sStr, $Pta, ($Ptb - $Pta)) ;-- character is * then it's Name*** which means there's $s4 = StringReplace($s4, "*", "") ;--no value for that specific property. If StringInStr($s4, Chr(1), 1) = 0 Then Return $s4 ;-- check for Chr(1) which seems to be found EndIf ; between values. If it's in the string that means there is no value for EndIf Return "" EndFunc ;==>_GetInfo ;-------------- simplified version of _GetByteString For this Class. --------------------- Func _GetByteString($sStr, $SnipUnicode) Local $sRet, $iLen, $iA, $iLen2, $A2[2] ; On Error Resume Next $iLen2 = 0 If ($SnipUnicode = False) Then ReDim $A2[StringLen($sStr)] For $iLen = 1 To StringLen($sStr) $iA = Asc(StringMid($sStr, $iLen, 1)) If $iA = 0 Then $iA = 42 ;-- converts 0-byte to * ;If UBound($A2) > $iLen - 1 Then $A2[$iLen - 1] = Chr($iA) Next Else ReDim $A2[ (StringLen($sStr) / 2)] For $iLen = 1 To StringLen($sStr) Step 2 $iA = Asc(StringMid($sStr, $iLen, 1)) If $iA = 0 Then $iA = 42 ;-- converts 0-byte to * $A2[$iLen2] = Chr($iA) $iLen2 = $iLen2 + 1 Next EndIf Return _Join($A2, "") EndFunc ;==>_GetByteString ;-------------------------------- Simplified version of _GetArray. ----------------------- Func _GetArray($sStr) Dim $iA, $Len1, $Len2, $AStr[2] ; On Error Resume Next $Len1 = StringLen($sStr) ConsoleWrite("$Len1 = " & $Len1 & @LF) If $Len1 > 1 Then ReDim $AStr[ ($Len1)] For $iA = 1 To $Len1 $AStr[$iA - 1] = Asc(StringMid($sStr, $iA, 1)) Next Return $AStr EndFunc ;==>_GetArray ;-------------------- return a number from 2 or 4 bytes. --------------- Func _GetNumFromBytes($ABytes) Dim $Num1 ; Err.Clear ; On Error Resume Next ;_GetNumFromBytes = -1 If UBound($ABytes) > 1 Then $Num1 = ($ABytes[0] + $ABytes[1] * 256) If (UBound($ABytes) > 3) Then $Num1 = $Num1 + ($ABytes[2] * 65536) + ($ABytes[3] * 16777216) EndIf Return $Num1 EndIf EndFunc ;==>_GetNumFromBytes Func _Join($aStrJ, $sDelim = "") ;mimic VB Join command (array to string with delimiter) Local $sTemp If IsArray($aStrJ) Then For $x = 0 To UBound($aStrJ) - 1 $sTemp = $sTemp & $aStrJ[$x] & $sDelim Next ;ConsoleWrite("Join $sTemp =" & $sTemp & @LF) Return $sTemp EndIf SetError(1) Return "" EndFunc ;==>_Join Func _Skip($hfile, $bytes) ;mimic FSO.skip Local $temp = FileRead($hfile, $bytes) If @error Then Return 0 Return 1 EndFunc ;==>_Skip Please post any bugs. Regards, eltorro Edit: Removed exta bbcode tag Edited July 19, 2006 by eltorro Regards, [indent]ElTorro[/indent][font="Book"] Decide, Commit, Achieve[/font]_ConfigIO.au3Language Translation --uses Google(tm) MsgBox Move XML wrapper UDF XML2TreeView Zip functionality Split your GUI Save Print ScreenZipPluginEdit In Place listviewSome of my scripts on Google code
BigDaddyO Posted July 19, 2006 Author Posted July 19, 2006 Here's the latest version Excelent!!! works Great eltorro. Now I just have to integrate that in with my main script and I'm all done. Oh, and if you change say $AInfo[0] = _GetInfo($sDat, "CompanyName") to "Applies to" it will give you the Operating Systems that the patch will work on. "Returns "" if it's not an OS patch" I tested this on some patches all the way back to 2004 with no problems. Thanks, Mike
GaryFrost Posted July 19, 2006 Posted July 19, 2006 (edited) I have to agreee great job, only problem I found when i was playing around with it this morning was the file version wasn't getting populated. I know the file I was testing has a version 1.0.0.0 or in the file it reads 1 hadn't had time to look at the logic that gets the info, but here was a quick test to see what information I could retrieve. Edit: Fixed problem if FileVersion is 1 expandcollapse popup;-- This script includes a class and a demo. Drop any PE file onto the script (EXE, DLL, OCX) ;-- to get file properties. The class, ClsProps, is a complete set of all functions needed ;-- get file properties using only VBS and the Textstream object. ;-- The file properties returned are those that are found when a file is right-clicked, ;-- Properties menu is clicked, and Version tab is selected. This information ;-- is included in most PE (portable executable) files. ;-- Thank you to Ed Gruberman (http://www.rjump.com) for help with this code. ;-- He corrected a bug and also worked out the "aspack" variation whereby some ;-- PE files have been compressed and require a different method to find the version info. j. ;-- NOTE: This script is written to be compact for pasting into scripts. It uses simplified versions of some functions from ;-- the Textstream Binary Ops and Base 64 download, using only the functionality needed to get file version info., ;-- in order to keep this class as small as possible. If you want to use functions such as _GetArray or _GetByteString ;-- you may want to download the other package. It provides a fairly complete set of methods for working with binary files. ;-- Demo script -------------------- ;Converted to AutoItV3 Steve Podhajecki [eltorro] steve@ocotillo.sytes.net Global $sAst, $ANums Global $Arg, $i2, $AReturn, $s ;See OnAutoItStart() ;$Arg = "Windows2000-KB914388-x86-ENU.EXE" ;$Arg= "C:\Windows\System32\cmdhere.dll" ;$Arg = "C:\Windows\System32\ZlibTool.ocx" ;$Arg = "C:\Windows\System32\Calc.exe" $i2 = _GetVersionInfo($Arg, $AReturn) Switch $i2 Case 1 MsgBox(266288, @ScriptName, "File path not valid") Case 2 MsgBox(266288, @ScriptName, "Unable to find Resource table header in file.") Case 3 MsgBox(266288, @ScriptName, "Unable to find file version info. in file.") Case 4 MsgBox(266288, @ScriptName, "This is not a PE file.") Case 5 MsgBox(266288, @ScriptName, "This is a 16-bit executable. It is not a PE file.") Case 0 ; success $s = "Company: " & $AReturn[0] & @CRLF $s = $s & "File Description: " & $AReturn[1] & @CRLF $s = $s & "File Version: " & $AReturn[2] & @CRLF $s = $s & "Product Name: " & $AReturn[3] & @CRLF $s = $s & "Copyright: " & $AReturn[4] & @CRLF $s = $s & "Original file name: " & $AReturn[5] & @CRLF $s = $s & "Installer Engine: " & $AReturn[6] & @CRLF $s = $s & "Applies to: " & $AReturn[7] & @CRLF $s = $s & "Build Date: " & $AReturn[8] & @CRLF $s = $s & "Installation Type: " & $AReturn[9] & @CRLF $s = $s & "Installer Version: " & $AReturn[10] & @CRLF $s = $s & "Internal Name: " & $AReturn[11] & @CRLF $s = $s & "KB Article Name: " & $AReturn[12] & @CRLF $s = $s & "Support Link: " & $AReturn[13] & @CRLF $s = $s & "Package Type: " & $AReturn[14] & @CRLF $s = $s & "Proc. Architecture: " & $AReturn[15] & @CRLF $s = $s & "Self-Extractor Version: " & $AReturn[16] & @CRLF $s = $s & "Display Name: " & $AReturn[17] & @CRLF MsgBox(266288, @ScriptName, $Arg & @CRLF & @CRLF & $s) EndSwitch ;-- End demo script ------------------------------------------------------------ ;-- //////////////////////// Start Class: ClsProps /////////////////////////////////////////////// ; ; Functions in this class. (All functions are needed for getting file version information.) ; ; Public Function _GetVersionInfo($sFilePath, $ARet2) - returns version information For PE files. ; On success $ARet2 returns array(5) containing version info. strings for file. ; Function return error codes: 0 = success. 1 = invalid file path. 2 = no .rsrc table listed in section table. ; 3 = failed to find version info. 4 = not a PE file. 5 = file is a 16-bit executable. ("NE" file rather than "PE") ; ; Private (internal) functions: ; _GetArray(StringIn) - convert a string to an array of byte values. ; _GetByteString(StringIn, $SnipUnicode) - convert a string to a manageable version. If $SnipUnicode = True then get only every 2nd byte. ; _GetNumFromBytes(array) - takes array of ubound 1 or 3. return numeric value for 2 or 4 bytes. ; ; ; ; ; ;----------------//////////// --- BEGIN Class HERE --- /////////////////////------------------------------------------- Func OnAutoItStart() ;uncomment this for drag to or commandline usage. If $CmdLine[0] = 0 Then MsgBox(266288, @ScriptName, "Drop A PE file [EXE-DLL-OCX] onto this script to get version information." & _ @CRLF & "Commad Prompt Usage:" & @CRLF & @ScriptName & " " & "myfile.[exe,dll,ocx]") Exit EndIf Global $Arg = $CmdLine[1] EndFunc ;==>OnAutoItStart ;-- The public function in this class: _GetVersionInfo ----------------------------------------- Func _GetVersionInfo($sFilePath, ByRef $ARet2) ;-- return array(5) Local $ARet, $s1, $sB, $Pt1, $sRes, $A1, $A4[4], $A2[4], $LocRes, $VLocRes, $SizeRes, $iOff, $Boo, $sVerString, $sMarker Local $iNum1, $iNum2, $iReadPt, $iNum3, $LocAspack, $VLocAspack, $VIOffset, $ReadOffset, $BooAspack ; On Error Resume Next If Not FileExists($sFilePath) Then Return 1 ;bad path. EndIf $sRes = ".rsrc" $sVerString = "VS_VER" $BooAspack = False $file = FileOpen($sFilePath, 0) $s1 = FileRead($sFilePath, 2048) FileClose($file) $file = 0 ;ConsoleWrite($s1) $A1 = _GetArray(StringMid($s1, 61, 2)) ;-- get number value at offset 60 that points to PE signature address. $iNum1 = (_GetNumFromBytes($A1) + 1) ;-- get offset of "PE00" $sB = _GetByteString($s1, False) ;-- get a workable string with Chr(0) replaced by "*". $sMarker = StringMid($sB, $iNum1, 4) ;ConsoleWrite("$sMarker ="&$sMarker&@LF) If ($sMarker <> "PE**") Then If StringLeft($sMarker, 2) = "NE" Then Return 5 ;-- 16 bit. Else Return 4 ;-- no PE signature found. EndIf EndIf $Pt1 = StringInStr($sB, $sRes) ;-- find .rsrc table. If ($Pt1 = 0) Then Return 2 ;no resource table header found. EndIf $Pt1 = $Pt1 + 12 ;-- size of raw data is 4 bytes at offset of 16 into the .rsrc table. $A1 = _GetArray(StringMid($s1, $Pt1, 12)) ;-- get the same string as a numeric array to Read offset numbers. For $iOff = 0 To 3 ;ConsoleWrite("$iOff ="&$iOff&@LF) $A4[$iOff] = $A1[$iOff] Next $VLocRes = _GetNumFromBytes($A4) For $iOff = 0 To 3 $A4[$iOff] = $A1[$iOff + 4] Next $SizeRes = _GetNumFromBytes($A4) ;--size of resource section in bytes. For $iOff = 0 To 3 $A4[$iOff] = $A1[$iOff + 8] Next $LocRes = _GetNumFromBytes($A4) ;-- offset location of resource section. $Pt1 = StringInStr(1, $sB, ".aspack") ;-- find .rsrc table. If ($Pt1 > 0) Then $BooAspack = True $Pt1 = $Pt1 + 12 ;-- virtual offset is first 4 bytes; raw offset is bytes 9-12. $A1 = _GetArray(StringMid($s1, $Pt1, 12)) For $iOff = 0 To 3 $A4[$iOff] = $A1[$iOff] Next $VLocAspack = _GetNumFromBytes($A4) For $iOff = 0 To 3 $A4[$iOff] = $A1[$iOff + 8] Next $LocAspack = _GetNumFromBytes($A4) EndIf $Boo = False $file = FileOpen($sFilePath, 0) ;MsgBox(0, "$LocRes", "$LocRes+12 =" & $LocRes + 12) ;~ ConsoleWrite("$LocRes+12 =" & $LocRes + 12 & @CRLF) _Skip($file, $LocRes + 12) $s1 = FileRead($file, 2) ;-- Read bytes 13,14 to get number of named resource types. $iNum1 = Asc($s1) ;-- number of names. ; ConsoleWrite("$iNum1 =" & $iNum1 & @LF) $s2 = FileRead($file, 2) ;-- Read bytes 15,16 to get number of numbered resource types. $iNum2 = Asc($s2);-- number of nums. ; ConsoleWrite("$iNum2 =" & $iNum2 & @LF) If $iNum2 = 0 Then ;-- no numbered entries. have to quit here. Return 3 EndIf If ($iNum1 > 0) Then _Skip($file, $iNum1 * 8) ;-- Skip past named entries. $iReadPt = $LocRes + 16 + ($iNum1 * 8) ;-- update file offset variable because this will be needed. ;ConsoleWrite("$iReadPt ="& $iReadPt&@LF) $Boo = False For $iOff = 1 To $iNum2 $s1 = FileRead($file, 8) $iReadPt = $iReadPt + 8 If (Asc($s1) = 16) Then ;-- this is version info. entry. $Boo = True ExitLoop EndIf Next If ($Boo = False) Then ;-- have to quit. no version info. entry found. ;ConsoleWrite("Return point 1" & @LF) Return 3 ;failed to find version info in resource table. EndIf $A1 = _GetArray($s1) ;-- get a byte array for version info entry at top level. ;ConsoleWrite("Ubound>>"&UBound($A1)) $iOff = 0 $iNum3 = 1 ;Do While 1 For $iNum1 = 0 To 2 ;-- get offset number to next level from 2nd 4 bytes of entry structure. $A4[$iNum1] = $A1[$iNum1 + 4] Next $A4[3] = 0 $iNum2 = _GetNumFromBytes($A4) If ($A1[7] > 127) Then ;-- high bit was set in entry offset value, so it;s just a pointer to another pointer. $iNum2 = $LocRes + $iNum2 + 16 _Skip($file, $iNum2 - $iReadPt) ;- 1) $s1 = FileRead($file, 8) $iReadPt = $iReadPt + (($iNum2 - $iReadPt) + 8) $A1 = _GetArray($s1) Else ;-- this is the offset of version info offset info.! $iOff = ($iNum2 + $LocRes) ;ExitLoop ExitLoop EndIf $iNum3 = $iNum3 + 1 If ($iNum3 > 10) Then ExitLoop WEnd ;Loop If ($iOff = 0) Then ;-- have to quit. no final offset found. ;ConsoleWrite("Return point 2" & @LF) Return 3 ;failed to find version info in resource table. EndIf _Skip($file, $iOff - $iReadPt) $s1 = FileRead($file, 8) $iReadPt = $iReadPt + (($iOff - $iReadPt) + 8) $A1 = _GetArray($s1) For $iNum1 = 0 To 3 $A4[$iNum1] = $A1[$iNum1] Next $VIOffset = _GetNumFromBytes($A4) ;--offset of version info. given in .rsrc section. $ReadOffset = (($VIOffset - $VLocRes) + $LocRes) For $iNum1 = 0 To 3 $A4[$iNum1] = $A1[$iNum1 + 4] Next $SizeRes = _GetNumFromBytes($A4) _Skip($file, $ReadOffset - $iReadPt) $s1 = FileRead($file, $SizeRes) ;-- read out the entire FileVersionInfo data area. FileClose($file) $file = 0 $sB = _GetByteString($s1, True) ;-- snip unicode. $Pt1 = StringInStr($sB, $sVerString) If ($Pt1 > 0) Then ;-- "VS_VER" was found, so process the string and quit. $ARet = _ProcessRes($sB) $ARet2 = $ARet Return 0 ; ok ElseIf ($BooAspack = True) Then ;-- if "VS_VER" was not found but there is an "aspack" section then try that. $ReadOffset = (($VIOffset - $VLocAspack) + $LocAspack) ;-- calculate a new file version info data offset. $file = FileOpen($sFilePath, 0) ;-- The file was closed and is now re-opened here. Keeping the file _Skip($file, $ReadOffset) ;-- open "just in case" wouldn;t have helped because the file pointer $s1 = FileRead($file, $SizeRes) ;-- for this read may be further back thean the pointer was when the file FileClose($file) ;-- was closed. So rather than try to sort out the read point, the file is just $file = 0;-- opened fresh and Skip is used. $sB = _GetByteString($s1, True) $Pt1 = StringInStr($sB, $sVerString) If ($Pt1 > 0) Then $ARet = _ProcessRes($sB) $ARet2 = $ARet Return 0 ; ok Else ;ConsoleWrite("Return point 3" & @LF) Return 3 ;failed to find version info in resource table. EndIf Else ;ConsoleWrite("Return point 4" & @LF) Return 3 ;failed to find version info in resource table. EndIf EndFunc ;==>_GetVersionInfo Func _ProcessRes($sDat) Dim $AInfo[18] ; On Error Resume Next $AInfo[0] = _GetInfo($sDat, "CompanyName") $AInfo[1] = _GetInfo($sDat, "FileDescription") $AInfo[2] = _GetInfo($sDat, "FileVersion") $AInfo[3] = _GetInfo($sDat, "ProductName") $AInfo[4] = _GetInfo($sDat, "LegalCopyright") $AInfo[5] = _GetInfo($sDat, "OriginalFilename") $AInfo[6] = _GetInfo($sDat, "Installer Engine") $AInfo[7] = _GetInfo($sDat, "Applies to") $AInfo[8] = _GetInfo($sDat, "Build Date") $AInfo[9] = _GetInfo($sDat, "Installation Type") $AInfo[10] = _GetInfo($sDat, "Installer Version") $AInfo[11] = _GetInfo($sDat, "InternalName") $AInfo[12] = _GetInfo($sDat, "KB Article Number") $AInfo[13] = _GetInfo($sDat, "Support Link") $AInfo[14] = _GetInfo($sDat, "Package Type") $AInfo[15] = _GetInfo($sDat, "Proc. Architecture") $AInfo[16] = _GetInfo($sDat, "Self-Extractor Version") $AInfo[17] = _GetInfo($sDat, "DisplayName") Return $AInfo EndFunc ;==>_ProcessRes Func _GetInfo($sStr, $sVal) ; had to rewrite this function because of incompatiblities ;between AutoIt3 and Vb string functions. Dim $Pta, $Ptb, $LenVal, $s4, $occurance = 1 $LenVal = StringLen($sVal) + 1 ;-- length of info string: "CompanyName" = 11 $Pta = StringInStr($sStr, $sVal) ;-- find string name. If ($Pta > 0) Then $Pta = $Pta + $LenVal $Ptb = $Pta + StringInStr(StringMid($sStr, $Pta + 1), "*") ;~ ConsoleWrite(StringMid($sStr, $Pta + 1) & @LF) If $Ptb > ($Pta + 2) Or ($Ptb = ($Pta + 2) And $sVal = "FileVersion") Then ;-- Name*value**. So start looking at 3rd character after. If that $s4 = StringMid($sStr, $Pta, ($Ptb - $Pta)) ;-- character is * then it's Name*** which means there's $s4 = StringReplace($s4, "*", "") ;--no value for that specific property. If StringInStr($s4, Chr(1), 1) = 0 Then Return $s4 ;-- check for Chr(1) which seems to be found EndIf ; between values. If it's in the string that means there is no value for EndIf Return "" EndFunc ;==>_GetInfo ;-------------- simplified version of _GetByteString For this Class. --------------------- Func _GetByteString($sStr, $SnipUnicode) Local $sRet, $iLen, $iA, $iLen2, $A2[2] ; On Error Resume Next $iLen2 = 0 If ($SnipUnicode = False) Then ReDim $A2[StringLen($sStr) ] For $iLen = 1 To StringLen($sStr) $iA = Asc(StringMid($sStr, $iLen, 1)) If $iA = 0 Then $iA = 42 ;-- converts 0-byte to * ;If UBound($A2) > $iLen - 1 Then $A2[$iLen - 1] = Chr($iA) Next Else ReDim $A2[ (StringLen($sStr) / 2) ] For $iLen = 1 To StringLen($sStr) Step 2 $iA = Asc(StringMid($sStr, $iLen, 1)) If $iA = 0 Then $iA = 42 ;-- converts 0-byte to * $A2[$iLen2] = Chr($iA) $iLen2 = $iLen2 + 1 Next EndIf Return _Join($A2, "") EndFunc ;==>_GetByteString ;-------------------------------- Simplified version of _GetArray. ----------------------- Func _GetArray($sStr) Dim $iA, $Len1, $Len2, $AStr[2] ; On Error Resume Next $Len1 = StringLen($sStr) If $Len1 > 1 Then ReDim $AStr[ ($Len1) ] For $iA = 1 To $Len1 $AStr[$iA - 1] = Asc(StringMid($sStr, $iA, 1)) Next Return $AStr EndFunc ;==>_GetArray ;-------------------- return a number from 2 or 4 bytes. --------------- Func _GetNumFromBytes($ABytes) Dim $Num1 ; Err.Clear ; On Error Resume Next ;_GetNumFromBytes = -1 If UBound($ABytes) > 1 Then $Num1 = ($ABytes[0] + $ABytes[1] * 256) If (UBound($ABytes) > 3) Then $Num1 = $Num1 + ($ABytes[2] * 65536) + ($ABytes[3] * 16777216) EndIf Return $Num1 EndIf EndFunc ;==>_GetNumFromBytes Func _Join($aStrJ, $sDelim = "") ;mimic VB Join command (array to string with delimiter) Local $sTemp If IsArray($aStrJ) Then For $x = 0 To UBound($aStrJ) - 1 $sTemp = $sTemp & $aStrJ[$x] & $sDelim Next ;ConsoleWrite("Join $sTemp =" & $sTemp & @LF) Return $sTemp EndIf SetError(1) Return "" EndFunc ;==>_Join Func _Skip($hfile, $bytes) ;mimic FSO.skip Local $temp = FileRead($hfile, $bytes) If @error Then Return 0 Return 1 EndFunc ;==>_Skip Edited July 19, 2006 by gafrost SciTE for AutoItDirections for Submitting Standard UDFs Don't argue with an idiot; people watching may not be able to tell the difference.
eltorro Posted July 19, 2006 Posted July 19, 2006 Just for curiosity, what does the vbscript return on the same file? Regards, [indent]ElTorro[/indent][font="Book"] Decide, Commit, Achieve[/font]_ConfigIO.au3Language Translation --uses Google(tm) MsgBox Move XML wrapper UDF XML2TreeView Zip functionality Split your GUI Save Print ScreenZipPluginEdit In Place listviewSome of my scripts on Google code
GaryFrost Posted July 19, 2006 Posted July 19, 2006 Just for curiosity, what does the vbscript return on the same file? Same problem in vb script, if the version number is a single digit it appears to be a problem that was overlooked, I think I resolved that problem. I decided to make it a little more generic, just change the $props array to what you want to collect. expandcollapse popup;-- This script includes a class and a demo. Drop any PE file onto the script (EXE, DLL, OCX) ;-- to get file properties. The class, ClsProps, is a complete set of all functions needed ;-- get file properties using only VBS and the Textstream object. ;-- The file properties returned are those that are found when a file is right-clicked, ;-- Properties menu is clicked, and Version tab is selected. This information ;-- is included in most PE (portable executable) files. ;-- Thank you to Ed Gruberman (http://www.rjump.com) for help with this code. ;-- He corrected a bug and also worked out the "aspack" variation whereby some ;-- PE files have been compressed and require a different method to find the version info. j. ;-- NOTE: This script is written to be compact for pasting into scripts. It uses simplified versions of some functions from ;-- the Textstream Binary Ops and Base 64 download, using only the functionality needed to get file version info., ;-- in order to keep this class as small as possible. If you want to use functions such as _GetArray or _GetByteString ;-- you may want to download the other package. It provides a fairly complete set of methods for working with binary files. ;-- Demo script -------------------- ;Converted to AutoItV3 Steve Podhajecki [eltorro] steve@ocotillo.sytes.net Global $sAst, $ANums Global $Arg, $i2, $AReturn, $s Global $props[18] = ["CompanyName", "FileDescription", "FileVersion", "ProductName", "LegalCopyright", _ "OriginalFilename", "Installer Engine", "Applies to", "Build Date", "Installation Type", _ "Installer Version", "InternalName", "KB Article Number", "Support Link", "Package Type", _ "Proc. Architecture", "Self-Extractor Version", "DisplayName"] ;See OnAutoItStart() ;$Arg = "Windows2000-KB914388-x86-ENU.EXE" ;$Arg= "C:\Windows\System32\cmdhere.dll" ;$Arg = "C:\Windows\System32\ZlibTool.ocx" ;$Arg = "C:\Windows\System32\Calc.exe" $i2 = _GetVersionInfo($Arg, $AReturn) Switch $i2 Case 1 MsgBox(266288, @ScriptName, "File path not valid") Case 2 MsgBox(266288, @ScriptName, "Unable to find Resource table header in file.") Case 3 MsgBox(266288, @ScriptName, "Unable to find file version info. in file.") Case 4 MsgBox(266288, @ScriptName, "This is not a PE file.") Case 5 MsgBox(266288, @ScriptName, "This is a 16-bit executable. It is not a PE file.") Case 0 ; success $s = "" For $x = 0 To UBound($props) - 1 $s &= $props[$x] & ": " & $AReturn[$x] & @CRLF Next MsgBox(266288, @ScriptName, $Arg & @CRLF & @CRLF & $s) EndSwitch ;-- End demo script ------------------------------------------------------------ ;-- //////////////////////// Start Class: ClsProps /////////////////////////////////////////////// ; ; Functions in this class. (All functions are needed for getting file version information.) ; ; Public Function _GetVersionInfo($sFilePath, $ARet2) - returns version information For PE files. ; On success $ARet2 returns array(5) containing version info. strings for file. ; Function return error codes: 0 = success. 1 = invalid file path. 2 = no .rsrc table listed in section table. ; 3 = failed to find version info. 4 = not a PE file. 5 = file is a 16-bit executable. ("NE" file rather than "PE") ; ; Private (internal) functions: ; _GetArray(StringIn) - convert a string to an array of byte values. ; _GetByteString(StringIn, $SnipUnicode) - convert a string to a manageable version. If $SnipUnicode = True then get only every 2nd byte. ; _GetNumFromBytes(array) - takes array of ubound 1 or 3. return numeric value for 2 or 4 bytes. ; ; ; ; ; ;----------------//////////// --- BEGIN Class HERE --- /////////////////////------------------------------------------- Func OnAutoItStart() ;uncomment this for drag to or commandline usage. If $CmdLine[0] = 0 Then MsgBox(266288, @ScriptName, "Drop A PE file [EXE-DLL-OCX] onto this script to get version information." & _ @CRLF & "Commad Prompt Usage:" & @CRLF & @ScriptName & " " & "myfile.[exe,dll,ocx]") Exit EndIf Global $Arg = $CmdLine[1] EndFunc ;==>OnAutoItStart ;-- The public function in this class: _GetVersionInfo ----------------------------------------- Func _GetVersionInfo($sFilePath, ByRef $ARet2) ;-- return array(5) Local $ARet, $s1, $sB, $Pt1, $sRes, $A1, $A4[4], $A2[4], $LocRes, $VLocRes, $SizeRes, $iOff, $Boo, $sVerString, $sMarker Local $iNum1, $iNum2, $iReadPt, $iNum3, $LocAspack, $VLocAspack, $VIOffset, $ReadOffset, $BooAspack ; On Error Resume Next If Not FileExists($sFilePath) Then Return 1 ;bad path. EndIf $sRes = ".rsrc" $sVerString = "VS_VER" $BooAspack = False $file = FileOpen($sFilePath, 0) $s1 = FileRead($sFilePath, 2048) FileClose($file) $file = 0 ;ConsoleWrite($s1) $A1 = _GetArray(StringMid($s1, 61, 2)) ;-- get number value at offset 60 that points to PE signature address. $iNum1 = (_GetNumFromBytes($A1) + 1) ;-- get offset of "PE00" $sB = _GetByteString($s1, False) ;-- get a workable string with Chr(0) replaced by "*". $sMarker = StringMid($sB, $iNum1, 4) ;~ ConsoleWrite("$sMarker ="&$sMarker&@LF) If ($sMarker <> "PE**") Then If StringLeft($sMarker, 2) = "NE" Then Return 5 ;-- 16 bit. Else Return 4 ;-- no PE signature found. EndIf EndIf $Pt1 = StringInStr($sB, $sRes) ;-- find .rsrc table. If ($Pt1 = 0) Then Return 2 ;no resource table header found. EndIf $Pt1 = $Pt1 + 12 ;-- size of raw data is 4 bytes at offset of 16 into the .rsrc table. $A1 = _GetArray(StringMid($s1, $Pt1, 12)) ;-- get the same string as a numeric array to Read offset numbers. For $iOff = 0 To 3 ;ConsoleWrite("$iOff ="&$iOff&@LF) $A4[$iOff] = $A1[$iOff] Next $VLocRes = _GetNumFromBytes($A4) For $iOff = 0 To 3 $A4[$iOff] = $A1[$iOff + 4] Next $SizeRes = _GetNumFromBytes($A4) ;--size of resource section in bytes. For $iOff = 0 To 3 $A4[$iOff] = $A1[$iOff + 8] Next $LocRes = _GetNumFromBytes($A4) ;-- offset location of resource section. $Pt1 = StringInStr(1, $sB, ".aspack") ;-- find .rsrc table. If ($Pt1 > 0) Then $BooAspack = True $Pt1 = $Pt1 + 12 ;-- virtual offset is first 4 bytes; raw offset is bytes 9-12. $A1 = _GetArray(StringMid($s1, $Pt1, 12)) For $iOff = 0 To 3 $A4[$iOff] = $A1[$iOff] Next $VLocAspack = _GetNumFromBytes($A4) For $iOff = 0 To 3 $A4[$iOff] = $A1[$iOff + 8] Next $LocAspack = _GetNumFromBytes($A4) EndIf $Boo = False $file = FileOpen($sFilePath, 0) ;MsgBox(0, "$LocRes", "$LocRes+12 =" & $LocRes + 12) ;~ ConsoleWrite("$LocRes+12 =" & $LocRes + 12 & @CRLF) _Skip($file, $LocRes + 12) $s1 = FileRead($file, 2) ;-- Read bytes 13,14 to get number of named resource types. $iNum1 = Asc($s1) ;-- number of names. ; ConsoleWrite("$iNum1 =" & $iNum1 & @LF) $s2 = FileRead($file, 2) ;-- Read bytes 15,16 to get number of numbered resource types. $iNum2 = Asc($s2);-- number of nums. ; ConsoleWrite("$iNum2 =" & $iNum2 & @LF) If $iNum2 = 0 Then ;-- no numbered entries. have to quit here. Return 3 EndIf If ($iNum1 > 0) Then _Skip($file, $iNum1 * 8) ;-- Skip past named entries. $iReadPt = $LocRes + 16 + ($iNum1 * 8) ;-- update file offset variable because this will be needed. ;ConsoleWrite("$iReadPt ="& $iReadPt&@LF) $Boo = False For $iOff = 1 To $iNum2 $s1 = FileRead($file, 8) $iReadPt = $iReadPt + 8 If (Asc($s1) = 16) Then ;-- this is version info. entry. $Boo = True ExitLoop EndIf Next If ($Boo = False) Then ;-- have to quit. no version info. entry found. ;ConsoleWrite("Return point 1" & @LF) Return 3 ;failed to find version info in resource table. EndIf $A1 = _GetArray($s1) ;-- get a byte array for version info entry at top level. ;ConsoleWrite("Ubound>>"&UBound($A1)) $iOff = 0 $iNum3 = 1 ;Do While 1 For $iNum1 = 0 To 2 ;-- get offset number to next level from 2nd 4 bytes of entry structure. $A4[$iNum1] = $A1[$iNum1 + 4] Next $A4[3] = 0 $iNum2 = _GetNumFromBytes($A4) If ($A1[7] > 127) Then ;-- high bit was set in entry offset value, so it;s just a pointer to another pointer. $iNum2 = $LocRes + $iNum2 + 16 _Skip($file, $iNum2 - $iReadPt) ;- 1) $s1 = FileRead($file, 8) $iReadPt = $iReadPt + (($iNum2 - $iReadPt) + 8) $A1 = _GetArray($s1) Else ;-- this is the offset of version info offset info.! $iOff = ($iNum2 + $LocRes) ;ExitLoop ExitLoop EndIf $iNum3 = $iNum3 + 1 If ($iNum3 > 10) Then ExitLoop WEnd ;Loop If ($iOff = 0) Then ;-- have to quit. no final offset found. ;ConsoleWrite("Return point 2" & @LF) Return 3 ;failed to find version info in resource table. EndIf _Skip($file, $iOff - $iReadPt) $s1 = FileRead($file, 8) $iReadPt = $iReadPt + (($iOff - $iReadPt) + 8) $A1 = _GetArray($s1) For $iNum1 = 0 To 3 $A4[$iNum1] = $A1[$iNum1] Next $VIOffset = _GetNumFromBytes($A4) ;--offset of version info. given in .rsrc section. $ReadOffset = (($VIOffset - $VLocRes) + $LocRes) For $iNum1 = 0 To 3 $A4[$iNum1] = $A1[$iNum1 + 4] Next $SizeRes = _GetNumFromBytes($A4) _Skip($file, $ReadOffset - $iReadPt) $s1 = FileRead($file, $SizeRes) ;-- read out the entire FileVersionInfo data area. FileClose($file) $file = 0 $sB = _GetByteString($s1, True) ;-- snip unicode. $Pt1 = StringInStr($sB, $sVerString) If ($Pt1 > 0) Then ;-- "VS_VER" was found, so process the string and quit. $ARet = _ProcessRes($sB) $ARet2 = $ARet Return 0 ; ok ElseIf ($BooAspack = True) Then ;-- if "VS_VER" was not found but there is an "aspack" section then try that. $ReadOffset = (($VIOffset - $VLocAspack) + $LocAspack) ;-- calculate a new file version info data offset. $file = FileOpen($sFilePath, 0) ;-- The file was closed and is now re-opened here. Keeping the file _Skip($file, $ReadOffset) ;-- open "just in case" wouldn;t have helped because the file pointer $s1 = FileRead($file, $SizeRes) ;-- for this read may be further back thean the pointer was when the file FileClose($file) ;-- was closed. So rather than try to sort out the read point, the file is just $file = 0;-- opened fresh and Skip is used. $sB = _GetByteString($s1, True) $Pt1 = StringInStr($sB, $sVerString) If ($Pt1 > 0) Then $ARet = _ProcessRes($sB) $ARet2 = $ARet Return 0 ; ok Else ;ConsoleWrite("Return point 3" & @LF) Return 3 ;failed to find version info in resource table. EndIf Else ;ConsoleWrite("Return point 4" & @LF) Return 3 ;failed to find version info in resource table. EndIf EndFunc ;==>_GetVersionInfo Func _ProcessRes($sDat) Dim $AInfo[UBound($props) ] ; On Error Resume Next For $x = 0 To UBound($props) - 1 $AInfo[$x] = _GetInfo($sDat, $props[$x]) Next Return $AInfo EndFunc ;==>_ProcessRes Func _GetInfo($sStr, $sVal) ; had to rewrite this function because of incompatiblities ;between AutoIt3 and Vb string functions. Dim $Pta, $Ptb, $LenVal, $s4, $occurance = 1 $LenVal = StringLen($sVal) + 1 ;-- length of info string: "CompanyName" = 11 $Pta = StringInStr($sStr, $sVal) ;-- find string name. If ($Pta > 0) Then $Pta = $Pta + $LenVal $Ptb = $Pta + StringInStr(StringMid($sStr, $Pta + 1), "*") ;~ ConsoleWrite(StringMid($sStr, $Pta + 1) & @LF) ;~ ConsoleWrite(StringMid($sStr, $Pta, ($Ptb - $Pta)) & @LF) If $Ptb > ($Pta + 2) Or ($Ptb = ($Pta + 2) And $sVal = "FileVersion") Then ;-- Name*value**. So start looking at 3rd character after. If that $s4 = StringMid($sStr, $Pta, ($Ptb - $Pta)) ;-- character is * then it's Name*** which means there's $s4 = StringReplace($s4, "*", "") ;--no value for that specific property. If StringInStr($s4, Chr(1), 1) = 0 Then Return $s4 ;-- check for Chr(1) which seems to be found EndIf ; between values. If it's in the string that means there is no value for EndIf Return "" EndFunc ;==>_GetInfo ;-------------- simplified version of _GetByteString For this Class. --------------------- Func _GetByteString($sStr, $SnipUnicode) Local $sRet, $iLen, $iA, $iLen2, $A2[2] ; On Error Resume Next $iLen2 = 0 If ($SnipUnicode = False) Then ReDim $A2[StringLen($sStr) ] For $iLen = 1 To StringLen($sStr) $iA = Asc(StringMid($sStr, $iLen, 1)) If $iA = 0 Then $iA = 42 ;-- converts 0-byte to * ;If UBound($A2) > $iLen - 1 Then $A2[$iLen - 1] = Chr($iA) Next Else ReDim $A2[ (StringLen($sStr) / 2) ] For $iLen = 1 To StringLen($sStr) Step 2 $iA = Asc(StringMid($sStr, $iLen, 1)) If $iA = 0 Then $iA = 42 ;-- converts 0-byte to * $A2[$iLen2] = Chr($iA) $iLen2 = $iLen2 + 1 Next EndIf Return _Join($A2, "") EndFunc ;==>_GetByteString ;-------------------------------- Simplified version of _GetArray. ----------------------- Func _GetArray($sStr) Dim $iA, $Len1, $Len2, $AStr[2] ; On Error Resume Next $Len1 = StringLen($sStr) If $Len1 > 1 Then ReDim $AStr[ ($Len1) ] For $iA = 1 To $Len1 $AStr[$iA - 1] = Asc(StringMid($sStr, $iA, 1)) Next Return $AStr EndFunc ;==>_GetArray ;-------------------- return a number from 2 or 4 bytes. --------------- Func _GetNumFromBytes($ABytes) Dim $Num1 ; Err.Clear ; On Error Resume Next ;_GetNumFromBytes = -1 If UBound($ABytes) > 1 Then $Num1 = ($ABytes[0] + $ABytes[1] * 256) If (UBound($ABytes) > 3) Then $Num1 = $Num1 + ($ABytes[2] * 65536) + ($ABytes[3] * 16777216) EndIf Return $Num1 EndIf EndFunc ;==>_GetNumFromBytes Func _Join($aStrJ, $sDelim = "") ;mimic VB Join command (array to string with delimiter) Local $sTemp If IsArray($aStrJ) Then For $x = 0 To UBound($aStrJ) - 1 $sTemp = $sTemp & $aStrJ[$x] & $sDelim Next ;ConsoleWrite("Join $sTemp =" & $sTemp & @LF) Return $sTemp EndIf SetError(1) Return "" EndFunc ;==>_Join Func _Skip($hfile, $bytes) ;mimic FSO.skip Local $temp = FileRead($hfile, $bytes) If @error Then Return 0 Return 1 EndFunc ;==>_Skip SciTE for AutoItDirections for Submitting Standard UDFs Don't argue with an idiot; people watching may not be able to tell the difference.
GaryFrost Posted July 19, 2006 Posted July 19, 2006 no need to change code with this one, if you want more that the basic information pass the information in wanted, i.e. expandcollapse popup"Windows2000-KB914388-x86-ENU.EXE" "CompanyName" "FileDescription" "FileVersion" "ProductName" "LegalCopyright" "OriginalFilename" "Installer Engine" "Applies to" "Build Date" oÝ÷ Ù«¢+Ø(ì´´Q¡¥ÌÍÉ¥ÁÐ¥¹±Õ̱Í̹µ¼¸É½À¹äA¥±½¹Ñ¼Ñ¡ÍÉ¥ÁСa°10°= `¤(촴ѼХ±ÁɽÁÉѥ̸Q¡±Ḭ́ ±ÍAɽÁ̰¥Ì½µÁ±ÑÍн±°Õ¹Ñ¥½¹Ì¹(ì´´Ð¥±ÁɽÁÉÑ¥ÌÕÍ¥¹½¹±äY L¹Ñ¡QáÑÍÑÉ´½©Ð¸(ì´´Q¡¥±ÁɽÁÉÑ¥ÌÉÑÕɹÉÑ¡½ÍÑ¡Ðɽչݡ¸¥±¥ÌÉ¥¡Ðµ±¥°(ì´´AɽÁÉѥ̵¹Ô¥Ì±¥°¹YÉÍ¥½¸Ñ¥ÌͱѸQ¡¥Ì¥¹½ÉµÑ¥½¸(ì´´¥Ì¥¹±Õ¥¸µ½ÍÐA¡Á½ÉѱáÕѱ¤¥±Ì¸((ì´´Q¡¹¬å½ÔѼÉÕɵ¸¡¡ÑÑÀè¼½ÝÝܹɩյÀ¹½´¤½È¡±ÀÝ¥Ñ Ñ¡¥Ì½¸(ì´´!½ÉÉÑÕ¹±Í¼Ý½É½ÕÐÑ¡ÅÕ½ÐíÍÁ¬ÅÕ½ÐìÙɥѥ½¸Ý¡Éäͽµ(ì´´A¥±Ì¡Ù¸½µÁÉÍ͹ÉÅեɥɹеѡ½Ñ¼¥¹Ñ¡ÙÉÍ¥½¸¥¹¼¸¨¸((ì´´9=QèQ¡¥ÌÍÉ¥ÁÐ¥ÌÝÉ¥ÑѸѼ½µÁнÈÁÍÑ¥¹¥¹Ñ¼ÍÉ¥ÁÑ̸%ÐÕÍÌÍ¥µÁ±¥¥ÙÉÍ¥½¹Ì½Í½µÕ¹Ñ¥½¹Ìɽ´(ì´´Ñ¡QáÑÍÑÉ´ ¥¹Éä=Á̹ ÍØÐ½Ý¹±½°ÕÍ¥¹½¹±äѡչѥ½¹±¥Ñä¹Ñ¼Ð¥±ÙÉÍ¥½¸¥¹¼¸°(ì´´¥¸½ÉÈѼÀÑ¡¥Ì±ÍÌÌ͵±°ÌÁ½ÍÍ¥±¸%å½ÔݹÐѼÕÍչѥ½¹ÌÍÕ Ì}ÑÉÉä½È}Ñ åÑMÑÉ¥¹(ì´´å½ÔµäݹÐѼ½Ý¹±½Ñ¡½Ñ¡ÈÁ¸%ÐÁɽ٥̥ɱ併Á±ÑÍнµÑ¡½Ì½Èݽɥ¹Ý¥Ñ ¥¹É䥱̸((ì´´µ¼ÍÉ¥Áд´´´´´´´´´´´´´´´´´´´(í ½¹ÙÉÑѼÕѽ%ÑXÌMÑÙA½¡©¤m±Ñ½ÉɽtÍÑÙ½½Ñ¥±±¼¹ÍåÑ̹¹Ð((íø±½°ÀÌØíÁɽÁÍlÄátôlÅÕ½Ðí ½µÁ¹å9µÅÕ½Ðì°ÅÕ½Ðí¥±ÍÉ¥ÁÑ¥½¸ÅÕ½Ðì°ÅÕ½Ðí¥±YÉÍ¥½¸ÅÕ½Ðì°ÅÕ½ÐíAɽÕÑ9µÅÕ½Ðì°ÅÕ½Ðí1± ½ÁåÉ¥¡ÐÅÕ½Ðì°|(íø$$ÅÕ½Ðí=É¥¥¹±¥±¹µÅÕ½Ðì°ÅÕ½Ðí%¹Íѱ±È¹¥¹ÅÕ½Ðì°ÅÕ½ÐíÁÁ±¥ÌѼÅÕ½Ðì°ÅÕ½Ðí Õ¥±ÑÅÕ½Ðì°ÅÕ½Ðí%¹Íѱ±Ñ¥½¸QåÁÅÕ½Ðì°|(íø$$ÅÕ½Ðí%¹Íѱ±ÈYÉÍ¥½¸ÅÕ½Ðì°ÅÕ½Ðí%¹Ñɹ±9µÅÕ½Ðì°ÅÕ½Ðí-ÉÑ¥±9ÕµÈÅÕ½Ðì°ÅÕ½ÐíMÕÁÁ½ÉÐ1¥¹¬ÅÕ½Ðì°ÅÕ½ÐíAQåÁÅÕ½Ðì°|(íø$$ÅÕ½ÐíAɽ¸É¡¥ÑÑÕÉÅÕ½Ðì°ÅÕ½ÐíM±µáÑÉѽÈYÉÍ¥½¸ÅÕ½Ðì°ÅÕ½Ðí¥ÍÁ±å9µÅÕ½Ðít(íM=¹Õѽ%ÑMÑÉÐ ¤(ìÀÌØíÉôÅÕ½Ðí]¥¹½ÝÌÈÀÀÀµ-äÄÐÌààµààØµ9T¹aÅÕ½Ðì(ìÀÌØíÉôÅÕ½Ðíé]%9=]MMeMQ4Ìɵ¡É¹±°ÅÕ½Ðì(ìÀÌØíÉôÅÕ½Ðíé]%9=]MMeMQ4ÌÉi±¥Q½½°¹½àÅÕ½Ðì(ìÀÌØíÉôÅÕ½Ðíé]%9=]MMeMQ4ÌÉ ±¹áÅÕ½Ðì(ìÀÌØíÉôÅÕ½Ðí]¥¹½ÝÌÈÀÀÀµ-äÄÐÌààµààØµ9T¹aÅÕ½ÐìÅÕ½Ðí ½µÁ¹å9µÅÕ½ÐìÅÕ½Ðí¥±ÍÉ¥ÁÑ¥½¸ÅÕ½ÐìÅÕ½Ðí¥±YÉÍ¥½¸ÅÕ½ÐìÅÕ½ÐíAɽÕÑ9µÅÕ½ÐìÅÕ½Ðí1± ½ÁåÉ¥¡ÐÅÕ½ÐìÅÕ½Ðí=É¥¥¹±¥±¹µÅÕ½ÐìÅÕ½Ðí%¹Íѱ±È¹¥¹ÅÕ½Ðì((ì´´¹µ¼ÍÉ¥Áд´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´((ì´´¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼MÑÉÐ ±ÍÌè ±ÍAɽÁ̼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼(ì(ìչѥ½¹Ì¥¸Ñ¡¥Ì±Í̸¡±°Õ¹Ñ¥½¹Ìɹ½ÈÑÑ¥¹¥±ÙÉÍ¥½¸¥¹½ÉµÑ¥½¸¸¤(ì(ìAÕ±¥Õ¹Ñ¥½¸}ÑYÉÍ¥½¹%¹¼ ÀÌØíÍ¥±AÑ °ÀÌØíIÐȤ´ÉÑÕɹÌÙÉÍ¥½¸¥¹½ÉµÑ¥½¸½ÈA¥±Ì¸(ì=¸ÍÕÍÌÀÌØíIÐÈÉÑÕɹÌÉÉä Ô¤½¹Ñ¥¹¥¹ÙÉÍ¥½¸¥¹¼¸ÍÑÉ¥¹Ì½È¥±¸(ìչѥ½¸ÉÑÕɸÉɽȽÌèÀôÍÕÍ̸Äô¥¹Ù±¥¥±ÁÑ ¸Èô¹¼¹ÉÍÉѱ±¥ÍÑ¥¸ÍÑ¥½¸Ñ±¸(ìÌô¥±Ñ¼¥¹ÙÉÍ¥½¸¥¹¼¸Ðô¹½ÐA¥±¸Ôô¥±¥ÌÄØµ¥ÐáÕѱ¸ ÅÕ½Ðí9ÅÕ½Ð쥱ÉÑ¡ÈÑ¡¸ÅÕ½ÐíAÅÕ½Ðì¤(ì(ìAÉ¥ÙÑ¡¥¹Ñɹ°¤Õ¹Ñ¥½¹Ìè(ì}ÑÉÉä¡MÑÉ¥¹%¸¤´½¹ÙÉÐÍÑÉ¥¹Ñ¼¸ÉÉä½åÑÙ±Õ̸(ì}Ñ åÑMÑÉ¥¹¡MÑÉ¥¹%¸°ÀÌØíM¹¥ÁU¹¥½¤´½¹ÙÉÐÍÑÉ¥¹Ñ¼µ¹±ÙÉÍ¥½¸¸%ÀÌØíM¹¥ÁU¹¥½ôQÉÕÑ¡¸Ð½¹±äÙÉäɹåѸ(ì}Ñ9յɽµ åÑÌ¡ÉÉ䤴ÑÌÉÉä½Õ½Õ¹Ä½È̸ÉÑÕɸ¹ÕµÉ¥Ù±Õ½ÈȽÈÐåÑ̸(ì(ì(ì(ì(ì(ì´´´´´´´´´´´´´´´´¼¼¼¼¼¼¼¼¼¼¼¼´´´ %8 ±ÍÌ!I´´´¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´()Õ¹=¹Õѽ%ÑMÑÉÐ ¤($íÕ¹½µµ¹ÐÑ¡¥Ì½ÈÉѼ½È½µµ¹±¥¹Õ͸(%%ÀÌØí µ1¥¹lÁtôÀQ¡¸($%5Í ½à ÈØØÈàà°MÉ¥ÁÑ9µ°ÅÕ½ÐíɽÀA¥±maµ10µ= at½¹Ñ¼Ñ¡¥ÌÍÉ¥ÁÐѼÐÙÉÍ¥½¸¥¹½ÉµÑ¥½¸¸ÅÕ½ÐìµÀì|($$$% I1µÀìÅÕ½Ðí ½µµAɽµÁÐUÍèÅÕ½ÐìµÀì I1µÀìMÉ¥ÁÑ9µµÀìÅÕ½ÐìÅÕ½ÐìµÀìÅÕ½Ðíµå¥±¹má±±°±½átÅÕ½Ðì¤($%á¥Ð(%¹%(%±½°ÀÌØíÉôÀÌØí µ1¥¹lÅt(%±½°ÀÌØíÁɽÁÍlÙtôlÅÕ½Ðí ½µÁ¹å9µÅÕ½Ðì°ÅÕ½Ðí¥±ÍÉ¥ÁÑ¥½¸ÅÕ½Ðì°ÅÕ½Ðí¥±YÉÍ¥½¸ÅÕ½Ðì°ÅÕ½ÐíAɽÕÑ9µÅÕ½Ðì°ÅÕ½Ðí1± ½ÁåÉ¥¡ÐÅÕ½Ðì°|($$$ÅÕ½Ðí=É¥¥¹±¥±¹µÅÕ½Ðít(%%ÀÌØí µ1¥¹lÁtÐìÄQ¡¸($%I¥´ÀÌØíÁɽÁÍlÀÌØí µ1¥¹lÁt´Åt($%½ÈÀÌØíàôÈQ¼ÀÌØí µ1¥¹lÁt($$%%ÀÌØíàÐìÄQ¡¸ÀÌØíÁɽÁÍlÀÌØíà´ÉtôÀÌØí µ1¥¹lÀÌØíát($%9áÐ(%¹%(%}5¥¸ ¤)¹Õ¹ìôôÐí=¹Õѽ%ÑMÑÉÐ()Õ¹}5¥¸ ¤(%±½°ÀÌØíÍÍаÀÌØí9ÕµÌ(%±½°ÀÌØíɰÀÌØí¤È°ÀÌØíIÑÕɸ°ÀÌØíÌ($($ÀÌØí¤Èô}ÑYÉÍ¥½¹%¹¼ ÀÌØíɰÀÌØíIÑÕɸ¤(%MÝ¥Ñ ÀÌØí¤È($% ÍÄ($$%5Í ½à ÈØØÈàà°MÉ¥ÁÑ9µ°ÅÕ½Ðí¥±ÁÑ ¹½ÐÙ±¥ÅÕ½Ðì¤($% ÍÈ($$%5Í ½à ÈØØÈàà°MÉ¥ÁÑ9µ°ÅÕ½ÐíU¹±Ñ¼¥¹IͽÕÉѱ¡È¥¸¥±¸ÅÕ½Ðì¤($% ÍÌ($$%5Í ½à ÈØØÈàà°MÉ¥ÁÑ9µ°ÅÕ½ÐíU¹±Ñ¼¥¹¥±ÙÉÍ¥½¸¥¹¼¸¥¸¥±¸ÅÕ½Ðì¤($% ÍÐ($$%5Í ½à ÈØØÈàà°MÉ¥ÁÑ9µ°ÅÕ½ÐíQ¡¥Ì¥Ì¹½ÐA¥±¸ÅÕ½Ðì¤($% ÍÔ($$%5Í ½à ÈØØÈàà°MÉ¥ÁÑ9µ°ÅÕ½ÐíQ¡¥Ì¥ÌÄØµ¥ÐáÕѱ¸%Х̹½ÐA¥±¸ÅÕ½Ðì¤($% ÍÀìÍÕÍÌ($$$ÀÌØíÌôÅÕ½ÐìÅÕ½Ðì($$%½ÈÀÌØíàôÀQ¼U ½Õ¹ ÀÌØíÁɽÁ̤´Ä($$$$ÀÌØí̵ÀìôÀÌØíÁɽÁÍlÀÌØíátµÀìÅÕ½ÐìèÅÕ½ÐìµÀìÀÌØíIÑÕɹlÀÌØíátµÀì I1($$%9áÐ($$%5Í ½à ÈØØÈàà°MÉ¥ÁÑ9µ°ÀÌØíɵÀì I1µÀì I1µÀìÀÌØí̤(%¹MÝ¥Ñ )¹Õ¹ìôôÐí}5¥¸(((ì´´Q¡ÁÕ±¥Õ¹Ñ¥½¸¥¸Ñ¡¥Ì±ÍÌè}ÑYÉÍ¥½¹%¹¼´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´)Õ¹}ÑYÉÍ¥½¹%¹¼ ÀÌØíÍ¥±AÑ ° åIÀÌØíIÐȤ촴ÉÑÕɸÉÉä Ô¤(%1½°ÀÌØíIаÀÌØíÌİÀÌØíͰÀÌØíAÐİÀÌØíÍḬÀÌØíİÀÌØíÑlÑt°ÀÌØíÉlÑt°ÀÌØí1½ḬÀÌØíY1½ḬÀÌØíM¥éḬÀÌØí¥=°ÀÌØí ½¼°ÀÌØíÍYÉMÑÉ¥¹°ÀÌØíÍ5ÉÈ(%1½°ÀÌØí¥9մİÀÌØí¥9մȰÀÌØí¥IAаÀÌØí¥9մ̰ÀÌØí1½ÍÁ¬°ÀÌØíY1½ÍÁ¬°ÀÌØíY%=ÍаÀÌØíI=ÍаÀÌØí ½½ÍÁ¬($ì=¸ÉɽÈIÍÕµ9áÐ(%%9½Ð¥±á¥ÍÑÌ ÀÌØíÍ¥±AÑ ¤Q¡¸($%IÑÕɸÄíÁÑ ¸(%¹%($ÀÌØíÍIÌôÅÕ½Ðì¹ÉÍÉÅÕ½Ðì($ÀÌØíÍYÉMÑÉ¥¹ôÅÕ½ÐíYM}YHÅÕ½Ðì($ÀÌØí ½½ÍÁ¬ô±Í($($($ÀÌØí¥±ô¥±=Á¸ ÀÌØíÍ¥±AÑ °À¤($ÀÌØíÌÄô¥±I ÀÌØíÍ¥±AÑ °ÈÀÐà¤(%¥± ±½Í ÀÌØí¥±¤($ÀÌØí¥±ôÀ($í ½¹Í½±]É¥Ñ ÀÌØíÌĤ($ÀÌØíÄô}ÑÉÉä¡MÑÉ¥¹5¥ ÀÌØíÌİØÄ°È¤¤ì´´Ð¹ÕµÈÙ±ÕнÍÐØÀÑ¡ÐÁ½¥¹ÑÌѼAÍ¥¹ÑÕÉÉÍ̸($($ÀÌØí¥9Õ´Äô¡}Ñ9յɽµ åÑÌ ÀÌØíĤ¬Ä¤ì´´Ð½ÍнÅÕ½ÐíAÀÀÅÕ½Ðì($ÀÌØíÍô}Ñ åÑMÑÉ¥¹ ÀÌØíÌݱͤ촴ÐݽɱÍÑÉ¥¹Ý¥Ñ ¡È À¤ÉÁ±äÅÕ½Ðì¨ÅÕ½Ðì¸($ÀÌØíÍ5ÉÈôMÑÉ¥¹5¥ ÀÌØíͰÀÌØí¥9մİФ(íø% ½¹Í½±]É¥Ñ ÅÕ½ÐìÀÌØíÍ5ÉÈôÅÕ½ÐìµÀìÀÌØíÍ5ÉȵÀí1¤(%% ÀÌØíÍ5ÉȱÐìÐìÅÕ½ÐíA¨¨ÅÕ½Ðì¤Q¡¸($%%MÑÉ¥¹1Ð ÀÌØíÍ5ÉȰȤôÅÕ½Ðí9ÅÕ½ÐìQ¡¸($$%IÑÕɸÔì´´ÄØ¥Ð¸($%±Í($$%IÑÕɸÐì´´¹¼AÍ¥¹ÑÕɽչ¸($%¹%(%¹%($($ÀÌØíAÐÄôMÑÉ¥¹%¹MÑÈ ÀÌØíͰÀÌØíÍI̤촴¥¹¹ÉÍÉѱ¸(%% ÀÌØíAÐÄôÀ¤Q¡¸($%IÑÕɸÈí¹¼ÉͽÕÉѱ¡È½Õ¹¸(%¹%($ÀÌØíAÐÄôÀÌØíAÐĬÄÈì´´Í¥é½ÉÜÑ¥ÌÐåÑÌнÍÐ½ÄØ¥¹Ñ¼Ñ¡¹ÉÍÉѱ¸($ÀÌØíÄô}ÑÉÉä¡MÑÉ¥¹5¥ ÀÌØíÌİÀÌØíAÐİÄȤ¤ì´´Ðѡ͵ÍÑÉ¥¹Ì¹ÕµÉ¥ÉÉäѼI½ÍйյÉ̸(%½ÈÀÌØí¥=ôÀQ¼Ì($$í ½¹Í½±]É¥Ñ ÅÕ½ÐìÀÌØí¥=ôÅÕ½ÐìµÀìÀÌØí¥=µÀí1¤($$ÀÌØíÑlÀÌØí¥=tôÀÌØíÅlÀÌØí¥=t(%9áÐ($ÀÌØíY1½IÌô}Ñ9յɽµ åÑÌ ÀÌØíФ(%½ÈÀÌØí¥=ôÀQ¼Ì($$ÀÌØíÑlÀÌØí¥=tôÀÌØíÅlÀÌØí¥=¬Ñt(%9áÐ($ÀÌØíM¥éIÌô}Ñ9յɽµ åÑÌ ÀÌØíФ촵ͥé½ÉͽÕÉÍÑ¥½¸¥¸åÑ̸(%½ÈÀÌØí¥=ôÀQ¼Ì($$ÀÌØíÑlÀÌØí¥=tôÀÌØíÅlÀÌØí¥=¬át(%9áÐ($ÀÌØí1½IÌô}Ñ9յɽµ åÑÌ ÀÌØíФ촴½Íб½Ñ¥½¸½ÉͽÕÉÍÑ¥½¸¸($ÀÌØíAÐÄôMÑÉ¥¹%¹MÑÈ Ä°ÀÌØíͰÅÕ½Ðì¹ÍÁ¬ÅÕ½Ðì¤ì´´¥¹¹ÉÍÉѱ¸(%% ÀÌØíAÐÄÐìÀ¤Q¡¸($$ÀÌØí ½½ÍÁ¬ôQÉÕ($$ÀÌØíAÐÄôÀÌØíAÐĬÄÈì´´Ù¥ÉÑÕ°½ÍХ̥ÉÍÐÐåÑÌìÉܽÍÐ¥ÌåÑÌä´Äȸ($$ÀÌØíÄô}ÑÉÉä¡MÑÉ¥¹5¥ ÀÌØíÌİÀÌØíAÐİÄȤ¤($%½ÈÀÌØí¥=ôÀQ¼Ì($$$ÀÌØíÑlÀÌØí¥=tôÀÌØíÅlÀÌØí¥=t($%9áÐ($$ÀÌØíY1½ÍÁ¬ô}Ñ9յɽµ åÑÌ ÀÌØíФ($%½ÈÀÌØí¥=ôÀQ¼Ì($$$ÀÌØíÑlÀÌØí¥=tôÀÌØíÅlÀÌØí¥=¬át($%9áÐ($$ÀÌØí1½ÍÁ¬ô}Ñ9յɽµ åÑÌ ÀÌØíФ(%¹%($($ÀÌØí ½¼ô±Í($ÀÌØí¥±ô¥±=Á¸ ÀÌØíÍ¥±AÑ °À¤($í5Í ½à À°ÅÕ½ÐìÀÌØí1½IÌÅÕ½Ðì°ÅÕ½ÐìÀÌØí1½I̬ÄÈôÅÕ½ÐìµÀìÀÌØí1½I̬ÄȤ(íø ½¹Í½±]É¥Ñ ÅÕ½ÐìÀÌØí1½I̬ÄÈôÅÕ½ÐìµÀìÀÌØí1½I̬ÄȵÀì I1¤(%}M¥À ÀÌØí¥±°ÀÌØí1½I̬ÄȤ($ÀÌØíÌÄô¥±I ÀÌØí¥±°È¤ì´´IåÑÌÄ̰ÄÐѼйյȽ¹µÉͽÕÉÑåÁ̸($ÀÌØí¥9Õ´ÄôÍ ÀÌØíÌĤ촴¹ÕµÈ½¹µÌ¸($ì ½¹Í½±]É¥Ñ ÅÕ½ÐìÀÌØí¥9Õ´ÄôÅÕ½ÐìµÀìÀÌØí¥9մĵÀì1¤($ÀÌØíÌÈô¥±I ÀÌØí¥±°È¤ì´´IåÑÌÄÔ°ÄØÑ¼Ð¹ÕµÈ½¹ÕµÉÉͽÕÉÑåÁ̸($($ÀÌØí¥9Õ´ÈôÍ ÀÌØíÌȤ촴¹ÕµÈ½¹ÕµÌ¸($ì ½¹Í½±]É¥Ñ ÅÕ½ÐìÀÌØí¥9Õ´ÈôÅÕ½ÐìµÀìÀÌØí¥9մȵÀì1¤(%%ÀÌØí¥9Õ´ÈôÀQ¡¸ì´´¹¼¹ÕµÉ¹Ñɥ̸¡ÙѼÅեСɸ($%IÑÕɸÌ(%¹%($($(%% ÀÌØí¥9Õ´ÄÐìÀ¤Q¡¸}M¥À ÀÌØí¥±°ÀÌØí¥9մĨà¤ì´´M¥ÀÁÍйµ¹Ñɥ̸($ÀÌØí¥IAÐôÀÌØí1½IÌ¬ÄØ¬ ÀÌØí¥9մĨà¤ì´´ÕÁÑ¥±½ÍÐÙÉ¥±ÕÍÑ¡¥ÌÝ¥±°¹¸($í ½¹Í½±]É¥Ñ ÅÕ½ÐìÀÌØí¥IAÐôÅÕ½ÐìµÀìÀÌØí¥IAеÀí1¤($ÀÌØí ½¼ô±Í(%½ÈÀÌØí¥=ôÄQ¼ÀÌØí¥9Õ´È($$ÀÌØíÌÄô¥±I ÀÌØí¥±°à¤($$ÀÌØí¥IAÐôÀÌØí¥IAЬà($%%¡Í ÀÌØíÌĤôÄØ¤Q¡¸ì´´Ñ¡¥Ì¥ÌÙÉÍ¥½¸¥¹¼¸¹ÑÉä¸($$$ÀÌØí ½¼ôQÉÕ($$%á¥Ñ1½½À($%¹%(%9áÐ(%% ÀÌØí ½¼ô±Í¤Q¡¸ì´´¡ÙѼÅեи¹¼ÙÉÍ¥½¸¥¹¼¸¹ÑÉä½Õ¹¸($$($$í ½¹Í½±]É¥Ñ ÅÕ½ÐíIÑÕɸÁ½¥¹ÐÄÅÕ½ÐìµÀì1¤($%IÑÕɸÌí¥±Ñ¼¥¹ÙÉÍ¥½¸¥¹¼¥¸ÉͽÕÉѱ¸(%¹%($($ÀÌØíÄô}ÑÉÉä ÀÌØíÌĤ촴ÐåÑÉÉä½ÈÙÉÍ¥½¸¥¹¼¹ÑÉäÐѽÀ±Ù°¸($í ½¹Í½±]É¥Ñ ÅÕ½ÐíU½Õ¹ÐìÐìÅÕ½ÐìµÀíU ½Õ¹ ÀÌØíĤ¤($ÀÌØí¥=ôÀ($ÀÌØí¥9Õ´ÌôÄ($í¼(%]¡¥±Ä($%½ÈÀÌØí¥9Õ´ÄôÀQ¼È촴нÍйյÈѼ¹áбٰɽ´É¹ÐåÑ̽¹ÑÉäÍÑÉÕÑÕɸ($$$ÀÌØíÑlÀÌØí¥9Õ´ÅtôÀÌØíÅlÀÌØí¥9մĬÑt($%9áÐ($$ÀÌØíÑlÍtôÀ($$ÀÌØí¥9Õ´Èô}Ñ9յɽµ åÑÌ ÀÌØíФ($$($%% ÀÌØíÅlÝtÐìÄÈܤQ¡¸ì´´¡¥ ¥ÐÝÌÍÐ¥¸¹ÑÉä½ÍÐٱհͼ¥ÐíÌ©ÕÍÐÁ½¥¹ÑÈѼ¹½Ñ¡ÈÁ½¥¹Ñȸ($$$ÀÌØí¥9Õ´ÈôÀÌØí1½I̬ÀÌØí¥9Õ´È¬ÄØ($$%}M¥À ÀÌØí¥±°ÀÌØí¥9Õ´È´ÀÌØí¥IAФì´Ä¤($$$ÀÌØíÌÄô¥±I ÀÌØí¥±°à¤($$$ÀÌØí¥IAÐôÀÌØí¥IAЬ ÀÌØí¥9Õ´È´ÀÌØí¥IAФ¬à¤($$$ÀÌØíÄô}ÑÉÉä ÀÌØíÌĤ($%±Íì´´Ñ¡¥Ì¥ÌÑ¡½ÍнÙÉÍ¥½¸¥¹¼½ÍÐ¥¹¼¸ÌÌì($$$ÀÌØí¥=ô ÀÌØí¥9մȬÀÌØí1½I̤($$$íá¥Ñ1½½À($$%á¥Ñ1½½À($%¹%($$ÀÌØí¥9Õ´ÌôÀÌØí¥9մ̬Ä($%% ÀÌØí¥9Õ´ÌÐìÄÀ¤Q¡¸á¥Ñ1½½À(%]¹($í1½½À(%% ÀÌØí¥=ôÀ¤Q¡¸ì´´¡ÙѼÅեи¹¼¥¹°½Íнչ¸($$í ½¹Í½±]É¥Ñ ÅÕ½ÐíIÑÕɸÁ½¥¹ÐÈÅÕ½ÐìµÀì1¤($%IÑÕɸÌí¥±Ñ¼¥¹ÙÉÍ¥½¸¥¹¼¥¸ÉͽÕÉѱ¸($$(%¹%(%}M¥À ÀÌØí¥±°ÀÌØí¥=´ÀÌØí¥IAФ($ÀÌØíÌÄô¥±I ÀÌØí¥±°à¤($ÀÌØí¥IAÐôÀÌØí¥IAЬ ÀÌØí¥=´ÀÌØí¥IAФ¬à¤($ÀÌØíÄô}ÑÉÉä ÀÌØíÌĤ(%½ÈÀÌØí¥9Õ´ÄôÀQ¼Ì($$ÀÌØíÑlÀÌØí¥9Õ´ÅtôÀÌØíÅlÀÌØí¥9Õ´Åt(%9áÐ($ÀÌØíY%=ÍÐô}Ñ9յɽµ åÑÌ ÀÌØíФ촵½ÍнÙÉÍ¥½¸¥¹¼¸¥Ù¸¥¸¹ÉÍÉÍÑ¥½¸¸($ÀÌØíI=ÍÐô ÀÌØíY%=ÍдÀÌØíY1½I̤¬ÀÌØí1½I̤(%½ÈÀÌØí¥9Õ´ÄôÀQ¼Ì($$ÀÌØíÑlÀÌØí¥9Õ´ÅtôÀÌØíÅlÀÌØí¥9մĬÑt(%9áÐ($ÀÌØíM¥éIÌô}Ñ9յɽµ åÑÌ ÀÌØíФ(%}M¥À ÀÌØí¥±°ÀÌØíI=ÍдÀÌØí¥IAФ($ÀÌØíÌÄô¥±I ÀÌØí¥±°ÀÌØíM¥éI̤촴ɽÕÐÑ¡¹Ñ¥É¥±YÉÍ¥½¹%¹¼Ñɸ(%¥± ±½Í ÀÌØí¥±¤($ÀÌØí¥±ôÀ($ÀÌØíÍô}Ñ åÑMÑÉ¥¹ ÀÌØíÌİQÉդ촴͹¥ÀÕ¹¥½¸($ÀÌØíAÐÄôMÑÉ¥¹%¹MÑÈ ÀÌØíͰÀÌØíÍYÉMÑÉ¥¹¤(%% ÀÌØíAÐÄÐìÀ¤Q¡¸ì´´ÅÕ½ÐíYM}YHÅÕ½ÐìÝ̽չ°Í¼ÁɽÍÌÑ¡ÍÑÉ¥¹¹Åեи($$ÀÌØíIÐô}AɽÍÍIÌ ÀÌØíͤ($$ÀÌØíIÐÈôÀÌØíIÐ($%IÑÕɸÀ콬(%±Í% ÀÌØí ½½ÍÁ¬ôQÉÕ¤Q¡¸ì´´¥ÅÕ½ÐíYM}YHÅÕ½ÐìÝ̹½Ð½Õ¹ÕÐѡɥ̸ÅÕ½ÐíÍÁ¬ÅÕ½ÐìÍÑ¥½¸Ñ¡¸ÑÉäѡи($$ÀÌØíI=ÍÐô ÀÌØíY%=ÍдÀÌØíY1½ÍÁ¬¤¬ÀÌØí1½ÍÁ¬¤ì´´±Õ±Ñ¹Ü¥±ÙÉÍ¥½¸¥¹¼Ñ½Íи($$ÀÌØí¥±ô¥±=Á¸ ÀÌØíÍ¥±AÑ °À¤ì´´Q¡¥±Ý̱½Í¹¥Ì¹½Üɵ½Á¹¡É¸-Á¥¹Ñ¡¥±($%}M¥À ÀÌØí¥±°ÀÌØíI=ÍФ촴½Á¸ÅÕ½Ðí©ÕÍÐ¥¸ÍÅÕ½Ðìݽձ¸íС١±ÁÕÍÑ¡¥±Á½¥¹ÑÈ($$ÀÌØíÌÄô¥±I ÀÌØí¥±°ÀÌØíM¥éI̤촴½ÈÑ¡¥ÌɵäÕÉѡȬѡ¸Ñ¡Á½¥¹ÑÈÝÌÝ¡¸Ñ¡¥±($%¥± ±½Í ÀÌØí¥±¤ì´´Ý̱½Í¸M¼ÉÑ¡ÈÑ¡¸ÑÉäѼͽÉнÕÐÑ¡ÉÁ½¥¹Ð°Ñ¡¥±¥Ì©ÕÍÐ($$ÀÌØí¥±ôÀì´´½Á¹ÉÍ ¹M¥À¥ÌÕ͸($$ÀÌØíÍô}Ñ åÑMÑÉ¥¹ ÀÌØíÌİQÉÕ¤($$ÀÌØíAÐÄôMÑÉ¥¹%¹MÑÈ ÀÌØíͰÀÌØíÍYÉMÑÉ¥¹¤($%% ÀÌØíAÐÄÐìÀ¤Q¡¸($$$ÀÌØíIÐô}AɽÍÍIÌ ÀÌØíͤ($$$ÀÌØíIÐÈôÀÌØíIÐ($$%IÑÕɸÀ콬($%±Í($$$í ½¹Í½±]É¥Ñ ÅÕ½ÐíIÑÕɸÁ½¥¹ÐÌÅÕ½ÐìµÀì1¤($$%IÑÕɸÌí¥±Ñ¼¥¹ÙÉÍ¥½¸¥¹¼¥¸ÉͽÕÉѱ¸($%¹%(%±Í($$í ½¹Í½±]É¥Ñ ÅÕ½ÐíIÑÕɸÁ½¥¹ÐÐÅÕ½ÐìµÀì1¤($%IÑÕɸÌí¥±Ñ¼¥¹ÙÉÍ¥½¸¥¹¼¥¸ÉͽÕÉѱ¸(%¹%)¹Õ¹ìôôÐí}ÑYÉÍ¥½¹%¹¼()Õ¹}AɽÍÍIÌ ÀÌØíÍФ(%¥´ÀÌØí%¹½mU ½Õ¹ ÀÌØíÁɽÁ̤t($ì=¸ÉɽÈIÍÕµ9áÐ(%½ÈÀÌØíàôÀQ¼U ½Õ¹ ÀÌØíÁɽÁ̤´Ä($$ÀÌØí%¹½lÀÌØíátô}Ñ%¹¼ ÀÌØíÍаÀÌØíÁɽÁÍlÀÌØíát¤(%9áÐ(%IÑÕɸÀÌØí%¹¼)¹Õ¹ìôôÐí}AɽÍÍIÌ()Õ¹}Ñ%¹¼ ÀÌØíÍMÑȰÀÌØíÍY°¤($ì¡Ñ¼ÉÝÉ¥ÑÑ¡¥Ìչѥ½¸Õͽ¥¹½µÁÑ¥±¥Ñ¥Ì($íÑݸÕѽ%Ð̹YÍÑÉ¥¹Õ¹Ñ¥½¹Ì¸(%¥´ÀÌØíAѰÀÌØíAѰÀÌØí1¹Y°°ÀÌØíÌаÀÌØí½ÕɹôÄ($ÀÌØí1¹Y°ôMÑÉ¥¹1¸ ÀÌØíÍY°¤¬Äì´´±¹Ñ ½¥¹¼ÍÑÉ¥¹èÅÕ½Ðí ½µÁ¹å9µÅÕ½ÐìôÄÄ($ÀÌØíAÑôMÑÉ¥¹%¹MÑÈ ÀÌØíÍMÑȰÀÌØíÍY°¤ì´´¥¹ÍÑÉ¥¹¹µ¸(%% ÀÌØíAÑÐìÀ¤Q¡¸($$ÀÌØíAÑôÀÌØíAѬÀÌØí1¹Y°($$ÀÌØíAÑôÀÌØíAѬMÑÉ¥¹%¹MÑÈ¡MÑÉ¥¹5¥ ÀÌØíÍMÑȰÀÌØíAѬĤ°ÅÕ½Ðì¨ÅÕ½Ðì¤(íø$$ ½¹Í½±]ɥѡMÑÉ¥¹5¥ ÀÌØíÍMÑȰÀÌØíAѬĤµÀì1¤(íø$$ ½¹Í½±]ɥѡMÑÉ¥¹5¥ ÀÌØíÍMÑȰÀÌØíAѰ ÀÌØíAÑ´ÀÌØíAѤ¤µÀì1¤($%%ÀÌØíAÑÐì ÀÌØíAѬȤ=È ÀÌØíAÑô ÀÌØíAѬȤ¹ÀÌØíÍY°ôÅÕ½Ðí¥±YÉÍ¥½¸ÅÕ½Ðì¤Q¡¸ì´´9µ©Ù±Õ¨¨¸M¼ÍÑÉб½½¥¹ÐÍÉ¡ÉÑÈÑȸ%Ñ¡Ð($$$ÀÌØíÌÐôMÑÉ¥¹5¥ ÀÌØíÍMÑȰÀÌØíAѰ ÀÌØíAÑ´ÀÌØíAѤ¤ì´´¡ÉÑȥ̨ѡ¸¥ÐÌäíÌ9µ¨¨¨Ý¡¥ µ¹ÌÑ¡ÉÌäíÌ($$$ÀÌØíÌÐôMÑÉ¥¹IÁ± ÀÌØíÌаÅÕ½Ðì¨ÅÕ½Ðì°ÅÕ½ÐìÅÕ½Ðì¤ì´µ¹¼Ù±Õ½ÈÑ¡ÐÍÁ¥¥ÁɽÁÉÑä¸($$%%MÑÉ¥¹%¹MÑÈ ÀÌØíÌа ¡È Ĥ°Ä¤ôÀQ¡¸IÑÕɸÀÌØíÌÐì´´¡¬½È ¡È Ĥݡ¥ ͵ÌѼ½Õ¹($%¹%ìÑݸٱÕ̸%¥ÐÌäíÌ¥¸Ñ¡ÍÑÉ¥¹Ñ¡Ðµ¹Ìѡɥ̹¼Ù±Õ½È($$(%¹%(%IÑÕɸÅÕ½ÐìÅÕ½Ðì)¹Õ¹ìôôÐí}Ñ%¹¼(ì´´´´´´´´´´´´´´Í¥µÁ±¥¥ÙÉÍ¥½¸½}Ñ åÑMÑÉ¥¹½ÈÑ¡¥Ì ±Í̸´´´´´´´´´´´´´´´´´´´´´)Õ¹}Ñ åÑMÑÉ¥¹ ÀÌØíÍMÑȰÀÌØíM¹¥ÁU¹¥½¤(%1½°ÀÌØíÍIаÀÌØí¥1¸°ÀÌØí¥°ÀÌØí¥1¸È°ÀÌØíÉlÉt($ì=¸ÉɽÈIÍÕµ9áÐ($ÀÌØí¥1¸ÈôÀ(%% ÀÌØíM¹¥ÁU¹¥½ô±Í¤Q¡¸($%I¥´ÀÌØíÉmMÑÉ¥¹1¸ ÀÌØíÍMÑȤt($%½ÈÀÌØí¥1¸ôÄQ¼MÑÉ¥¹1¸ ÀÌØíÍMÑȤ($$$ÀÌØí¥ôÍ¡MÑÉ¥¹5¥ ÀÌØíÍMÑȰÀÌØí¥1¸°Ä¤¤($$%%ÀÌØí¥ôÀQ¡¸ÀÌØí¥ôÐÈì´´½¹ÙÉÑÌÀµåÑѼ¨($$$í%U ½Õ¹ ÀÌØíȤÐìÀÌØí¥1¸´ÄQ¡¸($$$ÀÌØíÉlÀÌØí¥1¸´Åtô ¡È ÀÌØí¥¤($%9áÐ(%±Í($%I¥´ÀÌØíÉl¡MÑÉ¥¹1¸ ÀÌØíÍMÑȤ¼È¤t($%½ÈÀÌØí¥1¸ôÄQ¼MÑÉ¥¹1¸ ÀÌØíÍMÑȤMÑÀÈ($$$ÀÌØí¥ôÍ¡MÑÉ¥¹5¥ ÀÌØíÍMÑȰÀÌØí¥1¸°Ä¤¤($$%%ÀÌØí¥ôÀQ¡¸ÀÌØí¥ôÐÈì´´½¹ÙÉÑÌÀµåÑѼ¨($$$ÀÌØíÉlÀÌØí¥1¸Étô ¡È ÀÌØí¥¤($$$ÀÌØí¥1¸ÈôÀÌØí¥1¸È¬Ä($%9áÐ(%¹%(%IÑÕɸ})½¥¸ ÀÌØíȰÅÕ½ÐìÅÕ½Ðì¤)¹Õ¹ìôôÐí}Ñ åÑMÑÉ¥¹(ì´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´M¥µÁ±¥¥ÙÉÍ¥½¸½}ÑÉÉ临´´´´´´´´´´´´´´´´´´´´´´)Õ¹}ÑÉÉä ÀÌØíÍMÑȤ(%¥´ÀÌØí¥°ÀÌØí1¸Ä°ÀÌØí1¸È°ÀÌØíMÑÉlÉt($ì=¸ÉɽÈIÍÕµ9áÐ($ÀÌØí1¸ÄôMÑÉ¥¹1¸ ÀÌØíÍMÑȤ(%%ÀÌØí1¸ÄÐìÄQ¡¸I¥´ÀÌØíMÑÉl ÀÌØí1¸Ä¤t(%½ÈÀÌØí¥ôÄQ¼ÀÌØí1¸Ä($$ÀÌØíMÑÉlÀÌØí¥´ÅtôÍ¡MÑÉ¥¹5¥ ÀÌØíÍMÑȰÀÌØí¥°Ä¤¤(%9áÐ(%IÑÕɸÀÌØíMÑÈ)¹Õ¹ìôôÐí}ÑÉÉä(ì´´´´´´´´´´´´´´´´´´´´ÉÑÕɸ¹ÕµÈɽ´È½ÈÐåÑ̸´´´´´´´´´´´´´´´)Õ¹}Ñ9յɽµ åÑÌ ÀÌØí åÑ̤(%¥´ÀÌØí9Õ´Ä($ìÉȹ ±È($ì=¸ÉɽÈIÍÕµ9áÐ($í}Ñ9յɽµ åÑÌô´Ä(%%U ½Õ¹ ÀÌØí åÑ̤ÐìÄQ¡¸($$ÀÌØí9Õ´Äô ÀÌØí åÑÍlÁt¬ÀÌØí åÑÍlÅt¨ÈÔØ¤($%%¡U ½Õ¹ ÀÌØí åÑ̤Ðì̤Q¡¸($$$ÀÌØí9Õ´ÄôÀÌØí9մĬ ÀÌØí åÑÍlÉt¨ØÔÔÌØ¤¬ ÀÌØí åÑÍlÍt¨ÄØÜÜÜÈÄØ¤($%¹%($%IÑÕɸÀÌØí9Õ´Ä(%¹%)¹Õ¹ìôôÐí}Ñ9յɽµ åÑÌ()Õ¹})½¥¸ ÀÌØíMÑÉ(°ÀÌØíͱ¥´ôÅÕ½ÐìÅÕ½Ðì¤($íµ¥µ¥Y)½¥¸½µµ¹¡ÉÉäѼÍÑÉ¥¹Ý¥Ñ ±¥µ¥ÑȤ(%1½°ÀÌØíÍQµÀ(%%%ÍÉÉä ÀÌØíMÑÉ(¤Q¡¸($%½ÈÀÌØíàôÀQ¼U ½Õ¹ ÀÌØíMÑÉ(¤´Ä($$$ÀÌØíÍQµÀôÀÌØíÍQµÀµÀìÀÌØíMÑÉ)lÀÌØíátµÀìÀÌØíͱ¥´($%9áÐ($$í ½¹Í½±]É¥Ñ ÅÕ½Ðí)½¥¸ÀÌØíÍQµÀôÅÕ½ÐìµÀìÀÌØíÍQµÀµÀì1¤($%IÑÕɸÀÌØíÍQµÀ(%¹%(%MÑÉÉ½È Ä¤(%IÑÕɸÅÕ½ÐìÅÕ½Ðì)¹Õ¹ìôôÐí})½¥¸()Õ¹}M¥À ÀÌØí¡¥±°ÀÌØíåÑ̤($íµ¥µ¥M SciTE for AutoItDirections for Submitting Standard UDFs Don't argue with an idiot; people watching may not be able to tell the difference.
GaryFrost Posted July 19, 2006 Posted July 19, 2006 Here's an attempt to populate the resource properties from the file info of the file. my testing so far seems to work on patch files, autoit exes modified with resource hacker, and autoit exes not modified with resource hacker Tested against a couple of non-autoit exe's also. expandcollapse popup;-- This script includes a class and a demo. Drop any PE file onto the script (EXE, DLL, OCX) ;-- to get file properties. The class, ClsProps, is a complete set of all functions needed ;-- get file properties using only VBS and the Textstream object. ;-- The file properties returned are those that are found when a file is right-clicked, ;-- Properties menu is clicked, and Version tab is selected. This information ;-- is included in most PE (portable executable) files. ;-- Thank you to Ed Gruberman (http://www.rjump.com) for help with this code. ;-- He corrected a bug and also worked out the "aspack" variation whereby some ;-- PE files have been compressed and require a different method to find the version info. j. ;-- NOTE: This script is written to be compact for pasting into scripts. It uses simplified versions of some functions from ;-- the Textstream Binary Ops and Base 64 download, using only the functionality needed to get file version info., ;-- in order to keep this class as small as possible. If you want to use functions such as _GetArray or _GetByteString ;-- you may want to download the other package. It provides a fairly complete set of methods for working with binary files. ;-- Demo script -------------------- ;Converted to AutoItV3 Steve Podhajecki [eltorro] steve@ocotillo.sytes.net ; Modified by Steve Podhajecki, Gary Frost ;~ Global $props[18] = ["CompanyName", "FileDescription", "FileVersion", "ProductName", "LegalCopyright", _ ;~ "OriginalFilename", "Installer Engine", "Applies to", "Build Date", "Installation Type", _ ;~ "Installer Version", "InternalName", "KB Article Number", "Support Link", "Package Type", _ ;~ "Proc. Architecture", "Self-Extractor Version", "DisplayName"] ;See OnAutoItStart() ;$Arg = "Windows2000-KB914388-x86-ENU.EXE" ;$Arg= "C:\Windows\System32\cmdhere.dll" ;$Arg = "C:\Windows\System32\ZlibTool.ocx" ;$Arg = "C:\Windows\System32\Calc.exe" ;-- End demo script ------------------------------------------------------------ ;-- //////////////////////// Start Class: ClsProps /////////////////////////////////////////////// ; ; Functions in this class. (All functions are needed for getting file version information.) ; ; Public Function _GetVersionInfo($sFilePath, $ARet2) - returns version information For PE files. ; On success $ARet2 returns array(5) containing version info. strings for file. ; Function return error codes: 0 = success. 1 = invalid file path. 2 = no .rsrc table listed in section table. ; 3 = failed to find version info. 4 = not a PE file. 5 = file is a 16-bit executable. ("NE" file rather than "PE") ; ; Private (internal) functions: ; _GetArray(StringIn) - convert a string to an array of byte values. ; _GetByteString(StringIn, $SnipUnicode) - convert a string to a manageable version. If $SnipUnicode = True then get only every 2nd byte. ; _GetNumFromBytes(array) - takes array of ubound 1 or 3. return numeric value for 2 or 4 bytes. ; ; ; ; ; ;----------------//////////// --- BEGIN Class HERE --- /////////////////////------------------------------------------- Func OnAutoItStart() ;uncomment this for drag to or commandline usage. If $CmdLine[0] = 0 Then MsgBox(266288, @ScriptName, "Drop A PE file [EXE-DLL-OCX] onto this script to get version information." & _ @CRLF & "Commad Prompt Usage:" & @CRLF & @ScriptName & " " & "myfile.[exe,dll,ocx]") Exit EndIf Global $Arg = $CmdLine[1] _Main() EndFunc ;==>OnAutoItStart Func _Main() Global $sAst, $ANums Global $Arg, $i2, $AReturn, $s Global $props[1] $i2 = _GetVersionInfo($Arg, $AReturn) Switch $i2 Case 1 MsgBox(266288, @ScriptName, "File path not valid") Case 2 MsgBox(266288, @ScriptName, "Unable to find Resource table header in file.") Case 3 MsgBox(266288, @ScriptName, "Unable to find file version info. in file.") Case 4 MsgBox(266288, @ScriptName, "This is not a PE file.") Case 5 MsgBox(266288, @ScriptName, "This is a 16-bit executable. It is not a PE file.") Case 0 ; success $s = "" For $x = 0 To UBound($props) - 1 $s &= $props[$x] & ": " & $AReturn[$x] & @CRLF Next MsgBox(266288, @ScriptName, $Arg & @CRLF & @CRLF & $s) EndSwitch EndFunc ;==>_Main ;-- The public function in this class: _GetVersionInfo ----------------------------------------- Func _GetVersionInfo($sFilePath, ByRef $ARet2) ;-- return array(5) Local $ARet, $s1, $sB, $Pt1, $sRes, $A1, $A4[4], $A2[4], $LocRes, $VLocRes, $SizeRes, $iOff, $Boo, $sVerString, $sMarker Local $iNum1, $iNum2, $iReadPt, $iNum3, $LocAspack, $VLocAspack, $VIOffset, $ReadOffset, $BooAspack ; On Error Resume Next If Not FileExists($sFilePath) Then Return 1 ;bad path. EndIf $sRes = ".rsrc" $sVerString = "VS_VER" $BooAspack = False $file = FileOpen($sFilePath, 0) $s1 = FileRead($sFilePath, 2048) FileClose($file) $file = 0 ;ConsoleWrite($s1) $A1 = _GetArray(StringMid($s1, 61, 2)) ;-- get number value at offset 60 that points to PE signature address. $iNum1 = (_GetNumFromBytes($A1) + 1) ;-- get offset of "PE00" $sB = _GetByteString($s1, False) ;-- get a workable string with Chr(0) replaced by "*". $sMarker = StringMid($sB, $iNum1, 4) ;~ ConsoleWrite("$sMarker ="&$sMarker&@LF) If ($sMarker <> "PE**") Then If StringLeft($sMarker, 2) = "NE" Then Return 5 ;-- 16 bit. Else Return 4 ;-- no PE signature found. EndIf EndIf $Pt1 = StringInStr($sB, $sRes) ;-- find .rsrc table. If ($Pt1 = 0) Then Return 2 ;no resource table header found. EndIf $Pt1 = $Pt1 + 12 ;-- size of raw data is 4 bytes at offset of 16 into the .rsrc table. $A1 = _GetArray(StringMid($s1, $Pt1, 12)) ;-- get the same string as a numeric array to Read offset numbers. For $iOff = 0 To 3 ;ConsoleWrite("$iOff ="&$iOff&@LF) $A4[$iOff] = $A1[$iOff] Next $VLocRes = _GetNumFromBytes($A4) For $iOff = 0 To 3 $A4[$iOff] = $A1[$iOff + 4] Next $SizeRes = _GetNumFromBytes($A4) ;--size of resource section in bytes. For $iOff = 0 To 3 $A4[$iOff] = $A1[$iOff + 8] Next $LocRes = _GetNumFromBytes($A4) ;-- offset location of resource section. $Pt1 = StringInStr(1, $sB, ".aspack") ;-- find .rsrc table. If ($Pt1 > 0) Then $BooAspack = True $Pt1 = $Pt1 + 12 ;-- virtual offset is first 4 bytes; raw offset is bytes 9-12. $A1 = _GetArray(StringMid($s1, $Pt1, 12)) For $iOff = 0 To 3 $A4[$iOff] = $A1[$iOff] Next $VLocAspack = _GetNumFromBytes($A4) For $iOff = 0 To 3 $A4[$iOff] = $A1[$iOff + 8] Next $LocAspack = _GetNumFromBytes($A4) EndIf $Boo = False $file = FileOpen($sFilePath, 0) ;MsgBox(0, "$LocRes", "$LocRes+12 =" & $LocRes + 12) ;~ ConsoleWrite("$LocRes+12 =" & $LocRes + 12 & @CRLF) _Skip($file, $LocRes + 12) $s1 = FileRead($file, 2) ;-- Read bytes 13,14 to get number of named resource types. $iNum1 = Asc($s1) ;-- number of names. ; ConsoleWrite("$iNum1 =" & $iNum1 & @LF) $s2 = FileRead($file, 2) ;-- Read bytes 15,16 to get number of numbered resource types. $iNum2 = Asc($s2);-- number of nums. ; ConsoleWrite("$iNum2 =" & $iNum2 & @LF) If $iNum2 = 0 Then ;-- no numbered entries. have to quit here. Return 3 EndIf If ($iNum1 > 0) Then _Skip($file, $iNum1 * 8) ;-- Skip past named entries. $iReadPt = $LocRes + 16 + ($iNum1 * 8) ;-- update file offset variable because this will be needed. ;ConsoleWrite("$iReadPt ="& $iReadPt&@LF) $Boo = False For $iOff = 1 To $iNum2 $s1 = FileRead($file, 8) $iReadPt = $iReadPt + 8 If (Asc($s1) = 16) Then ;-- this is version info. entry. $Boo = True ExitLoop EndIf Next If ($Boo = False) Then ;-- have to quit. no version info. entry found. ;ConsoleWrite("Return point 1" & @LF) Return 3 ;failed to find version info in resource table. EndIf $A1 = _GetArray($s1) ;-- get a byte array for version info entry at top level. ;ConsoleWrite("Ubound>>"&UBound($A1)) $iOff = 0 $iNum3 = 1 ;Do While 1 For $iNum1 = 0 To 2 ;-- get offset number to next level from 2nd 4 bytes of entry structure. $A4[$iNum1] = $A1[$iNum1 + 4] Next $A4[3] = 0 $iNum2 = _GetNumFromBytes($A4) If ($A1[7] > 127) Then ;-- high bit was set in entry offset value, so it;s just a pointer to another pointer. $iNum2 = $LocRes + $iNum2 + 16 _Skip($file, $iNum2 - $iReadPt) ;- 1) $s1 = FileRead($file, 8) $iReadPt = $iReadPt + (($iNum2 - $iReadPt) + 8) $A1 = _GetArray($s1) Else ;-- this is the offset of version info offset info.! $iOff = ($iNum2 + $LocRes) ;ExitLoop ExitLoop EndIf $iNum3 = $iNum3 + 1 If ($iNum3 > 10) Then ExitLoop WEnd ;Loop If ($iOff = 0) Then ;-- have to quit. no final offset found. ;ConsoleWrite("Return point 2" & @LF) Return 3 ;failed to find version info in resource table. EndIf _Skip($file, $iOff - $iReadPt) $s1 = FileRead($file, 8) $iReadPt = $iReadPt + (($iOff - $iReadPt) + 8) $A1 = _GetArray($s1) For $iNum1 = 0 To 3 $A4[$iNum1] = $A1[$iNum1] Next $VIOffset = _GetNumFromBytes($A4) ;--offset of version info. given in .rsrc section. $ReadOffset = (($VIOffset - $VLocRes) + $LocRes) For $iNum1 = 0 To 3 $A4[$iNum1] = $A1[$iNum1 + 4] Next $SizeRes = _GetNumFromBytes($A4) _Skip($file, $ReadOffset - $iReadPt) $s1 = FileRead($file, $SizeRes) ;-- read out the entire FileVersionInfo data area. FileClose($file) $file = 0 $sB = _GetByteString($s1, True) ;-- snip unicode. $Pt1 = StringInStr($sB, $sVerString) If ($Pt1 > 0) Then ;-- "VS_VER" was found, so process the string and quit. $ARet = _ProcessRes($sB) $ARet2 = $ARet Return 0 ; ok ElseIf ($BooAspack = True) Then ;-- if "VS_VER" was not found but there is an "aspack" section then try that. $ReadOffset = (($VIOffset - $VLocAspack) + $LocAspack) ;-- calculate a new file version info data offset. $file = FileOpen($sFilePath, 0) ;-- The file was closed and is now re-opened here. Keeping the file _Skip($file, $ReadOffset) ;-- open "just in case" wouldn;t have helped because the file pointer $s1 = FileRead($file, $SizeRes) ;-- for this read may be further back thean the pointer was when the file FileClose($file) ;-- was closed. So rather than try to sort out the read point, the file is just $file = 0;-- opened fresh and Skip is used. $sB = _GetByteString($s1, True) $Pt1 = StringInStr($sB, $sVerString) If ($Pt1 > 0) Then $ARet = _ProcessRes($sB) $ARet2 = $ARet Return 0 ; ok Else ;ConsoleWrite("Return point 3" & @LF) Return 3 ;failed to find version info in resource table. EndIf Else ;ConsoleWrite("Return point 4" & @LF) Return 3 ;failed to find version info in resource table. EndIf EndFunc ;==>_GetVersionInfo Func _ProcessRes($sDat) ; find where the resource info starts Local $info_ptr = StringInStr($sDat, "StringFileInfo") + StringLen("StringFileInfo") Local $s_props = StringMid($sDat, $info_ptr) Local $occurance = 2 While 1 $info_ptr = StringInStr($s_props, Chr(1), 0, $occurance) + 1 ; find the beginning of a property name $s_prop = StringMid($s_props, $info_ptr) $prop_ptr = StringInStr($s_prop, "*") ; find the end of a property name If @error Then ExitLoop $s_prop = StringMid($s_prop, 1, $prop_ptr - 1) If Not StringLen($s_prop) Then ExitLoop ; no more resources If $s_prop = "VarFileInfo" Then ExitLoop ; no more resources $occurance += 1 ReDim $props[$occurance - 2] $props[$occurance - 3] = $s_prop ; resize and populate array with resource names WEnd Dim $AInfo[UBound($props) ] ; On Error Resume Next For $x = 0 To UBound($props) - 1 $AInfo[$x] = _GetInfo($sDat, $props[$x]) Next Return $AInfo EndFunc ;==>_ProcessRes Func _GetInfo($sStr, $sVal) ; had to rewrite this function because of incompatiblities ;between AutoIt3 and Vb string functions. Dim $Pta, $Ptb, $LenVal, $s4, $occurance = 1 $LenVal = StringLen($sVal) + 1 ;-- length of info string: "CompanyName" = 11 $Pta = StringInStr($sStr, $sVal) ;-- find string name. ;~ ConsoleWrite(String(StringMid($sStr, $Pta - 1, 1)) & @LF) If ($Pta > 0) Then $Pta = $Pta + $LenVal $Ptb = $Pta + StringInStr(StringMid($sStr, $Pta + 1), "*") ;~ ConsoleWrite(StringMid($sStr, $Pta + 1) & @LF) ;~ ConsoleWrite(StringMid($sStr, $Pta, ($Ptb - $Pta)) & @LF) If $Ptb > ($Pta + 2) And $sVal <> "FileVersion" Then $s4 = StringMid($sStr, $Pta, ($Ptb - $Pta)) $s4 = StringReplace($s4, "*", "") If StringInStr($s4, Chr(1), 1) = 0 Then Return $s4 ElseIf $sVal = "FileVersion" Then ; deal with file version $s4 = StringMid($sStr, $Pta, ($Ptb - $Pta)) If $Ptb = ($Pta + 2) Then $s4 &= '.0.0.0' EndIf $s4 = StringReplace($s4, "*", "") $s4 = StringReplace($s4, ",", ".") $s4 = StringStripWS($s4, 8) If StringInStr($s4, Chr(1), 1) = 0 Then Return $s4 ;-- check for Chr(1) which seems to be found EndIf EndIf Return "" EndFunc ;==>_GetInfo ;-------------- simplified version of _GetByteString For this Class. --------------------- Func _GetByteString($sStr, $SnipUnicode) Local $sRet, $iLen, $iA, $iLen2, $A2[2] ; On Error Resume Next $iLen2 = 0 If ($SnipUnicode = False) Then ReDim $A2[StringLen($sStr) ] For $iLen = 1 To StringLen($sStr) $iA = Asc(StringMid($sStr, $iLen, 1)) If $iA = 0 Then $iA = 42 ;-- converts 0-byte to * ;If UBound($A2) > $iLen - 1 Then $A2[$iLen - 1] = Chr($iA) Next Else ReDim $A2[ (StringLen($sStr) / 2) ] For $iLen = 1 To StringLen($sStr) Step 2 $iA = Asc(StringMid($sStr, $iLen, 1)) If $iA = 0 Then $iA = 42 ;-- converts 0-byte to * $A2[$iLen2] = Chr($iA) $iLen2 = $iLen2 + 1 Next EndIf Return _Join($A2, "") EndFunc ;==>_GetByteString ;-------------------------------- Simplified version of _GetArray. ----------------------- Func _GetArray($sStr) Dim $iA, $Len1, $Len2, $AStr[2] ; On Error Resume Next $Len1 = StringLen($sStr) If $Len1 > 1 Then ReDim $AStr[ ($Len1) ] For $iA = 1 To $Len1 $AStr[$iA - 1] = Asc(StringMid($sStr, $iA, 1)) Next Return $AStr EndFunc ;==>_GetArray ;-------------------- return a number from 2 or 4 bytes. --------------- Func _GetNumFromBytes($ABytes) Dim $Num1 ; Err.Clear ; On Error Resume Next ;_GetNumFromBytes = -1 If UBound($ABytes) > 1 Then $Num1 = ($ABytes[0] + $ABytes[1] * 256) If (UBound($ABytes) > 3) Then $Num1 = $Num1 + ($ABytes[2] * 65536) + ($ABytes[3] * 16777216) EndIf Return $Num1 EndIf EndFunc ;==>_GetNumFromBytes Func _Join($aStrJ, $sDelim = "") ;mimic VB Join command (array to string with delimiter) Local $sTemp If IsArray($aStrJ) Then For $x = 0 To UBound($aStrJ) - 1 $sTemp = $sTemp & $aStrJ[$x] & $sDelim Next ;ConsoleWrite("Join $sTemp =" & $sTemp & @LF) Return $sTemp EndIf SetError(1) Return "" EndFunc ;==>_Join Func _Skip($hfile, $bytes) ;mimic FSO.skip Local $temp = FileRead($hfile, $bytes) If @error Then Return 0 Return 1 EndFunc ;==>_Skip SciTE for AutoItDirections for Submitting Standard UDFs Don't argue with an idiot; people watching may not be able to tell the difference.
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now