Hi Mark,

I followed your suggestion and the SO threads touching on this area. (
http://stackoverflow.com/questions/4619701/python-64-bit-dll-com-server-registration-problem-on-64-bit-windows-7
)
Using py2exe I generated a dist folder with an exe of my overlay script.
Running this on a machine without Python installed shows all of the proper
log messages being generated and registry entry created for my overlay
handler, however there's no indication that explorer is loading and running
the overlay handler.

Following the SO examples I instead generated a COM DLL from the same
source. I can copy this dist directory and new DLL over to a machine
without Python and use regsrv32 to register the DLL without errors, but it
doesn't run the main method which is what sets the required registry
entries and calls UseCommandLine so it doesn't look like it's being
registered correctly at all.

Sorry for the newb questions, but I seem to have a big gap in my
understanding of the mechanism.

So, the way that I'm using it at least, py2exe is either generating an EXE
that will register correctly but not provide a COM object with the correct
environment for explorer to run the overlay handler, or a DLL that should
be self contained and functional but not provide the registration
functionality.

I've attached my primary script (Overlay Handler + logging and some helper
objects) as well as 2 py2exe setup scripts I've tried.

I'm hoping you can point out a huge flaw in my thinking, or an obvious step
that I'm missing in the registration process.

Thanks again for any help you can provide.


On Fri, Jan 8, 2016 at 3:30 PM, Alexander Jewell <balexjew...@gmail.com>
wrote:

> I created a fresh Python 2.7 environment with pywin and py2exe and
> stripped down my overlay handler to the basics to better troubleshoot.
>
> Running my script (command line or in PyCharm) registers everything
> correctly, just like before, but running the py2exe generated EXE seems to
> be registering but no overlays are appearing, which looks just like what
> happens when I restarted explorer.exe in my previous attempt (logged
> messages during registration, plus a manual check of the registry look good
> but nothing generated by IsMemberOf.
>
> I'm going to try generating a DLL and manually registering following the
> example here:
> http://stackoverflow.com/questions/4619701/python-64-bit-dll-com-server-registration-problem-on-64-bit-windows-7
>
> It looks like this example came out of a mail thread with you back in 2011
> so I'll try to follow the advice there and report back.
>
> Thanks again for the help Mark.
>
> On Wed, Jan 6, 2016 at 7:19 PM, Mark Hammond <mhamm...@skippinet.com.au>
> wrote:
>
>> On 7/01/2016 6:21 AM, Alexander Jewell wrote:
>>
>>> Unfortunately my end goal was to bundle the entire application as an exe
>>> with PyInstaller so that the end user does not actually have Python
>>> installed.
>>>
>>> Do you think it would be possible to package the overlay handler in such
>>> a way that explorer would not need access to an installed Python
>>> interpreter?
>>> Embedding(https://docs.python.org/3.4/extending/embedding.html)  seems
>>> to still require a Python interpreter but Cython sounds promising.
>>>
>>
>> I've used py2exe for this in the past and it works fine - you need to end
>> up with a stand-alone directory that functions independently of any
>> installed Python - py2exe bundles Python itself, pywin32, etc in just this
>> way. Last I tried though, it only worked with python 2.x
>>
>> Mark
>>
>>
>>> -Alex
>>>
>>> On Wed, Jan 6, 2016 at 5:37 AM, Mark Hammond <skippy.hamm...@gmail.com
>>> <mailto:skippy.hamm...@gmail.com>> wrote:
>>>
>>>     My guess is that the environment (eg, PATH, PYTHONPATH etc) for the
>>>     new explorer instance isn't setup correctly - how is the
>>>     explorer.exe process started when it *does* work? It's hard to
>>>     answer without more info, but Python ends up inside explorer.exe, so
>>>     the environment that explorer.exe starts with is important.
>>>
>>>     Mark
>>>
>>>     On 6/01/2016 8:29 AM, Alexander Jewell wrote:
>>>
>>>         So, thanks to the Tim Golden guide
>>>         <
>>> http://timgolden.me.uk/python/win32_how_do_i/add-my-own-icon-overlays.html
>>> >
>>>         (
>>> http://timgolden.me.uk/python/win32_how_do_i/add-my-own-icon-overlays.html
>>> )
>>>         and
>>>         other questions
>>>         <
>>> http://stackoverflow.com/questions/4775020/icon-overlay-issue-with-python#
>>> >
>>>         on
>>>         Stack Overflow I have a script that will show overlays on files
>>> and
>>>         folders based on their "state" similar to Tortoise SVN or
>>>         Dropbox. Works
>>>         great.
>>>
>>>         My problem is that once I restart the explorer.exe process or
>>> the OS
>>>         itself and open explorer there are no longer any overlays.
>>>
>>>         My first thought:
>>>
>>>           * Have the service that actually manages file state detect
>>> that no
>>>             requests have come in and just re-register the overlay
>>> handler
>>>
>>>         The problem here is that registration requires elevated
>>> permissions
>>>         which is acceptable on initial install of the application by the
>>> end
>>>         user but not every time they restart their machine.
>>>
>>>         Can anyone suggest what I might be missing here? I have the class
>>>         BaseOverlay and its children in a single .py file and register
>>>         from my
>>>         main app by calling this script using subprocess.
>>>
>>>
>>> |subprocess.check_call("C:\scripts\register_overlays.py",shell=True)|
>>>
>>>         Is Explorer not able to re-load the script as it is Python? Do I
>>>         need to
>>>         compile into a DLL or EXE? Would that change the registration
>>>         process?
>>>
>>>         Here's the registration call:
>>>
>>>         |win32com.server.register.UseCommandLine(BaseOverlay)|
>>>
>>>         Here's the class(simplified):
>>>
>>>         |classBaseOverlay:_reg_clsid_
>>>         ='{8D4B1C5D-F8AC-4FDA-961F-A0143CD97C41}'_reg_progid_
>>>         ='someoverlays'_reg_desc_ ='Icon Overlay Handler'_public_methods_
>>>         =['GetOverlayInfo','GetPriority','IsMemberOf']_com_interfaces_
>>>
>>> =[shell.IID_IShellIconOverlayIdentifier]defGetOverlayInfo(self):returnicon_path,0,shellcon.ISIOI_ICONFILE
>>>
>>> defGetPriority(self):return50defIsMemberOf(self,fname,attributes):returnwinerror.S_OK|
>>>
>>>         Thanks for any help you can provide,
>>>
>>>         Alex Jewell
>>>
>>>
>>>
>>>
>>>         _______________________________________________
>>>         python-win32 mailing list
>>>         python-win32@python.org <mailto:python-win32@python.org>
>>>         https://mail.python.org/mailman/listinfo/python-win32
>>>
>>>
>>>
>>
>
import logging
import subprocess
import _winreg as winreg
from logging.handlers import RotatingFileHandler

import winerror
from win32com.shell import shell, shellcon
import win32con
import win32com.server.register


class OSHelper(object):
    @staticmethod
    def restart_process(process_name):
        try:
            killed = subprocess.call(['taskkill', '/f', '/im', process_name])
        except subprocess.TimeoutExpired as e:
            print(e)
        except Exception as e:
            print(e)
            return False

        if killed == 0:
            try:
                subprocess.Popen([process_name])
            except Exception as e:
                print(e)
                return False

        return True


class OverlayLogger(object):
    log_file_name = r"C:\overlay_handlers\overlays.log"
    max_bytes = 5242880
    backup_count = 5
    log_level = logging.DEBUG
    log_format = "%(asctime)s : %(levelname)s : %(threadName)s : %(message)s"

    log = None

    def __init__(self):
        logger = logging.getLogger()
        for h in logger.handlers:
            logger.removeHandler(h)

        logger.setLevel(self.log_level)

        handler = RotatingFileHandler(self.log_file_name, maxBytes=self.max_bytes, backupCount=self.backup_count)
        handler.setLevel(self.log_level)
        formatter = logging.Formatter(self.log_format)
        handler.setFormatter(formatter)

        logger.addHandler(handler)
        logger.info("Overlay Logging Started")

        type(self).log = logger


class WindowsRegistry(object):
    def __init__(self, root, key):
        self._reg_root = root
        self._reg_key = key
        self.create_registry_key()

    def create_registry_key(self):
        try:
            winreg.CreateKey(self._reg_root, self._reg_key)
        except WindowsError as details:
            logging.getLogger(__name__).error(details)
            return False
        return True

    def set_registry_subkey(self, subkey, value):
        try:
            key = winreg.OpenKey(self._reg_root, self._reg_key, 0, winreg.KEY_WRITE)
            winreg.SetValueEx(key, subkey, 0, win32con.REG_SZ, value)
            winreg.CloseKey(key)
            return True
        except WindowsError as details:
            logging.getLogger(__name__).error(details)
            return False


class BaseOverlay:
    _reg_clsid_ = '{b8c0f456-1041-45b8-aa49-e56085a8d9fe}'
    _reg_progid_ = 'demo.overlays'
    _reg_desc_ = 'Icon Overlay Handler Example'
    _public_methods_ = ['GetOverlayInfo', 'GetPriority', 'IsMemberOf']
    _com_interfaces_ = [shell.IID_IShellIconOverlayIdentifier]

    handler_name = 'ExampleBaseOverlay'
    overlay_icon = r'C:\overlay_handlers\green_checkbox.ico'

    reg_base = winreg.HKEY_LOCAL_MACHINE
    reg_key_base = 'Software\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers'
    reg_key = "{0}\\{1}".format(reg_key_base, handler_name)

    def __init__(self):
        log = OverlayLogger().log
        log.debug("Initializing {0}".format(self.__class__.__name__))

    # Override to set an overlay icon
    # This is only called on initialization, changing the icon path later (e.g. based on the filename) will not work
    def GetOverlayInfo(self):
        return self.overlay_icon, 0, shellcon.ISIOI_ICONFILE

    # Override this method to set a higher or lower priority on this icon
    # Experiments haven't shown any effect, Windows seems to rely solely on the name of the Handler in the registry
    def GetPriority(self):
        return 50

    # Override this method in each handler to set the correct icon. In case of multiple overlays returning winerror.S_OK
    # Windows will pick the first handler in alphabetical order based on its name in the registry
    def IsMemberOf(self, fname, attributes):
        log = logging.getLogger(__name__).debug("Getting {0} overlay for file {1}".format(self.__class__.__name__, fname))
        if "py" in fname:
            return winerror.S_OK
        return winerror.E_FAIL

    @classmethod
    def register(cls):
        reg = WindowsRegistry(cls.reg_base, cls.reg_key)
        log = OverlayLogger().log
        log.info("Updating Registry Key for {0}...".format(cls.__name__))
        reg.set_registry_subkey(None, cls._reg_clsid_)
        log.info("Registering Overlay Handler {0}".format(cls.__name__))
        win32com.server.register.UseCommandLine(cls)


if __name__=='__main__':
    log = OverlayLogger().log
    log.info("Registration started")

    overlay_handlers = [BaseOverlay]

    for overlay_handler in overlay_handlers:
        overlay_handler.register()

    log.info("Registration complete")
    OSHelper.restart_process("explorer.exe")
    log.info("Finished restarting explorer")









from distutils.core import setup
import py2exe
import sys

try:
    import py2exe.mf as modulefinder
except ImportError:
    import modulefinder
import win32com

for p in win32com.__path__[1:]:
    modulefinder.AddPackagePath("win32com", p)
for extra in ["win32com.shell"]:
    __import__(extra)
    m = sys.modules[extra]
    for p in m.__path__[1:]:
        modulefinder.AddPackagePath(extra, p)


setup(console=['overlays.py'])
from distutils.core import setup
import py2exe
import sys

try:
    import py2exe.mf as modulefinder
except ImportError:
    import modulefinder
import win32com

for p in win32com.__path__[1:]:
    modulefinder.AddPackagePath("win32com", p)
for extra in ["win32com.shell"]:
    __import__(extra)
    m = sys.modules[extra]
    for p in m.__path__[1:]:
        modulefinder.AddPackagePath(extra, p)


class Target:
    def __init__(self, **kw):
        self.__dict__.update(kw)
        # for the version info resources (Properties -- Version)
        self.version = "1.0"
        self.company_name = "My name"
        self.copyright = "(C) 2011, My company"
        self.name = "heikki"


heikki_target = Target(
        description="my com server desc",
        # use module name for win32com exe/dll server
        modules=["overlays"],
        # specify which type of com server you want (exe and/or dll)
        create_exe=True,
        create_dll=False
)

setup(
        version="1.0",
        zipfile=None,
        description="my com server",
        name="heikki",
        author="Heikki",
        com_server=[heikki_target],
)
_______________________________________________
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32

Reply via email to