Welcome, guest | Sign In | My Account | Store | Cart

This is a recipe for extracting file version information from Windows files, using the Win32 API.

Python, 73 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# fileversion.py

import dynwin.windll as windll
import dynwin.oracle as oracle
import calldll
cstring = windll.cstring
version = windll.module('version')

vs_fixed_file_info = oracle.Oracle(
    'fixed file info structure',
    'Nllhhhhhhhhlllllll',
    ('signature',
     'struc_version',
     'file_version_msl',
     'file_version_msh',
     'file_version_lsl',
     'file_version_lsh',
     'product_version_msl',
     'product_version_msh',
     'product_version_lsl',
     'product_version_lsh',
     'file_flags_mask',
     'file_flags',
     'file_os',
     'file_type',
     'file_subtype',
     'file_date_ms',
     'file_date_ls')
    )

def _get_file_version_info_size(filename):
    return version.GetFileVersionInfoSize(cstring(filename), 0)

def _get_file_version_info(filename, verinfosize):
    buffer = windll.membuf(verinfosize)
    result = version.GetFileVersionInfo(cstring(filename),
                                        0,
                                        buffer.size(),
                                        buffer.address())
    return result, buffer

def _ver_query_value(buffer):
    verbufptr = windll.membuf(32)
    verbuflen = windll.membuf(32)
    result = version.VerQueryValue(buffer.address(),
                                   cstring('\\'),
                                   verbufptr,
                                   verbuflen)
    verbuffer = calldll.read_string(calldll.read_long(verbufptr),
                                    calldll.read_long(verbuflen))
    dict, size = vs_fixed_file_info.unpack(verbuffer)
    msh = str(dict['file_version_msh'])
    msl = str(dict['file_version_msl'])
    lsh = str(dict['file_version_lsh'])
    lsl = str(dict['file_version_lsl'])
    file_version = msh + '.' + msl + '.' + lsh + '.' + lsl
    return result, file_version

def get_file_version(filename):
    verinfosize = _get_file_version_info_size(filename)
    if not verinfosize: return None
    result, buffer = _get_file_version_info(filename, verinfosize)
    if not result: return None
    result, file_version = _ver_query_value(buffer)
    if not result: return None
    return file_version

if __name__ == '__main__':
    import os
    for file in os.listdir('c:\\winnt\\system32'):
        if not os.path.isdir(file):
            file_version = get_file_version(file)
            print file, file_version

Mark Hammond's otherwise excellent win32 extensions do not include a function for extracting file version information. Sam Rushing's windll, calldll, and npstruct modules (http://www.nightmare.com/~rushing/dynwin/) allow calling functions from any DLL, and provide a way to call the win32 API directly. Unfortunately, windll/calldll/npstruct is not documented very well, and it took me awhile to figure out how to do this. Hopefully, this recipe will help others who need to do the same thing or just provide a little more insight into using Sam's modules.