Расширение оболочки Windows DragDrop в Python

В проводнике Windows я хочу иметь возможность удалить любой файл (ы) поверх создаваемого мной типа файла и добавить его / их в мой файл.

Я ознакомился с демо-версиями win32com, и у нас нет ни одной, специально предназначенной для расширения оболочки, но это показалось достаточно простым.

Поэтому я начал писать тест, чтобы отловить событие drop для файлов.py. Я регистрируюсь + перезагружаюсь, но при перетаскивании визуально ничего не происходит, поэтому я определенно что-то пропустил.

(Я понимаю, что приведенный ниже код еще не будет выполнять операцию добавления.)

import pythoncom
from win32com.shell import shell, shellcon
import win32con
import winerror
import pywintypes
import win32traceutil


class ShellDropHandler:
    """
    Implements IDropTarget:
        https://docs.microsoft.com/en-us/windows/desktop/api/oleidl/nn-oleidl-idroptarget
    and registers it as file type extension:
        https://msdn.microsoft.com/en-us/library/cc144165(v=vs.85).aspx
    Also this interface is translated to python, here:
        http://timgolden.me.uk/pywin32-docs/PyIDropTarget.html
    """
    _name_ = __name__
    _reg_progid_ = "Python.ShellExtension."+_name_
    _reg_desc_ = "Shell DropHandler Extension ("+_name_+")"
    _reg_clsid_ = "{CED0336C-C9EE-4a7f-8D7F-C660393C399F}"
    _com_interfaces_ = [shell.IID_IShellExtInit, pythoncom.IID_IDropTarget]
    _public_methods_ = ['DragEnter','DragOver','DragLeave','Drop'] + shellcon.IShellExtInit_Methods

    def Initialize(self, folder, dataobj, hkey):
        print "init"
        self.dataobj = dataobj

    # --- now implement members of IDropTarget

    def DragEnter(self,dataObj,keyboardModifierState,point,dropEffectFlag):
        """
        Used to display drag-over effects.

        :param dataObj: the data being dragged over us in the form:
            https://docs.microsoft.com/en-us/windows/desktop/api/objidl/nn-objidl-idataobject
        :param keyboardModifierState: an or'ed combo of any of
            MK_CONTROL, MK_SHIFT, MK_ALT, MK_BUTTON, MK_LBUTTON, MK_MBUTTON, and MK_RBUTTON
        :param point: location, see
            https://msdn.microsoft.com/library/windows/hardware/ff569166
        :param dropEffectFlag: one of 
            DROPEFFECT_NONE, DROPEFFECT_COPY, DROPEFFECT_MOVE, DROPEFFECT_LINK, DROPEFFECT_SCROLL
            https://docs.microsoft.com/en-us/windows/desktop/com/dropeffect-constants

        :return: drop effect constant (see above)

        See also:
            dataObj format structure - https://docs.microsoft.com/en-us/windows/desktop/api/objidl/ns-objidl-tagformatetc
            standard format types - https://docs.microsoft.com/en-us/windows/desktop/dataxchg/standard-clipboard-formats
        """
        print "dragenter"
        return shellcon.DROPEFFECT_COPY

    def DragLeave(self):
        """
        Used to remove display of drag-over effects. 

        :return: hresult, I can't think of a reason for other than S_OK
        """
        print "dragleave"
        return winerror.S_OK

    def DragOver(self,keyboardModifierState,point,dropEffectFlag):
        """
        Used to update drag-over effects.

        :param keyboardModifierState: an or'ed combo of any of
            MK_CONTROL, MK_SHIFT, MK_ALT, MK_BUTTON, MK_LBUTTON, MK_MBUTTON, and MK_RBUTTON
        :param point: location, see
            https://msdn.microsoft.com/library/windows/hardware/ff569166
        :param dropEffectFlag: one of 
            DROPEFFECT_NONE, DROPEFFECT_COPY, DROPEFFECT_MOVE, DROPEFFECT_LINK, DROPEFFECT_SCROLL
            https://docs.microsoft.com/en-us/windows/desktop/com/dropeffect-constants
        :return: drop effect constant (see above)
        """
        print "dragOver"
        return shellcon.DROPEFFECT_COPY

    def Drop(self,dataObj,keyboardModifierState,point,dropEffectFlag):
        """
        This is the important one!

        :param dataObj: the data being dragged over us in the form:
            https://docs.microsoft.com/en-us/windows/desktop/api/objidl/nn-objidl-idataobject
        :param keyboardModifierState: an or'ed combo of any of
            MK_CONTROL, MK_SHIFT, MK_ALT, MK_BUTTON, MK_LBUTTON, MK_MBUTTON, and MK_RBUTTON
        :param point: location, see
            https://msdn.microsoft.com/library/windows/hardware/ff569166
        :param dropEffectFlag: one of 
            DROPEFFECT_NONE, DROPEFFECT_COPY, DROPEFFECT_MOVE, DROPEFFECT_LINK, DROPEFFECT_SCROLL
            https://docs.microsoft.com/en-us/windows/desktop/com/dropeffect-constants
        :return: hresult -- ususally S_OK
        """
        print "drop"
        # get the data out
        data=dataObj.GetData(format,medium)
        formatIn,formatOut=dataObj.GetCanonicalFormatEtc()
        if formatOut.cfFormat==win32con.CF_TEXT: # plain text
            text=data
        elif formatOut.cfFormat==win32con.CF_UNICODETEXT: # self explanatory, there
            text=data
        elif formatOut.cfFormat==win32con.CF_OEMTEXT: # multiline text
            text=data
        elif formatOut.cfFormat==win32con.CF_HDROP: # a list of files
            # data is a STGMEDIUM structture
            #    http://msdn.microsoft.com/en-us/library/windows/desktop/ms683812%28v=vs.85%29.aspx
            # that contains a dropfiles structure
            #    https://docs.microsoft.com/en-us/windows/desktop/api/shlobj_core/ns-shlobj_core-_dropfiles
            text=[]
            numFiles=shell.DragQueryFile(data.data_handle,-1)
            for i in range(numFiles):
                filename=shell.DragQueryFile(sm.data_handle,0)
                f=open(filename,'rb')
                text.append(f.read())
            text='\n'.join(text)
        else:
            return winerror.E_FAIL
        # now write the data to the target file
        print text
        # this also implies to "leave" an existing drag operation, therefore
        self.DragLeave()
        return winerror.S_OK


def DllRegisterServer():
    import _winreg
    key = _winreg.CreateKey(_winreg.HKEY_CLASSES_ROOT, "Python.File\\shellex")
    subkey = _winreg.CreateKey(key, "DropHandler")
    subkey2 = _winreg.CreateKey(subkey, ShellDropHandler._name_)
    _winreg.SetValueEx(subkey2, None, 0, _winreg.REG_SZ, ShellDropHandler._reg_clsid_)
    print ShellDropHandler._reg_desc_, "registration complete."


def DllUnregisterServer():
    import _winreg
    try:
        key = _winreg.DeleteKey(_winreg.HKEY_CLASSES_ROOT,
                                "Python.File\\shellex\\DropHandler\\"+ ShellDropHandler._name_)
    except WindowsError, details:
        import errno
        if details.errno != errno.ENOENT:
            raise
    print ShellDropHandler._reg_desc_, "unregistration complete."


if __name__ == '__main__':
    import os
    import sys
    import win32com.shell.shell as shell
    ASADMIN = 'asadmin'
    # run this elevated
    if sys.argv[-1] != ASADMIN:
        script = os.path.abspath(sys.argv[0])
        params = ' '.join([script] + sys.argv[1:] + [ASADMIN])
        shell.ShellExecuteEx(lpVerb='runas', lpFile=sys.executable, lpParameters=params)
        sys.exit(0)
    from win32com.server import register
    register.UseCommandLine(ShellDropHandler,
                   finalize_register = DllRegisterServer,
                   finalize_unregister = DllUnregisterServer)

И хотя я перезапустил проводник и попытался перетащить, единственное, что я получаю в win32traceutil, это:

# This window will display output from any programs that import win32traceutil
# win32com servers registered with '--debug' are in this category.
Registered: Python.ShellExtension.__main__ (for debugging)
Shell DropHandler Extension (__main__) registration complete.

Любые мысли приветствуются!

0 ответов

Другие вопросы по тегам