Index: application.config
===================================================================
--- application.config	(revision 0)
+++ application.config	(revision 0)
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<configuration>
+ <windows>
+  <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
+   <assemblyIdentity  processorArchitecture="X86" name="Microsoft.Windows.mysampleApp" type="win32" version="1.0.0.0"/>
+   <publisherPolicy apply="no"/>                     
+   <dependentAssembly>
+    <assemblyIdentity type="win32" processorArchitecture="x86" name="Microsoft.Windows.SampleAssembly" publicKeyToken="0000000000000000"/>
+    <bindingRedirect oldVersion="2.0.0.0" newVersion="2.0.1.0"/>
+   </dependentAssembly>
+  </assemblyBinding>
+ </windows>
+</configuration>
\ No newline at end of file
Index: application.manifest
===================================================================
--- application.manifest	(revision 0)
+++ application.manifest	(revision 0)
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+  <assemblyIdentity type="win32" 
+                    name="myOrganization.myDivision.mySampleApp" 
+                    version="6.0.0.0" 
+                    processorArchitecture="x86" 
+                    publicKeyToken="0000000000000000"
+  />
+  <dependency>
+    <dependentAssembly>
+      <assemblyIdentity type="win32" 
+                        name="Proseware.Research.SampleAssembly" 
+                        version="6.0.0.0" 
+                        processorArchitecture="X86" 
+                        publicKeyToken="0000000000000000" 
+                        language="*"
+      />
+    </dependentAssembly>
+  </dependency>
+</assembly>
\ No newline at end of file
Index: apptemplate.manifest
===================================================================
--- apptemplate.manifest	(revision 0)
+++ apptemplate.manifest	(revision 0)
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+  <assemblyIdentity
+    version="0.0.0.0"
+    processorArchitecture="x86"
+    name="myOrganization.myDivision.mySampleApp"
+    type="win32"
+  />
+  <description>Description</description>
+  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
+    <security>
+      <requestedPrivileges>
+        <requestedExecutionLevel level="asInvoker" uiAccess="false" />
+      </requestedPrivileges>
+    </security>
+  </trustInfo>
+  <dependency>
+    <dependentAssembly>
+      <assemblyIdentity
+         type="win32"
+         name="Microsoft.Windows.Common-Controls"
+         version="6.0.0.0"
+         processorArchitecture="x86"
+         publicKeyToken="6595b64144ccf1df"
+         language="*"
+       />
+    </dependentAssembly>
+  </dependency>
+  <dependency>
+    <dependentAssembly>
+      <assemblyIdentity
+         type="win32"
+         name="Microsoft.VC90.CRT"
+         version="9.0.21022.8"
+         processorArchitecture="x86"
+         publicKeyToken="1fc8b3b9a1e18e3b"
+       />
+    </dependentAssembly>
+  </dependency>
+</assembly>
\ No newline at end of file
Index: assembly.manifest
===================================================================
--- assembly.manifest	(revision 0)
+++ assembly.manifest	(revision 0)
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" 
+manifestVersion="1.0">
+    <assemblyIdentity type="win32" name="Microsoft.Tools.SampleAssembly" version="6.0.0.0" processorArchitecture="x86" publicKeyToken="0000000000000000"/>
+    <file name="sampleu.dll" hash="3eab067f82504bf271ed38112a4ccdf46094eb5a" hashalg="SHA1">
+        <comClass description="Font Property Page" clsid="{0BE35200-8F91-11CE-9DE3-00AA004BB851}"/>
+        <comClass description="Color Property Page" clsid="{0BE35201-8F91-11CE-9DE3-00AA004BB851}"/>
+        <comClass description="Picture Property Page" clsid="{0BE35202-8F91-11CE-9DE3-00AA004BB851}"/>
+    </file>
+    <file name="bar.dll" hash="ac72753e5bb20446d88a48c8f0aaae769a962338" hashalg="SHA1"/>
+    <file name="foo.dll" hash="a7312a1f6cfb46433001e0540458de60adcd5ec5" hashalg="SHA1">
+        <comClass description="Registrar Class" clsid="{44EC053A-400F-11D0-9DCD-00A0C90391D3}" progid="ATL.Registrar"/>
+    <comInterfaceProxyStub iid="{B6EA2051-048A-11D1-82B9-00C04FB9942E}" name=" IAxWinAmbientDispatch " tlbid="{34EC053A-400F-11D0-9DCD-00A0C90391D3}"/>
+        <typelib tlbid="{44EC0535-400F-11D0-9DCD-00A0C90391D3}" version="1.0" helpdir=""/>
+    </file>
+    <file name="sampledll.dll" hash="ba62960ceb15073d2598379307aad84f3a73dfcb" hashalg="SHA1"/>
+<windowClass>ToolbarWindow32</windowClass>
+        <windowClass>ComboBoxEx32</windowClass>
+        <windowClass>sample_trackbar32</windowClass>
+        <windowClass>sample_updown32</windowClass>
+</assembly>
\ No newline at end of file
Index: bindepend.py
===================================================================
--- bindepend.py	(revision 690)
+++ bindepend.py	(working copy)
@@ -43,6 +43,15 @@
 iswin = sys.platform[:3] == 'win'
 cygwin = sys.platform == 'cygwin'
 
+if iswin:
+    import __builtin__
+    __builtin__.__dict__["silent"] = False  # True suppresses all messages from the assembly dependency code
+    from manifest import RT_MANIFEST, GetManifestResources, ManifestFromXML
+    try:
+        import resource
+    except ImportError, detail:
+        resource = None
+
 excludes = {
     'KERNEL32.DLL':1,
     'ADVAPI.DLL':1,
@@ -78,6 +87,12 @@
     'PSAPI.DLL':1,
     'MSVCP80.DLL':1,
     'MSVCR80.DLL':1,
+    'MSVCP90.DLL':1,
+    'MSVCR90.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,
@@ -87,6 +102,8 @@
     r'/usr/lib/libGL.*':1,
     #
     '^/System/Library/Frameworks':1,
+    # MS assembly excludes
+    'Microsoft.Windows.Common-Controls':1,
 }
 
 excludesRe = re.compile('|'.join(excludes.keys()), re.I)
@@ -283,10 +300,93 @@
         for lib, npth in selectImports(pth, platform, xtrapath):
             if seen.get(string.upper(lib),0):
                 continue
+            if iswin:
+                for ftocnm, fn in selectAssemblies(npth):
+                    lTOC.append((ftocnm, fn, 'BINARY'))
             lTOC.append((lib, npth, 'BINARY'))
 
     return lTOC
 
+def getAssemblies(pth):
+    """Return the dependent assemblies of a binary."""
+    # 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)
+        if not silent:
+            print "W:", pth
+            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/"
+        return []
+    else:
+        # check the binary for embedded manifest
+        res = GetManifestResources(pth)
+    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 = ManifestFromXML(res[RT_MANIFEST][name][language])
+                except Exception, exc:
+                    if not silent:
+                        print "E: Could not parse manifest resource of"
+                        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 assembly.optional or \
+           excludesRe.search(assembly.name) or \
+           seen.get(assembly.getid().upper(),0):
+            if assembly.optional and not (
+               excludesRe.search(assembly.name) or 
+               seen.get(assembly.getid().upper(),0)) and not silent:
+                print "I: Skipping search for optional assembly", assembly.name
+            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":
+                    ftocnm = assembly.name + fext
+                else:
+                    ftocnm = os.path.basename(fn)
+                ftocnm, fn = [item.encode(sys.getfilesystemencoding()) 
+                              for item in 
+                              (os.path.join(assembly.name, ftocnm), fn)]
+                if not seen.get(fn.upper(),0):
+                    if not silent:
+                        print "I: Adding", ftocnm
+                    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.
 
@@ -302,19 +402,20 @@
     iswin = platform[:3] == 'win'
     for lib in dlls:
         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:
+        candidatelib = npth if npth else 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)
                 continue
@@ -324,7 +425,7 @@
 
         if npth:
             rv.append((lib, npth))
-        else:
+        elif not seen.get(lib.upper(),0):
             print "E: lib not found:", lib, "dependency of", pth
 
     return rv
@@ -388,7 +489,18 @@
     """Forwards to the correct getImports implementation for the platform.
     """
     if platform[:3] == 'win' or platform == 'cygwin':
-        return _getImports_pe(pth)
+        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 'I: Cannot get binary dependencies for non-PE file:'
+                print 'I:', pth
+            return []
     elif platform == 'darwin':
         return _getImports_otool(pth)
     else:
@@ -471,7 +583,11 @@
                       help='Target platform, required for cross-bundling (default: current platform)')
 
     opts, args = parser.parse_args()
+    __builtin__.__dict__["silent"] = True  # Suppress all messages from the assembly 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 690)
+++ 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,55 @@
         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]
+                restype = res[1] if len(res) > 1 else None
+                resname = res[2] if len(res) > 2 else None
+                reslang = res[3] if len(res) > 3 else 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 +787,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 +811,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 +1144,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 +1168,7 @@
         sys.exit(1)
 
     if config['hasRsrcUpdate']:
-        import icon, versionInfo
+        import icon, versionInfo, resource, manifest
 
     if config['hasUPX']:
         setupUPXFlags()
@@ -1146,6 +1184,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 690)
+++ 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,612 @@
+#!/usr/bin/env python
+
+"""
+manifest.py 2009-03 Florian Hoech
+
+Provides the following functionality:
+* Create, parse and write MS Windows Manifest files
+* Find files which are part of an assembly, by looking for shared and 
+  private assemblies
+* Update manifest resources in dll/exe files
+
+Implemented:
+* Shared and private assemblies support
+* Publisher configuration support (.policy files)
+* application configuration support (.config files)
+
+Not implemented:
+* Manifest validation (only very basic sanity checks are currently in place)
+* comClass, typelib, comInterfaceProxyStub, windowClass subelements of the 
+  file element
+* comInterfaceExternalProxyStub, windowClass subelements of the assembly 
+  element
+
+Reference:
+About Isolated Applications and Side-by-side Assemblies
+http://msdn.microsoft.com/en-us/library/aa374029(VS.85).aspx
+"""
+import __builtin__
+if not "silent" in __builtin__.__dict__:
+    __builtin__.__dict__["silent"] = False  # True suppresses all messages from the assembly dependency code
+try:
+    import hashlib
+except ImportError, detail:
+    hashlib = None
+    if not silent:
+        print 'I: ... file hash calculation unavailable -', detail
+import os.path
+from glob import glob
+from xml.dom import Node, minidom
+from xml.dom.minidom import Document, Element
+
+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
+
+try:
+    import resource
+except ImportError, detail:
+    resource = None
+    if not silent:
+        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/"
+
+RT_MANIFEST = 24
+
+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
+
+def find_files(files, searchpath):
+    rfiles = []
+    for file_ in files:
+        fn = file_.find(searchpath)
+        if fn:
+            rfiles.append(fn)
+        else:
+            # if any of our files does not exist,
+            # the assembly is incomplete
+            return []
+    return rfiles
+
+
+class _Dummy:
+    pass
+
+    
+class File(resource.File if resource else _Dummy):
+
+    """ 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)
+        self.hashalg = hashalg.upper() if hashalg else None
+        if os.path.isfile(filename) and hashalg and hashlib:
+            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, 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():
+
+    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):
+        """ 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")
+        """
+        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
+        # public key token
+        # "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:
+        manifest.dependentAssemblies.append(Manifest(*args, *kwargs)) """
+        self.dependentAssemblies.append(Manifest(manifestVersion, 
+                                        noInheritable, noInherit, type_, name, 
+                                        language, processorArchitecture, 
+                                        version, publicKeyToken, description, 
+                                        requestedExecutionLevel, uiAccess, 
+                                        dependentAssemblies, files, 
+                                        comInterfaceExternalProxyStubs))
+    
+    def add_file(self, name="", hashalg="", hash="", comClasses=None, 
+                 typelibs=None, comInterfaceProxyStubs=None, 
+                 windowClasses=None):
+        """ Shortcut for:
+        manifest.files.append(File(*args, *kwargs)) """
+        self.files.append(File(name, hashalg, hash, comClasses, 
+                          typelibs, comInterfaceProxyStubs, windowClasses))
+    
+    def find_files(self):
+        """ Search shared and private assemblies and return a list of all 
+        related files if found. If any files are not found, return an empty 
+        list."""
+        if None in (self.processorArchitecture, self.name, self.publicKeyToken, 
+                    self.version):
+                        if not silent:
+                            print "W: Assembly metadata incomplete - file search not possible"
+                        return []
+        if not silent:
+            print "I: Searching for files of assembly %s..." % self.getid()
+        files = []
+        # search winsxs
+        winsxs = os.path.join(os.getenv('SystemRoot'), 'WinSxS')
+        if os.path.isdir(winsxs):
+            manifests = os.path.join(winsxs, "Manifests")
+            if os.path.isdir(manifests):
+                for manifestpth in glob(os.path.join(manifests, 
+                                                     self.getid() + "_*.manifest")):
+                    assemblynm = os.path.basename(os.path.splitext(manifestpth)[0])
+                    if not os.path.isfile(manifestpth):
+                        if not silent:
+                            print "W: No such file", manifestpth
+                        continue
+                    if not silent:
+                        print "I: Found manifest", manifestpth
+                    assemblydir = os.path.join(winsxs, assemblynm)
+                    if not os.path.isdir(assemblydir):
+                        if not silent:
+                            print "W: No such dir", assemblydir
+                        continue
+                    try:
+                        manifest = ManifestFromXMLFile(manifestpth)
+                    except Exception, exc:
+                        if not silent:
+                            print "E: Could not parse manifest", manifestpth
+                            print "E:", exc
+                        pass
+                    else:
+                        rfiles = find_files(self.files or manifest.files, assemblydir)
+                        if rfiles:
+                            files.append(manifestpth)
+                            files.extend(rfiles)
+                            return files
+                    break
+            elif not silent:
+                print "W: No such dir", manifests
+        elif not silent:
+            print "W: No such dir", winsxs
+        # search for private assemblies
+        if not self.filename:
+            return []
+        dirnm = os.path.dirname(self.filename)
+        # if embedded in a dll the assembly may have the same name as the 
+        # dll, so we need to make sure we don't search for *.dll.dll
+        assemblynm, ext = os.path.splitext(self.name)
+        if ext.lower() == ".dll":
+            # discard the extension
+            pass
+        else:
+            assemblynm = self.name
+        for path in [os.path.join(dirnm, self.language or "*"),
+                     os.path.join(dirnm, self.language or "*", assemblynm), 
+                     dirnm, 
+                     os.path.join(dirnm, assemblynm)]:
+            for ext in (".dll", ".manifest"):
+                # private assemblies can have the manifest either as 
+                # separate file or embedded in a DLL
+                manifestpth = os.path.join(path, assemblynm + ext)
+                if not os.path.isfile(manifestpth):
+                    if not silent:
+                        print "W: No such file", manifestpth
+                    continue
+                if not silent:
+                    print "I: Found manifest", manifestpth
+                try:
+                    if ext == ".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:
+                    rfiles = find_files(self.files or manifest.files, path)
+                    if not rfiles:
+                        return []
+                    files.append(manifestpth)
+                    files.extend(rfiles)
+                break
+            if not os.path.isfile(manifestpth):
+                for file_ in self.files:
+                    fn = os.path.join(path, file_.name)
+                    if os.path.exists(fn):
+                        # if any of our files does exist without the manifest 
+                        # in the same dir, the assembly is incomplete
+                        return []
+        return files
+
+    def getid(self):
+        return '%s_%s_%s_%s' % (self.processorArchitecture, 
+                                self.name, 
+                                self.publicKeyToken, 
+                                ".".join([str(i) 
+                                          for i in 
+                                          self.version]))
+
+    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")
+                self.name = assemblyIdentity.getA("name")
+                self.language = assemblyIdentity.getA("language")
+                self.processorArchitecture = assemblyIdentity.getA("processorArchitecture")
+                self.version = [int(i) for i in (assemblyIdentity.getA("version") or "0.0.0.0").split(".")]
+                self.publicKeyToken = assemblyIdentity.getA("publicKeyToken")
+            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"
+            for dependency in (rootElement.getCEByTN("dependency") if 
+                               rootElement.tagName != "assemblyBinding" else 
+                               [rootElement]):
+                for dependentAssembly in dependency.getCEByTN("dependentAssembly"):
+                    manifest = ManifestFromDOM(dependentAssembly)
+                    manifest.optional = (dependency.getA("optional") or "").lower() == "yes"
+                    self.dependentAssemblies.append(manifest)
+            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.load_dom(minidom.parse(filename_or_file))
+        if isinstance(filename_or_file, (str, unicode)):
+            self.filename = filename_or_file
+        else:
+            self.filename = filename_or_file.filename
+    
+    def parse_string(self, xmlstr):
+        """ Load manifest from XML string """
+        self.load_dom(minidom.parseString(xmlstr))
+    
+    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")
+            ppE.setA("apply", "yes" if self.applyPublisherPolicy else "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)
+            relE.setA("uiAccess", "true" if self.uiAccess else "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 to dll/exe file dstpath, as manifest 
+        resource """
+        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 manifest resource in 
+    dll/exe file """
+    res = GetManifestResources(filename, names, languages)
+    if res and res[RT_MANIFEST]:
+        while isinstance(res, dict) and res.keys():
+            res = res[res.keys()[0]]
+    if isinstance(res, dict):
+        raise InvalidManifestError("No manifest resource found in '%s'" % 
+                                   filename)
+    manifest = ManifestFromXML(res)
+    manifest.filename = filename
+    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 manifest file """
+    manifest = Manifest()
+    manifest.parse(filename_or_file)
+    return manifest
+
+def GetManifestResources(filename, names=None, languages=None):
+    """ Get manifest resources from dll/exe file """
+    return resource.GetResources(filename, [RT_MANIFEST], names, languages)
+
+def UpdateManifestResourcesFromXML(dstpath, xmlstr, names=None, 
+                                   languages=None):
+    """ Update or add manifest XML to dll/exe file dstpath, as manifest 
+    resource """
+    if not silent:
+        print "I: Updating manifest in", dstpath
+    name = 1 if dstpath.lower().endswith(".exe") else 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 file srcpath to dll/exe file 
+    dstpath, as manifest resource """
+    if not silent:
+        print "I: Updating manifest from", srcpath, "to", dstpath
+    name = 1 if dstpath.lower().endswith(".exe") else 2
+    resource.UpdateResourcesFromDataFile(dstpath, srcpath, RT_MANIFEST, 
+                                         names or [name], 
+                                         languages or [0, "*"])
+
+if __name__ == "__main__":
+    import sys
+    
+    dstpath = sys.argv[1]
+    srcpath = sys.argv[2]
+    UpdateManifestResourcesFromXMLFile(dstpath, srcpath)
\ No newline at end of file
Index: publisher.policy
===================================================================
--- publisher.policy	(revision 0)
+++ publisher.policy	(revision 0)
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+   <assemblyIdentity type="win32-policy" publicKeyToken="0000000000000000" name="policy.2.0.Microsoft.Windows.SampleAssembly" version="1.1.0.0" processorArchitecture="x86"/>
+   <dependency>
+      <dependentAssembly>
+         <assemblyIdentity type="win32" name="Microsoft.Windows.SampleAssembly"  processorArchitecture="x86" publicKeyToken="75e377300ab7b886"/>
+         <bindingRedirect oldVersion="2.0.0.0" newVersion="2.0.1.0"/>
+      </dependentAssembly>
+   </dependency>
+</assembly>
\ No newline at end of file
Index: resource.py
===================================================================
--- resource.py	(revision 0)
+++ resource.py	(revision 0)
@@ -0,0 +1,187 @@
+#!/usr/bin/env python
+
+import os.path
+import pywintypes
+import win32api
+
+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():
+    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 to 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]:
+                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 to 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 to 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 to 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)
\ No newline at end of file

