0

I'm trying to get version of files through GetFileVersionInfoSizeW and VerQueryValueW. I got partial of the version printed out but not the entire thing. It also has some weird spaces between each character of the file version. Anyone has an idea what is wrong with it?

My guess is it is related to the Unicode interpretation of python3 since I had to change the GetFileVersionInfoSizeW and VerQueryValueW from the original GetFileVersionInfoSizeA and VerQueryValueA that ran normally in python2 (https://stackoverflow.com/a/38924793/7144869).

import array
from ctypes import *

def get_file_info(filename):
"""
Extract information from a file.
"""
# Get size needed for buffer (0 if no info)
size = windll.version.GetFileVersionInfoSizeW(filename, None)
# If no info in file -> empty string
if not size:
    return 'Failed'
# Create buffer
res = create_string_buffer(size)
# Load file informations into buffer res
windll.version.GetFileVersionInfoW(filename, None, size, res)
r = c_uint()
l = c_uint()
# Look for codepages
windll.version.VerQueryValueW(res, '\\VarFileInfo\\Translation',
                              byref(r), byref(l))
# If no codepage -> empty string
if not l.value:
    return ''
# Take the first codepage (what else ?)
codepages = array.array('H', string_at(r.value, l.value))
codepage = tuple(codepages[:2].tolist())
# Extract information
windll.version.VerQueryValueW(res, ('\\StringFileInfo\\%04x%04x\\'
+ 'FileVersion') % codepage, byref(r), byref(l))
return string_at(r.value, l.value)

print (get_file_info(r'C:\WINDOWS\system32\calc.exe').decode())
Community
  • 1
  • 1
Iker Hua
  • 403
  • 4
  • 14
  • When i run this code i get a error "OSError: exception: access violation reading 0x0000000016F42058" where the hex value corresponds to r as if its not a valid memory pointer. It fails on this line codepages = array.array('H', string_at(r.value, l.value)). I can run the Python2 version using Ascii fine but cant for the life of me get this to work in Python3 even using your code. If you could post your fully functioning code that would be great. – user1024792 Sep 25 '17 at 22:54
  • It's the same as the one up there but at return you do: `return wstring_at(r.value, l.value)` – Iker Hua Sep 27 '17 at 17:24
  • With what version of Python 3? Ive tested on several different machines with 3.5 and 3.6 and i get a memory access violation on every system on the codepages = array.array('H', string_at(r.value, l.value)) line. Do you have any idea's? There has to be something else different, i just cant see what it is. – user1024792 Sep 27 '17 at 20:21
  • I used 3.3. Hmmmm.It's weird... – Iker Hua Sep 27 '17 at 21:48

1 Answers1

0

The functions return what Microsoft calls "Unicode" strings, but it is really encoded UTF-16LE that ctypes.wstring can convert. l.value is a count of UTF16 characters, not bytes, so use the following to decode it properly. You won't need to .decode() the result as you are doing now.

return wstring_at(r.value, l.value)

Here's my working code:

from ctypes import *
from ctypes import wintypes as w

ver = WinDLL('version')
ver.GetFileVersionInfoSizeW.argtypes = w.LPCWSTR, w.LPDWORD
ver.GetFileVersionInfoSizeW.restype = w.DWORD
ver.GetFileVersionInfoW.argtypes = w.LPCWSTR, w.DWORD, w.DWORD, w.LPVOID
ver.GetFileVersionInfoW.restype = w.BOOL
ver.VerQueryValueW.argtypes = w.LPCVOID, w.LPCWSTR, POINTER(w.LPVOID), w.PUINT
ver.VerQueryValueW.restype = w.BOOL

def get_file_info(filename):
    size = ver.GetFileVersionInfoSizeW(filename, None)
    if not size:
        raise RuntimeError('version info not found')
    res = create_string_buffer(size)
    if not ver.GetFileVersionInfoW(filename, 0, size, res):
        raise RuntimeError('GetFileVersionInfoW failed')
    buf = w.LPVOID()
    length = w.UINT()
    # Look for codepages
    if not ver.VerQueryValueW(res, r'\VarFileInfo\Translation', byref(buf), byref(length)):
        raise RuntimeError('VerQueryValueW failed to find translation')
    if length.value == 0:
        raise RuntimeError('no code pages')
    codepages = array.array('H', string_at(buf.value, length.value))
    codepage = tuple(codepages[:2])
    # Extract information
    if not ver.VerQueryValueW(res, rf'\StringFileInfo\{codepage[0]:04x}{codepage[1]:04x}\FileVersion', byref(buf), byref(length)):
        raise RuntimeError('VerQueryValueW failed to find file version')
    return wstring_at(buf.value,length.value)

print(get_file_info(r'c:\windows\system32\calc.exe'))

Output:

10.0.19041.1 (WinBuild.160101.0800)
Mark Tolonen
  • 166,664
  • 26
  • 169
  • 251
  • Thanks a lot @Mark Tolonen These kinds of encoding/decoding are really confusing sometimes. Just a quick question though, how did you know about function wstring_at() Is there a reference online that I can read regarding to ctypes in python? – Iker Hua Mar 06 '17 at 07:08
  • @IkerHua The ctypes library has good documentation on docs.python.org. Also `dir(ctypes)` lists it and `help(ctypes.wstring_at)` describes it. – Mark Tolonen Mar 06 '17 at 15:26
  • I always receive an exception "access violation at 0x0000000074CF4CC0" for codepages = array.array('H', wstring_at(r.value, l.value)) is there anything to fix this? – s kop Jan 13 '22 at 13:49