Explorer-Erweiterung (shell extension) mit Python

Die Problemstellung: Eine Verzeichnisstruktur soll automatisch erstellt werden, und zwar über einen Eintrag im Context-Menü des Explorers.
Die ganze Vorgeschichte ist hier.
Nun die Lösung in Python. Das ganze ist recht übersichtlich:

# -*- coding: utf-8 -*-
# Erstellt einen Eintrag im Context-Menü des Explorers: CCD-Verzeichnis erstellen
# bei Click wird im gewählten Verzeichnis eine CCD-Verzeichnisstrukur
# (bin, build, lib, source, resources) erstellt.
import os.path
import pythoncom
from win32com.shell import shell, shellcon
import win32gui
import win32con
IContextMenu_Methods = ["QueryContextMenu", "InvokeCommand", "GetCommandString"]
IShellExtInit_Methods = ["Initialize"]
# HKCR Key Affected object types
# * All files
#AllFileSystemObjects All regular files and file folders
# Folder All folders, virtual and filesystem
# Directory File folders
#Directory\Background Directory-Background (Folder is open, one clicks on the white background...)
# Drive Root folders of all system drives
# Network Entire network
# NetShare All network shares
TYPES = [
'Directory\\Background',
'Directory',
]
SUBKEY = 'CCD-Verzeichnis'
CCDFOLDERS = [
'source',
'bin',
'build',
'lib',
'resource',
]
def alertError(hwnd, exc):
win32gui.MessageBox(hwnd, str(exc), str(exc.__class__), win32con.MB_OK)
class ShellExtension:
_reg_progid_ = "CCD.Verzeichnisersteller.ShellExtension.ContextMenu"
_reg_desc_ = "CCD-Verzeichnis Shell Extension (context menu)"
_reg_clsid_ = "{5C664DC4-5ADA-4385-9DEB-EDB51320A668}"
_com_interfaces_ = [shell.IID_IShellExtInit, shell.IID_IContextMenu]
_public_methods_ = IContextMenu_Methods + IShellExtInit_Methods
def Initialize(self, folder, dataobj, hkey):
self.dataobj = dataobj
def QueryContextMenu(self, hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags):
try:
msg = 'CCD-Verzeichnis ersellen'
idCmd = idCmdFirst
items = []
if (uFlags & 0x000F) == shellcon.CMF_NORMAL:
items.append(msg)
elif uFlags & shellcon.CMF_VERBSONLY:
items.append(msg)
elif uFlags & shellcon.CMF_EXPLORE:
items.append(msg)
else:
pass
win32gui.InsertMenu(hMenu, indexMenu,
win32con.MF_SEPARATOR|win32con.MF_BYPOSITION,
0, None)
indexMenu += 1
for item in items:
win32gui.InsertMenu(hMenu, indexMenu,
win32con.MF_STRING|win32con.MF_BYPOSITION,
idCmd, item)
indexMenu += 1
idCmd += 1
win32gui.InsertMenu(hMenu, indexMenu,
win32con.MF_SEPARATOR|win32con.MF_BYPOSITION,
0, None)
indexMenu += 1
return idCmd-idCmdFirst
except Exception, e:
alertError(None, e)
raise
def InvokeCommand(self, ci):
mask, hwnd, verb, params, dir, nShow, hotkey, hicon = ci
try:
if self.dataobj is None:
#background-click
fname = dir
else:
#get Files from dragObject
files = self.getDragFiles()
if not files:
return
fname = files[0]
self.CreateCCDFolderStructure(hwnd, fname)
except Exception, e:
alertError(hwnd, e)
raise
return
def GetCommandString(self, cmd, typ):
return "Erstellt eine CCD-Verzeichnisstruktur"
def getDragFiles(self):
# Format the DataObject using a formatec, then get DragQueryFile from it...
format_etc = win32con.CF_HDROP, None, 1, -1, pythoncom.TYMED_HGLOBAL
sm = self.dataobj.GetData(format_etc)
num_files = shell.DragQueryFile(sm.data_handle, -1)
files = [shell.DragQueryFile(sm.data_handle, i) for i in range(num_files)]
return files
def CreateCCDFolderStructure(self, hwnd, folder):
ok = win32gui.MessageBox(hwnd, u'wirklich eine CCD-Struktur ersellen in\n' + folder,
u'Erstellen bestägen',
win32con.MB_YESNO|win32con.MB_ICONQUESTION|win32con.MB_TASKMODAL|win32con.MB_SETFOREGROUND)
if ok != win32con.IDYES:
return
if not os.path.isdir(folder):
alertError(hwnd, folder + u'\nist kein Verzeichnis. Abgebrochen.')
try:
for ccd in CCDFOLDERS:
dir = os.path.join(folder, ccd)
os.mkdir(dir)
except Exception, e:
alertError(hwnd, e)
raise
return
def DllRegisterServer():
import _winreg
for typ in TYPES:
key = _winreg.CreateKey(_winreg.HKEY_CLASSES_ROOT, "%s\\shellex" % typ)
subkey = _winreg.CreateKey(key, "ContextMenuHandlers")
subkey2 = _winreg.CreateKey(subkey, SUBKEY)
_winreg.SetValueEx(subkey2, None, 0, _winreg.REG_SZ, ShellExtension._reg_clsid_)
print ShellExtension._reg_desc_, "registration complete."
def DllUnregisterServer():
import _winreg
for typ in TYPES:
try:
key = _winreg.DeleteKey(_winreg.HKEY_CLASSES_ROOT, "%s\\shellex\\ContextMenuHandlers\\%s" % (typ, SUBKEY))
except WindowsError, details:
import errno
if details.errno != errno.ENOENT:
raise
print ShellExtension._reg_desc_, "unregistration complete."
def main(argv):
from win32com.server import register
register.UseCommandLine(ShellExtension,
finalize_register = DllRegisterServer,
finalize_unregister = DllUnregisterServer)
if __name__=='__main__':
import sys
main(sys.argv)
view raw CcdFoldersWin32.py hosted with ❤ by GitHub

Für die Verwendung muss Python installiert sein (Empfehlung: 2.6) und passend zu der Python-Version PyWin32 (Empfehlung: 214.win32-py2.6)

Die Python-Datei in ein „kluges“ Verzeichnis legen (z.B. %ProgramFiles%ccd‑verzeichnis) und in der registry registrieren per doppelklick, oder über die Eingabeaufforderung mit ccd-verzeichnis.py –register. Wenn das ganze wieder entfernt werden soll, so geht dies nur über die Eingabeaufforderung mit ccd-verzeichnis.py –unregister.

Viel Spass.

Edits:
– Das ganze gibt’s natürlich auch als Nautilus-Erweiterung
– Vorlage für den Code war eine Beispiel-Anwendung des PyWin32-Projektes