Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

win32verstamp.py 7.0KB

1 year ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. """ Stamp a Win32 binary with version information.
  2. """
  3. import glob
  4. import optparse
  5. import os
  6. import struct
  7. import sys
  8. from win32api import BeginUpdateResource, EndUpdateResource, UpdateResource
  9. VS_FFI_SIGNATURE = -17890115 # 0xFEEF04BD
  10. VS_FFI_STRUCVERSION = 0x00010000
  11. VS_FFI_FILEFLAGSMASK = 0x0000003F
  12. VOS_NT_WINDOWS32 = 0x00040004
  13. null_byte = "\0".encode("ascii") # str in py2k, bytes in py3k
  14. #
  15. # Set VS_FF_PRERELEASE and DEBUG if Debug
  16. #
  17. def file_flags(debug):
  18. if debug:
  19. return 3 # VS_FF_DEBUG | VS_FF_PRERELEASE
  20. return 0
  21. def file_type(is_dll):
  22. if is_dll:
  23. return 2 # VFT_DLL
  24. return 1 # VFT_APP
  25. def VS_FIXEDFILEINFO(maj, min, sub, build, debug=0, is_dll=1):
  26. return struct.pack(
  27. "lllllllllllll",
  28. VS_FFI_SIGNATURE, # dwSignature
  29. VS_FFI_STRUCVERSION, # dwStrucVersion
  30. (maj << 16) | min, # dwFileVersionMS
  31. (sub << 16) | build, # dwFileVersionLS
  32. (maj << 16) | min, # dwProductVersionMS
  33. (sub << 16) | build, # dwProductVersionLS
  34. VS_FFI_FILEFLAGSMASK, # dwFileFlagsMask
  35. file_flags(debug), # dwFileFlags
  36. VOS_NT_WINDOWS32, # dwFileOS
  37. file_type(is_dll), # dwFileType
  38. 0x00000000, # dwFileSubtype
  39. 0x00000000, # dwFileDateMS
  40. 0x00000000, # dwFileDateLS
  41. )
  42. def nullterm(s):
  43. # get raw bytes for a NULL terminated unicode string.
  44. if sys.version_info[:2] < (3, 7):
  45. return (str(s) + "\0").encode("unicode-internal")
  46. else:
  47. return (str(s) + "\0").encode("utf-16le")
  48. def pad32(s, extra=2):
  49. # extra is normally 2 to deal with wLength
  50. l = 4 - ((len(s) + extra) & 3)
  51. if l < 4:
  52. return s + (null_byte * l)
  53. return s
  54. def addlen(s):
  55. return struct.pack("h", len(s) + 2) + s
  56. def String(key, value):
  57. key = nullterm(key)
  58. value = nullterm(value)
  59. result = struct.pack("hh", len(value) // 2, 1) # wValueLength, wType
  60. result = result + key
  61. result = pad32(result) + value
  62. return addlen(result)
  63. def StringTable(key, data):
  64. key = nullterm(key)
  65. result = struct.pack("hh", 0, 1) # wValueLength, wType
  66. result = result + key
  67. for k, v in data.items():
  68. result = result + String(k, v)
  69. result = pad32(result)
  70. return addlen(result)
  71. def StringFileInfo(data):
  72. result = struct.pack("hh", 0, 1) # wValueLength, wType
  73. result = result + nullterm("StringFileInfo")
  74. # result = pad32(result) + StringTable('040904b0', data)
  75. result = pad32(result) + StringTable("040904E4", data)
  76. return addlen(result)
  77. def Var(key, value):
  78. result = struct.pack("hh", len(value), 0) # wValueLength, wType
  79. result = result + nullterm(key)
  80. result = pad32(result) + value
  81. return addlen(result)
  82. def VarFileInfo(data):
  83. result = struct.pack("hh", 0, 1) # wValueLength, wType
  84. result = result + nullterm("VarFileInfo")
  85. result = pad32(result)
  86. for k, v in data.items():
  87. result = result + Var(k, v)
  88. return addlen(result)
  89. def VS_VERSION_INFO(maj, min, sub, build, sdata, vdata, debug=0, is_dll=1):
  90. ffi = VS_FIXEDFILEINFO(maj, min, sub, build, debug, is_dll)
  91. result = struct.pack("hh", len(ffi), 0) # wValueLength, wType
  92. result = result + nullterm("VS_VERSION_INFO")
  93. result = pad32(result) + ffi
  94. result = pad32(result) + StringFileInfo(sdata) + VarFileInfo(vdata)
  95. return addlen(result)
  96. def stamp(pathname, options):
  97. # For some reason, the API functions report success if the file is open
  98. # but doesnt work! Try and open the file for writing, just to see if it is
  99. # likely the stamp will work!
  100. try:
  101. f = open(pathname, "a+b")
  102. f.close()
  103. except IOError as why:
  104. print("WARNING: File %s could not be opened - %s" % (pathname, why))
  105. ver = options.version
  106. try:
  107. bits = [int(i) for i in ver.split(".")]
  108. vmaj, vmin, vsub, vbuild = bits
  109. except (IndexError, TypeError, ValueError):
  110. raise ValueError("--version must be a.b.c.d (all integers) - got %r" % ver)
  111. ifn = options.internal_name
  112. if not ifn:
  113. ifn = os.path.basename(pathname)
  114. ofn = options.original_filename
  115. if ofn is None:
  116. ofn = os.path.basename(pathname)
  117. sdata = {
  118. "Comments": options.comments,
  119. "CompanyName": options.company,
  120. "FileDescription": options.description,
  121. "FileVersion": ver,
  122. "InternalName": ifn,
  123. "LegalCopyright": options.copyright,
  124. "LegalTrademarks": options.trademarks,
  125. "OriginalFilename": ofn,
  126. "ProductName": options.product,
  127. "ProductVersion": ver,
  128. }
  129. vdata = {
  130. "Translation": struct.pack("hh", 0x409, 1252),
  131. }
  132. is_dll = options.dll
  133. if is_dll is None:
  134. is_dll = os.path.splitext(pathname)[1].lower() in ".dll .pyd".split()
  135. is_debug = options.debug
  136. if is_debug is None:
  137. is_debug = os.path.splitext(pathname)[0].lower().endswith("_d")
  138. # convert None to blank strings
  139. for k, v in list(sdata.items()):
  140. if v is None:
  141. sdata[k] = ""
  142. vs = VS_VERSION_INFO(vmaj, vmin, vsub, vbuild, sdata, vdata, is_debug, is_dll)
  143. h = BeginUpdateResource(pathname, 0)
  144. UpdateResource(h, 16, 1, vs)
  145. EndUpdateResource(h, 0)
  146. if options.verbose:
  147. print("Stamped:", pathname)
  148. if __name__ == "__main__":
  149. parser = optparse.OptionParser("%prog [options] filespec ...", description=__doc__)
  150. parser.add_option(
  151. "-q",
  152. "--quiet",
  153. action="store_false",
  154. dest="verbose",
  155. default=True,
  156. help="don't print status messages to stdout",
  157. )
  158. parser.add_option(
  159. "", "--version", default="0.0.0.0", help="The version number as m.n.s.b"
  160. )
  161. parser.add_option(
  162. "",
  163. "--dll",
  164. help="""Stamp the file as a DLL. Default is to look at the
  165. file extension for .dll or .pyd.""",
  166. )
  167. parser.add_option("", "--debug", help="""Stamp the file as a debug binary.""")
  168. parser.add_option("", "--product", help="""The product name to embed.""")
  169. parser.add_option("", "--company", help="""The company name to embed.""")
  170. parser.add_option("", "--trademarks", help="The trademark string to embed.")
  171. parser.add_option("", "--comments", help="The comments string to embed.")
  172. parser.add_option(
  173. "", "--copyright", help="""The copyright message string to embed."""
  174. )
  175. parser.add_option(
  176. "", "--description", metavar="DESC", help="The description to embed."
  177. )
  178. parser.add_option(
  179. "",
  180. "--internal-name",
  181. metavar="NAME",
  182. help="""The internal filename to embed. If not specified
  183. the base filename is used.""",
  184. )
  185. parser.add_option(
  186. "",
  187. "--original-filename",
  188. help="""The original filename to embed. If not specified
  189. the base filename is used.""",
  190. )
  191. options, args = parser.parse_args()
  192. if not args:
  193. parser.error("You must supply a file to stamp. Use --help for details.")
  194. for g in args:
  195. for f in glob.glob(g):
  196. stamp(f, options)