Расширение оболочки 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.
Любые мысли приветствуются!