Jump to content

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

[::script::GetFullVersionInfo] string version info for execs


  • Please log in to reply
28 replies to this topic
wOxxOm
  • Members
  • 371 posts
  • Last active: Feb 20 2015 12:10 PM
  • Joined: 09 Feb 2006
This script gets full version information for CompanyName/FileDescription/etc. string version info from executable. Because AHK doesn't include such a capability I took an example in VBA and recoded it in AHK

; LITTLE DEMONSTRATION: hot to fetch full string version information from
; executable files

oldErrMode:=dllCall("SetErrorMode",uint,1) ; SEM_FAILCRITICALERRORS=0x0001
verExts:="|exe|dll|drv|fon|ttf|vxd|sys|cpl|ocx|"
loop,%winDir%\*,,1
{  splitPath,A_LoopFileLongPath,,,ext
   ifInString,verExts,|%ext%|
   {  count ++
      traytip,,%A_LoopFileLongPath%
      info:=info . A_LoopFileLongPath "`t"
                 . FileGetFullVer(A_LoopFileLongPath,1|2|4|8|0xFFFF,"`t") "`n`n"
      if (count=5)
         break
   }
}
dllCall("SetErrorMode",uint,oldErrMode)

if !info
{  fileSelectFile,file,1
   info:=file "`t" FileGetFullVer(file,1|2|4|8|0x8000,"`t")
}

msgbox %info%
ExitApp


FileGetFullVer(file,verFlags=1,delim="")               ;;
{  ifEqual,delim,,delim=|
; FLAGS: 0x0001 - numeric info
;        0x0002 - file type
;        0x0004 - file description
;        0x0008 - company name
;        0x0010 - FileVersion
;        0x0020 - Comments
;        0x0040 - InternalName
;        0x0080 - LegalCopyright
;        0x0100 - LegalTrademarks
;        0x0200 - OriginalFilename
;        0x0400 - ProductName
;        0x0800 - ProductVersion
;        0x1000 - PrivateBuild
;        0x2000 - SpecialBuild
;        0x4000 - LegalTrademarks1

;        0x8000 - clean-up empty fields

; VersionInfoStrings
;   viPredefinedFirst = 0
;   viLanguage = 0
;   viComments = 1
;   viCompanyName = 2
;   viFileDescription = 3
;   viFileVersion = 4
;   viInternalName = 5
;   viLegalCopyright = 6
;   viLegalTrademarks = 7
;   viOriginalFilename = 8
;   viProductName = 9
;   viProductVersion = 10
;   viPrivateBuild = 11
;   viSpecialBuild = 12
;   viLegalTrademarks1 = 13  'Used by Office apps only?
;   viLegalTrademarks2 = 14  'Used by Office apps only?
;   viPredefinedLast = 14

;typedef struct tagVS_FIXEDFILEINFO
; 	0  DWORD dwSignature;
; 	4  DWORD dwStrucVersion;
; 	8  DWORD dwFileVersionMS;
; 	12 DWORD dwFileVersionLS;
; 	16 DWORD dwProductVersionMS;
; 	20 DWORD dwProductVersionLS;
; 	24 DWORD dwFileFlagsMask;
; 	28 DWORD dwFileFlags;
; 	32 DWORD dwFileOS;
; 	36 DWORD dwFileType;
; 	40 DWORD dwFileSubtype;
; 	44 DWORD dwFileDateMS;
; 	48 DWORD dwFileDateLS;

;   MAX_PATH = 260
;   ; ----- VS_VERSION.dwFileFlags -----
;   VS_FFI_SIGNATURE = 0xFEEF04BD
;   VS_FFI_STRUCVERSION = 0x10000
;   VS_FFI_FILEFLAGSMASK = 0x3F
;   ; ----- VS_VERSION.dwFileFlags -----
;   VS_FF_DEBUG = 0x1
;   VS_FF_PRERELEASE = 0x2
;   VS_FF_PATCHED = 0x4
;   VS_FF_PRIVATEBUILD = 0x8
;   VS_FF_INFOINFERRED = 0x10
;   VS_FF_SPECIALBUILD = 0x20
;   ; ----- VS_VERSION.dwFileOS -----
;   VOS_UNKNOWN = 0x0
;   VOS_DOS = 0x10000
;   VOS_OS216 = 0x20000
;   VOS_OS232 = 0x30000
;   VOS_NT = 0x40000
;   VOS_DOS_WINDOWS16 = 0x10001
;   VOS_DOS_WINDOWS32 = 0x10004
;   VOS_OS216_PM16 = 0x20002
;   VOS_OS232_PM32 = 0x30003
;   VOS_NT_WINDOWS32 = 0x40004
;   ; ----- VS_VERSION.dwFileType -----
;   VFT_UNKNOWN = 0x0
;   VFT_APP = 0x1
;   VFT_DLL = 0x2
;   VFT_DRV = 0x3
;   VFT_FONT = 0x4
;   VFT_VXD = 0x5
;   VFT_STATIC_LIB = 0x7
;   ; **** VS_VERSION.dwFileSubtype for VFT_WINDOWS_FONT ****
;   VFT2_FONT_RASTER = 0x1
;   VFT2_FONT_VECTOR = 0x2
;   VFT2_FONT_TRUETYPE = 0x3
;   ; ----- VS_VERSION.dwFileSubtype for VFT_WINDOWS_DRV -----
;   VFT2_UNKNOWN = 0x0
;   VFT2_DRV_PRINTER = 0x1
;   VFT2_DRV_KEYBOARD = 0x2
;   VFT2_DRV_LANGUAGE = 0x3
;   VFT2_DRV_DISPLAY = 0x4
;   VFT2_DRV_MOUSE = 0x5
;   VFT2_DRV_NETWORK = 0x6
;   VFT2_DRV_SYSTEM = 0x7
;   VFT2_DRV_INSTALLABLE = 0x8
;   VFT2_DRV_SOUND = 0x9
;   VFT2_DRV_COMM = 0xA

   version=
   dummy:=1
   fiSize:=dllCall("version\GetFileVersionInfoSizeA",str,file,uint,&dummy)
   varSetCapacity(fi,fiSize,0)
   loop,1
   {  if !dllCall("version\GetFileVersionInfoA",str,file, int,0, int,fiSize, uint,&fi)
         break
      if !dllCall("version\VerQueryValueA",uint,&fi, str,"\", uintp,fiFFI#, uintp,dummy)
         break
      varSetCapacity(fiFFI,13*4)
      dllCall("RtlMoveMemory",uint,&fiFFI,uint,fiFFI#,uint,13*4)
      version:=iif(verFlags & 1=0,""
                  ,"v" extractInteger(fiFFI,10,0,2) "." extractInteger(fiFFI,8,0,2)
                  . iif(extractInteger(fiFFI,12,0,2)=0,""
                        ,"." extractInteger(fiFFI,14,0,2) "." extractInteger(fiFFI,12,0,2)))
             . iif(verFlags & 2=0,""
                  ,"|" switch(extractInteger(fiFFI,36)+1,""
                        ,"Application","DLL"
                        ,"|" switch(extractInteger(fiFFI,40)+1,"","Printer ","Keyboard "
                              ,"Language ","Display ","Mouse ","Network ","System "
                              ,"Installable ", "Sound ","Comm. ") "driver"
                        ,"|" switch(extractInteger(fiFFI,40),"Raster","Vector","TrueType") " font"
                        ,"|VxD driver", "|Static Lib"))
      if (verFlags & 0xFFFE)
      {  if !dllCall("version\VerQueryValueA",uint,&fi, str,"\VarFileInfo\Translation", uintp,fiTrans#, uintp,dummy)
            break
         ifEqual,dummy,0, break
         fiTrans:=0
         dllCall("RtlMoveMemory",uintP,fiTrans,uint,fiTrans#,uint,4)

         Lang#:=fiTrans & 0xFFFF
         CP#:=fiTrans>>16

         varSetCapacity(lang,256,0)
         dummy:=dllCall("VerLanguageNameA",uint,Lang#, str,lang, uint,256)
         stringLeft,lang,lang,%dummy%

         sSubBlock:= "\StringFileInfo\" FmtHex(Lang#,4) . FmtHex(CP#,4) "\"
         Company:=verGetStdValue(fi,sSubBlock "CompanyName")
         if !Company
         {  ; Try U.S. English...?
            dummy:="\StringFileInfo\0409" FmtHex(CP#,4) "\"
            Company:=verGetStdValue(fi,dummy "CompanyName")
            if (Company)
               sSubBlock:=dummy ; We probably found the MS version bug.
         }

         version:=version
            . iif(verFlags & 4=0,"","|" verGetStdValue(fi,sSubBlock "FileDescription"))
            . iif(verFlags & 8=0,"","|" Company)
            . iif(verFlags & 0x10,"|" verGetStdValue(fi,sSubBlock "FileVersion"),"")
            . iif(verFlags & 0x20,"|" verGetStdValue(fi,sSubBlock "Comments"),"")
            . iif(verFlags & 0x40,"|" verGetStdValue(fi,sSubBlock "InternalName"),"")
            . iif(verFlags & 0x80,"|" verGetStdValue(fi,sSubBlock "LegalCopyright"),"")
            . iif(verFlags & 0x100,"|" verGetStdValue(fi,sSubBlock "LegalTrademarks"),"")
            . iif(verFlags & 0x200,"|" verGetStdValue(fi,sSubBlock "OriginalFilename"),"")
            . iif(verFlags & 0x400,"|" verGetStdValue(fi,sSubBlock "ProductName"),"")
            . iif(verFlags & 0x800,"|" verGetStdValue(fi,sSubBlock "ProductVersion"),"")
            . iif(verFlags & 0x1000,"|" verGetStdValue(fi,sSubBlock "PrivateBuild"),"")
            . iif(verFlags & 0x2000,"|" verGetStdValue(fi,sSubBlock "SpecialBuild"),"")
            . iif(verFlags & 0x4000,"|" verGetStdValue(fi,sSubBlock "LegalTrademarks1")
                                  . "|" verGetStdValue(fi,sSubBlock "LegalTrademarks2"),"")
      }
   }
   if (verFlags & 0x8000)
   {  dummy=
      version=|%version%|
      stringReplace,version,version,|v0.0|
      loop,parse,version,|
         if (A_LoopField)
            dummy:=dummy . delim . A_LoopField
      return strMid(dummy,2)
   }
   if (delim<>"|")
      stringReplace,version,version,|,%delim%,all
   return version
}

verGetStdValue(byref fi, value)        ;;
{  fiValue#:=0
   dummy:=0
   if !dllCall("version\VerQueryValueA",str,fi, str,value, uintp,fiValue#, uintp,dummy)
      return
   ifEqual,dummy,0, return

   len:=dllCall("lstrlenA",uint,fiValue#)
   varSetCapacity(fiValue,len+1,0)
   dllCall("RtlMoveMemory",str,fiValue, uint,fiValue#, uint,len)

      __trim:=A_AutoTrim
      AutoTrim,on
   fiValue=%fiValue%
      AutoTrim,%__trim%

   return fiValue
}
fmtHex(num,digits=8)                  ;; without "0x" padded with "0" ;;
{  varSetCapacity(s,digits+8,asc("0"))
   __format:=A_FormatInteger
   setformat,integer,hex
   num+=0
   s:=s . num
   stringReplace,s,s,0x
   setformat,integer,%__format%
   return strRight(s,digits)
}
iif(expr, a, b)                            ;;
{
   if (expr)
      return a
   else
      return b
}
strLeft(s, n)                            ;;
{
   stringLeft,s,s,%n%
   return s
}
strRight(s, n)                            ;;
{
   stringRight,s,s,%n%
   return s
}
strMid(s, begin, n=0x7FFF, Left=0) ; if L<>0 then mid to the left  ;;
{
   if Left=0
      stringMid,s,s,%begin%,%n%
   else
      stringMid,s,s,%begin%,%n%,L
   return s
}
ExtractInteger(ByRef pSource, pOffset = 0, pIsSigned = false, pSize = 4) ;;
; pSource is a string (buffer) whose memory area contains a raw/binary integer at pOffset.
; The caller should pass true for pSigned to interpret the result as signed vs. unsigned.
; pSize is the size of PSource's integer in bytes (e.g. 4 bytes for a DWORD or Int).
; pSource must be ByRef to avoid corruption during the formal-to-actual copying process
; (since pSource might contain valid data beyond its first binary zero).
{
	Loop %pSize%  ; Build the integer by adding up its bytes.
		result += *(&pSource + pOffset + A_Index-1) << 8*(A_Index-1)
	if (!pIsSigned OR pSize > 4 OR result < 0x80000000)
		return result  ; Signed vs. unsigned doesn't matter in these cases.
	; Otherwise, convert the value (now known to be 32-bit) to its signed counterpart:
	return -(0xFFFFFFFF - result + 1)
}

InsertInteger(pInteger, ByRef pDest, pOffset = 0, pSize = 4) ;;
; The caller must ensure that pDest has sufficient capacity.  To preserve any existing contents in pDest,
; only pSize number of bytes starting at pOffset are altered in it.
{
	Loop %pSize%  ; Copy each byte in the integer into the structure as raw binary data.
		DllCall("RtlFillMemory", UInt, &pDest + pOffset + A_Index-1, UInt, 1, UChar, pInteger >> 8*(A_Index-1) & 0xFF)
}

switch(idx,val1="",val2="",val3="",val4="",val5="",val6="",val7="",val8=""
          ,val9="",val10="",val11="",val12="",val13="",val14="",val15="",val16="")   ;;
{
   return val%idx%
}



BoBo
  • Guests
  • Last active:
  • Joined: --
Impressing! Thx. 8)

toralf
  • Moderators
  • 4035 posts
  • Last active: Aug 20 2014 04:23 PM
  • Joined: 31 Jan 2005
Thanks a lot, I was looking for this a long time. Do you also know how to SET/MODIDFY the comment?
Ciao
toralf
 
I use the latest AHK version (1.1.15+)
Please ask questions in forum on ahkscript.org. Why?
For online reference please use these Docs.

toralf
  • Moderators
  • 4035 posts
  • Last active: Aug 20 2014 04:23 PM
  • Joined: 31 Jan 2005
The code works on executables in the windir.

But I tried your function on Word documents to fetch the comments stored in the file info. Unfortunately the dllcall in
if !dllCall("version\GetFileVersionInfoA",str,file, int,0, int,fiSize, uint,&fi)
         break 
doesn't work and the loop breaks. Is there a solution to fetch the comments from a word document?

Edit: the fiSize is zero from
fiSize:=dllCall("version\GetFileVersionInfoSizeA",str,file,uint,&dummy)
So I guess the file info of word documents can't be accessed with the version.dll functions.
Ciao
toralf
 
I use the latest AHK version (1.1.15+)
Please ask questions in forum on ahkscript.org. Why?
For online reference please use these Docs.

PhiLho
  • Moderators
  • 6850 posts
  • Last active: Jan 02 2012 10:09 PM
  • Joined: 27 Dec 2005
toralf:
- AFAIK, this information is read-only, unless you use the same method as Resource Hacker. I don't know this method, perhaps it just does binary parsing and rewriting. Note that some virus checkers may not like this operation...

- GetFileVersionInfo just reads the resource of the executable. The Summary (is that the English name?) tab of the properties of a Word (Office) document is a very different beast. I believe it is serialized Ole information, but I don't know much more. I think that the Summary tab of other documents (simple text document, for example), is stored elsewhere (perhaps in the registry) by the OS. An image have differents information, like the size, resolution and color depth.
Posted Image vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2")

shimanov
  • Members
  • 610 posts
  • Last active: Jul 18 2006 08:35 PM
  • Joined: 25 Sep 2005
The information presented in the "Summary" tab of a file's properties is stored in an alternate stream on NTFS formatted partitions.

Check the free tool, Streams, from Sysinternals.

toralf
  • Moderators
  • 4035 posts
  • Last active: Aug 20 2014 04:23 PM
  • Joined: 31 Jan 2005
I knew about alternate streams, but I did check again. I used the tool you gave the link too, but the app tells me that the information is not being found (no streams are found). But the files have text in ther comment field.
It seams to be in the stream. But the funny thing is that it is not only limited to NTFS. If you send these files by email or put them to a server and load them back, the data is still there. I guess that wouldn't happen with an normal alternate stream.
Ciao
toralf
 
I use the latest AHK version (1.1.15+)
Please ask questions in forum on ahkscript.org. Why?
For online reference please use these Docs.

PhiLho
  • Moderators
  • 6850 posts
  • Last active: Jan 02 2012 10:09 PM
  • Joined: 27 Dec 2005
Very interesting information, I didn't knew about these alternate streams.
Note: the streams tool actually want file pattern, not directory. "streams ." or "streams C:\tmp" doesn't work, you have to use -s or to add \* to get it working.

Play with streams:
> echo Foo Bar Gah > ARealFile.txt:aStreamName
> more < ARealFile.txt:aStreamName
Foo Bar Gah

You can use different stream names for the same file...

I will play and see if these streams can be accessed from AHK. Probably should work with Run %comspec% /c .
The streams written from the Summary tab for the file Properties have strange names: ♣DocumentSummaryInformation and ♣SummaryInformation (displayed as a cross under cmd, as a clover in my browser) and seems harder to access (probably binary data).

Mmm, I see that shimanov actually provided similar information in the topic he gave a link to.
Posted Image vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2")

toralf
  • Moderators
  • 4035 posts
  • Last active: Aug 20 2014 04:23 PM
  • Joined: 31 Jan 2005
Hi PhiLho,

I couldn't get the streams visble with streams.exe. How did you do it?

I tried absolut and relative path. I tried \*, *.doc, *.* but I never got the ♣DocumentSummaryInformation shown.
Ciao
toralf
 
I use the latest AHK version (1.1.15+)
Please ask questions in forum on ahkscript.org. Why?
For online reference please use these Docs.

PhiLho
  • Moderators
  • 6850 posts
  • Last active: Jan 02 2012 10:09 PM
  • Joined: 27 Dec 2005
I created a little text file (in WinXP SP2).
Show properties (Alt+Enter in Explorer), go to Summary tab (or whatever it is really called in your locale... "Résumé" en français...)
In Simple mode (not Advanced), you can enter some data. Validate.
> streams .\*

Streams v1.53 - Enumerate alternate NTFS data streams
Copyright (C) 1999-2005 Mark Russinovich
Sysinternals - www.sysinternals.com

E:\tmp\FooBar.txt:
   :♣DocumentSummaryInformation:$DATA   116
   :♣SummaryInformation:$DATA   256
   :anotherStream:$DATA 14
          :stream:$DATA 20
   :{4c8cc155-6c1e-11d1-8e41-00c04fb9386d}:$DATA        0
The steam and anotherStream were created using the redirection method I explain above.
Posted Image vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2")

toralf
  • Moderators
  • 4035 posts
  • Last active: Aug 20 2014 04:23 PM
  • Joined: 31 Jan 2005
I could reproduce it with a text file. But for word files it doesn't show anything.
Ciao
toralf
 
I use the latest AHK version (1.1.15+)
Please ask questions in forum on ahkscript.org. Why?
For online reference please use these Docs.

PhiLho
  • Moderators
  • 6850 posts
  • Last active: Jan 02 2012 10:09 PM
  • Joined: 27 Dec 2005
To quote myself:

The Summary (is that the English name?) tab of the properties of a Word (Office) document is a very different beast. I believe it is serialized Ole information, but I don't know much more.

You can have Office summary information even on non-NTFS systems... In this case, the additional tab is a shell extension.

OK, searching a bit on CodeProject, I found an article which can be of interest for you...
Posted Image vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2")

toralf
  • Moderators
  • 4035 posts
  • Last active: Aug 20 2014 04:23 PM
  • Joined: 31 Jan 2005
Thank you for the link to the CodeProject. It seams close to what I want. (Is it also a command line tool?) The screenshot suggests that the text in the streams is shown as binary. I wonder how he is editing the streams? In Hex-Mode via a GUI?
I can't compile the source code, and I do not understand the code that he posted in his post, So I do not know how to find out if this could be of any use. I come to the conclusion that I badly need help, since this is to much over my head and knowledge.
Ciao
toralf
 
I use the latest AHK version (1.1.15+)
Please ask questions in forum on ahkscript.org. Why?
For online reference please use these Docs.

PhiLho
  • Moderators
  • 6850 posts
  • Last active: Jan 02 2012 10:09 PM
  • Joined: 27 Dec 2005
Another CodeProject article. The demo isn't compiled, alas.

Found yet another article explaining a bit the mystery of the streams shown by the Sysinternals tool.
Note: to read the remainder of the article, you can enter the login name/password pair: busy/boss
Found the trick somewhere on the Net (in a forum or discussion list). It seems the access was generated/given with BugMeNot...

An old Byte article explains some of the concepts being Ole.

PS.: most of these links are found searching OLE Document / Structured Document on Google.

(I write this as I investigate...)

Aha! I found an interesting article by Microsoft itself! They give a DLL (by actually an ActiveX) to access Office document properties. Alas, you need Com to use it. Perhaps one can write VBScript or JScript code to use it.

Aha again! Found a command line tool to access these properties! We are getting close.
I have not much time to test it right now, but I am sure you will be eager to test it yourself...

That's all for now. :-)
Posted Image vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2")

toralf
  • Moderators
  • 4035 posts
  • Last active: Aug 20 2014 04:23 PM
  • Joined: 31 Jan 2005
Thanks a lot.
I had an older version of outwit (1.23) on my disc, but didn't know it had these capabilities. I downloaded the new version (1.26) and tested it. It works. I can read the comments of the document. *happy*

Now, of cause, I "need" (want) a way to edit/add comments to a word file. I will take a look at the articles.

Thanks a again for your help, very much appreciated.
Ciao
toralf
 
I use the latest AHK version (1.1.15+)
Please ask questions in forum on ahkscript.org. Why?
For online reference please use these Docs.