# A sample implementation of IEmptyVolumeCache - see
# http://msdn2.microsoft.com/en-us/library/aa969271.aspx for an overview.
#
# * Execute this script to register the handler
# * Start the "disk cleanup" tool - look for "pywin32 compiled files"
import sys, os, stat, time
import pythoncom
from win32com.shell import shell,shellcon
from win32com.server.exception import COMException
import win32gui
import win32con
import winerror
# Our shell extension.
IEmptyVolumeCache_Methods = "Initialize GetSpaceUsed Purge ShowProperties Deactivate".split()
IEmptyVolumeCache2_Methods = "InitializeEx".split()
ico = os.path.join(sys.prefix, "py.ico")
if not os.path.isfile(ico):
ico = os.path.join(sys.prefix, "PC", "py.ico")
if not os.path.isfile(ico):
ico = None
print "Can't find python.ico - no icon will be installed"
class EmptyVolumeCache:
_reg_progid_ = "Python.ShellExtension.EmptyVolumeCache"
_reg_desc_ = "Python Sample Shell Extension (disk cleanup)"
_reg_clsid_ = "{EADD0777-2968-4c72-A999-2BF5F756259C}"
_reg_icon_ = ico
_com_interfaces_ = [shell.IID_IEmptyVolumeCache, shell.IID_IEmptyVolumeCache2]
_public_methods_ = IEmptyVolumeCache_Methods + IEmptyVolumeCache2_Methods
def Initialize(self, hkey, volume, flags):
# This should never be called, except on win98.
print "Unless we are on 98, Initialize call is unexpected!"
raise COMException(hresult=winerror.E_NOTIMPL)
def InitializeEx(self, hkey, volume, key_name, flags):
# Must return a tuple of:
# (display_name, description, button_name, flags)
print "InitializeEx called with", hkey, volume, key_name, flags
self.volume = volume
if flags & shellcon.EVCF_SETTINGSMODE:
print "We are being run on a schedule"
# In this case, "because there is no opportunity for user
# feedback, only those files that are extremely safe to clean up
# should be touched. You should ignore the initialization
# method's pcwszVolume parameter and clean unneeded files
# regardless of what drive they are on."
self.volume = None # flag as 'any disk will do'
elif flags & shellcon.EVCF_OUTOFDISKSPACE:
# In this case, "the handler should be aggressive about deleting
# files, even if it results in a performance loss. However, the
# handler obviously should not delete files that would cause an
# application to fail or the user to lose data."
print "We are being run as we are out of disk-space"
else:
# This case is not documented - we are guessing :)
print "We are being run because the user asked"
# For the sake of demo etc, we tell the shell to only show us when
# there are > 0 bytes available. Our GetSpaceUsed will check the
# volume, so will return 0 when we are on a different disk
flags = shellcon.EVCF_DONTSHOWIFZERO | shellcon.EVCF_ENABLEBYDEFAULT
return ("pywin32 compiled files",
"Removes all .pyc and .pyo files in the pywin32 directories",
"click me!",
flags
)
def _GetDirectories(self):
root_dir = os.path.abspath(os.path.dirname(os.path.dirname(win32gui.__file__)))
if self.volume is not None and \
not root_dir.lower().startswith(self.volume.lower()):
return []
return [os.path.join(root_dir, p)
for p in ('win32', 'win32com', 'win32comext', 'isapi')]
def _WalkCallback(self, arg, directory, files):
# callback function for os.path.walk - no need to be member, but its
# close to the callers :)
callback, total_list = arg
for file in files:
fqn = os.path.join(directory, file).lower()
if file.endswith(".pyc") or file.endswith(".pyo"):
# See below - total_list == None means delete files,
# otherwise it is a list where the result is stored. Its a
# list simply due to the way os.walk works - only [0] is
# referenced
if total_list is None:
print "Deleting file", fqn
# Should do callback.PurgeProcess - left as an exercise :)
os.remove(fqn)
else:
total_list[0] += os.stat(fqn)[stat.ST_SIZE]
# and callback to the tool
if callback:
# for the sake of seeing the progress bar do its thing,
# we take longer than we need to...
# ACK - for some bizarre reason this screws up the XP
# cleanup manager - clues welcome!! :)
## print "Looking in", directory, ", but waiting a while..."
## time.sleep(3)
# now do it
used = total_list[0]
callback.ScanProgress(used, 0, "Looking at " + fqn)
def GetSpaceUsed(self, callback):
total = [0] # See _WalkCallback above
try:
for d in self._GetDirectories():
os.path.walk(d, self._WalkCallback, (callback, total))
print "After looking in", d, "we have", total[0], "bytes"
except pythoncom.error, (hr, msg, exc, arg):
# This will be raised by the callback when the user selects 'cancel'.
if hr != winerror.E_ABORT:
raise # that's the documented error code!
print "User cancelled the operation"
return total[0]
def Purge(self, amt_to_free, callback):
print "Purging", amt_to_free, "bytes..."
# we ignore amt_to_free - it is generally what we returned for
# GetSpaceUsed
try:
for d in self._GetDirectories():
os.path.walk(d, self._WalkCallback, (callback, None))
except pythoncom.error, (hr, msg, exc, arg):
# This will be raised by the callback when the user selects 'cancel'.
if hr != winerror.E_ABORT:
raise # that's the documented error code!
print "User cancelled the operation"
def ShowProperties(self, hwnd):
raise COMException(hresult=winerror.E_NOTIMPL)
def Deactivate(self):
print "Deactivate called"
return 0
def DllRegisterServer():
# Also need to register specially in:
# HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches
# See link at top of file.
import _winreg
kn = r"Software\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\%s" \
% (EmptyVolumeCache._reg_desc_,)
key = _winreg.CreateKey(_winreg.HKEY_LOCAL_MACHINE, kn)
_winreg.SetValueEx(key, None, 0, _winreg.REG_SZ, EmptyVolumeCache._reg_clsid_)
def DllUnregisterServer():
import _winreg
kn = r"Software\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\%s" \
% (EmptyVolumeCache._reg_desc_,)
try:
key = _winreg.DeleteKey(_winreg.HKEY_LOCAL_MACHINE, kn)
except WindowsError, details:
import errno
if details.errno != errno.ENOENT:
raise
print EmptyVolumeCache._reg_desc_, "unregistration complete."
if __name__=='__main__':
from win32com.server import register
register.UseCommandLine(EmptyVolumeCache,
finalize_register = DllRegisterServer,
finalize_unregister = DllUnregisterServer)
|