123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727 |
- # This is a port of the Vista SDK "FolderView" sample, and associated
- # notes at http://shellrevealed.com/blogs/shellblog/archive/2007/03/15/Shell-Namespace-Extension_3A00_-Creating-and-Using-the-System-Folder-View-Object.aspx
- # A key difference to shell_view.py is that this version uses the default
- # IShellView provided by the shell (via SHCreateShellFolderView) rather
- # than our own.
- # XXX - sadly, it doesn't work quite like the original sample. Oh well,
- # another day...
- import sys
- import os
- import pickle
- import random
- import win32api
- import winxpgui as win32gui # the needs vista, let alone xp!
- import win32con
- import winerror
- import commctrl
- import pythoncom
- from win32com.util import IIDToInterfaceName
- from win32com.server.exception import COMException
- from win32com.server.util import wrap as _wrap
- from win32com.server.util import NewEnum as _NewEnum
- from win32com.shell import shell, shellcon
- from win32com.axcontrol import axcontrol # IObjectWithSite
- from win32com.propsys import propsys
- GUID=pythoncom.MakeIID
- # If set, output spews to the win32traceutil collector...
- debug=0
- # wrap a python object in a COM pointer
- def wrap(ob, iid=None):
- return _wrap(ob, iid, useDispatcher=(debug>0))
- def NewEnum(seq, iid):
- return _NewEnum(seq, iid=iid, useDispatcher=(debug>0))
- # The sample makes heavy use of "string ids" (ie, integer IDs defined in .h
- # files, loaded at runtime from a (presumably localized) DLL. We cheat.
- _sids = {} # strings, indexed bystring_id,
- def LoadString(sid):
- return _sids[sid]
- # fn to create a unique string ID
- _last_ids = 0
- def _make_ids(s):
- global _last_ids
- _last_ids += 1
- _sids[_last_ids] = s
- return _last_ids
- # These strings are what the user sees and would be localized.
- # XXX - its possible that the shell might persist these values, so
- # this scheme wouldn't really be suitable in a real ap.
- IDS_UNSPECIFIED = _make_ids("unspecified")
- IDS_SMALL = _make_ids("small")
- IDS_MEDIUM = _make_ids("medium")
- IDS_LARGE = _make_ids("large")
- IDS_CIRCLE = _make_ids("circle")
- IDS_TRIANGLE = _make_ids("triangle")
- IDS_RECTANGLE = _make_ids("rectangle")
- IDS_POLYGON = _make_ids("polygon")
- IDS_DISPLAY = _make_ids("Display")
- IDS_DISPLAY_TT = _make_ids("Display the item.")
- IDS_SETTINGS = _make_ids("Settings")
- IDS_SETTING1 = _make_ids("Setting 1")
- IDS_SETTING2 = _make_ids("Setting 2")
- IDS_SETTING3 = _make_ids("Setting 3")
- IDS_SETTINGS_TT = _make_ids("Modify settings.")
- IDS_SETTING1_TT = _make_ids("Modify setting 1.")
- IDS_SETTING2_TT = _make_ids("Modify setting 2.")
- IDS_SETTING3_TT = _make_ids("Modify setting 3.")
- IDS_LESSTHAN5 = _make_ids("Less Than 5")
- IDS_5ORGREATER = _make_ids("Five or Greater")
- del _make_ids, _last_ids
- # Other misc resource stuff
- IDI_ICON1 = 100
- IDI_SETTINGS = 101
- # The sample defines a number of "category ids". Each one gets
- # its own GUID.
- CAT_GUID_NAME=GUID("{de094c9d-c65a-11dc-ba21-005056c00008}")
- CAT_GUID_SIZE=GUID("{de094c9e-c65a-11dc-ba21-005056c00008}")
- CAT_GUID_SIDES=GUID("{de094c9f-c65a-11dc-ba21-005056c00008}")
- CAT_GUID_LEVEL=GUID("{de094ca0-c65a-11dc-ba21-005056c00008}")
- # The next category guid is NOT based on a column (see
- # ViewCategoryProvider::EnumCategories()...)
- CAT_GUID_VALUE="{de094ca1-c65a-11dc-ba21-005056c00008}"
- GUID_Display=GUID("{4d6c2fdd-c689-11dc-ba21-005056c00008}")
- GUID_Settings=GUID("{4d6c2fde-c689-11dc-ba21-005056c00008}")
- GUID_Setting1=GUID("{4d6c2fdf-c689-11dc-ba21-005056c00008}")
- GUID_Setting2=GUID("{4d6c2fe0-c689-11dc-ba21-005056c00008}")
- GUID_Setting3=GUID("{4d6c2fe1-c689-11dc-ba21-005056c00008}")
- # Hrm - not sure what to do about the std keys.
- # Probably need a simple parser for propkey.h
- PKEY_ItemNameDisplay = ("{B725F130-47EF-101A-A5F1-02608C9EEBAC}", 10)
- PKEY_PropList_PreviewDetails = ("{C9944A21-A406-48FE-8225-AEC7E24C211B}", 8)
- # Not sure what the "3" here refers to - docs say PID_FIRST_USABLE (2) be
- # used. Presumably it is the 'propID' value in the .propdesc file!
- # note that the following GUIDs are also references in the .propdesc file
- PID_SOMETHING=3
- # These are our 'private' PKEYs
- # Col 2, name="Sample.AreaSize"
- PKEY_Sample_AreaSize=("{d6f5e341-c65c-11dc-ba21-005056c00008}", PID_SOMETHING)
- # Col 3, name="Sample.NumberOfSides"
- PKEY_Sample_NumberOfSides = ("{d6f5e342-c65c-11dc-ba21-005056c00008}", PID_SOMETHING)
- # Col 4, name="Sample.DirectoryLevel"
- PKEY_Sample_DirectoryLevel = ("{d6f5e343-c65c-11dc-ba21-005056c00008}", PID_SOMETHING)
- # We construct a PIDL from a pickle of a dict - turn it back into a
- # dict (we should *never* be called with a PIDL that the last elt is not
- # ours, so it is safe to assume we created it (assume->"ass" = "u" + "me" :)
- def pidl_to_item(pidl):
- # Note that only the *last* elt in the PIDL is certainly ours,
- # but it contains everything we need encoded as a dict.
- return pickle.loads(pidl[-1])
- # Start of msdn sample port...
- # make_item_enum replaces the sample's entire EnumIDList.cpp :)
- def make_item_enum(level, flags):
- pidls = []
- nums = """zero one two three four five size seven eight nine ten""".split()
- for i, name in enumerate(nums):
- size = random.randint(0,255)
- sides = 1
- while sides in [1,2]:
- sides = random.randint(0,5)
- is_folder = (i % 2) != 0
- # check the flags say to include it.
- # (This seems strange; if you ask the same folder for, but appear
- skip = False
- if not (flags & shellcon.SHCONTF_STORAGE):
- if is_folder:
- skip = not (flags & shellcon.SHCONTF_FOLDERS)
- else:
- skip = not (flags & shellcon.SHCONTF_NONFOLDERS)
- if not skip:
- data = dict(name=name, size=size, sides=sides, level=level, is_folder=is_folder)
- pidls.append([pickle.dumps(data)])
- return NewEnum(pidls, shell.IID_IEnumIDList)
- # start of Utils.cpp port
- def DisplayItem(shell_item_array, hwnd_parent=0):
- # Get the first ShellItem and display its name
- if shell_item_array is None:
- msg = "You must select something!"
- else:
- si = shell_item_array.GetItemAt(0)
- name = si.GetDisplayName(shellcon.SIGDN_NORMALDISPLAY)
- msg = "%d items selected, first is %r" % (shell_item_array.GetCount(), name)
- win32gui.MessageBox(hwnd_parent, msg, "Hello", win32con.MB_OK)
- # end of Utils.cpp port
- # start of sample's FVCommands.cpp port
- class Command:
- def __init__(self, guid, ids, ids_tt, idi, flags, callback, children):
- self.guid = guid; self.ids = ids; self.ids_tt = ids_tt
- self.idi = idi; self.flags = flags; self.callback = callback;
- self.children = children
- assert not children or isinstance(children[0], Command)
- def tuple(self):
- return self.guid, self.ids, self.ids_tt, self.idi, self.flags, self.callback, self.children
- # command callbacks - called back directly by us - see ExplorerCommand.Invoke
- def onDisplay(items, bindctx):
- DisplayItem(items)
- def onSetting1(items, bindctx):
- win32gui.MessageBox(0, LoadString(IDS_SETTING1), "Hello", win32con.MB_OK)
- def onSetting2(items, bindctx):
- win32gui.MessageBox(0, LoadString(IDS_SETTING2), "Hello", win32con.MB_OK)
- def onSetting3(items, bindctx):
- win32gui.MessageBox(0, LoadString(IDS_SETTING3), "Hello", win32con.MB_OK)
- taskSettings = [
- Command(GUID_Setting1, IDS_SETTING1, IDS_SETTING1_TT, IDI_SETTINGS, 0, onSetting1, None),
- Command(GUID_Setting2, IDS_SETTING2, IDS_SETTING2_TT, IDI_SETTINGS, 0, onSetting2, None),
- Command(GUID_Setting3, IDS_SETTING3, IDS_SETTING3_TT, IDI_SETTINGS, 0, onSetting3, None),
- ]
- tasks = [
- Command(GUID_Display, IDS_DISPLAY, IDS_DISPLAY_TT, IDI_ICON1, 0, onDisplay, None ),
- Command(GUID_Settings, IDS_SETTINGS, IDS_SETTINGS_TT, IDI_SETTINGS, shellcon.ECF_HASSUBCOMMANDS, None, taskSettings),
- ]
- class ExplorerCommandProvider:
- _com_interfaces_ = [shell.IID_IExplorerCommandProvider]
- _public_methods_ = shellcon.IExplorerCommandProvider_Methods
- def GetCommands(self, site, iid):
- items = [wrap(ExplorerCommand(t)) for t in tasks]
- return NewEnum(items, shell.IID_IEnumExplorerCommand)
- class ExplorerCommand:
- _com_interfaces_ = [shell.IID_IExplorerCommand]
- _public_methods_ = shellcon.IExplorerCommand_Methods
- def __init__(self, cmd):
- self.cmd = cmd
- # The sample also appears to ignore the pidl args!?
- def GetTitle(self, pidl):
- return LoadString(self.cmd.ids)
- def GetToolTip(self, pidl):
- return LoadString(self.cmd.ids_tt)
- def GetIcon(self, pidl):
- # Return a string of the usual "dll,resource_id" format
- # todo - just return any ".ico that comes with python" + ",0" :)
- raise COMException(hresult=winerror.E_NOTIMPL)
- def GetState(self, shell_items, slow_ok):
- return shellcon.ECS_ENABLED
- def GetFlags(self):
- return self.cmd.flags
- def GetCanonicalName(self):
- return self.cmd.guid
- def Invoke(self, items, bind_ctx):
- # If no function defined - just return S_OK
- if self.cmd.callback:
- self.cmd.callback(items, bind_ctx)
- else:
- print "No callback for command ", LoadString(self.cmd.ids)
- def EnumSubCommands(self):
- if not self.cmd.children:
- return None
- items = [wrap(ExplorerCommand(c))
- for c in self.cmd.children]
- return NewEnum(items, shell.IID_IEnumExplorerCommand)
- # end of sample's FVCommands.cpp port
- # start of sample's Category.cpp port
- class FolderViewCategorizer:
- _com_interfaces_ = [shell.IID_ICategorizer]
- _public_methods_ = shellcon.ICategorizer_Methods
- description = None # subclasses should set their own
- def __init__(self, shell_folder):
- self.sf = shell_folder
- # Determines the relative order of two items in their item identifier lists.
- def CompareCategory(self, flags, cat1, cat2):
- return cat1-cat2
- # Retrieves the name of a categorizer, such as "Group By Device
- # Type", that can be displayed in the user interface.
- def GetDescription(self, cch):
- return self.description
- # Retrieves information about a category, such as the default
- # display and the text to display in the user interface.
- def GetCategoryInfo(self, catid):
- # Note: this isn't always appropriate! See overrides below
- return 0, str(catid) # ????
- class FolderViewCategorizer_Name(FolderViewCategorizer):
- description = "Alphabetical"
- def GetCategory(self, pidls):
- ret = []
- for pidl in pidls:
- val = self.sf.GetDetailsEx(pidl, PKEY_ItemNameDisplay)
- ret.append(val)
- return ret
- class FolderViewCategorizer_Size(FolderViewCategorizer):
- description = "Group By Size"
- def GetCategory(self, pidls):
- ret = []
- for pidl in pidls:
- # Why don't we just get the size of the PIDL?
- val = self.sf.GetDetailsEx(pidl, PKEY_Sample_AreaSize)
- val = int(val) # it probably came in a VT_BSTR variant
- if val < 255//3:
- cid = IDS_SMALL
- elif val < 2 * 255 // 3:
- cid = IDS_MEDIUM
- else:
- cid = IDS_LARGE
- ret.append(cid)
- return ret
- def GetCategoryInfo(self, catid):
- return 0, LoadString(catid)
- class FolderViewCategorizer_Sides(FolderViewCategorizer):
- description = "Group By Sides"
- def GetCategory(self, pidls):
- ret = []
- for pidl in pidls:
- val = self.sf.GetDetailsEx(pidl, PKEY_ItemNameDisplay)
- if val==0:
- cid = IDS_CIRCLE
- elif val==3:
- cid = IDS_TRIANGLE
- elif val==4:
- cid = IDS_RECTANGLE
- elif val==5:
- cid = IDS_POLYGON
- else:
- cid = IDS_UNSPECIFIED
- ret.append(cid)
- return ret
- def GetCategoryInfo(self, catid):
- return 0, LoadString(catid)
- class FolderViewCategorizer_Value(FolderViewCategorizer):
- description = "Group By Value"
- def GetCategory(self, pidls):
- ret = []
- for pidl in pidls:
- val = self.sf.GetDetailsEx(pidl, PKEY_ItemNameDisplay)
- if val in "one two three four".split():
- ret.append(IDS_LESSTHAN5)
- else:
- ret.append(IDS_5ORGREATER)
- return ret
- def GetCategoryInfo(self, catid):
- return 0, LoadString(catid)
- class FolderViewCategorizer_Level(FolderViewCategorizer):
- description = "Group By Value"
- def GetCategory(self, pidls):
- return [self.sf.GetDetailsEx(pidl, PKEY_Sample_DirectoryLevel) for pidl in pidls]
- class ViewCategoryProvider:
- _com_interfaces_ = [shell.IID_ICategoryProvider]
- _public_methods_ = shellcon.ICategoryProvider_Methods
- def __init__(self, shell_folder):
- self.shell_folder = shell_folder
- def CanCategorizeOnSCID(self, pkey):
- return pkey in [PKEY_ItemNameDisplay, PKEY_Sample_AreaSize,
- PKEY_Sample_NumberOfSides, PKEY_Sample_DirectoryLevel]
- # Creates a category object.
- def CreateCategory(self, guid, iid):
- if iid == shell.IID_ICategorizer:
- if guid == CAT_GUID_NAME:
- klass = FolderViewCategorizer_Name
- elif guid == CAT_GUID_SIDES:
- klass = FolderViewCategorizer_Sides
- elif guid == CAT_GUID_SIZE:
- klass = FolderViewCategorizer_Size
- elif guid == CAT_GUID_VALUE:
- klass = FolderViewCategorizer_Value
- elif guid == CAT_GUID_LEVEL:
- klass = FolderViewCategorizer_Level
- else:
- raise COMException(hresult=winerror.E_INVALIDARG)
- return wrap(klass(self.shell_folder))
- raise COMException(hresult=winerror.E_NOINTERFACE)
- # Retrieves the enumerator for the categories.
- def EnumCategories(self):
- # These are additional categories beyond the columns
- seq = [CAT_GUID_VALUE]
- return NewEnum(seq, pythoncom.IID_IEnumGUID)
- # Retrieves a globally unique identifier (GUID) that represents
- # the categorizer to use for the specified Shell column.
- def GetCategoryForSCID(self, scid):
- if scid==PKEY_ItemNameDisplay:
- guid = CAT_GUID_NAME
- elif scid == PKEY_Sample_AreaSize:
- guid = CAT_GUID_SIZE
- elif scid == PKEY_Sample_NumberOfSides:
- guid = CAT_GUID_SIDES
- elif scid == PKEY_Sample_DirectoryLevel:
- guid = CAT_GUID_LEVEL
- elif scid == pythoncom.IID_NULL:
- # This can be called with a NULL
- # format ID. This will happen if you have a category,
- # not based on a column, that gets stored in the
- # property bag. When a return is made to this item,
- # it will call this function with a NULL format id.
- guid = CAT_GUID_VALUE
- else:
- raise COMException(hresult=winerror.E_INVALIDARG)
- return guid
- # Retrieves the name of the specified category. This is where
- # additional categories that appear under the column
- # related categories in the UI, get their display names.
- def GetCategoryName(self, guid, cch):
- if guid == CAT_GUID_VALUE:
- return "Value"
- raise COMException(hresult=winerror.E_FAIL)
- # Enables the folder to override the default grouping.
- def GetDefaultCategory(self):
- return CAT_GUID_LEVEL, (pythoncom.IID_NULL, 0)
- # end of sample's Category.cpp port
- # start of sample's ContextMenu.cpp port
- MENUVERB_DISPLAY = 0
- folderViewImplContextMenuIDs = [
- ("display", MENUVERB_DISPLAY, 0, ),
- ]
- class ContextMenu:
- _reg_progid_ = "Python.ShellFolderSample.ContextMenu"
- _reg_desc_ = "Python FolderView Context Menu"
- _reg_clsid_ = "{fed40039-021f-4011-87c5-6188b9979764}"
- _com_interfaces_ = [shell.IID_IShellExtInit, shell.IID_IContextMenu, axcontrol.IID_IObjectWithSite]
- _public_methods_ = shellcon.IContextMenu_Methods + shellcon.IShellExtInit_Methods + ["GetSite", "SetSite"]
- _context_menu_type_ = "PythonFolderViewSampleType"
- def __init__(self):
- self.site = None
- self.dataobj = None
- def Initialize(self, folder, dataobj, hkey):
- self.dataobj = dataobj
- def QueryContextMenu(self, hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags):
- s = LoadString(IDS_DISPLAY);
- win32gui.InsertMenu(hMenu, indexMenu, win32con.MF_BYPOSITION, idCmdFirst + MENUVERB_DISPLAY, s);
- indexMenu += 1
- # other verbs could go here...
- # indicate that we added one verb.
- return 1
- def InvokeCommand(self, ci):
- mask, hwnd, verb, params, dir, nShow, hotkey, hicon = ci
- # this seems very convuluted, but its what the sample does :)
- for verb_name, verb_id, flag in folderViewImplContextMenuIDs:
- if isinstance(verb, int):
- matches = verb==verb_id
- else:
- matches = verb==verb_name
- if matches:
- break
- else:
- assert False, ci # failed to find our ID
- if verb_id == MENUVERB_DISPLAY:
- sia = shell.SHCreateShellItemArrayFromDataObject(self.dataobj)
- DisplayItem(hwnd, sia)
- else:
- assert False, ci # Got some verb we weren't expecting?
- def GetCommandString(self, cmd, typ):
- raise COMException(hresult=winerror.E_NOTIMPL)
- def SetSite(self, site):
- self.site = site
- def GetSite(self, iid):
- return self.site
- # end of sample's ContextMenu.cpp port
- # start of sample's ShellFolder.cpp port
- class ShellFolder:
- _com_interfaces_ = [shell.IID_IBrowserFrameOptions,
- pythoncom.IID_IPersist,
- shell.IID_IPersistFolder,
- shell.IID_IPersistFolder2,
- shell.IID_IShellFolder,
- shell.IID_IShellFolder2,
- ]
- _public_methods_ = shellcon.IBrowserFrame_Methods + \
- shellcon.IPersistFolder2_Methods + \
- shellcon.IShellFolder2_Methods
- _reg_progid_ = "Python.ShellFolderSample.Folder2"
- _reg_desc_ = "Python FolderView sample"
- _reg_clsid_ = "{bb8c24ad-6aaa-4cec-ac5e-c429d5f57627}"
- max_levels = 5
- def __init__(self, level=0):
- self.current_level = level
- self.pidl = None # set when Initialize is called
- def ParseDisplayName(self, hwnd, reserved, displayName, attr):
- #print "ParseDisplayName", displayName
- raise COMException(hresult=winerror.E_NOTIMPL)
- def EnumObjects(self, hwndOwner, flags):
- if self.current_level >= self.max_levels:
- return None
- return make_item_enum(self.current_level+1, flags)
- def BindToObject(self, pidl, bc, iid):
- tail = pidl_to_item(pidl)
- # assert tail['is_folder'], "BindToObject should only be called on folders?"
- # *sob*
- # No point creating object just to have QI fail.
- if iid not in ShellFolder._com_interfaces_:
- raise COMException(hresult=winerror.E_NOTIMPL)
- child = ShellFolder(self.current_level+1)
- # hrmph - not sure what multiple PIDLs here mean?
- # assert len(pidl)==1, pidl # expecting just relative child PIDL
- child.Initialize(self.pidl + pidl)
- return wrap(child, iid)
- def BindToStorage(self, pidl, bc, iid):
- return self.BindToObject(pidl, bc, iid)
- def CompareIDs(self, param, id1, id2):
- return 0 # XXX - todo - implement this!
- def CreateViewObject(self, hwnd, iid):
- if iid == shell.IID_IShellView:
- com_folder = wrap(self)
- return shell.SHCreateShellFolderView(com_folder)
- elif iid == shell.IID_ICategoryProvider:
- return wrap(ViewCategoryProvider(self))
- elif iid == shell.IID_IContextMenu:
- ws = wrap(self)
- dcm = (hwnd, None, self.pidl, ws, None)
- return shell.SHCreateDefaultContextMenu(dcm, iid)
- elif iid == shell.IID_IExplorerCommandProvider:
- return wrap(ExplorerCommandProvider())
- else:
- raise COMException(hresult=winerror.E_NOINTERFACE)
- def GetAttributesOf(self, pidls, attrFlags):
- assert len(pidls)==1, "sample only expects 1 too!"
- assert len(pidls[0])==1, "expect relative pidls!"
- item = pidl_to_item(pidls[0])
- flags = 0
- if item['is_folder']:
- flags |= shellcon.SFGAO_FOLDER
- if item['level'] < self.max_levels:
- flags |= shellcon.SFGAO_HASSUBFOLDER
- return flags
- # Retrieves an OLE interface that can be used to carry out
- # actions on the specified file objects or folders.
- def GetUIObjectOf(self, hwndOwner, pidls, iid, inout):
- assert len(pidls)==1, "oops - arent expecting more than one!"
- assert len(pidls[0])==1, "assuming relative pidls!"
- item = pidl_to_item(pidls[0])
- if iid == shell.IID_IContextMenu:
- ws = wrap(self)
- dcm = (hwndOwner, None, self.pidl, ws, pidls)
- return shell.SHCreateDefaultContextMenu(dcm, iid)
- elif iid == shell.IID_IExtractIconW:
- dxi = shell.SHCreateDefaultExtractIcon()
- # dxi is IDefaultExtractIconInit
- if item['is_folder']:
- dxi.SetNormalIcon("shell32.dll", 4)
- else:
- dxi.SetNormalIcon("shell32.dll", 1)
- # just return the dxi - let Python QI for IID_IExtractIconW
- return dxi
- elif iid == pythoncom.IID_IDataObject:
- return shell.SHCreateDataObject(self.pidl, pidls, None, iid);
- elif iid == shell.IID_IQueryAssociations:
- elts = []
- if item['is_folder']:
- elts.append((shellcon.ASSOCCLASS_FOLDER, None, None))
- elts.append((shellcon.ASSOCCLASS_PROGID_STR, None, ContextMenu._context_menu_type_))
- return shell.AssocCreateForClasses(elts, iid)
- raise COMException(hresult=winerror.E_NOINTERFACE)
- # Retrieves the display name for the specified file object or subfolder.
- def GetDisplayNameOf(self, pidl, flags):
- item = pidl_to_item(pidl)
- if flags & shellcon.SHGDN_FORPARSING:
- if flags & shellcon.SHGDN_INFOLDER:
- return item['name']
- else:
- if flags & shellcon.SHGDN_FORADDRESSBAR:
- sigdn = shellcon.SIGDN_DESKTOPABSOLUTEEDITING
- else:
- sigdn = shellcon.SIGDN_DESKTOPABSOLUTEPARSING
- parent = shell.SHGetNameFromIDList(self.pidl, sigdn)
- return parent + "\\" + item['name']
- else:
- return item['name']
- def SetNameOf(self, hwndOwner, pidl, new_name, flags):
- raise COMException(hresult=winerror.E_NOTIMPL)
- def GetClassID(self):
- return self._reg_clsid_
- # IPersistFolder method
- def Initialize(self, pidl):
- self.pidl = pidl
- # IShellFolder2 methods
- def EnumSearches(self):
- raise COMException(hresult=winerror.E_NOINTERFACE)
- # Retrieves the default sorting and display columns.
- def GetDefaultColumn(self, dwres):
- # result is (sort, display)
- return 0, 0
- # Retrieves the default state for a specified column.
- def GetDefaultColumnState(self, iCol):
- if iCol < 3:
- return shellcon.SHCOLSTATE_ONBYDEFAULT | shellcon.SHCOLSTATE_TYPE_STR
- raise COMException(hresult=winerror.E_INVALIDARG)
- # Requests the GUID of the default search object for the folder.
- def GetDefaultSearchGUID(self):
- raise COMException(hresult=winerror.E_NOTIMPL)
- # Helper function for getting the display name for a column.
- def _GetColumnDisplayName(self, pidl, pkey):
- item = pidl_to_item(pidl)
- is_folder = item['is_folder']
- if pkey == PKEY_ItemNameDisplay:
- val = item['name']
- elif pkey == PKEY_Sample_AreaSize and not is_folder:
- val = "%d Sq. Ft." % item['size']
- elif pkey == PKEY_Sample_NumberOfSides and not is_folder:
- val = str(item['sides']) # not sure why str()
- elif pkey == PKEY_Sample_DirectoryLevel:
- val = str(item['level'])
- else:
- val = ''
- return val
- # Retrieves detailed information, identified by a
- # property set ID (FMTID) and property ID (PID),
- # on an item in a Shell folder.
- def GetDetailsEx(self, pidl, pkey):
- item = pidl_to_item(pidl)
- is_folder = item['is_folder']
- if not is_folder and pkey == PKEY_PropList_PreviewDetails:
- return "prop:Sample.AreaSize;Sample.NumberOfSides;Sample.DirectoryLevel"
- return self._GetColumnDisplayName(pidl, pkey)
- # Retrieves detailed information, identified by a
- # column index, on an item in a Shell folder.
- def GetDetailsOf(self, pidl, iCol):
- key = self.MapColumnToSCID(iCol);
- if pidl is None:
- data = [(commctrl.LVCFMT_LEFT, "Name"),
- (commctrl.LVCFMT_CENTER, "Size"),
- (commctrl.LVCFMT_CENTER, "Sides"),
- (commctrl.LVCFMT_CENTER, "Level"),]
- if iCol >= len(data):
- raise COMException(hresult=winerror.E_FAIL)
- fmt, val = data[iCol]
- else:
- fmt = 0 # ?
- val = self._GetColumnDisplayName(pidl, key)
- cxChar = 24
- return fmt, cxChar, val
- # Converts a column name to the appropriate
- # property set ID (FMTID) and property ID (PID).
- def MapColumnToSCID(self, iCol):
- data = [PKEY_ItemNameDisplay, PKEY_Sample_AreaSize,
- PKEY_Sample_NumberOfSides, PKEY_Sample_DirectoryLevel]
- if iCol >= len(data):
- raise COMException(hresult=winerror.E_FAIL)
- return data[iCol]
- # IPersistFolder2 methods
- # Retrieves the PIDLIST_ABSOLUTE for the folder object.
- def GetCurFolder(self):
- # The docs say this is OK, but I suspect its a problem in this case :)
- #assert self.pidl, "haven't been initialized?"
- return self.pidl
- # end of sample's ShellFolder.cpp port
- def get_schema_fname():
- me = win32api.GetFullPathName(__file__)
- sc = os.path.splitext(me)[0] + ".propdesc"
- assert os.path.isfile(sc), sc
- return sc
- def DllRegisterServer():
- import _winreg
- if sys.getwindowsversion()[0] < 6:
- print "This sample only works on Vista"
- sys.exit(1)
- key = _winreg.CreateKey(_winreg.HKEY_LOCAL_MACHINE,
- "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\" \
- "Explorer\\Desktop\\Namespace\\" + \
- ShellFolder._reg_clsid_)
- _winreg.SetValueEx(key, None, 0, _winreg.REG_SZ, ShellFolder._reg_desc_)
- # And special shell keys under our CLSID
- key = _winreg.CreateKey(_winreg.HKEY_CLASSES_ROOT,
- "CLSID\\" + ShellFolder._reg_clsid_ + "\\ShellFolder")
- # 'Attributes' is an int stored as a binary! use struct
- attr = shellcon.SFGAO_FOLDER | shellcon.SFGAO_HASSUBFOLDER | \
- shellcon.SFGAO_BROWSABLE
- import struct
- s = struct.pack("i", attr)
- _winreg.SetValueEx(key, "Attributes", 0, _winreg.REG_BINARY, s)
- # register the context menu handler under the FolderViewSampleType type.
- keypath = "%s\\shellex\\ContextMenuHandlers\\%s" % (ContextMenu._context_menu_type_, ContextMenu._reg_desc_)
- key = _winreg.CreateKey(_winreg.HKEY_CLASSES_ROOT, keypath)
- _winreg.SetValueEx(key, None, 0, _winreg.REG_SZ, ContextMenu._reg_clsid_)
- propsys.PSRegisterPropertySchema(get_schema_fname())
- print ShellFolder._reg_desc_, "registration complete."
- def DllUnregisterServer():
- import _winreg
- paths = [
- "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Desktop\\Namespace\\" + ShellFolder._reg_clsid_,
- "%s\\shellex\\ContextMenuHandlers\\%s" % (ContextMenu._context_menu_type_, ContextMenu._reg_desc_),
- ]
- for path in paths:
- try:
- _winreg.DeleteKey(_winreg.HKEY_LOCAL_MACHINE, path)
- except WindowsError, details:
- import errno
- if details.errno != errno.ENOENT:
- print "FAILED to remove %s: %s" % (path, details)
- propsys.PSUnregisterPropertySchema(get_schema_fname())
- print ShellFolder._reg_desc_, "unregistration complete."
- if __name__=='__main__':
- from win32com.server import register
- register.UseCommandLine(ShellFolder, ContextMenu,
- debug = debug,
- finalize_register = DllRegisterServer,
- finalize_unregister = DllUnregisterServer)
|