versioninfo.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652
  1. # -*- coding: utf-8 -*-
  2. #-----------------------------------------------------------------------------
  3. # Copyright (c) 2013-2021, PyInstaller Development Team.
  4. #
  5. # Distributed under the terms of the GNU General Public License (version 2
  6. # or later) with exception for distributing the bootloader.
  7. #
  8. # The full license is in the file COPYING.txt, distributed with this software.
  9. #
  10. # SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
  11. #-----------------------------------------------------------------------------
  12. import codecs
  13. import struct
  14. from PyInstaller.compat import win32api
  15. # ::TODO:: #1920 revert to using pypi version
  16. import pefile
  17. def pefile_check_control_flow_guard(filename):
  18. """
  19. Checks if the specified PE file has CFG (Control Flow Guard) enabled.
  20. Parameters
  21. ----------
  22. filename : str
  23. Path to the PE file to inspect.
  24. Returns
  25. ----------
  26. bool
  27. True if file is a PE file with CFG enabled. False if CFG is not
  28. enabled or if file could not be processed using pefile library.
  29. """
  30. try:
  31. pe = pefile.PE(filename)
  32. # https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
  33. # IMAGE_DLLCHARACTERISTICS_GUARD_CF = 0x4000
  34. return bool(pe.OPTIONAL_HEADER.DllCharacteristics & 0x4000)
  35. except Exception:
  36. return False
  37. # TODO implement read/write version information with pefile library.
  38. # PE version info doc: http://msdn.microsoft.com/en-us/library/ms646981.aspx
  39. def pefile_read_version(filename):
  40. """
  41. Return structure like:
  42. {
  43. # Translation independent information.
  44. # VS_FIXEDFILEINFO - Contains version information about a file. This information is language and code page independent.
  45. u'FileVersion': (1, 2, 3, 4),
  46. u'ProductVersion': (9, 10, 11, 12),
  47. # PE files might contain several translations of version information.
  48. # VS_VERSIONINFO - Depicts the organization of data in a file-version resource. It is the root structure that contains all other file-version information structures.
  49. u'translations': {
  50. 'lang_id1' : {
  51. u'Comments': u'日本語, Unicode 対応.',
  52. u'CompanyName': u'your company.',
  53. u'FileDescription': u'your file desc.',
  54. u'FileVersion': u'1, 2, 3, 4',
  55. u'InternalName': u'your internal name.',
  56. u'LegalCopyright': u'your legal copyright.',
  57. u'LegalTrademarks': u'your legal trademarks.',
  58. u'OriginalFilename': u'your original filename.',
  59. u'PrivateBuild': u'5, 6, 7, 8',
  60. u'ProductName': u'your product name',
  61. u'ProductVersion': u'9, 10, 11, 12',
  62. u'SpecialBuild': u'13, 14, 15, 16',
  63. },
  64. 'lang_id2' : {
  65. ...
  66. }
  67. }
  68. }
  69. Version info can contain multiple languages.
  70. """
  71. # TODO
  72. vers = {
  73. 'FileVersion': (0, 0, 0, 0),
  74. 'ProductVersion': (0, 0, 0, 0),
  75. 'translations': {
  76. 'lang_id1': {
  77. 'Comments': '',
  78. 'CompanyName': '',
  79. 'FileDescription': '',
  80. 'FileVersion': '',
  81. 'InternalName': '',
  82. 'LegalCopyright': '',
  83. 'LegalTrademarks': '',
  84. 'OriginalFilename': '',
  85. 'PrivateBuild': '',
  86. 'ProductName': '',
  87. 'ProductVersion': '',
  88. 'SpecialBuild': '',
  89. }
  90. }
  91. }
  92. pe = pefile.PE(filename)
  93. #ffi = pe.VS_FIXEDFILEINFO
  94. #vers['FileVersion'] = (ffi.FileVersionMS >> 16, ffi.FileVersionMS & 0xFFFF, ffi.FileVersionLS >> 16, ffi.FileVersionLS & 0xFFFF)
  95. #vers['ProductVersion'] = (ffi.ProductVersionMS >> 16, ffi.ProductVersionMS & 0xFFFF, ffi.ProductVersionLS >> 16, ffi.ProductVersionLS & 0xFFFF)
  96. #print(pe.VS_FIXEDFILEINFO.FileVersionMS)
  97. # TODO Only first available language is used for now.
  98. #vers = pe.FileInfo[0].StringTable[0].entries
  99. from pprint import pprint
  100. pprint(pe.VS_FIXEDFILEINFO)
  101. print(dir(pe.VS_FIXEDFILEINFO))
  102. print(repr(pe.VS_FIXEDFILEINFO))
  103. print(pe.dump_info())
  104. pe.close()
  105. return vers
  106. # Ensures no code from the executable is executed.
  107. LOAD_LIBRARY_AS_DATAFILE = 2
  108. def getRaw(text):
  109. """
  110. Encodes text as UTF-16LE (Microsoft 'Unicode') for use in structs.
  111. """
  112. return text.encode('UTF-16LE')
  113. def decode(pathnm):
  114. h = win32api.LoadLibraryEx(pathnm, 0, LOAD_LIBRARY_AS_DATAFILE)
  115. res = win32api.EnumResourceNames(h, pefile.RESOURCE_TYPE['RT_VERSION'])
  116. if not len(res):
  117. return None
  118. data = win32api.LoadResource(h, pefile.RESOURCE_TYPE['RT_VERSION'],
  119. res[0])
  120. vs = VSVersionInfo()
  121. j = vs.fromRaw(data)
  122. win32api.FreeLibrary(h)
  123. return vs
  124. def nextDWord(offset):
  125. """ Align `offset` to the next 4-byte boundary """
  126. return ((offset + 3) >> 2) << 2
  127. class VSVersionInfo:
  128. """
  129. WORD wLength; // length of the VS_VERSION_INFO structure
  130. WORD wValueLength; // length of the Value member
  131. WORD wType; // 1 means text, 0 means binary
  132. WCHAR szKey[]; // Contains the Unicode string "VS_VERSION_INFO".
  133. WORD Padding1[];
  134. VS_FIXEDFILEINFO Value;
  135. WORD Padding2[];
  136. WORD Children[]; // zero or more StringFileInfo or VarFileInfo
  137. // structures (or both) that are children of the
  138. // current version structure.
  139. """
  140. def __init__(self, ffi=None, kids=None):
  141. self.ffi = ffi
  142. self.kids = kids or []
  143. def fromRaw(self, data):
  144. i, (sublen, vallen, wType, nm) = parseCommon(data)
  145. #vallen is length of the ffi, typ is 0, nm is 'VS_VERSION_INFO'.
  146. i = nextDWord(i)
  147. # Now a VS_FIXEDFILEINFO
  148. self.ffi = FixedFileInfo()
  149. j = self.ffi.fromRaw(data, i)
  150. i = j
  151. while i < sublen:
  152. j = i
  153. i, (csublen, cvallen, ctyp, nm) = parseCommon(data, i)
  154. if nm.strip() == u'StringFileInfo':
  155. sfi = StringFileInfo()
  156. k = sfi.fromRaw(csublen, cvallen, nm, data, i, j+csublen)
  157. self.kids.append(sfi)
  158. i = k
  159. else:
  160. vfi = VarFileInfo()
  161. k = vfi.fromRaw(csublen, cvallen, nm, data, i, j+csublen)
  162. self.kids.append(vfi)
  163. i = k
  164. i = j + csublen
  165. i = nextDWord(i)
  166. return i
  167. def toRaw(self):
  168. raw_name = getRaw(u'VS_VERSION_INFO')
  169. rawffi = self.ffi.toRaw()
  170. vallen = len(rawffi)
  171. typ = 0
  172. sublen = 6 + len(raw_name) + 2
  173. pad = b''
  174. if sublen % 4:
  175. pad = b'\000\000'
  176. sublen = sublen + len(pad) + vallen
  177. pad2 = b''
  178. if sublen % 4:
  179. pad2 = b'\000\000'
  180. tmp = b''.join([kid.toRaw() for kid in self.kids ])
  181. sublen = sublen + len(pad2) + len(tmp)
  182. return (struct.pack('hhh', sublen, vallen, typ)
  183. + raw_name + b'\000\000' + pad + rawffi + pad2 + tmp)
  184. def __eq__(self, other):
  185. return self.toRaw() == other
  186. def __str__(self, indent=u''):
  187. indent = indent + u' '
  188. tmp = [kid.__str__(indent+u' ')
  189. for kid in self.kids]
  190. tmp = u', \n'.join(tmp)
  191. return (u"""# UTF-8
  192. #
  193. # For more details about fixed file info 'ffi' see:
  194. # http://msdn.microsoft.com/en-us/library/ms646997.aspx
  195. VSVersionInfo(
  196. %sffi=%s,
  197. %skids=[
  198. %s
  199. %s]
  200. )
  201. """ % (indent, self.ffi.__str__(indent), indent, tmp, indent))
  202. def __repr__(self):
  203. return ("versioninfo.VSVersionInfo(ffi=%r, kids=%r)" %
  204. (self.ffi, self.kids))
  205. def parseCommon(data, start=0):
  206. i = start + 6
  207. (wLength, wValueLength, wType) = struct.unpack('3h', data[start:i])
  208. i, text = parseUString(data, i, i+wLength)
  209. return i, (wLength, wValueLength, wType, text)
  210. def parseUString(data, start, limit):
  211. i = start
  212. while i < limit:
  213. if data[i:i+2] == b'\000\000':
  214. break
  215. i += 2
  216. text = data[start:i].decode('UTF-16LE')
  217. i += 2
  218. return i, text
  219. class FixedFileInfo:
  220. """
  221. DWORD dwSignature; //Contains the value 0xFEEFO4BD
  222. DWORD dwStrucVersion; // binary version number of this structure.
  223. // The high-order word of this member contains
  224. // the major version number, and the low-order
  225. // word contains the minor version number.
  226. DWORD dwFileVersionMS; // most significant 32 bits of the file's binary
  227. // version number
  228. DWORD dwFileVersionLS; //
  229. DWORD dwProductVersionMS; // most significant 32 bits of the binary version
  230. // number of the product with which this file was
  231. // distributed
  232. DWORD dwProductVersionLS; //
  233. DWORD dwFileFlagsMask; // bitmask that specifies the valid bits in
  234. // dwFileFlags. A bit is valid only if it was
  235. // defined when the file was created.
  236. DWORD dwFileFlags; // VS_FF_DEBUG, VS_FF_PATCHED etc.
  237. DWORD dwFileOS; // VOS_NT, VOS_WINDOWS32 etc.
  238. DWORD dwFileType; // VFT_APP etc.
  239. DWORD dwFileSubtype; // 0 unless VFT_DRV or VFT_FONT or VFT_VXD
  240. DWORD dwFileDateMS;
  241. DWORD dwFileDateLS;
  242. """
  243. def __init__(self, filevers=(0, 0, 0, 0), prodvers=(0, 0, 0, 0),
  244. mask=0x3f, flags=0x0, OS=0x40004, fileType=0x1,
  245. subtype=0x0, date=(0, 0)):
  246. self.sig = 0xfeef04bd
  247. self.strucVersion = 0x10000
  248. self.fileVersionMS = (filevers[0] << 16) | (filevers[1] & 0xffff)
  249. self.fileVersionLS = (filevers[2] << 16) | (filevers[3] & 0xffff)
  250. self.productVersionMS = (prodvers[0] << 16) | (prodvers[1] & 0xffff)
  251. self.productVersionLS = (prodvers[2] << 16) | (prodvers[3] & 0xffff)
  252. self.fileFlagsMask = mask
  253. self.fileFlags = flags
  254. self.fileOS = OS
  255. self.fileType = fileType
  256. self.fileSubtype = subtype
  257. self.fileDateMS = date[0]
  258. self.fileDateLS = date[1]
  259. def fromRaw(self, data, i):
  260. (self.sig,
  261. self.strucVersion,
  262. self.fileVersionMS,
  263. self.fileVersionLS,
  264. self.productVersionMS,
  265. self.productVersionLS,
  266. self.fileFlagsMask,
  267. self.fileFlags,
  268. self.fileOS,
  269. self.fileType,
  270. self.fileSubtype,
  271. self.fileDateMS,
  272. self.fileDateLS) = struct.unpack('13L', data[i:i + 52])
  273. return i + 52
  274. def toRaw(self):
  275. return struct.pack('13L', self.sig,
  276. self.strucVersion,
  277. self.fileVersionMS,
  278. self.fileVersionLS,
  279. self.productVersionMS,
  280. self.productVersionLS,
  281. self.fileFlagsMask,
  282. self.fileFlags,
  283. self.fileOS,
  284. self.fileType,
  285. self.fileSubtype,
  286. self.fileDateMS,
  287. self.fileDateLS)
  288. def __eq__(self, other):
  289. return self.toRaw() == other
  290. def __str__(self, indent=u''):
  291. fv = (self.fileVersionMS >> 16, self.fileVersionMS & 0xffff,
  292. self.fileVersionLS >> 16, self.fileVersionLS & 0xFFFF)
  293. pv = (self.productVersionMS >> 16, self.productVersionMS & 0xffff,
  294. self.productVersionLS >> 16, self.productVersionLS & 0xFFFF)
  295. fd = (self.fileDateMS, self.fileDateLS)
  296. tmp = [u'FixedFileInfo(',
  297. u'# filevers and prodvers should be always a tuple with four items: (1, 2, 3, 4)',
  298. u'# Set not needed items to zero 0.',
  299. u'filevers=%s,' % (fv,),
  300. u'prodvers=%s,' % (pv,),
  301. u"# Contains a bitmask that specifies the valid bits 'flags'r",
  302. u'mask=%s,' % hex(self.fileFlagsMask),
  303. u'# Contains a bitmask that specifies the Boolean attributes of the file.',
  304. u'flags=%s,' % hex(self.fileFlags),
  305. u'# The operating system for which this file was designed.',
  306. u'# 0x4 - NT and there is no need to change it.',
  307. u'OS=%s,' % hex(self.fileOS),
  308. u'# The general type of file.',
  309. u'# 0x1 - the file is an application.',
  310. u'fileType=%s,' % hex(self.fileType),
  311. u'# The function of the file.',
  312. u'# 0x0 - the function is not defined for this fileType',
  313. u'subtype=%s,' % hex(self.fileSubtype),
  314. u'# Creation date and time stamp.',
  315. u'date=%s' % (fd,),
  316. u')'
  317. ]
  318. return (u'\n'+indent+u' ').join(tmp)
  319. def __repr__(self):
  320. fv = (self.fileVersionMS >> 16, self.fileVersionMS & 0xffff,
  321. self.fileVersionLS >> 16, self.fileVersionLS & 0xffff)
  322. pv = (self.productVersionMS >> 16, self.productVersionMS & 0xffff,
  323. self.productVersionLS >> 16, self.productVersionLS & 0xffff)
  324. fd = (self.fileDateMS, self.fileDateLS)
  325. return ('versioninfo.FixedFileInfo(filevers=%r, prodvers=%r, '
  326. 'mask=0x%x, flags=0x%x, OS=0x%x, '
  327. 'fileType=%r, subtype=0x%x, date=%r)' %
  328. (fv, pv,
  329. self.fileFlagsMask, self.fileFlags, self.fileOS,
  330. self.fileType, self.fileSubtype, fd))
  331. class StringFileInfo(object):
  332. """
  333. WORD wLength; // length of the version resource
  334. WORD wValueLength; // length of the Value member in the current
  335. // VS_VERSION_INFO structure
  336. WORD wType; // 1 means text, 0 means binary
  337. WCHAR szKey[]; // Contains the Unicode string 'StringFileInfo'.
  338. WORD Padding[];
  339. StringTable Children[]; // list of zero or more String structures
  340. """
  341. def __init__(self, kids=None):
  342. self.name = u'StringFileInfo'
  343. self.kids = kids or []
  344. def fromRaw(self, sublen, vallen, name, data, i, limit):
  345. self.name = name
  346. while i < limit:
  347. st = StringTable()
  348. j = st.fromRaw(data, i, limit)
  349. self.kids.append(st)
  350. i = j
  351. return i
  352. def toRaw(self):
  353. raw_name = getRaw(self.name)
  354. vallen = 0
  355. typ = 1
  356. sublen = 6 + len(raw_name) + 2
  357. pad = b''
  358. if sublen % 4:
  359. pad = b'\000\000'
  360. tmp = b''.join([kid.toRaw() for kid in self.kids])
  361. sublen = sublen + len(pad) + len(tmp)
  362. return (struct.pack('hhh', sublen, vallen, typ)
  363. + raw_name + b'\000\000' + pad + tmp)
  364. def __eq__(self, other):
  365. return self.toRaw() == other
  366. def __str__(self, indent=u''):
  367. newindent = indent + u' '
  368. tmp = [kid.__str__(newindent)
  369. for kid in self.kids]
  370. tmp = u', \n'.join(tmp)
  371. return (u'%sStringFileInfo(\n%s[\n%s\n%s])'
  372. % (indent, newindent, tmp, newindent))
  373. def __repr__(self):
  374. return 'versioninfo.StringFileInfo(%r)' % self.kids
  375. class StringTable:
  376. """
  377. WORD wLength;
  378. WORD wValueLength;
  379. WORD wType;
  380. WCHAR szKey[];
  381. String Children[]; // list of zero or more String structures.
  382. """
  383. def __init__(self, name=None, kids=None):
  384. self.name = name or u''
  385. self.kids = kids or []
  386. def fromRaw(self, data, i, limit):
  387. i, (cpsublen, cpwValueLength, cpwType, self.name) = parseCodePage(data, i, limit) # should be code page junk
  388. i = nextDWord(i)
  389. while i < limit:
  390. ss = StringStruct()
  391. j = ss.fromRaw(data, i, limit)
  392. i = j
  393. self.kids.append(ss)
  394. i = nextDWord(i)
  395. return i
  396. def toRaw(self):
  397. raw_name = getRaw(self.name)
  398. vallen = 0
  399. typ = 1
  400. sublen = 6 + len(raw_name) + 2
  401. tmp = []
  402. for kid in self.kids:
  403. raw = kid.toRaw()
  404. if len(raw) % 4:
  405. raw = raw + b'\000\000'
  406. tmp.append(raw)
  407. tmp = b''.join(tmp)
  408. sublen += len(tmp)
  409. return (struct.pack('hhh', sublen, vallen, typ)
  410. + raw_name + b'\000\000' + tmp)
  411. def __eq__(self, other):
  412. return self.toRaw() == other
  413. def __str__(self, indent=u''):
  414. newindent = indent + u' '
  415. tmp = (u',\n%s' % newindent).join(str(kid) for kid in self.kids)
  416. return (u"%sStringTable(\n%su'%s',\n%s[%s])"
  417. % (indent, newindent, self.name, newindent, tmp))
  418. def __repr__(self):
  419. return 'versioninfo.StringTable(%r, %r)' % (self.name, self.kids)
  420. class StringStruct:
  421. """
  422. WORD wLength;
  423. WORD wValueLength;
  424. WORD wType;
  425. WCHAR szKey[];
  426. WORD Padding[];
  427. String Value[];
  428. """
  429. def __init__(self, name=None, val=None):
  430. self.name = name or u''
  431. self.val = val or u''
  432. def fromRaw(self, data, i, limit):
  433. i, (sublen, vallen, typ, self.name) = parseCommon(data, i)
  434. limit = i + sublen
  435. i = nextDWord(i)
  436. i, self.val = parseUString(data, i, limit)
  437. return i
  438. def toRaw(self):
  439. raw_name = getRaw(self.name)
  440. raw_val = getRaw(self.val)
  441. # TODO document the size of vallen and sublen.
  442. vallen = len(raw_val) + 2
  443. typ = 1
  444. sublen = 6 + len(raw_name) + 2
  445. pad = b''
  446. if sublen % 4:
  447. pad = b'\000\000'
  448. sublen = sublen + len(pad) + vallen
  449. abcd = (struct.pack('hhh', sublen, vallen, typ)
  450. + raw_name + b'\000\000' + pad
  451. + raw_val + b'\000\000')
  452. return abcd
  453. def __eq__(self, other):
  454. return self.toRaw() == other
  455. def __str__(self, indent=''):
  456. return u"StringStruct(u'%s', u'%s')" % (self.name, self.val)
  457. def __repr__(self):
  458. return 'versioninfo.StringStruct(%r, %r)' % (self.name, self.val)
  459. def parseCodePage(data, i, limit):
  460. i, (sublen, wValueLength, wType, nm) = parseCommon(data, i)
  461. return i, (sublen, wValueLength, wType, nm)
  462. class VarFileInfo:
  463. """
  464. WORD wLength; // length of the version resource
  465. WORD wValueLength; // length of the Value member in the current
  466. // VS_VERSION_INFO structure
  467. WORD wType; // 1 means text, 0 means binary
  468. WCHAR szKey[]; // Contains the Unicode string 'VarFileInfo'.
  469. WORD Padding[];
  470. Var Children[]; // list of zero or more Var structures
  471. """
  472. def __init__(self, kids=None):
  473. self.kids = kids or []
  474. def fromRaw(self, sublen, vallen, name, data, i, limit):
  475. self.sublen = sublen
  476. self.vallen = vallen
  477. self.name = name
  478. i = nextDWord(i)
  479. while i < limit:
  480. vs = VarStruct()
  481. j = vs.fromRaw(data, i, limit)
  482. self.kids.append(vs)
  483. i = j
  484. return i
  485. def toRaw(self):
  486. self.vallen = 0
  487. self.wType = 1
  488. self.name = u'VarFileInfo'
  489. raw_name = getRaw(self.name)
  490. sublen = 6 + len(raw_name) + 2
  491. pad = b''
  492. if sublen % 4:
  493. pad = b'\000\000'
  494. tmp = b''.join([kid.toRaw() for kid in self.kids])
  495. self.sublen = sublen + len(pad) + len(tmp)
  496. return (struct.pack('hhh', self.sublen, self.vallen, self.wType)
  497. + raw_name + b'\000\000' + pad + tmp)
  498. def __eq__(self, other):
  499. return self.toRaw() == other
  500. def __str__(self, indent=''):
  501. return (indent + "VarFileInfo([%s])" %
  502. ', '.join(str(kid) for kid in self.kids))
  503. def __repr__(self):
  504. return 'versioninfo.VarFileInfo(%r)' % self.kids
  505. class VarStruct:
  506. """
  507. WORD wLength; // length of the version resource
  508. WORD wValueLength; // length of the Value member in the current
  509. // VS_VERSION_INFO structure
  510. WORD wType; // 1 means text, 0 means binary
  511. WCHAR szKey[]; // Contains the Unicode string 'Translation'
  512. // or a user-defined key string value
  513. WORD Padding[]; //
  514. WORD Value[]; // list of one or more values that are language
  515. // and code-page identifiers
  516. """
  517. def __init__(self, name=None, kids=None):
  518. self.name = name or u''
  519. self.kids = kids or []
  520. def fromRaw(self, data, i, limit):
  521. i, (self.sublen, self.wValueLength, self.wType, self.name) = parseCommon(data, i)
  522. i = nextDWord(i)
  523. for j in range(0, self.wValueLength, 2):
  524. kid = struct.unpack('h', data[i:i+2])[0]
  525. self.kids.append(kid)
  526. i += 2
  527. return i
  528. def toRaw(self):
  529. self.wValueLength = len(self.kids) * 2
  530. self.wType = 0
  531. raw_name = getRaw(self.name)
  532. sublen = 6 + len(raw_name) + 2
  533. pad = b''
  534. if sublen % 4:
  535. pad = b'\000\000'
  536. self.sublen = sublen + len(pad) + self.wValueLength
  537. tmp = b''.join([struct.pack('h', kid) for kid in self.kids])
  538. return (struct.pack('hhh', self.sublen, self.wValueLength, self.wType)
  539. + raw_name + b'\000\000' + pad + tmp)
  540. def __eq__(self, other):
  541. return self.toRaw() == other
  542. def __str__(self, indent=u''):
  543. return u"VarStruct(u'%s', %r)" % (self.name, self.kids)
  544. def __repr__(self):
  545. return 'versioninfo.VarStruct(%r, %r)' % (self.name, self.kids)
  546. def SetVersion(exenm, versionfile):
  547. if isinstance(versionfile, VSVersionInfo):
  548. vs = versionfile
  549. else:
  550. with codecs.open(versionfile, 'r', 'utf-8') as fp:
  551. txt = fp.read()
  552. vs = eval(txt)
  553. # Remember overlay
  554. pe = pefile.PE(exenm, fast_load=True)
  555. overlay_before = pe.get_overlay()
  556. pe.close()
  557. hdst = win32api.BeginUpdateResource(exenm, 0)
  558. win32api.UpdateResource(hdst, pefile.RESOURCE_TYPE['RT_VERSION'], 1, vs.toRaw())
  559. win32api.EndUpdateResource (hdst, 0)
  560. if overlay_before:
  561. # Check if the overlay is still present
  562. pe = pefile.PE(exenm, fast_load=True)
  563. overlay_after = pe.get_overlay()
  564. pe.close()
  565. # If the update removed the overlay data, re-append it
  566. if not overlay_after:
  567. with open(exenm, 'ab') as exef:
  568. exef.write(overlay_before)