Index: bindepend.py
===================================================================
--- bindepend.py	(revision 701)
+++ bindepend.py	(working copy)
@@ -44,6 +44,16 @@
 cygwin = sys.platform == 'cygwin'
 darwin = sys.platform[:7] == 'darwin'
 
+silent = False  # True suppresses all informative messages from the dependency code
+
+if iswin:
+    
+    from manifest import RT_MANIFEST, GetManifestResources, Manifest
+    try:
+        from manifest import resource
+    except ImportError, detail:
+        resource = None
+
 excludes = {
     'KERNEL32.DLL':1,
     'ADVAPI.DLL':1,
@@ -76,9 +86,12 @@
     'GLU32.DLL':1,
     'GLUB32.DLL':1,
     'NETAPI32.DLL':1,
+    'MSCOREE.DLL':1,
     'PSAPI.DLL':1,
-    'MSVCP80.DLL':1,
-    'MSVCR80.DLL':1,
+    'IERTUTIL.DLL':1,
+    'POWRPROF.DLL':1,
+    'SHLWAPI.DLL':1,
+    'URLMON.DLL':1,
     # regex excludes
     # don't include in the bundle the libc and the tls stuff
     r'^/usr/lib/tls':1,
@@ -86,6 +99,8 @@
     r'^/lib/tls':1,
     # libGL can reference some hw specific libraries (like nvidia libs)
     r'/usr/lib/libGL.*':1,
+    # MS assembly excludes
+    'Microsoft.Windows.Common-Controls':1,
 }
 
 # Darwin has a stable ABI for applications, so there is no need
@@ -280,18 +295,126 @@
        Return LTOC expanded by all the binary dependencies of the entries
        in LTOC, except those listed in the module global EXCLUDES"""
     for nm, pth, typ in lTOC:
-        fullnm = string.upper(os.path.basename(pth))
         if seen.get(string.upper(nm),0):
             continue
-        #print "I: analyzing", pth
+        if not silent:
+            print "I: Analyzing", pth
         seen[string.upper(nm)] = 1
+        if iswin:
+            for ftocnm, fn in selectAssemblies(pth):
+                lTOC.append((ftocnm, fn, 'BINARY'))
         for lib, npth in selectImports(pth, platform, xtrapath):
-            if seen.get(string.upper(lib),0):
+            if seen.get(string.upper(lib),0) or seen.get(string.upper(npth),0):
                 continue
+            seen[string.upper(npth)] = 1
             lTOC.append((lib, npth, 'BINARY'))
 
     return lTOC
 
+def getAssemblies(pth):
+    """Return the dependent assemblies of a binary."""
+    if pth.lower().endswith(".manifest"):
+        return []
+    # check for manifest file
+    manifestnm = pth + ".manifest"
+    if os.path.isfile(manifestnm):
+        fd = open(manifestnm, "rb")
+        res = {RT_MANIFEST: {1: {0: fd.read()}}}
+        fd.close()
+    elif not resource:
+        # resource access unavailable (needs pywin32)
+        return []
+    else:
+        # check the binary for embedded manifest
+        try:
+            res = GetManifestResources(pth)
+        except resource.pywintypes.error, exc:
+            if exc.args[0] == resource.ERROR_BAD_EXE_FORMAT:
+                if not silent:
+                    print 'I: Cannot get manifest resource from non-PE file:'
+                    print 'I:', pth
+                return []
+    rv = []
+    if RT_MANIFEST in res and len(res[RT_MANIFEST]):
+        for name in res[RT_MANIFEST]:
+            for language in res[RT_MANIFEST][name]:
+                # check the manifest for dependent assemblies
+                try:
+                    manifest = Manifest()
+                    manifest.filename = ":".join([pth, str(RT_MANIFEST), 
+                                                  str(name), str(language)])
+                    manifest.parse_string(res[RT_MANIFEST][name][language],
+                                          False)
+                except Exception, exc:
+                    if not silent:
+                        print ("E: Cannot parse manifest resource %s, %s "
+                               "from") % (name, language)
+                        print "E:", pth
+                        print "E:", exc
+                    pass
+                else:
+                    if manifest.dependentAssemblies and not silent:
+                        print "I: Dependent assemblies of %s:" % pth
+                        print "I:", ", ".join([assembly.getid() 
+                                               for assembly in 
+                                               manifest.dependentAssemblies])
+                    rv.extend(manifest.dependentAssemblies)
+    return rv
+    
+def selectAssemblies(pth):
+    """Return a binary's dependent assemblies files that should be included.
+
+    Return a list of pairs (name, fullpath)
+    """
+    rv = []
+    for assembly in getAssemblies(pth):
+        if excludesRe.search(assembly.name):
+            if not silent:
+                print "I: Skipping assembly", assembly.getid()
+            continue
+        if seen.get(assembly.getid().upper(),0):
+            continue
+        elif assembly.optional:
+            if not silent:
+                print "I: Skipping optional assembly", assembly.getid()
+            continue
+        files = assembly.find_files()
+        if files:
+            seen[assembly.getid().upper()] = 1
+            for fn in files:
+                fname, fext = os.path.splitext(fn)
+                if fext.lower() == ".manifest":
+                    nm = assembly.name + fext
+                else:
+                    nm = os.path.basename(fn)
+                if nm.lower() == (assembly.name + ".dll").lower():
+                    # If single DLL assembly with embedded manifest, do not 
+                    # create a subfolder
+                    ftocnm = nm
+                else:
+                    ftocnm = os.path.join(assembly.name, nm)
+                if assembly.language not in (None, "", "*", "neutral"):
+                    ftocnm = os.path.join(assembly.getlanguage(), 
+                                          ftocnm)
+                nm, ftocnm, fn = [item.encode(sys.getfilesystemencoding()) 
+                                  for item in 
+                                  (nm,
+                                   ftocnm, 
+                                   fn)]
+                if not seen.get(fn.upper(),0):
+                    if not silent:
+                        print "I: Adding", ftocnm
+                    seen[nm.upper()] = 1
+                    seen[fn.upper()] = 1
+                    rv.append((ftocnm, fn))
+                else:
+                    #print "I: skipping", ftocnm, "part of assembly", \
+                    #      assembly.name, "dependency of", pth
+                    pass
+        elif not silent:
+            print "E: Assembly", assembly.getid(), "not found"
+    return rv
+
 def selectImports(pth, platform=sys.platform, xtrapath=None):
     """Return the dependencies of a binary that should be included.
 
@@ -304,31 +427,43 @@
         assert isinstance(xtrapath, list)
         xtrapath = [os.path.dirname(pth)] + xtrapath # make a copy
     dlls = getImports(pth, platform=platform)
-    iswin = platform[:3] == 'win'
     for lib in dlls:
+        if seen.get(string.upper(lib),0):
+            continue
         if not iswin and not cygwin:
-                # plain win case
+            # all other platforms
             npth = lib
             dir, lib = os.path.split(lib)
             if excludes.get(dir,0):
                 continue
         else:
-            # all other platforms
+            # plain win case
             npth = getfullnameof(lib, xtrapath)
 
-        # now npth is a candidate lib
+        # now npth is a candidate lib if found
         # check again for excludes but with regex FIXME: split the list
-        if excludesRe.search(npth):
-            if 'libpython' not in npth and 'Python.framework' not in npth:
+        if npth:
+            candidatelib = npth
+        else:
+            candidatelib = lib
+        if excludesRe.search(candidatelib):
+            if 'libpython' not in candidatelib and \
+               'Python.framework' not in candidatelib:
                 # skip libs not containing (libpython or Python.framework)
-                #print "I: skipping %20s <- %s" % (npth, pth)
+                if not silent and \
+                   not seen.get(string.upper(npth),0):
+                    print "I: Skipping", lib, "dependency of", \
+                          os.path.basename(pth)
                 continue
             else:
-                #print "I: inserting %20s <- %s" % (npth, pth)
                 pass
-
+        
         if npth:
-            rv.append((lib, npth))
+            if not seen.get(string.upper(npth),0):
+                if not silent:
+                    print "I: Adding", lib, "dependency of", \
+                          os.path.basename(pth)
+                rv.append((lib, npth))
         else:
             print "E: lib not found:", lib, "dependency of", pth
 
@@ -393,7 +528,21 @@
     """Forwards to the correct getImports implementation for the platform.
     """
     if platform[:3] == 'win' or platform == 'cygwin':
-        return _getImports_pe(pth)
+        if pth.lower().endswith(".manifest"):
+            return []
+        try:
+            return _getImports_pe(pth)
+        except Exception, exception:
+            # Assemblies can pull in files which aren't necessarily PE, 
+            # but are still needed by the assembly. Any additional binary 
+            # dependencies should already have been handled by 
+            # selectAssemblies in that case, so just warn, return an empty 
+            # list and continue.
+            if not silent:
+                print 'W: Cannot get binary dependencies for file:'
+                print 'W:', pth
+                print 'W:', exception
+            return []
     elif platform == 'darwin':
         return _getImports_otool(pth)
     else:
@@ -476,7 +625,11 @@
                       help='Target platform, required for cross-bundling (default: current platform)')
 
     opts, args = parser.parse_args()
+    silent = True  # Suppress all informative messages from the dependency code
     import glob
     for a in args:
         for fn in glob.glob(a):
-            print fn, getImports(fn, opts.target_platform)
+            imports = getImports(fn, opts.target_platform)
+            if opts.target_platform == "win32":
+                imports.extend([a.getid() for a in getAssemblies(fn)])
+            print fn, imports
Index: Build.py
===================================================================
--- Build.py	(revision 701)
+++ Build.py	(working copy)
@@ -627,6 +627,8 @@
         self.name = kws.get('name',None)
         self.icon = kws.get('icon',None)
         self.versrsrc = kws.get('version',None)
+        self.manifest = kws.get('manifest',None)
+        self.resources = kws.get('resources',[])
         self.strip = kws.get('strip',None)
         self.upx = kws.get('upx',None)
         self.crypt = kws.get('crypt', 0)
@@ -660,6 +662,8 @@
             ('debug',    _check_guts_eq),
             ('icon',     _check_guts_eq),
             ('versrsrc', _check_guts_eq),
+            ('manifest', _check_guts_eq),
+            ('resources', _check_guts_eq),
             ('strip',    _check_guts_eq),
             ('upx',      _check_guts_eq),
             ('crypt',    _check_guts_eq),
@@ -679,10 +683,10 @@
         if not data:
             return True
 
-        icon, versrsrc = data[3:5]
-        if (icon or versrsrc) and not config['hasRsrcUpdate']:
+        icon, versrsrc, manifest, resources = data[3:7]
+        if (icon or versrsrc or manifest or resources) and not config['hasRsrcUpdate']:
             # todo: really ignore :-)
-            print "ignoring icon and version resources = platform not capable"
+            print "ignoring icon, version, manifest and resources = platform not capable"
 
         mtm = data[-1]
         crypt = data[-2]
@@ -722,21 +726,64 @@
         exe = os.path.join(HOMEPATH, exe)
         if target_iswin or cygwin:
             exe = exe + '.exe'
-        if config['hasRsrcUpdate']:
+        if config['hasRsrcUpdate'] and (self.icon or self.versrsrc or 
+                                        self.manifest or self.resources):
+            tmpnm = tempfile.mktemp()
+            shutil.copy2(exe, tmpnm)
+            os.chmod(tmpnm, 0755)
             if self.icon:
-                tmpnm = tempfile.mktemp()
-                shutil.copy2(exe, tmpnm)
-                os.chmod(tmpnm, 0755)
                 icon.CopyIcons(tmpnm, self.icon)
-                trash.append(tmpnm)
-                exe = tmpnm
             if self.versrsrc:
-                tmpnm = tempfile.mktemp()
-                shutil.copy2(exe, tmpnm)
-                os.chmod(tmpnm, 0755)
                 versionInfo.SetVersion(tmpnm, self.versrsrc)
-                trash.append(tmpnm)
-                exe = tmpnm
+            if self.manifest:
+                manifest.UpdateManifestResourcesFromXMLFile(tmpnm, self.manifest, [1])
+            for res in self.resources:
+                res = res.split(",")
+                for i in range(len(res[1:])):
+                    try:
+                        res[i + 1] = int(res[i + 1])
+                    except ValueError:
+                        pass
+                resfile = res[0]
+                if len(res) > 1:
+                    restype = res[1]
+                else:
+                    restype = None
+                if len(res) > 2:
+                    resname = res[2]
+                else:
+                    restype = None
+                if len(res) > 3:
+                    reslang = res[3]
+                else:
+                    restype = None
+                try:
+                    resource.UpdateResourcesFromResFile(tmpnm, resfile, 
+                                                        [restype or "*"], 
+                                                        [resname or "*"], 
+                                                        [reslang or "*"])
+                except resource.pywintypes.error, exc:
+                    if exc.args[0] != resource.ERROR_BAD_EXE_FORMAT:
+                        print "E:", str(exc)
+                        continue
+                    if not restype or not resname:
+                        print "E: resource type and/or name not specified"
+                        continue
+                    if "*" in (restype, resname):
+                        print ("E: no wildcards allowed for resource type "
+                               "and name when source file does not contain "
+                               "resources")
+                        continue
+                    try:
+                        resource.UpdateResourcesFromDataFile(tmpnm, 
+                                                             resfile, 
+                                                             restype, 
+                                                             [resname], 
+                                                             [reslang or 0])
+                    except resource.pywintypes.error, exc:
+                        print "E:", str(exc)
+            trash.append(tmpnm)
+            exe = tmpnm
         exe = checkCache(exe, self.strip, self.upx and config['hasUPX'])
         self.copy(exe, outf)
         if self.append_pkg:
@@ -749,7 +796,7 @@
         os.chmod(self.name, 0755)
         _save_data(self.out,
                    (self.name, self.console, self.debug, self.icon,
-                    self.versrsrc, self.strip, self.upx, self.crypt, mtime(self.name)))
+                    self.versrsrc, self.manifest, self.resources, self.strip, self.upx, self.crypt, mtime(self.name)))
         for item in trash:
             os.remove(item)
         return 1
@@ -773,7 +820,7 @@
         os.chmod(self.name, 0755)
         _save_data(self.out,
                    (self.name, self.console, self.debug, self.icon,
-                    self.versrsrc, self.strip, self.upx, mtime(self.name)))
+                    self.versrsrc, self.manifest, self.resources, self.strip, self.upx, mtime(self.name)))
         return 1
 
 
@@ -1106,7 +1153,7 @@
 
 def main(specfile, configfilename):
     global target_platform, target_iswin, config
-    global icon, versionInfo
+    global icon, versionInfo, resource, manifest
 
     try:
         config = _load_data(configfilename)
@@ -1130,7 +1177,7 @@
         sys.exit(1)
 
     if config['hasRsrcUpdate']:
-        import icon, versionInfo
+        import icon, versionInfo, resource, manifest
 
     if config['hasUPX']:
         setupUPXFlags()
@@ -1146,6 +1193,9 @@
     parser.add_option('-C', '--configfile',
                       default=os.path.join(HOMEPATH, 'config.dat'),
                       help='Name of generated configfile (default: %default)')
+    parser.add_option('-o', '--buildpath',
+                      default=None,
+                      help='Buildpath')
     opts, args = parser.parse_args()
     if len(args) != 1:
         parser.error('Requires exactly one .spec-file')
Index: Makespec.py
===================================================================
--- Makespec.py	(revision 701)
+++ Makespec.py	(working copy)
@@ -135,7 +135,7 @@
 
 def main(scripts, configfile=None, name=None, tk=0, freeze=0, console=1, debug=0,
          strip=0, upx=0, comserver=0, ascii=0, workdir=None,
-         pathex=[], version_file=None, icon_file=None, crypt=None):
+         pathex=[], version_file=None, icon_file=None, manifest_file=None, resources=[], crypt=None):
 
     try:
         config = eval(open(configfile, 'r').read())
@@ -168,6 +168,12 @@
         exe_options = "%s, version='%s'" % (exe_options, quote_win_filepath(version_file))
     if icon_file:
         exe_options = "%s, icon='%s'" % (exe_options, quote_win_filepath(icon_file))
+    if manifest_file:
+        exe_options = "%s, manifest='%s'" % (exe_options, quote_win_filepath(manifest_file))
+    if resources:
+        for i in range(len(resources)):
+            resources[i] = quote_win_filepath(resources[i])
+        exe_options = "%s, resources=%s" % (exe_options, repr(resources))
     if not ascii and config['hasUnicode']:
         scripts.insert(0, os.path.join(HOME, 'support', 'useUnicode.py'))
     for i in range(len(scripts)):
@@ -278,12 +284,29 @@
                  dest="version_file", metavar="FILE",
                  help="add a version resource from FILE to the exe "
                       "(Windows only)")
-    g.add_option("--icon", type="string", dest="icon_file",
+    g.add_option("-i", "--icon", type="string", dest="icon_file",
                  metavar="FILE.ICO or FILE.EXE,ID",
                  help="If FILE is an .ico file, add the icon to the final "
                       "executable. Otherwise, the syntax 'file.exe,id' to "
                       "extract the icon with the specified id "
                       "from file.exe and add it to the final executable")
+    g.add_option("-m", "--manifest", type="string",
+                 dest="manifest_file", metavar="FILE",
+                 help="add manifest FILE to the exe "
+                      "(Windows only)")
+    g.add_option("-r", "--resource", type="string", default=[], dest="resources",
+                 metavar="FILE[,TYPE[,NAME[,LANGUAGE]]]", action="append",
+                 help="add/update resource of the given type, name and language "
+                      "from FILE to the final executable. FILE can be a "
+                      "data file or an exe/dll. For data files, atleast "
+                      "TYPE and NAME need to be specified, LANGUAGE defaults "
+                      "to 0 or may be specified as wildcard * to update all "
+                      "resources of the given TYPE and NAME. For exe/dll "
+                      "files, all resources from FILE will be added/updated "
+                      "to the final executable if TYPE, NAME and LANGUAGE "
+                      "are omitted or specified as wildcard *."
+                      "Multiple resources are allowed, using this option "
+                      "multiple times.")
 
     opts,args = p.parse_args()
 
Index: manifest.py
===================================================================
--- manifest.py	(revision 0)
+++ manifest.py	(revision 0)
@@ -0,0 +1,989 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2009, Florian Hoech
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301, USA
+
+# DEV NOTES
+#
+# Currently not implemented in the Manifest class:
+# * Validation (only very basic sanity checks are currently in place)
+# * comClass, typelib, comInterfaceProxyStub and windowClass child elements of 
+#   the file element
+# * comInterfaceExternalProxyStub and windowClass child elements of the 
+#   assembly element
+# * Application Configuration File and Multilanguage User Interface (MUI) 
+#   support when searching for assembly files
+#
+# Isolated Applications and Side-by-side Assemblies:
+# http://msdn.microsoft.com/en-us/library/dd408052%28VS.85%29.aspx
+#
+# Changelog:
+# 2009-08-21  fix: Corrected assembly searching sequence for localized 
+#                  assemblies
+#             fix: Allow assemblies with no dependent files
+#
+# 2009-07-31  chg: Find private assemblies even if unversioned
+#             add: Manifest.same_id method to check if two manifests have the
+#                  same assemblyIdentity
+#
+# 2009-07-30  fix: Potential failure in File.calc_hash method if hash
+#                  algorythm not supported
+#             add: Publisher configuration (policy) support when searching for 
+#                  assembly files
+#             fix: Private assemblies are now actually found if present (and no
+#                  shared assembly exists)
+#             add: Python 2.3 compatibility (oldest version supported by 
+#                  pyinstaller)
+#
+# 2009-07-28  chg: Code cleanup, removed a bit of redundancy
+#             add: silent mode (set silent attribute on module)
+#             chg: Do not print messages in silent mode
+#
+# 2009-06-18  chg: Use glob instead of regular expression in Manifest.find_files
+#
+# 2009-05-04  fix: Don't fail if manifest has empty description
+#             fix: Manifests created by the toxml, toprettyxml, writexml or 
+#                  writeprettyxml methods are now correctly recognized by 
+#                  Windows, which expects the XML declaration to be ordered
+#                  version-encoding-standalone (standalone being optional)
+#             add: 'encoding' keyword argument in toxml, toprettyxml, writexml 
+#                  and writeprettyxml methods
+#             chg: UpdateManifestResourcesFromXML and 
+#                  UpdateManifestResourcesFromXMLFile: set resource name 
+#                  depending on file type ie. exe or dll
+#             fix: typo in __main__: UpdateManifestResourcesFromDataFile
+#                  should have been UpdateManifestResourcesFromXMLFile
+#
+# 2009-03-21  First version
+
+"""
+manifest.py
+
+Create, parse and write MS Windows Manifest files.
+Find files which are part of an assembly, by searching shared and 
+private assemblies.
+Update or add manifest resources in Win32 PE files.
+
+Commandline usage:
+manifest.py <dstpath> <xmlpath>
+Updates or adds manifest <xmlpath> as resource in Win32 PE file <dstpath>.
+
+"""
+
+try:
+    import hashlib
+except ImportError, detail:
+    hashlib = None
+    import md5
+    import sha
+import os.path
+from glob import glob
+import re
+import sys
+from xml.dom import Node, minidom
+from xml.dom.minidom import Document, Element
+
+try:
+    import resource
+except ImportError, detail:
+    resource = None
+    print "W:", detail
+    print "W: Cannot check for assembly dependencies - resource access "
+    print "W: unavailable. To enable resource access, please install "
+    print "W: http://sourceforge.net/projects/pywin32/"
+
+silent = False  # True suppresses all messages
+
+LANGUAGE_NEUTRAL_NT5 = "x-ww"
+LANGUAGE_NEUTRAL_NT6 = "none"
+RT_MANIFEST = 24
+
+Document.aChild = Document.appendChild
+Document.cE = Document.createElement
+Document.cT = Document.createTextNode
+Document.getEById = Document.getElementById
+Document.getEByTN = Document.getElementsByTagName
+Element.aChild = Element.appendChild
+Element.getA = Element.getAttribute
+Element.getEByTN = Element.getElementsByTagName
+Element.remA = Element.removeAttribute
+Element.setA = Element.setAttribute
+
+
+def getChildElementsByTagName(self, tagName):
+    """ Return child elements of type tagName if found, else [] """
+    result = []
+    for child in self.childNodes:
+        if isinstance(child, Element):
+            if child.tagName == tagName:
+                result.append(child)
+    return result
+
+
+def getFirstChildElementByTagName(self, tagName):
+    """ Return the first element of type tagName if found, else None """
+    for child in self.childNodes:
+        if isinstance(child, Element):
+            if child.tagName == tagName:
+                return child
+    return None
+
+
+Document.getCEByTN = getChildElementsByTagName
+Document.getFCEByTN = getFirstChildElementByTagName
+Element.getCEByTN = getChildElementsByTagName
+Element.getFCEByTN = getFirstChildElementByTagName
+
+
+class _Hash(object):
+
+    def __init__(self):
+        self.md5 = md5.new
+        self.sha = sha.new
+
+if hashlib is None:
+    hashlib = _Hash()
+
+
+class _Dummy:
+    pass
+
+
+if resource:
+    _File = resource.File
+else:
+    _File = _Dummy
+
+    
+class File(_File):
+
+    """ A file referenced by an assembly inside a manifest. """
+    
+    def __init__(self, filename="", hashalg=None, hash=None, comClasses=None, 
+                 typelibs=None, comInterfaceProxyStubs=None, 
+                 windowClasses=None):
+        if resource:
+            resource.File.__init__(self, filename)
+        else:
+            self.filename = filename
+        self.name = os.path.basename(filename)
+        if hashalg:
+            self.hashalg = hashalg.upper()
+        else:
+            self.hashalg = None
+        if os.path.isfile(filename) and hashalg and hashlib and \
+           hasattr(hashlib, hashalg.lower()):
+            self.calc_hash()
+        else:
+            self.hash = hash
+        self.comClasses = comClasses or [] # TO-DO: implement
+        self.typelibs = typelibs or [] # TO-DO: implement
+        self.comInterfaceProxyStubs = comInterfaceProxyStubs or [] # TO-DO: implement
+        self.windowClasses = windowClasses or [] # TO-DO: implement
+
+    def calc_hash(self, hashalg=None):
+        """
+        Calculate the hash of the file.
+        
+        Will be called automatically from the constructor if the file exists 
+        and hashalg is given (and supported), but may also be called manually 
+        e.g. to update the hash if the file has changed.
+        
+        """
+        fd = open(self.filename, "rb")
+        buf = fd.read()
+        fd.close()
+        if hashalg:
+            self.hashalg = hashalg.upper()
+        self.hash = getattr(hashlib, self.hashalg.lower())(buf).hexdigest()
+
+    def find(self, searchpath):
+        if not silent:
+            print "I: Searching for file", self.name
+        fn = os.path.join(searchpath, self.name)
+        if os.path.isfile(fn):
+            if not silent:
+                print "I: Found file", fn
+            return fn
+        else:
+            if not silent:
+                print "W: No such file", fn
+            return None
+
+
+class InvalidManifestError(Exception):
+    pass
+
+
+class Manifest(object):
+    
+    # Manifests:
+    # http://msdn.microsoft.com/en-us/library/aa375365%28VS.85%29.aspx
+
+    """
+    Manifest constructor.
+    
+    To build a basic manifest for your application:
+      mf = Manifest(type='win32', name='YourAppName', language='*', 
+                    processorArchitecture='x86', version=[1, 0, 0, 0])
+    
+    To write the XML to a manifest file:
+      mf.writexml("YourAppName.exe.manifest")
+    or
+      mf.writeprettyxml("YourAppName.exe.manifest")
+    
+    """
+
+    def __init__(self, manifestVersion=None, noInheritable=False, 
+                 noInherit=False, type_=None, name=None, language=None, 
+                 processorArchitecture=None, version=None, 
+                 publicKeyToken=None, description=None, 
+                 requestedExecutionLevel=None, uiAccess=None, 
+                 dependentAssemblies=None, files=None, 
+                 comInterfaceExternalProxyStubs=None):
+        self.filename = None
+        self.optional = None
+        self.manifestType = "assembly"
+        self.manifestVersion = manifestVersion or [1, 0]
+        self.noInheritable = noInheritable
+        self.noInherit = noInherit
+        self.type = type_
+        self.name = name
+        self.language = language
+        self.processorArchitecture = processorArchitecture
+        self.version = version
+        self.publicKeyToken = publicKeyToken
+        # publicKeyToken:
+        # A 16-character hexadecimal string that represents the last 8 bytes 
+        # of the SHA-1 hash of the public key under which the assembly is 
+        # signed. The public key used to sign the catalog must be 2048 bits 
+        # or greater. Required for all shared side-by-side assemblies.
+        # http://msdn.microsoft.com/en-us/library/aa375692(VS.85).aspx
+        self.applyPublisherPolicy = None
+        self.description = None
+        self.requestedExecutionLevel = requestedExecutionLevel
+        self.uiAccess = uiAccess
+        self.dependentAssemblies = dependentAssemblies or []
+        self.bindingRedirects = []
+        self.files = files or []
+        self.comInterfaceExternalProxyStubs = comInterfaceExternalProxyStubs or [] # TO-DO: implement
+    
+    def add_dependent_assembly(self, manifestVersion=None, noInheritable=False, 
+                 noInherit=False, type_=None, name=None, language=None, 
+                 processorArchitecture=None, version=None, 
+                 publicKeyToken=None, description=None, 
+                 requestedExecutionLevel=None, uiAccess=None, 
+                 dependentAssemblies=None, files=None, 
+                 comInterfaceExternalProxyStubs=None):
+        """
+        Shortcut for self.dependentAssemblies.append(Manifest(*args, **kwargs))
+        """
+        self.dependentAssemblies.append(Manifest(manifestVersion, 
+                                        noInheritable, noInherit, type_, name, 
+                                        language, processorArchitecture, 
+                                        version, publicKeyToken, description, 
+                                        requestedExecutionLevel, uiAccess, 
+                                        dependentAssemblies, files, 
+                                        comInterfaceExternalProxyStubs))
+        if self.filename:
+            # Enable search for private assembly by assigning bogus filename
+            # (only the directory has to be correct)
+            self.dependentAssemblies[-1].filename = ":".join((self.filename, 
+                                                              name))
+    
+    def add_file(self, name="", hashalg="", hash="", comClasses=None, 
+                 typelibs=None, comInterfaceProxyStubs=None, 
+                 windowClasses=None):
+        """ Shortcut for manifest.files.append """
+        self.files.append(File(name, hashalg, hash, comClasses, 
+                          typelibs, comInterfaceProxyStubs, windowClasses))
+    
+    def find_files(self, ignore_policies=True):
+        """ Search shared and private assemblies and return a list of files.
+        
+        If any files are not found, return an empty list.
+        
+        IMPORTANT NOTE: For the purpose of getting the dependent assembly 
+        files of an executable, the publisher configuration (aka policy)
+        should be ignored (which is the default). Setting ignore_policies=False 
+        is only useful to find out which files are actually loaded at
+        runtime.
+        
+        """
+        
+        # Shared Assemblies:
+        # http://msdn.microsoft.com/en-us/library/aa375996%28VS.85%29.aspx
+        #
+        # Private Assemblies:
+        # http://msdn.microsoft.com/en-us/library/aa375674%28VS.85%29.aspx
+        #
+        # Assembly Searching Sequence:
+        # http://msdn.microsoft.com/en-us/library/aa374224%28VS.85%29.aspx
+        #
+        # NOTE:
+        # Multilanguage User Interface (MUI) support not yet implemented
+        
+        files = []
+        
+        languages = []
+        if self.language not in (None, "", "*", "neutral"):
+            languages.append(self.getlanguage())
+            if "-" in self.language:
+                # language-culture syntax, e.g. en-us
+                # Add only the language part
+                languages.append(self.language.split("-")[0])
+            if self.language not in ("en-us", "en"):
+                languages.append("en-us")
+            if self.language != "en":
+                languages.append("en")
+        languages.append(self.getlanguage("*"))
+        
+        winsxs = os.path.join(os.getenv("SystemRoot"), "WinSxS")
+        if not os.path.isdir(winsxs) and not silent:
+            print "W: No such dir", winsxs
+        manifests = os.path.join(winsxs, "Manifests")
+        if not os.path.isdir(manifests) and not silent:
+            print "W: No such dir", manifests
+        if not ignore_policies and self.version:
+            if sys.getwindowsversion() < (6, ):
+                # Windows XP
+                pcfiles = os.path.join(winsxs, "Policies")
+                if not os.path.isdir(pcfiles) and not silent:
+                    print "W: No such dir", pcfiles
+            else:
+                # Vista or later
+                pcfiles = manifests
+        
+        for language in languages:
+            version = self.version
+            
+            # Search for publisher configuration
+            if not ignore_policies and version:
+                # Publisher Configuration (aka policy)
+                # A publisher configuration file globally redirects 
+                # applications and assemblies having a dependence on one 
+                # version of a side-by-side assembly to use another version of 
+                # the same assembly. This enables applications and assemblies 
+                # to use the updated assembly without having to rebuild all of 
+                # the affected applications.
+                # http://msdn.microsoft.com/en-us/library/aa375680%28VS.85%29.aspx
+                #
+                # Under Windows XP and 2003, policies are stored as 
+                # <version>.policy files inside 
+                # %SystemRoot%\WinSxS\Policies\<name>
+                # Under Vista and later, policies are stored as 
+                # <name>.manifest files inside %SystemRoot%\winsxs\Manifests
+                redirected = False
+                if os.path.isdir(pcfiles):
+                    if not silent:
+                        print ("I: Searching for publisher configuration %s..." %
+                               self.getpolicyid(True, language=language))
+                    if sys.getwindowsversion() < (6, ):
+                        # Windows XP
+                        policies = os.path.join(pcfiles, 
+                                                self.getpolicyid(True,
+                                                                 language=language) + 
+                                                ".policy")
+                    else:
+                        # Vista or later
+                        policies = os.path.join(pcfiles, 
+                                                self.getpolicyid(True,
+                                                                 language=language) + 
+                                                ".manifest")
+                    for manifestpth in glob(policies):
+                        if not os.path.isfile(manifestpth):
+                            if not silent:
+                                print "W: Not a file", manifestpth
+                            continue
+                        if not silent:
+                            print "I: Found", manifestpth
+                        try:
+                            policy = ManifestFromXMLFile(manifestpth)
+                        except Exception, exc:
+                            if not silent:
+                                print "E: Could not parse file", manifestpth
+                                print "E:", str(exc)
+                            pass
+                        else:
+                            if not silent:
+                                print ("I: Checking publisher policy for "
+                                       "binding redirects")
+                            for assembly in policy.dependentAssemblies:
+                                if not assembly.same_id(self, True) or \
+                                   assembly.optional:
+                                    continue
+                                for redirect in \
+                                    assembly.bindingRedirects:
+                                    if not silent:
+                                        old = "-".join([".".join([str(i) 
+                                                                  for i in 
+                                                                  part]) 
+                                                        for part in 
+                                                        redirect[0]])
+                                        new = ".".join([str(i) 
+                                                        for i in
+                                                        redirect[1]])
+                                        print "I: Found redirect for " \
+                                              "version(s)", old, "->", new
+                                    if version >= redirect[0][0] and \
+                                       version <= redirect[0][-1] and \
+                                       version != redirect[1]:
+                                        if not silent:
+                                            print "I: Applying redirect", \
+                                                  ".".join([str(i) 
+                                                            for i in
+                                                            version]), \
+                                                  "->", new
+                                        version = redirect[1]
+                                        redirected = True
+                    if not redirected and not silent:
+                        print "I: Publisher configuration not used"
+            
+            # Search for assemblies according to assembly searching sequence
+            paths = []
+            if os.path.isdir(manifests):
+                # Add winsxs search paths
+                paths.extend(glob(os.path.join(manifests, 
+                                               self.getid(language=language, 
+                                                          version=version) + 
+                                               "_*.manifest")))
+            if self.filename:
+                # Add private assembly search paths
+                dirnm = os.path.dirname(self.filename)
+                if language in (LANGUAGE_NEUTRAL_NT5, 
+                                LANGUAGE_NEUTRAL_NT6):
+                    for ext in (".dll", ".manifest"):
+                        paths.extend(glob(os.path.join(dirnm, self.name)))
+                        paths.extend(glob(os.path.join(dirnm, self.name, 
+                                                       self.name + ext)))
+                else:
+                    for ext in (".dll", ".manifest"):
+                        paths.extend(glob(os.path.join(dirnm, language, 
+                                                       self.name + ext)))
+                    for ext in (".dll", ".manifest"):
+                        paths.extend(glob(os.path.join(dirnm, language, 
+                                                       self.name, 
+                                                       self.name + ext)))
+            if not silent:
+                print ("I: Searching for assembly %s..." % 
+                       self.getid(language=language, 
+                                  version=version))
+            for manifestpth in paths:
+                if not os.path.isfile(manifestpth):
+                    if not silent:
+                        print "W: Not a file", manifestpth
+                    continue
+                assemblynm = os.path.basename(
+                    os.path.splitext(manifestpth)[0])
+                if not silent:
+                    if manifestpth.endswith(".dll"):
+                        print "I: Found manifest in", manifestpth
+                    else:
+                        print "I: Found manifest", manifestpth
+                try:
+                    if manifestpth.endswith(".dll"):
+                        manifest = ManifestFromResFile(manifestpth, [1])
+                    else:
+                        manifest = ManifestFromXMLFile(manifestpth)
+                except Exception, exc:
+                    if not silent:
+                        print "E: Could not parse manifest", manifestpth
+                        print "E:", exc
+                    pass
+                else:
+                    if manifestpth.startswith(winsxs):
+                        assemblydir = os.path.join(winsxs, assemblynm)
+                        if not os.path.isdir(assemblydir):
+                            if not silent:
+                                print "W: No such dir", assemblydir
+                                print "W: Assembly incomplete"
+                            return []
+                    else:
+                        assemblydir = os.path.dirname(manifestpth)
+                    files.append(manifestpth)
+                    for file_ in self.files or manifest.files:
+                        fn = file_.find(assemblydir)
+                        if fn:
+                            files.append(fn)
+                        else:
+                            # If any of our files does not exist,
+                            # the assembly is incomplete
+                            if not silent:
+                                print "W: Assembly incomplete"
+                            return []
+                return files
+
+        print "W: Assembly not found"
+        return []
+
+    def getid(self, language=None, version=None):
+        """
+        Return an identification string which uniquely names a manifest.
+
+        This string is a combination of the manifest's processorArchitecture, 
+        name, publicKeyToken, version and language.
+        
+        Arguments:
+        version (tuple or list of integers) - If version is given, use it 
+                                              instead of the manifest's 
+                                              version.
+        
+        """
+        if not self.name:
+            if not silent:
+                print "W: Assembly metadata incomplete"
+            return ""
+        id = []
+        if self.processorArchitecture:
+            id.append(self.processorArchitecture)
+        id.append(self.name)
+        if self.publicKeyToken:
+            id.append(self.publicKeyToken)
+        if version or self.version:
+            id.append(".".join([str(i) for i in version or self.version]))
+        if not language:
+            language = self.getlanguage()
+        if language:
+            id.append(language)
+        return "_".join(id)
+    
+    def getlanguage(self, language=None, windowsversion=None):
+        """
+        Get and return the manifest's language as string.
+        
+        Can be either language-culture e.g. 'en-us' or a string indicating 
+        language neutrality, e.g. 'x-ww' on Windows XP or 'none' on Vista 
+        and later.
+        
+        """
+        if not language:
+            language = self.language
+        if language in (None, "", "*", "neutral"):
+            return (LANGUAGE_NEUTRAL_NT5,
+                    LANGUAGE_NEUTRAL_NT6)[(windowsversion or 
+                                           sys.getwindowsversion()) >= (6, )]
+        return language
+    
+    def getpolicyid(self, fuzzy=True, language=None, windowsversion=None):
+        """
+        Return an identification string which can be used to find a policy.
+
+        This string is a combination of the manifest's processorArchitecture, 
+        major and minor version, name, publicKeyToken and language.
+        
+        Arguments:
+        fuzzy (boolean)             - If False, insert the full version in 
+                                      the id string. Default is True (omit).
+        windowsversion              - If not specified (or None), default to 
+        (tuple or list of integers)   sys.getwindowsversion().
+        
+        """
+        if not self.name:
+            if not silent:
+                print "W: Assembly metadata incomplete"
+            return ""
+        id = []
+        if self.processorArchitecture:
+            id.append(self.processorArchitecture)
+        name = []
+        name.append("policy")
+        if self.version:
+            name.append(str(self.version[0]))
+            name.append(str(self.version[1]))
+        name.append(self.name)
+        id.append(".".join(name))
+        if self.publicKeyToken:
+            id.append(self.publicKeyToken)
+        if self.version and (windowsversion or sys.getwindowsversion()) >= (6, ):
+            # Vista and later
+            if fuzzy:
+                id.append("*")
+            else:
+                id.append(".".join([str(i) for i in self.version]))
+        if not language:
+            language = self.getlanguage(windowsversion=windowsversion)
+        if language:
+            id.append(language)
+        id.append("*")
+        id = "_".join(id)
+        if self.version and (windowsversion or sys.getwindowsversion()) < (6, ):
+            # Windows XP
+            if fuzzy:
+                id = os.path.join(id, "*")
+            else:
+                id = os.path.join(id, ".".join([str(i) for i in self.version]))
+        return id
+
+    def load_dom(self, domtree, initialize=True):
+        """
+        Load manifest from DOM tree.
+        
+        If initialize is True (default), reset existing attributes first.
+        
+        """
+        if domtree.nodeType == Node.DOCUMENT_NODE:
+            rootElement = domtree.documentElement
+        elif domtree.nodeType == Node.ELEMENT_NODE:
+            rootElement = domtree
+        else:
+            raise InvalidManifestError("Invalid root element node type " + 
+                                       str(rootElement.nodeType) + 
+                                       " - has to be one of (DOCUMENT_NODE, "
+                                       "ELEMENT_NODE)")
+        allowed_names = ("assembly", "assemblyBinding", "configuration", 
+                         "dependentAssembly")
+        if rootElement.tagName not in allowed_names:
+            raise InvalidManifestError("Invalid root element <" + 
+                                       rootElement.tagName + 
+                                       "> - has to be one of " + 
+                                       repr(allowed_names))
+        # print "I: loading manifest metadata from element <%s>" % \
+              # rootElement.tagName
+        if rootElement.tagName == "configuration":
+            for windows in rootElement.getCEByTN("windows"):
+                for assemblyBinding in windows.getCEByTN("assemblyBinding"):
+                    self.load_dom(assemblyBinding)
+        else:
+            if initialize:
+                self.__init__()
+            self.manifestType = rootElement.tagName
+            self.manifestVersion = [int(i) 
+                                    for i in 
+                                    (rootElement.getA("manifestVersion") or 
+                                     "1.0").split(".")]
+            self.noInheritable = bool(rootElement.getFCEByTN("noInheritable"))
+            self.noInherit = bool(rootElement.getFCEByTN("noInherit"))
+            for assemblyIdentity in rootElement.getCEByTN("assemblyIdentity"):
+                self.type = assemblyIdentity.getA("type") or None
+                self.name = assemblyIdentity.getA("name") or None
+                self.language = assemblyIdentity.getA("language") or None
+                self.processorArchitecture = assemblyIdentity.getA(
+                    "processorArchitecture") or None
+                version = assemblyIdentity.getA("version")
+                if version:
+                    self.version = [int(i) for i in version.split(".")]
+                self.publicKeyToken = assemblyIdentity.getA("publicKeyToken") or None
+            for publisherPolicy in rootElement.getCEByTN("publisherPolicy"):
+                self.applyPublisherPolicy = (publisherPolicy.getA("apply") or 
+                                             "").lower() == "yes"
+            for description in rootElement.getCEByTN("description"):
+                if description.firstChild:
+                    self.description = description.firstChild.wholeText
+            for trustInfo in rootElement.getCEByTN("trustInfo"):
+                for security in trustInfo.getCEByTN("security"):
+                    for requestedPrivileges in \
+                        security.getCEByTN("requestedPrivileges"):
+                        for requestedExecutionLevel in \
+                            requestedPrivileges.getCEByTN(
+                                "requestedExecutionLevel"):
+                            self.requestedExecutionLevel = \
+                                requestedExecutionLevel.getA("level")
+                            self.uiAccess = (
+                                requestedExecutionLevel.getA("uiAccess") or 
+                                "").lower() == "true"
+            if rootElement.tagName == "assemblyBinding":
+                dependencies = [rootElement]
+            else:
+                dependencies = rootElement.getCEByTN("dependency")
+            for dependency in dependencies:
+                for dependentAssembly in dependency.getCEByTN(
+                    "dependentAssembly"):
+                    manifest = ManifestFromDOM(dependentAssembly)
+                    manifest.optional = (dependency.getA("optional") or 
+                                         "").lower() == "yes"
+                    self.dependentAssemblies.append(manifest)
+                    if self.filename:
+                        # Enable search for private assembly by assigning bogus 
+                        # filename (only the directory has to be correct)
+                        self.dependentAssemblies[-1].filename = ":".join(
+                            (self.filename, manifest.name))
+            for bindingRedirect in rootElement.getCEByTN("bindingRedirect"):
+                oldVersion = [[int(i) for i in part.split(".")] 
+                              for part in 
+                              bindingRedirect.getA("oldVersion").split("-")]
+                newVersion = [int(i) 
+                              for i in 
+                              bindingRedirect.getA("newVersion").split(".")]
+                self.bindingRedirects.append((oldVersion, newVersion))
+            for file_ in rootElement.getCEByTN("file"):
+                self.add_file(name=file_.getA("name"),
+                              hashalg=file_.getA("hashalg"),
+                              hash=file_.getA("hash"))
+    
+    def parse(self, filename_or_file):
+        """ Load manifest from file or file object """
+        self.__init__()
+        if isinstance(filename_or_file, (str, unicode)):
+            self.filename = filename_or_file
+        else:
+            self.filename = filename_or_file.filename
+        self.load_dom(minidom.parse(filename_or_file), False)
+    
+    def parse_string(self, xmlstr, initialize=True):
+        """ Load manifest from XML string """
+        self.load_dom(minidom.parseString(xmlstr), initialize)
+    
+    def same_id(self, manifest, skip_version_check=False):
+        """
+        Return a bool indicating if another manifest has the same identitiy.
+        
+        This is done by comparing language, name, processorArchitecture, 
+        publicKeyToken, type and version.
+        
+        """
+        if skip_version_check:
+            version_check = True
+        else:
+            version_check = self.version == manifest.version
+        return (self.language == manifest.language and
+                self.name == manifest.name and
+                self.processorArchitecture == manifest.processorArchitecture and
+                self.publicKeyToken == manifest.publicKeyToken and
+                self.type == manifest.type and
+                version_check)
+    
+    def todom(self):
+        """ Return the manifest as DOM tree """
+        doc = Document()
+        docE = doc.cE(self.manifestType)
+        if self.manifestType == "assemblyBinding":
+            cfg = doc.cE("configuration")
+            win = doc.cE("windows")
+            win.aChild(docE)
+            cfg.aChild(win)
+            doc.aChild(cfg)
+        else:
+            doc.aChild(docE)
+        if self.manifestType != "dependentAssembly":
+            docE.setA("xmlns", "urn:schemas-microsoft-com:asm.v1")
+            if self.manifestType != "assemblyBinding":
+                docE.setA("manifestVersion", 
+                          ".".join([str(i) for i in self.manifestVersion]))
+        if self.noInheritable:
+            docE.aChild(doc.cE("noInheritable"))
+        if self.noInherit:
+            docE.aChild(doc.cE("noInherit"))
+        aId = doc.cE("assemblyIdentity")
+        if self.type:
+            aId.setAttribute("type", self.type)
+        if self.name:
+            aId.setAttribute("name", self.name)
+        if self.language:
+            aId.setAttribute("language", self.language)
+        if self.processorArchitecture:
+            aId.setAttribute("processorArchitecture", 
+                             self.processorArchitecture)
+        if self.version:
+            aId.setAttribute("version", 
+                             ".".join([str(i) for i in self.version]))
+        if self.publicKeyToken:
+            aId.setAttribute("publicKeyToken", self.publicKeyToken)
+        if aId.hasAttributes():
+            docE.aChild(aId)
+        else:
+            aId.unlink()
+        if self.applyPublisherPolicy != None:
+            ppE = doc.cE("publisherPolicy")
+            if self.applyPublisherPolicy:
+                ppE.setA("apply", "yes")
+            else:
+                ppE.setA("apply", "no")
+            docE.aChild(ppE)
+        if self.description:
+            descE = doc.cE("description")
+            descE.aChild(doc.cT(self.description))
+            docE.aChild(descE)
+        if self.requestedExecutionLevel in ("asInvoker", "highestAvailable", 
+                                            "requireAdministrator"):
+            tE = doc.cE("trustInfo")
+            tE.setA("xmlns", "urn:schemas-microsoft-com:asm.v3")
+            sE = doc.cE("security")
+            rpE = doc.cE("requestedPrivileges")
+            relE = doc.cE("requestedExecutionLevel")
+            relE.setA("level", self.requestedExecutionLevel)
+            if self.uiAccess:
+                relE.setA("uiAccess", "true")
+            else:
+                relE.setA("uiAccess", "false")
+            rpE.aChild(relE)
+            sE.aChild(rpE)
+            tE.aChild(sE)
+            docE.aChild(tE)
+        if self.dependentAssemblies:
+            for assembly in self.dependentAssemblies:
+                if self.manifestType != "assemblyBinding":
+                    dE = doc.cE("dependency")
+                    if assembly.optional:
+                        dE.setAttribute("optional", "yes")
+                daE = doc.cE("dependentAssembly")
+                adom = assembly.todom()
+                for child in adom.documentElement.childNodes:
+                    daE.aChild(child.cloneNode(False))
+                adom.unlink()
+                if self.manifestType != "assemblyBinding":
+                    dE.aChild(daE)
+                    docE.aChild(dE)
+                else:
+                    docE.aChild(daE)
+        if self.bindingRedirects:
+            for bindingRedirect in self.bindingRedirects:
+                brE = doc.cE("bindingRedirect")
+                brE.setAttribute("oldVersion", 
+                                 "-".join([".".join([str(i) 
+                                                     for i in 
+                                                     part]) 
+                                           for part in 
+                                           bindingRedirect[0]]))
+                brE.setAttribute("newVersion", 
+                                 ".".join([str(i) for i in bindingRedirect[1]]))
+                docE.aChild(brE)
+        if self.files:
+            for file_ in self.files:
+                fE = doc.cE("file")
+                for attr in ("name", "hashalg", "hash"):
+                    val = getattr(file_, attr)
+                    if val:
+                        fE.setA(attr, val)
+                docE.aChild(fE)
+        return doc
+    
+    def toprettyxml(self, indent="  ", newl=os.linesep, encoding="UTF-8"):
+        """ Return the manifest as pretty-printed XML """
+        domtree = self.todom()
+        # WARNING: The XML declaration has to follow the order 
+        # version-encoding-standalone (standalone being optional), otherwise 
+        # if it is embedded in an exe the exe will fail to launch! 
+        # ('application configuration incorrect')
+        xmlstr = domtree.toprettyxml(
+            indent, newl, encoding).strip(os.linesep).replace(
+                '<?xml version="1.0" encoding="UTF-8"?>', 
+                '<?xml version="1.0" encoding="%s" standalone="yes"?>' % 
+                encoding)
+        domtree.unlink()
+        return xmlstr
+    
+    def toxml(self, encoding="UTF-8"):
+        """ Return the manifest as XML """
+        domtree = self.todom()
+        # WARNING: The XML declaration has to follow the order 
+        # version-encoding-standalone (standalone being optional), otherwise 
+        # if it is embedded in an exe the exe will fail to launch! 
+        # ('application configuration incorrect')
+        xmlstr = domtree.toxml(encoding).replace(
+            '<?xml version="1.0" encoding="UTF-8"?>', 
+            '<?xml version="1.0" encoding="%s" standalone="yes"?>' % encoding)
+        domtree.unlink()
+        return xmlstr
+
+    def update_resources(self, dstpath, names=None, languages=None):
+        """ Update or add manifest resource in dll/exe file dstpath """
+        UpdateManifestResourcesFromXML(dstpath, self.toprettyxml(), names, 
+                                       languages)
+    
+    def writeprettyxml(self, filename_or_file, indent="  ", newl=os.linesep, 
+                       encoding="UTF-8"):
+        """ Write the manifest as XML to a file or file object """
+        if isinstance(filename_or_file, (str, unicode)):
+            filename_or_file = open(filename_or_file, "wb")
+        xmlstr = self.toprettyxml(indent, newl, encoding)
+        filename_or_file.write(xmlstr)
+        filename_or_file.close()
+    
+    def writexml(self, filename_or_file, indent="  ", newl=os.linesep, 
+                 encoding="UTF-8"):
+        """ Write the manifest as XML to a file or file object """
+        if isinstance(filename_or_file, (str, unicode)):
+            filename_or_file = open(filename_or_file, "wb")
+        xmlstr = self.toxml(indent, newl, encoding)
+        filename_or_file.write(xmlstr)
+        filename_or_file.close()
+
+
+def ManifestFromResFile(filename, names=None, languages=None):
+    """ Create and return manifest instance from resource in dll/exe file """
+    res = GetManifestResources(filename, names, languages)
+    pth = []
+    if res and res[RT_MANIFEST]:
+        while isinstance(res, dict) and res.keys():
+            key = res.keys()[0]
+            pth.append(str(key))
+            res = res[key]
+    if isinstance(res, dict):
+        raise InvalidManifestError("No matching manifest resource found in '%s'" % 
+                                   filename)
+    manifest = Manifest()
+    manifest.filename = ":".join([filename] + pth)
+    manifest.parse_string(res, False)
+    return manifest
+
+
+def ManifestFromDOM(domtree):
+    """ Create and return manifest instance from DOM tree """
+    manifest = Manifest()
+    manifest.load_dom(domtree)
+    return manifest
+
+
+def ManifestFromXML(xmlstr):
+    """ Create and return manifest instance from XML """
+    manifest = Manifest()
+    manifest.parse_string(xmlstr)
+    return manifest
+
+
+def ManifestFromXMLFile(filename_or_file):
+    """ Create and return manifest instance from file """
+    manifest = Manifest()
+    manifest.parse(filename_or_file)
+    return manifest
+
+
+def GetManifestResources(filename, names=None, languages=None):
+    """ Get manifest resources from file """
+    return resource.GetResources(filename, [RT_MANIFEST], names, languages)
+
+
+def UpdateManifestResourcesFromXML(dstpath, xmlstr, names=None, 
+                                   languages=None):
+    """ Update or add manifest XML as resource in dstpath """
+    if not silent:
+        print "I: Updating manifest in", dstpath
+    if dstpath.lower().endswith(".exe"):
+        name = 1 
+    else:
+        name = 2
+    resource.UpdateResources(dstpath, xmlstr, RT_MANIFEST, names or [name], 
+                             languages or [0, "*"])
+
+
+def UpdateManifestResourcesFromXMLFile(dstpath, srcpath, names=None, 
+                                       languages=None):
+    """ Update or add manifest XML from srcpath as resource in dstpath """
+    if not silent:
+        print "I: Updating manifest from", srcpath, "in", dstpath
+    if dstpath.lower().endswith(".exe"):
+        name = 1 
+    else:
+        name = 2
+    resource.UpdateResourcesFromDataFile(dstpath, srcpath, RT_MANIFEST, 
+                                         names or [name], 
+                                         languages or [0, "*"])
+
+
+if __name__ == "__main__":    
+    dstpath = sys.argv[1]
+    srcpath = sys.argv[2]
+    UpdateManifestResourcesFromXMLFile(dstpath, srcpath)
Index: resource.py
===================================================================
--- resource.py	(revision 0)
+++ resource.py	(revision 0)
@@ -0,0 +1,271 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2009, Florian Hoech
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301, USA
+
+"""
+resource.py
+
+Read and write resources from/to Win32 PE files.
+
+Commandline usage:
+resource.py <dstpath> <srcpath>
+Updates or adds resources from file <srcpath> in file <dstpath>.
+
+2009-03 Florian Hoech
+
+"""
+
+import os.path
+import pywintypes
+import win32api
+
+silent = False  # True suppresses all messages
+
+LOAD_LIBRARY_AS_DATAFILE = 2
+ERROR_BAD_EXE_FORMAT = 193
+ERROR_RESOURCE_DATA_NOT_FOUND = 1812
+ERROR_RESOURCE_TYPE_NOT_FOUND = 1813
+ERROR_RESOURCE_NAME_NOT_FOUND = 1814
+ERROR_RESOURCE_LANG_NOT_FOUND = 1815
+
+
+class File(object):
+
+    """ Win32 PE file class. """
+
+    def __init__(self, filename):
+        self.filename = filename
+    
+    def get_resources(self, types=None, names=None, languages=None):
+        """
+        Get resources.
+        
+        types = a list of resource types to search for (None = all)
+        names = a list of resource names to search for (None = all)
+        languages = a list of resource languages to search for (None = all)
+        Return a dict of the form {type_: {name: {language: data}}} which 
+        might also be empty if no matching resources were found.
+        
+        """
+        return GetResources(self.filename, types, names, languages)
+    
+    def update_resources(self, data, type_, names=None, languages=None):
+        """
+        Update or add resource data.
+        
+        type_ = resource type to update
+        names = a list of resource names to update (None = all)
+        languages = a list of resource languages to update (None = all)
+        
+        """
+        UpdateResources(self.filename, data, type_, names, languages)
+    
+    def update_resources_from_datafile(self, srcpath, type_, names=None, 
+                                       languages=None):
+        """
+        Update or add resource data from file srcpath.
+        
+        type_ = resource type to update
+        names = a list of resource names to update (None = all)
+        languages = a list of resource languages to update (None = all)
+        
+        """
+        UpdateResourcesFromDataFile(self.filename, srcpath, type_, names, 
+                                    languages)
+    
+    def update_resources_from_dict(self, res, types=None, names=None, 
+                                   languages=None):
+        """
+        Update or add resources from resource dict.
+        
+        types = a list of resource types to update (None = all)
+        names = a list of resource names to update (None = all)
+        languages = a list of resource languages to update (None = all)
+        
+        """
+        UpdateResourcesFromDict(self.filename, res, types, names, 
+                                languages)
+    
+    def update_resources_from_resfile(self, srcpath, types=None, names=None, 
+                                      languages=None):
+        """
+        Update or add resources from dll/exe file srcpath.
+        
+        types = a list of resource types to update (None = all)
+        names = a list of resource names to update (None = all)
+        languages = a list of resource languages to update (None = all)
+        
+        """
+        UpdateResourcesFromResFile(self.filename, srcpath, types, names, 
+                                   languages)
+
+
+def _GetResources(hsrc, types=None, names=None, languages=None):
+    """
+    Get resources from hsrc.
+    
+    types = a list of resource types to search for (None = all)
+    names = a list of resource names to search for (None = all)
+    languages = a list of resource languages to search for (None = all)
+    Return a dict of the form {type_: {name: {language: data}}} which 
+    might also be empty if no matching resources were found.
+    
+    """
+    res = {}
+    try:
+        # print "I: Enumerating resource types"
+        enum_types = win32api.EnumResourceTypes(hsrc)
+        if types and not "*" in types:
+            enum_types = filter(lambda type_: 
+                                type_ in types, 
+                                enum_types)
+        for type_ in enum_types:
+            # print "I: Enumerating resources of type", type_
+            enum_names = win32api.EnumResourceNames(hsrc, type_)
+            if names and not "*" in names:
+                enum_names = filter(lambda name: 
+                                    name in names, 
+                                    enum_names)
+            for name in enum_names:
+                # print "I: Enumerating resources of type", type_, "name", name
+                enum_languages = win32api.EnumResourceLanguages(hsrc, 
+                                                                type_, 
+                                                                name)
+                if languages and not "*" in languages:
+                    enum_languages = filter(lambda language: 
+                                            language in languages, 
+                                            enum_languages)
+                for language in enum_languages:
+                    data = win32api.LoadResource(hsrc, type_, name, language)
+                    if not type_ in res:
+                        res[type_] = {}
+                    if not name in res[type_]:
+                        res[type_][name] = {}
+                    res[type_][name][language] = data
+    except pywintypes.error, exception:
+        if exception.args[0] in (ERROR_RESOURCE_DATA_NOT_FOUND,
+                                 ERROR_RESOURCE_TYPE_NOT_FOUND,
+                                 ERROR_RESOURCE_NAME_NOT_FOUND,
+                                 ERROR_RESOURCE_LANG_NOT_FOUND):
+                                     # print "I:", exception.args[1] + ":", \
+                                         # exception.args[2]
+                                     pass
+        else:
+            raise exception
+    return res
+
+
+def GetResources(filename, types=None, names=None, languages=None):
+    """
+    Get resources from dll/exe file.
+    
+    types = a list of resource types to search for (None = all)
+    names = a list of resource names to search for (None = all)
+    languages = a list of resource languages to search for (None = all)
+    Return a dict of the form {type_: {name: {language: data}}} which 
+    might also be empty if no matching resources were found.
+    
+    """
+    hsrc = win32api.LoadLibraryEx(filename, 0, LOAD_LIBRARY_AS_DATAFILE)
+    res = _GetResources(hsrc, types, names, languages)
+    win32api.FreeLibrary(hsrc)
+    return res
+
+
+def UpdateResources(dstpath, data, type_, names=None, languages=None):
+    """
+    Update or add resource data in dll/exe file dstpath.
+    
+    type_ = resource type to update
+    names = a list of resource names to update (None = all)
+    languages = a list of resource languages to update (None = all)
+    
+    """
+    # look for existing resources
+    res = GetResources(dstpath, [type_], names, languages)
+    # add type_, names and languages not already present in existing resources
+    if not type_ in res and type_ != "*":
+        res[type_] = {}
+    if names:
+        for name in names:
+            if not name in res[type_] and name != "*":
+                res[type_][name] = []
+                if languages:
+                    for language in languages:
+                        if not language in res[type_][name] and language != "*":
+                            res[type_][name].append(language)
+    # add resource to destination, overwriting existing resources
+    hdst = win32api.BeginUpdateResource(dstpath, 0)
+    for type_ in res:
+        for name in res[type_]:
+            for language in res[type_][name]:
+                if not silent:
+                    print "I: Updating resource type", type_, "name", name, \
+                          "language", language
+                win32api.UpdateResource(hdst, type_, name, data, language)
+    win32api.EndUpdateResource(hdst, 0)
+
+
+def UpdateResourcesFromDataFile(dstpath, srcpath, type_, names=None, 
+                                languages=None):
+    """
+    Update or add resource data from file srcpath in dll/exe file dstpath.
+    
+    type_ = resource type to update
+    names = a list of resource names to update (None = all)
+    languages = a list of resource languages to update (None = all)
+    
+    """
+    src = open(srcpath, "rb")
+    data = src.read()
+    src.close()
+    UpdateResources(dstpath, data, type_, names, languages)
+
+
+def UpdateResourcesFromDict(dstpath, res, types=None, names=None, 
+                            languages=None):
+    """
+    Update or add resources from resource dict in dll/exe file dstpath.
+    
+    types = a list of resource types to update (None = all)
+    names = a list of resource names to update (None = all)
+    languages = a list of resource languages to update (None = all)
+    
+    """
+    for type_ in res:
+        if not types or type_ in types:
+            for name in res[type_]:
+                if not names or name in names:
+                    for language in res[type_][name]:
+                        if not languages or language in languages:
+                            UpdateResources(dstpath, 
+                                            res[type_][name][language], 
+                                            [type_], [name], [language])
+
+
+def UpdateResourcesFromResFile(dstpath, srcpath, types=None, names=None, 
+                               languages=None):
+    """
+    Update or add resources from dll/exe file srcpath in dll/exe file dstpath.
+    
+    types = a list of resource types to update (None = all)
+    names = a list of resource names to update (None = all)
+    languages = a list of resource languages to update (None = all)
+    
+    """
+    res = GetResources(srcpath, types, names, languages)
+    UpdateResourcesFromDict(dstpath, res)

