install pywin32-220
This commit is contained in:
parent
15d9012f5a
commit
93f7d415a0
581 changed files with 100203 additions and 0 deletions
4
Lib/site-packages/win32comext/shell/__init__.py
Normal file
4
Lib/site-packages/win32comext/shell/__init__.py
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# See if we have a special directory for the binaries (for developers)
|
||||
import win32com
|
||||
win32com.__PackageSupportBuildPath__(__path__)
|
||||
|
||||
53
Lib/site-packages/win32comext/shell/demos/IActiveDesktop.py
Normal file
53
Lib/site-packages/win32comext/shell/demos/IActiveDesktop.py
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
from win32com.shell import shell, shellcon
|
||||
import pythoncom
|
||||
import time
|
||||
website='http://sourceforge.net/projects/pywin32/'
|
||||
iad=pythoncom.CoCreateInstance(shell.CLSID_ActiveDesktop, None, pythoncom.CLSCTX_INPROC_SERVER, shell.IID_IActiveDesktop)
|
||||
opts=iad.GetDesktopItemOptions()
|
||||
if not (opts['ActiveDesktop'] and opts['EnableComponents']):
|
||||
print('Warning: Enabling Active Desktop')
|
||||
opts['ActiveDesktop']=True
|
||||
opts['EnableComponents']=True
|
||||
iad.SetDesktopItemOptions(opts)
|
||||
iad.ApplyChanges(0xffff)
|
||||
iad=None
|
||||
## apparently takes a short while for it to become active
|
||||
time.sleep(2)
|
||||
iad=pythoncom.CoCreateInstance(shell.CLSID_ActiveDesktop, None, pythoncom.CLSCTX_INPROC_SERVER, shell.IID_IActiveDesktop)
|
||||
|
||||
cnt=iad.GetDesktopItemCount()
|
||||
print('Count:', cnt)
|
||||
for i in range(cnt):
|
||||
print(iad.GetDesktopItem(i))
|
||||
|
||||
component={
|
||||
'ID': cnt+1,
|
||||
'ComponentType': shellcon.COMP_TYPE_WEBSITE,
|
||||
'CurItemState': shellcon.IS_NORMAL,
|
||||
'SubscribedURL': website,
|
||||
'Source' : website,
|
||||
'FriendlyName' : 'Pywin32 on SF',
|
||||
'Checked' : True, ## this controls whether item is currently displayed
|
||||
'NoScroll' : False,
|
||||
'Dirty': False,
|
||||
'Pos': {'Top':69, 'Left':69, 'Height': 400, 'Width': 400, 'zIndex': 1002,
|
||||
'CanResize': True, 'CanResizeX': True, 'CanResizeY': True,
|
||||
'PreferredLeftPercent': 0, 'PreferredTopPercent': 0},
|
||||
'Original': {'Top': 33, 'Left': 304, 'Height': 362, 'Width': 372, 'ItemState': shellcon.IS_NORMAL},
|
||||
'Restored': {'Top': 33, 'Left': 304, 'Height': 362, 'Width': 372, 'ItemState': shellcon.IS_NORMAL}
|
||||
}
|
||||
|
||||
|
||||
try:
|
||||
existing_item=iad.GetDesktopItemBySource(website)
|
||||
except pythoncom.com_error:
|
||||
pass
|
||||
else:
|
||||
iad.RemoveDesktopItem(existing_item)
|
||||
iad.ApplyChanges(0xffff)
|
||||
|
||||
iad.AddDesktopItem(component)
|
||||
iad.ApplyChanges(0xffff) ## need to check which AD_APPLY constants are actually needed
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
# Sample implementation of IFileOperationProgressSink that just prints
|
||||
# some basic info
|
||||
|
||||
import pythoncom
|
||||
from win32com.shell import shell, shellcon
|
||||
from win32com.server.policy import DesignatedWrapPolicy
|
||||
|
||||
tsf_flags = list((k,v) for k,v in list(shellcon.__dict__.items()) if k.startswith('TSF_'))
|
||||
def decode_flags(flags):
|
||||
if flags == 0:
|
||||
return 'TSF_NORMAL'
|
||||
flag_txt = ''
|
||||
for k,v in tsf_flags:
|
||||
if flags & v:
|
||||
if flag_txt:
|
||||
flag_txt = flag_txt + '|' + k
|
||||
else:
|
||||
flag_txt = k
|
||||
return flag_txt
|
||||
|
||||
class FileOperationProgressSink(DesignatedWrapPolicy):
|
||||
_com_interfaces_ = [shell.IID_IFileOperationProgressSink]
|
||||
_public_methods_ = [
|
||||
"StartOperations", "FinishOperations", "PreRenameItem", "PostRenameItem",
|
||||
"PreMoveItem", "PostMoveItem", "PreCopyItem", "PostCopyItem", "PreDeleteItem",
|
||||
"PostDeleteItem", "PreNewItem", "PostNewItem", "UpdateProgress",
|
||||
"ResetTimer", "PauseTimer", "ResumeTimer"
|
||||
]
|
||||
|
||||
def __init__(self):
|
||||
self._wrap_(self)
|
||||
|
||||
def StartOperations(self):
|
||||
print ('StartOperations')
|
||||
|
||||
def FinishOperations(self, Result):
|
||||
print(('FinishOperations: HRESULT ', Result))
|
||||
|
||||
def PreRenameItem(self, Flags, Item, NewName):
|
||||
print(('PreRenameItem: Renaming ' + Item.GetDisplayName(shellcon.SHGDN_FORPARSING) + \
|
||||
' to ' + NewName))
|
||||
|
||||
def PostRenameItem(self, Flags, Item, NewName, hrRename, NewlyCreated):
|
||||
if NewlyCreated is not None:
|
||||
newfile = NewlyCreated.GetDisplayName(shellcon.SHGDN_FORPARSING)
|
||||
else:
|
||||
newfile = 'not renamed, HRESULT ' + str(hrRename)
|
||||
print(('PostRenameItem: renamed ' + \
|
||||
Item.GetDisplayName(shellcon.SHGDN_FORPARSING) + ' to ' + newfile))
|
||||
|
||||
def PreMoveItem(self, Flags, Item, DestinationFolder, NewName):
|
||||
print(('PreMoveItem: Moving ' + \
|
||||
Item.GetDisplayName(shellcon.SHGDN_FORPARSING) + ' to ' + \
|
||||
DestinationFolder.GetDisplayName(shellcon.SHGDN_FORPARSING) + '\\' + str(NewName)))
|
||||
|
||||
def PostMoveItem(self, Flags, Item, DestinationFolder, NewName, hrMove, NewlyCreated):
|
||||
if NewlyCreated is not None:
|
||||
newfile = NewlyCreated.GetDisplayName(shellcon.SHGDN_FORPARSING)
|
||||
else:
|
||||
newfile = 'not copied, HRESULT ' + str(hrMove)
|
||||
print(('PostMoveItem: Moved ' + \
|
||||
Item.GetDisplayName(shellcon.SHGDN_FORPARSING) + ' to ' + newfile))
|
||||
|
||||
def PreCopyItem(self, Flags, Item, DestinationFolder, NewName):
|
||||
if not NewName:
|
||||
NewName = ''
|
||||
print(('PreCopyItem: Copying ' + \
|
||||
Item.GetDisplayName(shellcon.SHGDN_FORPARSING) + ' to ' + \
|
||||
DestinationFolder.GetDisplayName(shellcon.SHGDN_FORPARSING) + '\\' + NewName))
|
||||
print(('Flags: ', decode_flags(Flags)))
|
||||
|
||||
def PostCopyItem(self, Flags, Item, DestinationFolder, NewName, hrCopy, NewlyCreated):
|
||||
if NewlyCreated is not None:
|
||||
newfile = NewlyCreated.GetDisplayName(shellcon.SHGDN_FORPARSING)
|
||||
else:
|
||||
newfile = 'not copied, HRESULT ' + str(hrCopy)
|
||||
print(('PostCopyItem: Copied ' + Item.GetDisplayName(shellcon.SHGDN_FORPARSING) + ' to ' + newfile))
|
||||
print(('Flags: ', decode_flags(Flags)))
|
||||
|
||||
def PreDeleteItem(self, Flags, Item):
|
||||
print(('PreDeleteItem: Deleting ' + Item.GetDisplayName(shellcon.SHGDN_FORPARSING)))
|
||||
|
||||
def PostDeleteItem(self, Flags, Item, hrDelete, NewlyCreated):
|
||||
print(('PostDeleteItem: Deleted ' + Item.GetDisplayName(shellcon.SHGDN_FORPARSING)))
|
||||
if NewlyCreated:
|
||||
print((' Moved to recycle bin - ' + NewlyCreated.GetDisplayName(shellcon.SHGDN_FORPARSING)))
|
||||
|
||||
def PreNewItem(self, Flags, DestinationFolder, NewName):
|
||||
print(('PreNewItem: Creating ' + DestinationFolder.GetDisplayName(shellcon.SHGDN_FORPARSING) + '\\' + NewName))
|
||||
|
||||
def PostNewItem(self, Flags, DestinationFolder, NewName, TemplateName, FileAttributes, hrNew, NewItem):
|
||||
print(('PostNewItem: Created ' + NewItem.GetDisplayName(shellcon.SHGDN_FORPARSING)))
|
||||
|
||||
def UpdateProgress(self, WorkTotal, WorkSoFar):
|
||||
print(('UpdateProgress: ', WorkSoFar, WorkTotal))
|
||||
|
||||
def ResetTimer(self):
|
||||
print ('ResetTimer')
|
||||
|
||||
def PauseTimer(self):
|
||||
print ('PauseTimer')
|
||||
|
||||
def ResumeTimer(self):
|
||||
print ('ResumeTimer')
|
||||
|
||||
def CreateSink():
|
||||
return pythoncom.WrapObject(FileOperationProgressSink(), shell.IID_IFileOperationProgressSink)
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
from win32com.shell import shell, shellcon
|
||||
import pythoncom, win32api, os, sys
|
||||
|
||||
temp_dir=win32api.GetTempPath()
|
||||
linkname=win32api.GetTempFileName(temp_dir,'cmd')[0]
|
||||
os.remove(linkname)
|
||||
linkname+='.lnk'
|
||||
print('Link name:',linkname)
|
||||
ish=pythoncom.CoCreateInstance(shell.CLSID_ShellLink, None,
|
||||
pythoncom.CLSCTX_INPROC_SERVER, shell.IID_IShellLink)
|
||||
ish.SetPath(os.environ['cOMSPEC'])
|
||||
ish.SetWorkingDirectory(os.path.split(sys.executable)[0])
|
||||
ish.SetDescription('shortcut made by python')
|
||||
|
||||
console_props={
|
||||
'Signature':shellcon.NT_CONSOLE_PROPS_SIG,
|
||||
'InsertMode':True,
|
||||
'FullScreen':False, ## True looks like "DOS Mode" from win98!
|
||||
'FontFamily':54,
|
||||
'CursorSize':75, ## pct of character size
|
||||
'ScreenBufferSize':(152, 256),
|
||||
'AutoPosition':False,
|
||||
'FontSize':(4, 5),
|
||||
'FaceName':'',
|
||||
'HistoryBufferSize':32,
|
||||
'InputBufferSize':0,
|
||||
'QuickEdit':True,
|
||||
'Font':0, ## 0 should always be present, use win32console.GetNumberOfConsoleFonts() to find how many available
|
||||
'FillAttribute':7,
|
||||
'PopupFillAttribute':245,
|
||||
'WindowSize':(128, 32),
|
||||
'WindowOrigin':(0, 0),
|
||||
'FontWeight':400,
|
||||
'HistoryNoDup':False,
|
||||
'NumberOfHistoryBuffers':32,
|
||||
## ColorTable copied from a 'normal' console shortcut, with some obvious changes
|
||||
## These do not appear to be documented. From experimentation, [0] is background, [7] is foreground text
|
||||
'ColorTable':(255, 8388608, 32768, 8421376, 128, 8388736, 32896, 12582912,
|
||||
8421504, 16711680, 65280, 16776960, 255, 16711935, 65535, 16777215)
|
||||
}
|
||||
|
||||
ishdl=ish.QueryInterface(shell.IID_IShellLinkDataList)
|
||||
ishdl.AddDataBlock(console_props)
|
||||
ipf=ish.QueryInterface(pythoncom.IID_IPersistFile)
|
||||
ipf.Save(linkname,1)
|
||||
os.startfile(linkname)
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
# ITransferAdviseSink implementation template
|
||||
|
||||
import pythoncom
|
||||
from win32com.shell import shell, shellcon
|
||||
from win32com.server.policy import DesignatedWrapPolicy
|
||||
|
||||
tsf_flags = list((k,v) for k,v in list(shellcon.__dict__.items()) if k.startswith('TSF_'))
|
||||
def decode_flags(flags):
|
||||
if flags == 0:
|
||||
return 'TSF_NORMAL'
|
||||
flag_txt = ''
|
||||
for k,v in tsf_flags:
|
||||
if flags & v:
|
||||
if flag_txt:
|
||||
flag_txt = flag_txt + '|' + k
|
||||
else:
|
||||
flag_txt = k
|
||||
return flag_txt
|
||||
|
||||
TRANSFER_ADVISE_STATES = {}
|
||||
for k,v in list(shellcon.__dict__.items()):
|
||||
if k.startswith('TS_'):
|
||||
TRANSFER_ADVISE_STATES[v] = k
|
||||
|
||||
def decode_flags(flags):
|
||||
if flags == 0:
|
||||
return 'TSF_NORMAL'
|
||||
flag_txt = ''
|
||||
for k,v in tsf_flags:
|
||||
if flags & v:
|
||||
if flag_txt:
|
||||
flag_txt = flag_txt + '|' + k
|
||||
else:
|
||||
flag_txt = k
|
||||
return flag_txt
|
||||
|
||||
class TransferAdviseSink(DesignatedWrapPolicy):
|
||||
_com_interfaces_ = [shell.IID_ITransferAdviseSink]
|
||||
_public_methods_ = [
|
||||
"UpdateProgress", "UpdateTransferState", "ConfirmOverwrite",
|
||||
"ConfirmEncryptionLoss", "FileFailure", "SubStreamFailure", "PropertyFailure"
|
||||
]
|
||||
|
||||
def __init__(self):
|
||||
self._wrap_(self)
|
||||
|
||||
def UpdateProgress(self, SizeCurrent, SizeTotal, FilesCurrent, FilesTotal, FoldersCurrent, FoldersTotal):
|
||||
print ('UpdateProgress - processed so far:')
|
||||
print(('\t %s out of %s bytes' %(SizeCurrent, SizeTotal)))
|
||||
print(('\t %s out of %s files' %(FilesCurrent, FilesTotal)))
|
||||
print(('\t %s out of %s folders' %(FoldersCurrent, FoldersTotal)))
|
||||
|
||||
def UpdateTransferState(self, State):
|
||||
print(('Current state: ', TRANSFER_ADVISE_STATES.get(State, '??? Unknown state %s ???' %State)))
|
||||
|
||||
def ConfirmOverwrite(self, Source, DestParent , Name):
|
||||
print(('ConfirmOverwrite: ', Source.GetDisplayName(shellcon.SHGDN_FORPARSING),
|
||||
DestParent.GetDisplayName(shellcon.SHGDN_FORPARSING),
|
||||
Name))
|
||||
|
||||
def ConfirmEncryptionLoss(self, Source):
|
||||
print(('ConfirmEncryptionLoss:', Source.GetDisplayName(shellcon.SHGDN_FORPARSING)))
|
||||
|
||||
def FileFailure(self, Item, ItemName , Error):
|
||||
print(('FileFailure:', Item.GetDisplayName(shellcon.SHGDN_FORPARSING), ItemName))
|
||||
|
||||
def SubStreamFailure(self, Item, StreamName , Error):
|
||||
print ('SubStreamFailure:\n')
|
||||
|
||||
def PropertyFailure(self, Item, key , Error):
|
||||
print ('PropertyFailure:\n')
|
||||
|
||||
def CreateSink():
|
||||
return pythoncom.WrapObject(TransferAdviseSink(), shell.IID_ITransferAdviseSink, shell.IID_ITransferAdviseSink)
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
import pythoncom
|
||||
from win32com.shell import shell, shellcon
|
||||
import win32api, os
|
||||
|
||||
class InternetShortcut:
|
||||
def __init__( self ):
|
||||
self._base = pythoncom.CoCreateInstance(
|
||||
shell.CLSID_InternetShortcut, None,
|
||||
pythoncom.CLSCTX_INPROC_SERVER, shell.IID_IUniformResourceLocator
|
||||
)
|
||||
def load( self, filename ):
|
||||
# Get an IPersist interface
|
||||
# which allows save/restore of object to/from files
|
||||
self._base.QueryInterface( pythoncom.IID_IPersistFile ).Load( filename )
|
||||
def save( self, filename ):
|
||||
self._base.QueryInterface( pythoncom.IID_IPersistFile ).Save( filename, 1 )
|
||||
def __getattr__( self, name ):
|
||||
if name != "_base":
|
||||
return getattr( self._base, name )
|
||||
|
||||
temp_dir=win32api.GetTempPath()
|
||||
linkname=win32api.GetTempFileName(temp_dir, 'ish')[0]
|
||||
print('Link:',linkname)
|
||||
os.remove(linkname)
|
||||
linkname+='.url'
|
||||
|
||||
ish=InternetShortcut()
|
||||
ish.SetURL('http://sourceforge.net/projects/pywin32/')
|
||||
ish.save(linkname)
|
||||
|
||||
## IUniformResourceLocator also give access to IPropertySetStorage
|
||||
pss=ish.QueryInterface(pythoncom.IID_IPropertySetStorage)
|
||||
ps=pss.Open(shell.FMTID_InternetSite)
|
||||
property_ids=[(k,v) for k,v in shellcon.__dict__.items() if k.startswith('PID_INTSITE_')]
|
||||
for pname, pval in property_ids:
|
||||
print(pname, ps.ReadMultiple((pval,))[0])
|
||||
|
||||
ps=pss.Open(shell.FMTID_Intshcut)
|
||||
property_ids=[(k,v) for k,v in shellcon.__dict__.items() if k.startswith('PID_IS_')]
|
||||
for pname, pval in property_ids:
|
||||
print(pname, ps.ReadMultiple((pval,))[0])
|
||||
|
||||
new_sh=InternetShortcut()
|
||||
new_sh.load(linkname)
|
||||
new_sh.InvokeCommand('Open')
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
# A couple of samples using SHBrowseForFolder
|
||||
|
||||
import sys, os
|
||||
from win32com.shell import shell, shellcon
|
||||
import win32gui
|
||||
|
||||
# A callback procedure - called by SHBrowseForFolder
|
||||
def BrowseCallbackProc(hwnd, msg, lp, data):
|
||||
if msg== shellcon.BFFM_INITIALIZED:
|
||||
win32gui.SendMessage(hwnd, shellcon.BFFM_SETSELECTION, 1, data)
|
||||
elif msg == shellcon.BFFM_SELCHANGED:
|
||||
# Set the status text of the
|
||||
# For this message, 'lp' is the address of the PIDL.
|
||||
pidl = shell.AddressAsPIDL(lp)
|
||||
try:
|
||||
path = shell.SHGetPathFromIDList(pidl)
|
||||
win32gui.SendMessage(hwnd, shellcon.BFFM_SETSTATUSTEXT, 0, path)
|
||||
except shell.error:
|
||||
# No path for this PIDL
|
||||
pass
|
||||
|
||||
if __name__=='__main__':
|
||||
# Demonstrate a dialog with the cwd selected as the default - this
|
||||
# must be done via a callback function.
|
||||
flags = shellcon.BIF_STATUSTEXT
|
||||
shell.SHBrowseForFolder(0, # parent HWND
|
||||
None, # root PIDL.
|
||||
"Default of %s" % os.getcwd(), # title
|
||||
flags, # flags
|
||||
BrowseCallbackProc, # callback function
|
||||
os.getcwd() # 'data' param for the callback
|
||||
)
|
||||
# Browse from this directory down only.
|
||||
# Get the PIDL for the cwd.
|
||||
desktop = shell.SHGetDesktopFolder()
|
||||
cb, pidl, extra = desktop.ParseDisplayName(0, None, os.getcwd())
|
||||
shell.SHBrowseForFolder(0, # parent HWND
|
||||
pidl, # root PIDL.
|
||||
"From %s down only" % os.getcwd(), # title
|
||||
)
|
||||
52
Lib/site-packages/win32comext/shell/demos/create_link.py
Normal file
52
Lib/site-packages/win32comext/shell/demos/create_link.py
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
# link.py
|
||||
# From a demo by Mark Hammond, corrupted by Mike Fletcher
|
||||
# (and re-corrupted by Mark Hammond :-)
|
||||
from win32com.shell import shell
|
||||
import pythoncom, os
|
||||
|
||||
class PyShortcut:
|
||||
def __init__( self ):
|
||||
self._base = pythoncom.CoCreateInstance(
|
||||
shell.CLSID_ShellLink, None,
|
||||
pythoncom.CLSCTX_INPROC_SERVER, shell.IID_IShellLink
|
||||
)
|
||||
def load( self, filename ):
|
||||
# Get an IPersist interface
|
||||
# which allows save/restore of object to/from files
|
||||
self._base.QueryInterface( pythoncom.IID_IPersistFile ).Load( filename )
|
||||
def save( self, filename ):
|
||||
self._base.QueryInterface( pythoncom.IID_IPersistFile ).Save( filename, 0 )
|
||||
def __getattr__( self, name ):
|
||||
if name != "_base":
|
||||
return getattr( self._base, name )
|
||||
|
||||
if __name__=='__main__':
|
||||
import sys
|
||||
if len(sys.argv)<2:
|
||||
print("Usage: %s LinkFile [path [, args[, description[, working_dir]]]]\n\nIf LinkFile does not exist, it will be created using the other args")
|
||||
sys.exit(1)
|
||||
file = sys.argv[1]
|
||||
shortcut = PyShortcut()
|
||||
if os.path.exists( file ):
|
||||
# load and dump info from file...
|
||||
shortcut.load( file )
|
||||
# now print data...
|
||||
print('Shortcut in file %s to file:\n\t%s\nArguments:\n\t%s\nDescription:\n\t%s\nWorking Directory:\n\t%s\nItemIDs:\n\t<skipped>'%(
|
||||
file,
|
||||
shortcut.GetPath(shell.SLGP_SHORTPATH)[0],
|
||||
shortcut.GetArguments(),
|
||||
shortcut.GetDescription(),
|
||||
shortcut.GetWorkingDirectory(),
|
||||
#shortcut.GetIDList(),
|
||||
))
|
||||
else:
|
||||
if len(sys.argv) <3:
|
||||
print("Link file does not exist\nYou must supply the path, args, description and working_dir as args")
|
||||
sys.exit(1)
|
||||
# create the shortcut using rest of args...
|
||||
data = map( None, sys.argv[2:], ("SetPath", "SetArguments", "SetDescription", "SetWorkingDirectory") )
|
||||
for value, function in data:
|
||||
if value and function:
|
||||
# call function on each non-null value
|
||||
getattr( shortcut, function)( value )
|
||||
shortcut.save( file )
|
||||
47
Lib/site-packages/win32comext/shell/demos/dump_link.py
Normal file
47
Lib/site-packages/win32comext/shell/demos/dump_link.py
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
# dump_link.py - dumps information about shell shortcuts
|
||||
#
|
||||
import sys, os
|
||||
from win32com.shell import shell, shellcon
|
||||
import pythoncom
|
||||
import glob
|
||||
from win32com.storagecon import *
|
||||
|
||||
def DumpLink(fname):
|
||||
shellLink = pythoncom.CoCreateInstance(shell.CLSID_ShellLink, None, pythoncom.CLSCTX_INPROC_SERVER, shell.IID_IShellLink)
|
||||
persistFile = shellLink.QueryInterface(pythoncom.IID_IPersistFile)
|
||||
persistFile.Load(fname,STGM_READ)
|
||||
shellLink.Resolve(0, shell.SLR_ANY_MATCH | shell.SLR_NO_UI)
|
||||
fname, findData = shellLink.GetPath(0)
|
||||
print("Filename:", fname, ", UNC=", shellLink.GetPath(shell.SLGP_UNCPRIORITY)[0])
|
||||
print("Description:", shellLink.GetDescription())
|
||||
print("Working Directory:", shellLink.GetWorkingDirectory())
|
||||
print("Icon:", shellLink.GetIconLocation())
|
||||
|
||||
def FavDumper(nothing, path, names):
|
||||
# called by os.path.walk
|
||||
for name in names:
|
||||
print(name, end=' ')
|
||||
try:
|
||||
DumpLink(name)
|
||||
except pythoncom.com_error:
|
||||
print(" - not a link")
|
||||
|
||||
def DumpFavorites():
|
||||
favfold = str(shell.SHGetSpecialFolderPath(0, shellcon.CSIDL_FAVORITES))
|
||||
print("Your favourites are at", favfold)
|
||||
os.path.walk(favfold, FavDumper, None)
|
||||
|
||||
if __name__=='__main__':
|
||||
if len(sys.argv)>1:
|
||||
for fspec in sys.argv[1:]:
|
||||
files = glob.glob(fspec)
|
||||
if files:
|
||||
for file in files:
|
||||
print(file)
|
||||
DumpLink(file)
|
||||
print()
|
||||
else:
|
||||
print("Can not find", fspec)
|
||||
else:
|
||||
print("Dumping your favorites folder!")
|
||||
DumpFavorites()
|
||||
117
Lib/site-packages/win32comext/shell/demos/explorer_browser.py
Normal file
117
Lib/site-packages/win32comext/shell/demos/explorer_browser.py
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
# A sample of using Vista's IExplorerBrowser interfaces...
|
||||
# Currently doesn't quite work:
|
||||
# * CPU sits at 100% while running.
|
||||
|
||||
import sys
|
||||
import pythoncom
|
||||
from win32com.shell import shell, shellcon
|
||||
import win32gui, win32con, win32api
|
||||
from win32com.server.util import wrap, unwrap
|
||||
|
||||
# event handler for the browser.
|
||||
IExplorerBrowserEvents_Methods = """OnNavigationComplete OnNavigationFailed
|
||||
OnNavigationPending OnViewCreated""".split()
|
||||
class EventHandler:
|
||||
_com_interfaces_ = [shell.IID_IExplorerBrowserEvents]
|
||||
_public_methods_ = IExplorerBrowserEvents_Methods
|
||||
|
||||
def OnNavigationComplete(self, pidl):
|
||||
print("OnNavComplete", pidl)
|
||||
|
||||
def OnNavigationFailed(self, pidl):
|
||||
print("OnNavigationFailed", pidl)
|
||||
|
||||
def OnNavigationPending(self, pidl):
|
||||
print("OnNavigationPending", pidl)
|
||||
|
||||
def OnViewCreated(self, view):
|
||||
print("OnViewCreated", view)
|
||||
# And if our demo view has been registered, it may well
|
||||
# be that view!
|
||||
try:
|
||||
pyview = unwrap(view)
|
||||
print("and look - its a Python implemented view!", pyview)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
class MainWindow:
|
||||
def __init__(self):
|
||||
message_map = {
|
||||
win32con.WM_DESTROY: self.OnDestroy,
|
||||
win32con.WM_COMMAND: self.OnCommand,
|
||||
win32con.WM_SIZE: self.OnSize,
|
||||
}
|
||||
# Register the Window class.
|
||||
wc = win32gui.WNDCLASS()
|
||||
hinst = wc.hInstance = win32api.GetModuleHandle(None)
|
||||
wc.lpszClassName = "test_explorer_browser"
|
||||
wc.lpfnWndProc = message_map # could also specify a wndproc.
|
||||
classAtom = win32gui.RegisterClass(wc)
|
||||
# Create the Window.
|
||||
style = win32con.WS_OVERLAPPEDWINDOW | win32con.WS_VISIBLE
|
||||
self.hwnd = win32gui.CreateWindow( classAtom, "Python IExplorerBrowser demo", style, \
|
||||
0, 0, win32con.CW_USEDEFAULT, win32con.CW_USEDEFAULT, \
|
||||
0, 0, hinst, None)
|
||||
eb = pythoncom.CoCreateInstance(shellcon.CLSID_ExplorerBrowser, None, pythoncom.CLSCTX_ALL, shell.IID_IExplorerBrowser)
|
||||
# as per MSDN docs, hook up events early
|
||||
self.event_cookie = eb.Advise(wrap(EventHandler()))
|
||||
|
||||
eb.SetOptions(shellcon.EBO_SHOWFRAMES)
|
||||
rect = win32gui.GetClientRect(self.hwnd)
|
||||
# Set the flags such that the folders autoarrange and non web view is presented
|
||||
flags = (shellcon.FVM_LIST, shellcon.FWF_AUTOARRANGE | shellcon.FWF_NOWEBVIEW)
|
||||
eb.Initialize(self.hwnd, rect, (0, shellcon.FVM_DETAILS))
|
||||
if len(sys.argv)==2:
|
||||
# If an arg was specified, ask the desktop parse it.
|
||||
# You can pass anything explorer accepts as its '/e' argument -
|
||||
# eg, "::{guid}\::{guid}" etc.
|
||||
# "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}" is "My Computer"
|
||||
pidl = shell.SHGetDesktopFolder().ParseDisplayName(0, None, sys.argv[1])[1]
|
||||
else:
|
||||
# And start browsing at the root of the namespace.
|
||||
pidl = []
|
||||
eb.BrowseToIDList(pidl, shellcon.SBSP_ABSOLUTE)
|
||||
# and for some reason the "Folder" view in the navigator pane doesn't
|
||||
# magically synchronize itself - so let's do that ourself.
|
||||
# Get the tree control.
|
||||
sp = eb.QueryInterface(pythoncom.IID_IServiceProvider)
|
||||
try:
|
||||
tree = sp.QueryService(shell.IID_INameSpaceTreeControl,
|
||||
shell.IID_INameSpaceTreeControl)
|
||||
except pythoncom.com_error as exc:
|
||||
# this should really only fail if no "nav" frame exists...
|
||||
print("Strange - failed to get the tree control even though " \
|
||||
"we asked for a EBO_SHOWFRAMES")
|
||||
print(exc)
|
||||
else:
|
||||
# get the IShellItem for the selection.
|
||||
si = shell.SHCreateItemFromIDList(pidl, shell.IID_IShellItem)
|
||||
# set it to selected.
|
||||
tree.SetItemState(si, shellcon.NSTCIS_SELECTED, shellcon.NSTCIS_SELECTED)
|
||||
|
||||
#eb.FillFromObject(None, shellcon.EBF_NODROPTARGET);
|
||||
#eb.SetEmptyText("No known folders yet...");
|
||||
self.eb = eb
|
||||
|
||||
def OnCommand(self, hwnd, msg, wparam, lparam):
|
||||
pass
|
||||
|
||||
def OnDestroy(self, hwnd, msg, wparam, lparam):
|
||||
print("tearing down ExplorerBrowser...")
|
||||
self.eb.Unadvise(self.event_cookie)
|
||||
self.eb.Destroy()
|
||||
self.eb = None
|
||||
print("shutting down app...")
|
||||
win32gui.PostQuitMessage(0)
|
||||
|
||||
def OnSize(self, hwnd, msg, wparam, lparam):
|
||||
x = win32api.LOWORD(lparam)
|
||||
y = win32api.HIWORD(lparam)
|
||||
self.eb.SetRect(None, (0, 0, x, y))
|
||||
|
||||
def main():
|
||||
w=MainWindow()
|
||||
win32gui.PumpMessages()
|
||||
|
||||
if __name__=='__main__':
|
||||
main()
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
# A sample shell column provider
|
||||
# Mainly ported from MSDN article:
|
||||
# Using Shell Column Handlers for Detailed File Information,
|
||||
# Raymond Chen, Microsoft Corporation, February 2000
|
||||
#
|
||||
# To demostrate:
|
||||
# * Execute this script to register the namespace.
|
||||
# * Open Windows Explorer
|
||||
# * Right-click an explorer column header - select "More"
|
||||
# * Locate column 'pyc size' or 'pyo size', and add it to the view.
|
||||
# This handler is providing that column data.
|
||||
import sys, os, stat
|
||||
import pythoncom
|
||||
from win32com.shell import shell, shellcon
|
||||
import commctrl
|
||||
import winerror
|
||||
from win32com.server.util import wrap
|
||||
from pywintypes import IID
|
||||
|
||||
IPersist_Methods = ["GetClassID"]
|
||||
IColumnProvider_Methods = IPersist_Methods + \
|
||||
["Initialize", "GetColumnInfo", "GetItemData"]
|
||||
|
||||
class ColumnProvider:
|
||||
_reg_progid_ = "Python.ShellExtension.ColumnProvider"
|
||||
_reg_desc_ = "Python Sample Shell Extension (Column Provider)"
|
||||
_reg_clsid_ = IID("{0F14101A-E05E-4070-BD54-83DFA58C3D68}")
|
||||
_com_interfaces_ = [pythoncom.IID_IPersist,
|
||||
shell.IID_IColumnProvider,
|
||||
]
|
||||
_public_methods_ = IColumnProvider_Methods
|
||||
# IPersist
|
||||
def GetClassID(self):
|
||||
return self._reg_clsid_
|
||||
# IColumnProvider
|
||||
def Initialize(self, colInit):
|
||||
flags, reserved, name = colInit
|
||||
print("ColumnProvider initializing for file", name)
|
||||
def GetColumnInfo(self, index):
|
||||
# We support exactly 2 columns - 'pyc size' and 'pyo size'
|
||||
if index in [0,1]:
|
||||
# As per the MSDN sample, use our CLSID as the fmtid
|
||||
if index==0:
|
||||
ext = ".pyc"
|
||||
else:
|
||||
ext = ".pyo"
|
||||
title = ext + " size"
|
||||
description = "Size of compiled %s file" % ext
|
||||
col_id = (self._reg_clsid_, # fmtid
|
||||
index) # pid
|
||||
col_info = (
|
||||
col_id, # scid
|
||||
pythoncom.VT_I4, # vt
|
||||
commctrl.LVCFMT_RIGHT, # fmt
|
||||
20, #cChars
|
||||
shellcon.SHCOLSTATE_TYPE_INT | \
|
||||
shellcon.SHCOLSTATE_SECONDARYUI, # csFlags
|
||||
title,
|
||||
description)
|
||||
return col_info
|
||||
return None # Indicate no more columns.
|
||||
def GetItemData(self, colid, colData):
|
||||
fmt_id, pid = colid
|
||||
fmt_id==self._reg_clsid_
|
||||
flags, attr, reserved, ext, name = colData
|
||||
if ext.lower() not in [".py", ".pyw"]:
|
||||
return None
|
||||
if pid==0:
|
||||
ext = ".pyc"
|
||||
else:
|
||||
ext = ".pyo"
|
||||
check_file = os.path.splitext(name)[0] + ext
|
||||
try:
|
||||
st = os.stat(check_file)
|
||||
return st[stat.ST_SIZE]
|
||||
except OSError:
|
||||
# No file
|
||||
return None
|
||||
|
||||
def DllRegisterServer():
|
||||
import winreg
|
||||
# Special ColumnProvider key
|
||||
key = winreg.CreateKey(winreg.HKEY_CLASSES_ROOT,
|
||||
"Folder\\ShellEx\\ColumnHandlers\\" + \
|
||||
str(ColumnProvider._reg_clsid_ ))
|
||||
winreg.SetValueEx(key, None, 0, winreg.REG_SZ, ColumnProvider._reg_desc_)
|
||||
print(ColumnProvider._reg_desc_, "registration complete.")
|
||||
|
||||
def DllUnregisterServer():
|
||||
import winreg
|
||||
try:
|
||||
key = winreg.DeleteKey(winreg.HKEY_CLASSES_ROOT,
|
||||
"Folder\\ShellEx\\ColumnHandlers\\" + \
|
||||
str(ColumnProvider._reg_clsid_) )
|
||||
except WindowsError as details:
|
||||
import errno
|
||||
if details.errno != errno.ENOENT:
|
||||
raise
|
||||
print(ColumnProvider._reg_desc_, "unregistration complete.")
|
||||
|
||||
if __name__=='__main__':
|
||||
from win32com.server import register
|
||||
register.UseCommandLine(ColumnProvider,
|
||||
finalize_register = DllRegisterServer,
|
||||
finalize_unregister = DllUnregisterServer)
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
# A sample context menu handler.
|
||||
# Adds a 'Hello from Python' menu entry to .py files. When clicked, a
|
||||
# simple message box is displayed.
|
||||
#
|
||||
# To demostrate:
|
||||
# * Execute this script to register the context menu.
|
||||
# * Open Windows Explorer, and browse to a directory with a .py file.
|
||||
# * Right-Click on a .py file - locate and click on 'Hello from Python' on
|
||||
# the context menu.
|
||||
|
||||
import pythoncom
|
||||
from win32com.shell import shell, shellcon
|
||||
import win32gui
|
||||
import win32con
|
||||
|
||||
class ShellExtension:
|
||||
_reg_progid_ = "Python.ShellExtension.ContextMenu"
|
||||
_reg_desc_ = "Python Sample Shell Extension (context menu)"
|
||||
_reg_clsid_ = "{CED0336C-C9EE-4a7f-8D7F-C660393C381F}"
|
||||
_com_interfaces_ = [shell.IID_IShellExtInit, shell.IID_IContextMenu]
|
||||
_public_methods_ = shellcon.IContextMenu_Methods + shellcon.IShellExtInit_Methods
|
||||
|
||||
def Initialize(self, folder, dataobj, hkey):
|
||||
print("Init", folder, dataobj, hkey)
|
||||
self.dataobj = dataobj
|
||||
|
||||
def QueryContextMenu(self, hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags):
|
||||
print("QCM", hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags)
|
||||
# Query the items clicked on
|
||||
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)
|
||||
if num_files>1:
|
||||
msg = "&Hello from Python (with %d files selected)" % num_files
|
||||
else:
|
||||
fname = shell.DragQueryFile(sm.data_handle, 0)
|
||||
msg = "&Hello from Python (with '%s' selected)" % fname
|
||||
idCmd = idCmdFirst
|
||||
items = ['First Python content menu item']
|
||||
if (uFlags & 0x000F) == shellcon.CMF_NORMAL: # Check == here, since CMF_NORMAL=0
|
||||
print("CMF_NORMAL...")
|
||||
items.append(msg)
|
||||
elif uFlags & shellcon.CMF_VERBSONLY:
|
||||
print("CMF_VERBSONLY...")
|
||||
items.append(msg + " - shortcut")
|
||||
elif uFlags & shellcon.CMF_EXPLORE:
|
||||
print("CMF_EXPLORE...")
|
||||
items.append(msg + " - normal file, right-click in Explorer")
|
||||
elif uFlags & CMF_DEFAULTONLY:
|
||||
print("CMF_DEFAULTONLY...\r\n")
|
||||
else:
|
||||
print("** unknown flags", uFlags)
|
||||
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 # Must return number of menu items we added.
|
||||
|
||||
def InvokeCommand(self, ci):
|
||||
mask, hwnd, verb, params, dir, nShow, hotkey, hicon = ci
|
||||
win32gui.MessageBox(hwnd, "Hello", "Wow", win32con.MB_OK)
|
||||
|
||||
def GetCommandString(self, cmd, typ):
|
||||
# If GetCommandString returns the same string for all items then
|
||||
# the shell seems to ignore all but one. This is even true in
|
||||
# Win7 etc where there is no status bar (and hence this string seems
|
||||
# ignored)
|
||||
return "Hello from Python (cmd=%d)!!" % (cmd,)
|
||||
|
||||
def DllRegisterServer():
|
||||
import winreg
|
||||
key = winreg.CreateKey(winreg.HKEY_CLASSES_ROOT,
|
||||
"Python.File\\shellex")
|
||||
subkey = winreg.CreateKey(key, "ContextMenuHandlers")
|
||||
subkey2 = winreg.CreateKey(subkey, "PythonSample")
|
||||
winreg.SetValueEx(subkey2, None, 0, winreg.REG_SZ, ShellExtension._reg_clsid_)
|
||||
print(ShellExtension._reg_desc_, "registration complete.")
|
||||
|
||||
def DllUnregisterServer():
|
||||
import winreg
|
||||
try:
|
||||
key = winreg.DeleteKey(winreg.HKEY_CLASSES_ROOT,
|
||||
"Python.File\\shellex\\ContextMenuHandlers\\PythonSample")
|
||||
except WindowsError as details:
|
||||
import errno
|
||||
if details.errno != errno.ENOENT:
|
||||
raise
|
||||
print(ShellExtension._reg_desc_, "unregistration complete.")
|
||||
|
||||
if __name__=='__main__':
|
||||
from win32com.server import register
|
||||
register.UseCommandLine(ShellExtension,
|
||||
finalize_register = DllRegisterServer,
|
||||
finalize_unregister = DllUnregisterServer)
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
# A sample shell copy hook.
|
||||
|
||||
# To demostrate:
|
||||
# * Execute this script to register the context menu.
|
||||
# * Open Windows Explorer
|
||||
# * Attempt to move or copy a directory.
|
||||
# * Note our hook's dialog is displayed.
|
||||
import sys, os
|
||||
import pythoncom
|
||||
from win32com.shell import shell, shellcon
|
||||
import win32gui
|
||||
import win32con
|
||||
import winerror
|
||||
|
||||
# Our shell extension.
|
||||
class ShellExtension:
|
||||
_reg_progid_ = "Python.ShellExtension.CopyHook"
|
||||
_reg_desc_ = "Python Sample Shell Extension (copy hook)"
|
||||
_reg_clsid_ = "{1845b6ba-2bbd-4197-b930-46d8651497c1}"
|
||||
_com_interfaces_ = [shell.IID_ICopyHook]
|
||||
_public_methods_ = ["CopyCallBack"]
|
||||
|
||||
def CopyCallBack(self, hwnd, func, flags,
|
||||
srcName, srcAttr, destName, destAttr):
|
||||
# This function should return:
|
||||
# IDYES Allows the operation.
|
||||
# IDNO Prevents the operation on this folder but continues with any other operations that have been approved (for example, a batch copy operation).
|
||||
# IDCANCEL Prevents the current operation and cancels any pending operations.
|
||||
print("CopyCallBack", hwnd, func, flags, srcName, srcAttr, destName, destAttr)
|
||||
return win32gui.MessageBox(hwnd, "Allow operation?", "CopyHook",
|
||||
win32con.MB_YESNO)
|
||||
|
||||
def DllRegisterServer():
|
||||
import winreg
|
||||
key = winreg.CreateKey(winreg.HKEY_CLASSES_ROOT,
|
||||
"directory\\shellex\\CopyHookHandlers\\" +
|
||||
ShellExtension._reg_desc_)
|
||||
winreg.SetValueEx(key, None, 0, winreg.REG_SZ, ShellExtension._reg_clsid_)
|
||||
key = winreg.CreateKey(winreg.HKEY_CLASSES_ROOT,
|
||||
"*\\shellex\\CopyHookHandlers\\" +
|
||||
ShellExtension._reg_desc_)
|
||||
winreg.SetValueEx(key, None, 0, winreg.REG_SZ, ShellExtension._reg_clsid_)
|
||||
print(ShellExtension._reg_desc_, "registration complete.")
|
||||
|
||||
def DllUnregisterServer():
|
||||
import winreg
|
||||
try:
|
||||
key = winreg.DeleteKey(winreg.HKEY_CLASSES_ROOT,
|
||||
"directory\\shellex\\CopyHookHandlers\\" +
|
||||
ShellExtension._reg_desc_)
|
||||
except WindowsError as details:
|
||||
import errno
|
||||
if details.errno != errno.ENOENT:
|
||||
raise
|
||||
try:
|
||||
key = winreg.DeleteKey(winreg.HKEY_CLASSES_ROOT,
|
||||
"*\\shellex\\CopyHookHandlers\\" +
|
||||
ShellExtension._reg_desc_)
|
||||
except WindowsError as details:
|
||||
import errno
|
||||
if details.errno != errno.ENOENT:
|
||||
raise
|
||||
print(ShellExtension._reg_desc_, "unregistration complete.")
|
||||
|
||||
if __name__=='__main__':
|
||||
from win32com.server import register
|
||||
register.UseCommandLine(ShellExtension,
|
||||
finalize_register = DllRegisterServer,
|
||||
finalize_unregister = DllUnregisterServer)
|
||||
#!/usr/bin/env python
|
||||
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
# 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 as xxx_todo_changeme:
|
||||
# This will be raised by the callback when the user selects 'cancel'.
|
||||
(hr, msg, exc, arg) = xxx_todo_changeme.args
|
||||
# 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 as xxx_todo_changeme1:
|
||||
# This will be raised by the callback when the user selects 'cancel'.
|
||||
(hr, msg, exc, arg) = xxx_todo_changeme1.args
|
||||
# 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 as 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)
|
||||
727
Lib/site-packages/win32comext/shell/demos/servers/folder_view.py
Normal file
727
Lib/site-packages/win32comext/shell/demos/servers/folder_view.py
Normal file
|
|
@ -0,0 +1,727 @@
|
|||
# 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 as 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)
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
# A sample icon handler. Sets the icon for Python files to a random
|
||||
# ICO file. ICO files are found in the Python directory - generally there will
|
||||
# be 3 icons found.
|
||||
#
|
||||
# To demostrate:
|
||||
# * Execute this script to register the context menu.
|
||||
# * Open Windows Explorer, and browse to a directory with a .py file.
|
||||
# * Note the pretty, random selection of icons!
|
||||
import sys, os
|
||||
import pythoncom
|
||||
from win32com.shell import shell, shellcon
|
||||
import win32gui
|
||||
import win32con
|
||||
import winerror
|
||||
|
||||
# Use glob to locate ico files, and random.choice to pick one.
|
||||
import glob, random
|
||||
ico_files = glob.glob(os.path.join(sys.prefix, "*.ico"))
|
||||
if not ico_files:
|
||||
ico_files = glob.glob(os.path.join(sys.prefix, "PC", "*.ico"))
|
||||
if not ico_files:
|
||||
print("WARNING: Can't find any icon files")
|
||||
|
||||
# Our shell extension.
|
||||
IExtractIcon_Methods = "Extract GetIconLocation".split()
|
||||
IPersistFile_Methods = "IsDirty Load Save SaveCompleted GetCurFile".split()
|
||||
|
||||
class ShellExtension:
|
||||
_reg_progid_ = "Python.ShellExtension.IconHandler"
|
||||
_reg_desc_ = "Python Sample Shell Extension (icon handler)"
|
||||
_reg_clsid_ = "{a97e32d7-3b78-448c-b341-418120ea9227}"
|
||||
_com_interfaces_ = [shell.IID_IExtractIcon, pythoncom.IID_IPersistFile]
|
||||
_public_methods_ = IExtractIcon_Methods + IPersistFile_Methods
|
||||
|
||||
def Load(self, filename, mode):
|
||||
self.filename = filename
|
||||
self.mode = mode
|
||||
|
||||
def GetIconLocation(self, flags):
|
||||
# note - returning a single int will set the HRESULT (eg, S_FALSE,
|
||||
# E_PENDING - see MS docs for details.
|
||||
return random.choice(ico_files), 0, 0
|
||||
|
||||
def Extract(self, fname, index, size):
|
||||
return winerror.S_FALSE
|
||||
|
||||
def DllRegisterServer():
|
||||
import winreg
|
||||
key = winreg.CreateKey(winreg.HKEY_CLASSES_ROOT,
|
||||
"Python.File\\shellex")
|
||||
subkey = winreg.CreateKey(key, "IconHandler")
|
||||
winreg.SetValueEx(subkey, None, 0, winreg.REG_SZ, ShellExtension._reg_clsid_)
|
||||
print(ShellExtension._reg_desc_, "registration complete.")
|
||||
|
||||
def DllUnregisterServer():
|
||||
import winreg
|
||||
try:
|
||||
key = winreg.DeleteKey(winreg.HKEY_CLASSES_ROOT,
|
||||
"Python.File\\shellex\\IconHandler")
|
||||
except WindowsError as details:
|
||||
import errno
|
||||
if details.errno != errno.ENOENT:
|
||||
raise
|
||||
print(ShellExtension._reg_desc_, "unregistration complete.")
|
||||
|
||||
if __name__=='__main__':
|
||||
from win32com.server import register
|
||||
register.UseCommandLine(ShellExtension,
|
||||
finalize_register = DllRegisterServer,
|
||||
finalize_unregister = DllUnregisterServer)
|
||||
853
Lib/site-packages/win32comext/shell/demos/servers/shell_view.py
Normal file
853
Lib/site-packages/win32comext/shell/demos/servers/shell_view.py
Normal file
|
|
@ -0,0 +1,853 @@
|
|||
# A sample shell namespace view
|
||||
|
||||
# To demostrate:
|
||||
# * Execute this script to register the namespace.
|
||||
# * Open Windows Explorer, and locate the new "Python Path Shell Browser"
|
||||
# folder off "My Computer"
|
||||
# * Browse this tree - .py files are shown expandable, with classes and
|
||||
# methods selectable. Selecting a Python file, or a class/method, will
|
||||
# display the file using Scintilla.
|
||||
# Known problems:
|
||||
# * Classes and methods don't have icons - this is a demo, so we keep it small
|
||||
# See icon_handler.py for examples of how to work with icons.
|
||||
#
|
||||
#
|
||||
# Notes on PIDLs
|
||||
# PIDLS are complicated, but fairly well documented in MSDN. If you need to
|
||||
# do much with these shell extensions, you must understand their concept.
|
||||
# Here is a short-course, as it applies to this sample:
|
||||
# A PIDL identifies an item, much in the same way that a filename does
|
||||
# (however, the shell is not limited to displaying "files").
|
||||
# An "ItemID" is a single string, each being an item in the hierarchy.
|
||||
# A "PIDL" is a list of these strings.
|
||||
# All shell etc functions work with PIDLs, so even in the case where
|
||||
# an ItemID is conceptually used, a 1-item list is always used.
|
||||
# Conceptually, think of:
|
||||
# pidl = pathname.split("\\") # pidl is a list of "parent" items.
|
||||
# # each item is a string 'item id', but these are ever used directly
|
||||
# As there is no concept of passing a single item, to open a file using only
|
||||
# a relative filename, conceptually you would say:
|
||||
# open_file([filename]) # Pass a single-itemed relative "PIDL"
|
||||
# and continuing the analogy, a "listdir" type function would return a list
|
||||
# of single-itemed lists - each list containing the relative PIDL of the child.
|
||||
#
|
||||
# Each PIDL entry is a binary string, and may contain any character. For
|
||||
# PIDLs not created by you, they can not be interpreted - they are just
|
||||
# blobs. PIDLs created by you (ie, children of your IShellFolder) can
|
||||
# store and interpret the string however makes most sense for your application.
|
||||
# (but within PIDL rules - they must be persistable, etc)
|
||||
# There is no reason that pickled strings, for example, couldn't be used
|
||||
# as an EntryID.
|
||||
# This application takes a simple approach - each PIDL is a string of form
|
||||
# "directory\0directory_name", "file\0file_name" or
|
||||
# "object\0file_name\0class_name[.method_name"
|
||||
# The first string in each example is literal (ie, the word 'directory',
|
||||
# 'file' or 'object', and every other string is variable. We use '\0' as
|
||||
# a field sep just 'cos we can (and 'cos it can't possibly conflict with the
|
||||
# string content)
|
||||
|
||||
import sys, os
|
||||
import _thread
|
||||
import pyclbr
|
||||
import pythoncom
|
||||
import win32gui, win32gui_struct, win32api, win32con, winerror
|
||||
import commctrl
|
||||
from win32com.shell import shell, shellcon
|
||||
from win32com.server.util import wrap, NewEnum
|
||||
from win32com.server.exception import COMException
|
||||
from win32com.util import IIDToInterfaceName
|
||||
from pywin.scintilla import scintillacon
|
||||
|
||||
# Set this to 1 to cause debug version to be registered and used. A debug
|
||||
# version will spew output to win32traceutil.
|
||||
debug=0
|
||||
if debug:
|
||||
import win32traceutil
|
||||
|
||||
# markh is toying with an implementation that allows auto reload of a module
|
||||
# if this attribute exists.
|
||||
com_auto_reload = True
|
||||
|
||||
# Helper function to get a system IShellFolder interface, and the PIDL within
|
||||
# that folder for an existing file/directory.
|
||||
def GetFolderAndPIDLForPath(filename):
|
||||
desktop = shell.SHGetDesktopFolder()
|
||||
info = desktop.ParseDisplayName(0, None, os.path.abspath(filename))
|
||||
cchEaten, pidl, attr = info
|
||||
# We must walk the ID list, looking for one child at a time.
|
||||
folder = desktop
|
||||
while len(pidl) > 1:
|
||||
this = pidl.pop(0)
|
||||
folder = folder.BindToObject([this], None, shell.IID_IShellFolder)
|
||||
# We are left with the pidl for the specific item. Leave it as
|
||||
# a list, so it remains a valid PIDL.
|
||||
return folder, pidl
|
||||
|
||||
# A cache of pyclbr module objects, so we only parse a given filename once.
|
||||
clbr_modules = {} # Indexed by path, item is dict as returned from pyclbr
|
||||
def get_clbr_for_file(path):
|
||||
try:
|
||||
objects = clbr_modules[path]
|
||||
except KeyError:
|
||||
dir, filename = os.path.split(path)
|
||||
base, ext = os.path.splitext(filename)
|
||||
objects = pyclbr.readmodule_ex(base, [dir])
|
||||
clbr_modules[path] = objects
|
||||
return objects
|
||||
|
||||
# Our COM interfaces.
|
||||
|
||||
# Base class for a shell folder.
|
||||
# All child classes use a simple PIDL of the form:
|
||||
# "object_type\0object_name[\0extra ...]"
|
||||
class ShellFolderBase:
|
||||
_com_interfaces_ = [shell.IID_IBrowserFrameOptions,
|
||||
pythoncom.IID_IPersist,
|
||||
shell.IID_IPersistFolder,
|
||||
shell.IID_IShellFolder,
|
||||
]
|
||||
|
||||
_public_methods_ = shellcon.IBrowserFrame_Methods + \
|
||||
shellcon.IPersistFolder_Methods + \
|
||||
shellcon.IShellFolder_Methods
|
||||
|
||||
def GetFrameOptions(self, mask):
|
||||
#print "GetFrameOptions", self, mask
|
||||
return 0
|
||||
def ParseDisplayName(self, hwnd, reserved, displayName, attr):
|
||||
print("ParseDisplayName", displayName)
|
||||
# return cchEaten, pidl, attr
|
||||
def BindToStorage(self, pidl, bc, iid):
|
||||
print("BTS", iid, IIDToInterfaceName(iid))
|
||||
def BindToObject(self, pidl, bc, iid):
|
||||
# We may be passed a set of relative PIDLs here - ie
|
||||
# [pidl_of_dir, pidl_of_child_dir, pidl_of_file, pidl_of_function]
|
||||
# But each of our PIDLs keeps the fully qualified name anyway - so
|
||||
# just jump directly to the last.
|
||||
final_pidl = pidl[-1]
|
||||
typ, extra = final_pidl.split('\0', 1)
|
||||
if typ == "directory":
|
||||
klass = ShellFolderDirectory
|
||||
elif typ == "file":
|
||||
klass = ShellFolderFile
|
||||
elif typ == "object":
|
||||
klass = ShellFolderObject
|
||||
else:
|
||||
raise RuntimeError("What is " + repr(typ))
|
||||
ret = wrap(klass(extra), iid, useDispatcher = (debug>0))
|
||||
return ret
|
||||
|
||||
# A ShellFolder for an object with CHILDREN on the file system
|
||||
# Note that this means our "File" folder is *not* a 'FileSystem' folder,
|
||||
# as it's children (functions and classes) are not on the file system.
|
||||
#
|
||||
class ShellFolderFileSystem(ShellFolderBase):
|
||||
def _GetFolderAndPIDLForPIDL(self, my_idl):
|
||||
typ, name = my_idl[0].split('\0')
|
||||
return GetFolderAndPIDLForPath(name)
|
||||
# Interface methods
|
||||
def CompareIDs(self, param, id1, id2):
|
||||
if id1 < id2:
|
||||
return -1
|
||||
if id1 == id2:
|
||||
return 0
|
||||
return 1
|
||||
def GetUIObjectOf(self, hwndOwner, pidls, iid, inout):
|
||||
# delegate to the shell.
|
||||
assert len(pidls)==1, "oops - arent expecting more than one!"
|
||||
pidl = pidls[0]
|
||||
folder, child_pidl = self._GetFolderAndPIDLForPIDL(pidl)
|
||||
try:
|
||||
inout, ret = folder.GetUIObjectOf(hwndOwner, [child_pidl], iid,
|
||||
inout, iid)
|
||||
except pythoncom.com_error as xxx_todo_changeme:
|
||||
(hr, desc, exc, arg) = xxx_todo_changeme.args
|
||||
raise COMException(hresult=hr)
|
||||
return inout, ret
|
||||
# return object of IID
|
||||
def GetDisplayNameOf(self, pidl, flags):
|
||||
# delegate to the shell.
|
||||
folder, child_pidl = self._GetFolderAndPIDLForPIDL(pidl)
|
||||
ret = folder.GetDisplayNameOf(child_pidl, flags)
|
||||
return ret
|
||||
def GetAttributesOf(self, pidls, attrFlags):
|
||||
ret_flags = -1
|
||||
for pidl in pidls:
|
||||
pidl = pidl[0] # ??
|
||||
typ, name = pidl.split('\0')
|
||||
flags = shellcon.SHGFI_ATTRIBUTES
|
||||
rc, info = shell.SHGetFileInfo(name, 0, flags)
|
||||
hIcon, iIcon, dwAttr, name, typeName = info
|
||||
# All our items, even files, have sub-items
|
||||
extras = shellcon.SFGAO_HASSUBFOLDER | \
|
||||
shellcon.SFGAO_FOLDER | \
|
||||
shellcon.SFGAO_FILESYSANCESTOR | \
|
||||
shellcon.SFGAO_BROWSABLE
|
||||
ret_flags &= (dwAttr | extras)
|
||||
return ret_flags
|
||||
|
||||
class ShellFolderDirectory(ShellFolderFileSystem):
|
||||
def __init__(self, path):
|
||||
self.path = os.path.abspath(path)
|
||||
def CreateViewObject(self, hwnd, iid):
|
||||
# delegate to the shell.
|
||||
folder, child_pidl = GetFolderAndPIDLForPath(self.path)
|
||||
return folder.CreateViewObject(hwnd, iid)
|
||||
def EnumObjects(self, hwndOwner, flags):
|
||||
pidls = []
|
||||
for fname in os.listdir(self.path):
|
||||
fqn = os.path.join(self.path, fname)
|
||||
if os.path.isdir(fqn):
|
||||
type_name = "directory"
|
||||
type_class = ShellFolderDirectory
|
||||
else:
|
||||
base, ext = os.path.splitext(fname)
|
||||
if ext in [".py", ".pyw"]:
|
||||
type_class = ShellFolderFile
|
||||
type_name = "file"
|
||||
else:
|
||||
type_class = None
|
||||
if type_class is not None:
|
||||
pidls.append( [type_name + "\0" + fqn] )
|
||||
return NewEnum(pidls, iid=shell.IID_IEnumIDList,
|
||||
useDispatcher=(debug>0))
|
||||
|
||||
def GetDisplayNameOf(self, pidl, flags):
|
||||
final_pidl=pidl[-1]
|
||||
full_fname=final_pidl.split('\0')[-1]
|
||||
return os.path.split(full_fname)[-1]
|
||||
|
||||
def GetAttributesOf(self, pidls, attrFlags):
|
||||
return shellcon.SFGAO_HASSUBFOLDER|shellcon.SFGAO_FOLDER|shellcon.SFGAO_FILESYSANCESTOR|shellcon.SFGAO_BROWSABLE
|
||||
|
||||
# As per comments above, even though this manages a file, it is *not* a
|
||||
# ShellFolderFileSystem, as the children are not on the file system.
|
||||
class ShellFolderFile(ShellFolderBase):
|
||||
def __init__(self, path):
|
||||
self.path = os.path.abspath(path)
|
||||
def EnumObjects(self, hwndOwner, flags):
|
||||
objects = get_clbr_for_file(self.path)
|
||||
pidls = []
|
||||
for name, ob in objects.items():
|
||||
pidls.append( ["object\0" + self.path + "\0" + name] )
|
||||
return NewEnum(pidls, iid=shell.IID_IEnumIDList,
|
||||
useDispatcher=(debug>0))
|
||||
|
||||
def GetAttributesOf(self, pidls, attrFlags):
|
||||
ret_flags = -1
|
||||
for pidl in pidls:
|
||||
assert len(pidl)==1, "Expecting relative pidls"
|
||||
pidl = pidl[0]
|
||||
typ, filename, obname = pidl.split('\0')
|
||||
obs = get_clbr_for_file(filename)
|
||||
ob = obs[obname]
|
||||
flags = shellcon.SFGAO_BROWSABLE | shellcon.SFGAO_FOLDER | \
|
||||
shellcon.SFGAO_FILESYSANCESTOR
|
||||
if hasattr(ob, "methods"):
|
||||
flags |= shellcon.SFGAO_HASSUBFOLDER
|
||||
ret_flags &= flags
|
||||
return ret_flags
|
||||
|
||||
def GetDisplayNameOf(self, pidl, flags):
|
||||
assert len(pidl)==1, "Expecting relative PIDL"
|
||||
typ, fname, obname = pidl[0].split('\0')
|
||||
fqname = os.path.splitext(fname)[0] + "." + obname
|
||||
if flags & shellcon.SHGDN_INFOLDER:
|
||||
ret = obname
|
||||
else: # SHGDN_NORMAL is the default
|
||||
ret = fqname
|
||||
# No need to look at the SHGDN_FOR* modifiers.
|
||||
return ret
|
||||
|
||||
def CreateViewObject(self, hwnd, iid):
|
||||
return wrap(ScintillaShellView(hwnd, self.path), iid, useDispatcher=debug>0)
|
||||
# A ShellFolder for our Python objects
|
||||
class ShellFolderObject(ShellFolderBase):
|
||||
def __init__(self, details):
|
||||
self.path, details = details.split('\0')
|
||||
if details.find(".")>0:
|
||||
self.class_name, self.method_name = details.split(".")
|
||||
else:
|
||||
self.class_name = details
|
||||
self.method_name = None
|
||||
def CreateViewObject(self, hwnd, iid):
|
||||
mod_objects = get_clbr_for_file(self.path)
|
||||
object = mod_objects[self.class_name]
|
||||
if self.method_name is None:
|
||||
lineno = object.lineno
|
||||
else:
|
||||
lineno = object.methods[self.method_name]
|
||||
return wrap(ScintillaShellView(hwnd, self.path, lineno),
|
||||
iid, useDispatcher=debug>0)
|
||||
|
||||
def EnumObjects(self, hwndOwner, flags):
|
||||
assert self.method_name is None, "Should not be enuming methods!"
|
||||
mod_objects = get_clbr_for_file(self.path)
|
||||
my_objects = mod_objects[self.class_name]
|
||||
pidls = []
|
||||
for func_name, lineno in my_objects.methods.items():
|
||||
pidl = ["object\0" + self.path + "\0" +
|
||||
self.class_name + "." + func_name]
|
||||
pidls.append(pidl)
|
||||
return NewEnum(pidls, iid=shell.IID_IEnumIDList,
|
||||
useDispatcher=(debug>0))
|
||||
def GetDisplayNameOf(self, pidl, flags):
|
||||
assert len(pidl)==1, "Expecting relative PIDL"
|
||||
typ, fname, obname = pidl[0].split('\0')
|
||||
class_name, method_name = obname.split(".")
|
||||
fqname = os.path.splitext(fname)[0] + "." + obname
|
||||
if flags & shellcon.SHGDN_INFOLDER:
|
||||
ret = method_name
|
||||
else: # SHGDN_NORMAL is the default
|
||||
ret = fqname
|
||||
# No need to look at the SHGDN_FOR* modifiers.
|
||||
return ret
|
||||
def GetAttributesOf(self, pidls, attrFlags):
|
||||
ret_flags = -1
|
||||
for pidl in pidls:
|
||||
assert len(pidl)==1, "Expecting relative pidls"
|
||||
flags = shellcon.SFGAO_BROWSABLE | shellcon.SFGAO_FOLDER | \
|
||||
shellcon.SFGAO_FILESYSANCESTOR
|
||||
ret_flags &= flags
|
||||
return ret_flags
|
||||
|
||||
# The "Root" folder of our namespace. As all children are directories,
|
||||
# it is derived from ShellFolderFileSystem
|
||||
# This is the only COM object actually registered and externally created.
|
||||
class ShellFolderRoot(ShellFolderFileSystem):
|
||||
_reg_progid_ = "Python.ShellExtension.Folder"
|
||||
_reg_desc_ = "Python Path Shell Browser"
|
||||
_reg_clsid_ = "{f6287035-3074-4cb5-a8a6-d3c80e206944}"
|
||||
def GetClassID(self):
|
||||
return self._reg_clsid_
|
||||
def Initialize(self, pidl):
|
||||
# This is the PIDL of us, as created by the shell. This is our
|
||||
# top-level ID. All other items under us have PIDLs defined
|
||||
# by us - see the notes at the top of the file.
|
||||
#print "Initialize called with pidl", repr(pidl)
|
||||
self.pidl = pidl
|
||||
def CreateViewObject(self, hwnd, iid):
|
||||
return wrap(FileSystemView(self, hwnd), iid, useDispatcher=debug>0)
|
||||
|
||||
def EnumObjects(self, hwndOwner, flags):
|
||||
items = [ ["directory\0" + p] for p in sys.path if os.path.isdir(p)]
|
||||
return NewEnum(items, iid=shell.IID_IEnumIDList,
|
||||
useDispatcher=(debug>0))
|
||||
def GetDisplayNameOf(self, pidl, flags):
|
||||
## return full path for sys.path dirs, since they don't appear under a parent folder
|
||||
final_pidl=pidl[-1]
|
||||
display_name=final_pidl.split('\0')[-1]
|
||||
return display_name
|
||||
|
||||
# Simple shell view implementations
|
||||
|
||||
# Uses a builtin listview control to display simple lists of directories
|
||||
# or filenames.
|
||||
class FileSystemView:
|
||||
_public_methods_ = shellcon.IShellView_Methods
|
||||
_com_interfaces_ = [pythoncom.IID_IOleWindow,
|
||||
shell.IID_IShellView,
|
||||
]
|
||||
def __init__(self, folder, hwnd):
|
||||
self.hwnd_parent = hwnd # provided by explorer.
|
||||
self.hwnd = None # intermediate window for catching command notifications.
|
||||
self.hwnd_child = None # our ListView
|
||||
self.activate_state = None
|
||||
self.hmenu = None
|
||||
self.browser = None
|
||||
self.folder = folder
|
||||
self.children = None
|
||||
|
||||
# IOleWindow
|
||||
def GetWindow(self):
|
||||
return self.hwnd
|
||||
|
||||
def ContextSensitiveHelp(self, enter_mode):
|
||||
raise COMException(hresult=winerror.E_NOTIMPL)
|
||||
|
||||
# IShellView
|
||||
def CreateViewWindow(self, prev, settings, browser, rect):
|
||||
print("FileSystemView.CreateViewWindow", prev, settings, browser, rect)
|
||||
self.cur_foldersettings = settings
|
||||
self.browser = browser
|
||||
self._CreateMainWindow(prev, settings, browser, rect)
|
||||
self._CreateChildWindow(prev)
|
||||
|
||||
# This isn't part of the sample, but the most convenient place to
|
||||
# test/demonstrate how you can get an IShellBrowser from a HWND
|
||||
# (but ONLY when you are in the same process as the IShellBrowser!)
|
||||
# Obviously it is not necessary here - we already have the browser!
|
||||
browser_ad = win32gui.SendMessage(self.hwnd_parent, win32con.WM_USER+7, 0, 0)
|
||||
browser_ob = pythoncom.ObjectFromAddress(browser_ad, shell.IID_IShellBrowser)
|
||||
assert browser==browser_ob
|
||||
# and make a call on the object to prove it doesn't die :)
|
||||
assert browser.QueryActiveShellView()==browser_ob.QueryActiveShellView()
|
||||
|
||||
def _CreateMainWindow(self, prev, settings, browser, rect):
|
||||
# Creates a parent window that hosts the view window. This window
|
||||
# gets the control notifications etc sent from the child.
|
||||
style = win32con.WS_CHILD | win32con.WS_VISIBLE #
|
||||
wclass_name = "ShellViewDemo_DefView"
|
||||
# Register the Window class.
|
||||
wc = win32gui.WNDCLASS()
|
||||
wc.hInstance = win32gui.dllhandle
|
||||
wc.lpszClassName = wclass_name
|
||||
wc.style = win32con.CS_VREDRAW | win32con.CS_HREDRAW
|
||||
try:
|
||||
win32gui.RegisterClass(wc)
|
||||
except win32gui.error as details:
|
||||
# Should only happen when this module is reloaded
|
||||
if details[0] != winerror.ERROR_CLASS_ALREADY_EXISTS:
|
||||
raise
|
||||
|
||||
message_map = {
|
||||
win32con.WM_DESTROY: self.OnDestroy,
|
||||
win32con.WM_COMMAND: self.OnCommand,
|
||||
win32con.WM_NOTIFY: self.OnNotify,
|
||||
win32con.WM_CONTEXTMENU: self.OnContextMenu,
|
||||
win32con.WM_SIZE: self.OnSize,
|
||||
}
|
||||
|
||||
self.hwnd = win32gui.CreateWindow( wclass_name, "", style, \
|
||||
rect[0], rect[1], rect[2]-rect[0], rect[3]-rect[1],
|
||||
self.hwnd_parent, 0, win32gui.dllhandle, None)
|
||||
win32gui.SetWindowLong(self.hwnd, win32con.GWL_WNDPROC, message_map)
|
||||
print("View 's hwnd is", self.hwnd)
|
||||
return self.hwnd
|
||||
|
||||
def _CreateChildWindow(self, prev):
|
||||
# Creates the list view window.
|
||||
assert self.hwnd_child is None, "already have a window"
|
||||
assert self.cur_foldersettings is not None, "no settings"
|
||||
style = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_BORDER | \
|
||||
commctrl.LVS_SHAREIMAGELISTS | commctrl.LVS_EDITLABELS
|
||||
|
||||
view_mode, view_flags = self.cur_foldersettings
|
||||
if view_mode==shellcon.FVM_ICON:
|
||||
style |= commctrl.LVS_ICON | commctrl.LVS_AUTOARRANGE
|
||||
elif view_mode==shellcon.FVM_SMALLICON:
|
||||
style |= commctrl.LVS_SMALLICON | commctrl.LVS_AUTOARRANGE
|
||||
elif view_mode==shellcon.FVM_LIST:
|
||||
style |= commctrl.LVS_LIST | commctrl.LVS_AUTOARRANGE
|
||||
elif view_mode==shellcon.FVM_DETAILS:
|
||||
style |= commctrl.LVS_REPORT | commctrl.LVS_AUTOARRANGE
|
||||
else:
|
||||
# XP 'thumbnails' etc
|
||||
view_mode = shellcon.FVM_DETAILS
|
||||
# Default to 'report'
|
||||
style |= commctrl.LVS_REPORT | commctrl.LVS_AUTOARRANGE
|
||||
|
||||
for f_flag, l_flag in [
|
||||
(shellcon.FWF_SINGLESEL, commctrl.LVS_SINGLESEL),
|
||||
(shellcon.FWF_ALIGNLEFT, commctrl.LVS_ALIGNLEFT),
|
||||
(shellcon.FWF_SHOWSELALWAYS, commctrl.LVS_SHOWSELALWAYS),
|
||||
]:
|
||||
if view_flags & f_flag:
|
||||
style |= l_flag
|
||||
|
||||
self.hwnd_child = win32gui.CreateWindowEx(
|
||||
win32con.WS_EX_CLIENTEDGE,
|
||||
"SysListView32", None, style,
|
||||
0, 0, 0, 0,
|
||||
self.hwnd, 1000, 0, None)
|
||||
|
||||
cr = win32gui.GetClientRect(self.hwnd)
|
||||
win32gui.MoveWindow(self.hwnd_child,
|
||||
0, 0, cr[2]-cr[0], cr[3]-cr[1],
|
||||
True)
|
||||
|
||||
# Setup the columns for the view.
|
||||
lvc, extras = win32gui_struct.PackLVCOLUMN(fmt=commctrl.LVCFMT_LEFT,
|
||||
subItem=1,
|
||||
text='Name',
|
||||
cx=300)
|
||||
win32gui.SendMessage(self.hwnd_child, commctrl.LVM_INSERTCOLUMN,
|
||||
0, lvc)
|
||||
|
||||
lvc, extras = win32gui_struct.PackLVCOLUMN(fmt=commctrl.LVCFMT_RIGHT,
|
||||
subItem=1,
|
||||
text='Exists',
|
||||
cx=50)
|
||||
win32gui.SendMessage(self.hwnd_child, commctrl.LVM_INSERTCOLUMN,
|
||||
1, lvc)
|
||||
# and fill it with the content
|
||||
self.Refresh()
|
||||
|
||||
def GetCurrentInfo(self):
|
||||
return self.cur_foldersettings
|
||||
|
||||
def UIActivate(self, activate_state):
|
||||
print("OnActivate")
|
||||
|
||||
def _OnActivate(self, activate_state):
|
||||
if self.activate_state == activate_state:
|
||||
return
|
||||
self._OnDeactivate() # restore menu's first, if necessary.
|
||||
if activate_state != shellcon.SVUIA_DEACTIVATE:
|
||||
assert self.hmenu is None, "Should have destroyed it!"
|
||||
self.hmenu = win32gui.CreateMenu()
|
||||
widths = 0,0,0,0,0,0
|
||||
# Ask explorer to add its standard items.
|
||||
self.browser.InsertMenusSB(self.hmenu, widths)
|
||||
# Merge with these standard items
|
||||
self._MergeMenus(activate_state)
|
||||
self.browser.SetMenuSB(self.hmenu, 0, self.hwnd);
|
||||
self.activate_state = activate_state
|
||||
|
||||
def _OnDeactivate(self):
|
||||
if self.browser is not None and self.hmenu is not None:
|
||||
self.browser.SetMenuSB(0, 0, 0)
|
||||
self.browser.RemoveMenusSB(self.hmenu)
|
||||
win32gui.DestroyMenu(self.hmenu)
|
||||
self.hmenu = None
|
||||
self.hsubmenus = None
|
||||
self.activate_state = shellcon.SVUIA_DEACTIVATE
|
||||
|
||||
def _MergeMenus(self, activate_state):
|
||||
# Merge the operations we support into the top-level menus.
|
||||
# NOTE: This function it *not* called each time the selection changes.
|
||||
# SVUIA_ACTIVATE_FOCUS really means "have a selection?"
|
||||
have_sel = activate_state == shellcon.SVUIA_ACTIVATE_FOCUS
|
||||
# only do "file" menu here, and only 1 item on it!
|
||||
mid = shellcon.FCIDM_MENU_FILE
|
||||
# Get the hmenu for the menu
|
||||
buf, extras = win32gui_struct.EmptyMENUITEMINFO(win32con.MIIM_SUBMENU)
|
||||
win32gui.GetMenuItemInfo(self.hmenu,
|
||||
mid,
|
||||
False,
|
||||
buf)
|
||||
data = win32gui_struct.UnpackMENUITEMINFO(buf)
|
||||
submenu = data[3]
|
||||
print("Do someting with the file menu!")
|
||||
|
||||
def Refresh(self):
|
||||
stateMask = commctrl.LVIS_SELECTED | commctrl.LVIS_DROPHILITED
|
||||
state = 0
|
||||
self.children = []
|
||||
# Enumerate and store the child PIDLs
|
||||
for cid in self.folder.EnumObjects(self.hwnd, 0):
|
||||
self.children.append(cid)
|
||||
|
||||
for row_index, data in enumerate(self.children):
|
||||
assert len(data)==1, "expecting just a child PIDL"
|
||||
typ, path = data[0].split('\0')
|
||||
desc = os.path.exists(path) and "Yes" or "No"
|
||||
prop_vals = (path, desc)
|
||||
# first col
|
||||
data, extras = win32gui_struct.PackLVITEM(
|
||||
item=row_index,
|
||||
subItem=0,
|
||||
text=prop_vals[0],
|
||||
state=state,
|
||||
stateMask=stateMask)
|
||||
win32gui.SendMessage(self.hwnd_child,
|
||||
commctrl.LVM_INSERTITEM,
|
||||
row_index, data)
|
||||
# rest of the cols.
|
||||
col_index = 1
|
||||
for prop_val in prop_vals[1:]:
|
||||
data, extras = win32gui_struct.PackLVITEM(
|
||||
item=row_index,
|
||||
subItem=col_index,
|
||||
text=prop_val)
|
||||
|
||||
win32gui.SendMessage(self.hwnd_child,
|
||||
commctrl.LVM_SETITEM,
|
||||
0, data)
|
||||
col_index += 1
|
||||
|
||||
def SelectItem(self, pidl, flag):
|
||||
# For the sake of brevity, we don't implement this yet.
|
||||
# You would need to locate the index of the item in the shell-view
|
||||
# with that PIDL, then ask the list-view to select it.
|
||||
print("Please implement SelectItem for PIDL", pidl)
|
||||
|
||||
def GetItemObject(self, item_num, iid):
|
||||
raise COMException(hresult=winerror.E_NOTIMPL)
|
||||
|
||||
def TranslateAccelerator(self, msg):
|
||||
return winerror.S_FALSE
|
||||
|
||||
def DestroyViewWindow(self):
|
||||
win32gui.DestroyWindow(self.hwnd)
|
||||
self.hwnd = None
|
||||
print("Destroyed view window")
|
||||
|
||||
# Message handlers.
|
||||
def OnDestroy(self, hwnd, msg, wparam, lparam):
|
||||
print("OnDestory")
|
||||
|
||||
def OnCommand(self, hwnd, msg, wparam, lparam):
|
||||
print("OnCommand")
|
||||
|
||||
def OnNotify(self, hwnd, msg, wparam, lparam):
|
||||
hwndFrom, idFrom, code = win32gui_struct.UnpackWMNOTIFY(lparam)
|
||||
#print "OnNotify code=0x%x (0x%x, 0x%x)" % (code, wparam, lparam)
|
||||
if code == commctrl.NM_SETFOCUS:
|
||||
# Control got focus - Explorer may not know - tell it
|
||||
if self.browser is not None:
|
||||
self.browser.OnViewWindowActive(None)
|
||||
# And do our menu thang
|
||||
self._OnActivate(shellcon.SVUIA_ACTIVATE_FOCUS)
|
||||
elif code == commctrl.NM_KILLFOCUS:
|
||||
self._OnDeactivate()
|
||||
elif code == commctrl.NM_DBLCLK:
|
||||
# This DblClick implementation leaves a little to be desired :)
|
||||
# It demonstrates some useful concepts, such as asking the
|
||||
# folder for its context-menu and invoking a command from it.
|
||||
# However, as our folder delegates IContextMenu to the shell
|
||||
# itself, the end result is that the folder is opened in
|
||||
# its "normal" place in Windows explorer rather than inside
|
||||
# our shell-extension.
|
||||
# Determine the selected items.
|
||||
sel = []
|
||||
n = -1
|
||||
while 1:
|
||||
n = win32gui.SendMessage(self.hwnd_child,
|
||||
commctrl.LVM_GETNEXTITEM,
|
||||
n,
|
||||
commctrl.LVNI_SELECTED)
|
||||
if n==-1:
|
||||
break
|
||||
sel.append(self.children[n][-1:])
|
||||
print("Selection is", sel)
|
||||
hmenu = win32gui.CreateMenu()
|
||||
try:
|
||||
# Get the IContextMenu for the items.
|
||||
inout, cm = self.folder.GetUIObjectOf(self.hwnd_parent, sel,
|
||||
shell.IID_IContextMenu, 0)
|
||||
|
||||
# As per 'Q179911', we need to determine if the default operation
|
||||
# should be 'open' or 'explore'
|
||||
flags = shellcon.CMF_DEFAULTONLY
|
||||
try:
|
||||
self.browser.GetControlWindow(shellcon.FCW_TREE)
|
||||
flags |= shellcon.CMF_EXPLORE
|
||||
except pythoncom.com_error:
|
||||
pass
|
||||
# *sob* - delegating to the shell does work - but lands us
|
||||
# in the original location. Q179911 also shows that
|
||||
# ShellExecuteEx should work - but I can't make it work as
|
||||
# described (XP: function call succeeds, but another thread
|
||||
# shows a dialog with text of E_INVALID_PARAM, and new
|
||||
# Explorer window opens with desktop view. Vista: function
|
||||
# call succeeds, but no window created at all.
|
||||
# On Vista, I'd love to get an IExplorerBrowser interface
|
||||
# from the shell, but a QI fails, and although the
|
||||
# IShellBrowser does appear to support IServiceProvider, I
|
||||
# still can't get it
|
||||
if 0:
|
||||
id_cmd_first = 1 # TrackPopupMenu makes it hard to use 0
|
||||
cm.QueryContextMenu(hmenu, 0, id_cmd_first, -1, flags)
|
||||
# Find the default item in the returned menu.
|
||||
cmd = win32gui.GetMenuDefaultItem(hmenu, False, 0)
|
||||
if cmd == -1:
|
||||
print("Oops: _doDefaultActionFor found no default menu")
|
||||
else:
|
||||
ci = 0, self.hwnd_parent, cmd-id_cmd_first, None, None, 0, 0, 0
|
||||
cm.InvokeCommand(ci)
|
||||
else:
|
||||
rv = shell.ShellExecuteEx(
|
||||
hwnd = self.hwnd_parent,
|
||||
nShow=win32con.SW_NORMAL,
|
||||
lpClass="folder",
|
||||
lpVerb="explore",
|
||||
lpIDList=sel[0])
|
||||
print("ShellExecuteEx returned", rv)
|
||||
finally:
|
||||
win32gui.DestroyMenu(hmenu)
|
||||
|
||||
def OnContextMenu(self, hwnd, msg, wparam, lparam):
|
||||
# Get the selected items.
|
||||
pidls = []
|
||||
n = -1
|
||||
while 1:
|
||||
n = win32gui.SendMessage(self.hwnd_child,
|
||||
commctrl.LVM_GETNEXTITEM,
|
||||
n,
|
||||
commctrl.LVNI_SELECTED)
|
||||
if n==-1:
|
||||
break
|
||||
pidls.append(self.children[n][-1:])
|
||||
|
||||
spt = win32api.GetCursorPos()
|
||||
if not pidls:
|
||||
print("Ignoring background click")
|
||||
return
|
||||
# Get the IContextMenu for the items.
|
||||
inout, cm = self.folder.GetUIObjectOf(self.hwnd_parent, pidls, shell.IID_IContextMenu, 0)
|
||||
hmenu = win32gui.CreatePopupMenu()
|
||||
sel = None
|
||||
# As per 'Q179911', we need to determine if the default operation
|
||||
# should be 'open' or 'explore'
|
||||
try:
|
||||
flags = 0
|
||||
try:
|
||||
self.browser.GetControlWindow(shellcon.FCW_TREE)
|
||||
flags |= shellcon.CMF_EXPLORE
|
||||
except pythoncom.com_error:
|
||||
pass
|
||||
id_cmd_first = 1 # TrackPopupMenu makes it hard to use 0
|
||||
cm.QueryContextMenu(hmenu, 0, id_cmd_first, -1, flags)
|
||||
tpm_flags = win32con.TPM_LEFTALIGN | win32con.TPM_RETURNCMD | \
|
||||
win32con.TPM_RIGHTBUTTON
|
||||
sel = win32gui.TrackPopupMenu(hmenu,
|
||||
tpm_flags,
|
||||
spt[0], spt[1],
|
||||
0, self.hwnd, None)
|
||||
print("TrackPopupMenu returned", sel)
|
||||
finally:
|
||||
win32gui.DestroyMenu(hmenu)
|
||||
if sel:
|
||||
ci = 0, self.hwnd_parent, sel-id_cmd_first, None, None, 0, 0, 0
|
||||
cm.InvokeCommand(ci)
|
||||
|
||||
def OnSize(self, hwnd, msg, wparam, lparam):
|
||||
#print "OnSize", self.hwnd_child, win32api.LOWORD(lparam), win32api.HIWORD(lparam)
|
||||
if self.hwnd_child is not None:
|
||||
x = win32api.LOWORD(lparam)
|
||||
y = win32api.HIWORD(lparam)
|
||||
win32gui.MoveWindow(self.hwnd_child, 0, 0, x, y, False)
|
||||
|
||||
# This uses scintilla to display a filename, and optionally jump to a line
|
||||
# number.
|
||||
class ScintillaShellView:
|
||||
_public_methods_ = shellcon.IShellView_Methods
|
||||
_com_interfaces_ = [pythoncom.IID_IOleWindow,
|
||||
shell.IID_IShellView,
|
||||
]
|
||||
def __init__(self, hwnd, filename, lineno = None):
|
||||
self.filename = filename
|
||||
self.lineno = lineno
|
||||
self.hwnd_parent = hwnd
|
||||
self.hwnd = None
|
||||
def _SendSci(self, msg, wparam=0, lparam=0):
|
||||
return win32gui.SendMessage(self.hwnd, msg, wparam, lparam)
|
||||
# IShellView
|
||||
def CreateViewWindow(self, prev, settings, browser, rect):
|
||||
print("ScintillaShellView.CreateViewWindow", prev, settings, browser, rect)
|
||||
# Make sure scintilla.dll is loaded. If not, find it on sys.path
|
||||
# (which it generally is for Pythonwin)
|
||||
try:
|
||||
win32api.GetModuleHandle("Scintilla.dll")
|
||||
except win32api.error:
|
||||
for p in sys.path:
|
||||
fname = os.path.join(p, "Scintilla.dll")
|
||||
if not os.path.isfile(fname):
|
||||
fname = os.path.join(p, "Build", "Scintilla.dll")
|
||||
if os.path.isfile(fname):
|
||||
win32api.LoadLibrary(fname)
|
||||
break
|
||||
else:
|
||||
raise RuntimeError("Can't find scintilla!")
|
||||
|
||||
style = win32con.WS_CHILD | win32con.WS_VSCROLL | \
|
||||
win32con.WS_HSCROLL | win32con.WS_CLIPCHILDREN | \
|
||||
win32con.WS_VISIBLE
|
||||
self.hwnd = win32gui.CreateWindow("Scintilla", "Scintilla", style,
|
||||
rect[0], rect[1], rect[2]-rect[0], rect[3]-rect[1],
|
||||
self.hwnd_parent, 1000, 0, None)
|
||||
|
||||
message_map = {
|
||||
win32con.WM_SIZE: self.OnSize,
|
||||
}
|
||||
# win32gui.SetWindowLong(self.hwnd, win32con.GWL_WNDPROC, message_map)
|
||||
|
||||
file_data = file(self.filename, "U").read()
|
||||
|
||||
self._SetupLexer()
|
||||
self._SendSci(scintillacon.SCI_ADDTEXT, len(file_data), file_data)
|
||||
if self.lineno != None:
|
||||
self._SendSci(scintillacon.SCI_GOTOLINE, self.lineno)
|
||||
print("Scintilla's hwnd is", self.hwnd)
|
||||
|
||||
def _SetupLexer(self):
|
||||
h = self.hwnd
|
||||
styles = [
|
||||
((0, 0, 200, 0, 0x808080), None, scintillacon.SCE_P_DEFAULT ),
|
||||
((0, 2, 200, 0, 0x008000), None, scintillacon.SCE_P_COMMENTLINE ),
|
||||
((0, 2, 200, 0, 0x808080), None, scintillacon.SCE_P_COMMENTBLOCK ),
|
||||
((0, 0, 200, 0, 0x808000), None, scintillacon.SCE_P_NUMBER ),
|
||||
((0, 0, 200, 0, 0x008080), None, scintillacon.SCE_P_STRING ),
|
||||
((0, 0, 200, 0, 0x008080), None, scintillacon.SCE_P_CHARACTER ),
|
||||
((0, 0, 200, 0, 0x008080), None, scintillacon.SCE_P_TRIPLE ),
|
||||
((0, 0, 200, 0, 0x008080), None, scintillacon.SCE_P_TRIPLEDOUBLE),
|
||||
((0, 0, 200, 0, 0x000000), 0x008080, scintillacon.SCE_P_STRINGEOL),
|
||||
((0, 1, 200, 0, 0x800000), None, scintillacon.SCE_P_WORD),
|
||||
((0, 1, 200, 0, 0xFF0000), None, scintillacon.SCE_P_CLASSNAME ),
|
||||
((0, 1, 200, 0, 0x808000), None, scintillacon.SCE_P_DEFNAME),
|
||||
((0, 0, 200, 0, 0x000000), None, scintillacon.SCE_P_OPERATOR),
|
||||
((0, 0, 200, 0, 0x000000), None, scintillacon.SCE_P_IDENTIFIER ),
|
||||
]
|
||||
self._SendSci(scintillacon.SCI_SETLEXER, scintillacon.SCLEX_PYTHON, 0)
|
||||
self._SendSci(scintillacon.SCI_SETSTYLEBITS, 5)
|
||||
baseFormat = (-402653169, 0, 200, 0, 0, 0, 49, 'Courier New')
|
||||
for f, bg, stylenum in styles:
|
||||
self._SendSci(scintillacon.SCI_STYLESETFORE, stylenum, f[4])
|
||||
self._SendSci(scintillacon.SCI_STYLESETFONT, stylenum, baseFormat[7])
|
||||
if f[1] & 1: self._SendSci(scintillacon.SCI_STYLESETBOLD, stylenum, 1)
|
||||
else: self._SendSci(scintillacon.SCI_STYLESETBOLD, stylenum, 0)
|
||||
if f[1] & 2: self._SendSci(scintillacon.SCI_STYLESETITALIC, stylenum, 1)
|
||||
else: self._SendSci(scintillacon.SCI_STYLESETITALIC, stylenum, 0)
|
||||
self._SendSci(scintillacon.SCI_STYLESETSIZE, stylenum, int(baseFormat[2]/20))
|
||||
if bg is not None:
|
||||
self._SendSci(scintillacon.SCI_STYLESETBACK, stylenum, bg)
|
||||
self._SendSci(scintillacon.SCI_STYLESETEOLFILLED, stylenum, 1) # Only needed for unclosed strings.
|
||||
|
||||
# IOleWindow
|
||||
def GetWindow(self):
|
||||
return self.hwnd
|
||||
|
||||
def UIActivate(self, activate_state):
|
||||
print("OnActivate")
|
||||
|
||||
def DestroyViewWindow(self):
|
||||
win32gui.DestroyWindow(self.hwnd)
|
||||
self.hwnd = None
|
||||
print("Destroyed scintilla window")
|
||||
|
||||
def TranslateAccelerator(self, msg):
|
||||
return winerror.S_FALSE
|
||||
|
||||
def OnSize(self, hwnd, msg, wparam, lparam):
|
||||
x = win32api.LOWORD(lparam)
|
||||
y = win32api.HIWORD(lparam)
|
||||
win32gui.MoveWindow(self.hwnd, 0, 0, x, y, False)
|
||||
|
||||
def DllRegisterServer():
|
||||
import winreg
|
||||
key = winreg.CreateKey(winreg.HKEY_LOCAL_MACHINE,
|
||||
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\" \
|
||||
"Explorer\\Desktop\\Namespace\\" + \
|
||||
ShellFolderRoot._reg_clsid_)
|
||||
winreg.SetValueEx(key, None, 0, winreg.REG_SZ, ShellFolderRoot._reg_desc_)
|
||||
# And special shell keys under our CLSID
|
||||
key = winreg.CreateKey(winreg.HKEY_CLASSES_ROOT,
|
||||
"CLSID\\" + ShellFolderRoot._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)
|
||||
print(ShellFolderRoot._reg_desc_, "registration complete.")
|
||||
|
||||
def DllUnregisterServer():
|
||||
import winreg
|
||||
try:
|
||||
key = winreg.DeleteKey(winreg.HKEY_LOCAL_MACHINE,
|
||||
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\" \
|
||||
"Explorer\\Desktop\\Namespace\\" + \
|
||||
ShellFolderRoot._reg_clsid_)
|
||||
except WindowsError as details:
|
||||
import errno
|
||||
if details.errno != errno.ENOENT:
|
||||
raise
|
||||
print(ShellFolderRoot._reg_desc_, "unregistration complete.")
|
||||
|
||||
if __name__=='__main__':
|
||||
from win32com.server import register
|
||||
register.UseCommandLine(ShellFolderRoot,
|
||||
debug = debug,
|
||||
finalize_register = DllRegisterServer,
|
||||
finalize_unregister = DllUnregisterServer)
|
||||
15
Lib/site-packages/win32comext/shell/demos/shellexecuteex.py
Normal file
15
Lib/site-packages/win32comext/shell/demos/shellexecuteex.py
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
from win32com.shell import shell, shellcon
|
||||
import win32con
|
||||
|
||||
def ExplorePIDL():
|
||||
pidl = shell.SHGetSpecialFolderLocation(0, shellcon.CSIDL_DESKTOP)
|
||||
print("The desktop is at", shell.SHGetPathFromIDList(pidl))
|
||||
shell.ShellExecuteEx(fMask=shellcon.SEE_MASK_NOCLOSEPROCESS,
|
||||
nShow=win32con.SW_NORMAL,
|
||||
lpClass="folder",
|
||||
lpVerb="explore",
|
||||
lpIDList=pidl)
|
||||
print("Done!")
|
||||
|
||||
if __name__=='__main__':
|
||||
ExplorePIDL()
|
||||
41
Lib/site-packages/win32comext/shell/demos/viewstate.py
Normal file
41
Lib/site-packages/win32comext/shell/demos/viewstate.py
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
"""
|
||||
Demonstrates how to propagate a folder's view state to all its subfolders
|
||||
The format of the ColInfo stream is apparently undocumented, but
|
||||
it can be read raw from one folder and copied to another's view state.
|
||||
"""
|
||||
|
||||
from win32com.shell import shell, shellcon
|
||||
import pythoncom
|
||||
import os, sys
|
||||
|
||||
template_folder=os.path.split(sys.executable)[0]
|
||||
print('Template folder:', template_folder)
|
||||
template_pidl=shell.SHILCreateFromPath(template_folder,0)[0]
|
||||
template_pb=shell.SHGetViewStatePropertyBag(template_pidl, "Shell", shellcon.SHGVSPB_FOLDERNODEFAULTS, pythoncom.IID_IPropertyBag)
|
||||
|
||||
# Column info has to be read as a stream
|
||||
# This may blow up if folder has never been opened in Explorer and has no ColInfo yet
|
||||
template_iunk=template_pb.Read('ColInfo',pythoncom.VT_UNKNOWN)
|
||||
template_stream=template_iunk.QueryInterface(pythoncom.IID_IStream)
|
||||
streamsize=template_stream.Stat()[2]
|
||||
template_colinfo=template_stream.Read(streamsize)
|
||||
|
||||
def update_colinfo(not_used, dir_name, fnames):
|
||||
for fname in fnames:
|
||||
full_fname=os.path.join(dir_name,fname)
|
||||
if os.path.isdir(full_fname):
|
||||
print(full_fname)
|
||||
pidl=shell.SHILCreateFromPath(full_fname,0)[0]
|
||||
pb=shell.SHGetViewStatePropertyBag(pidl, "Shell", shellcon.SHGVSPB_FOLDERNODEFAULTS, pythoncom.IID_IPropertyBag)
|
||||
## not all folders already have column info, and we're replacing it anyway
|
||||
pb.Write('ColInfo', template_stream)
|
||||
iunk=pb.Read('ColInfo',pythoncom.VT_UNKNOWN)
|
||||
s=iunk.QueryInterface(pythoncom.IID_IStream)
|
||||
s.Write(template_colinfo)
|
||||
s=None
|
||||
## attribute names read from registry, can't find any way to enumerate IPropertyBag
|
||||
for attr in ('Address','Buttons','Col','Vid','WFlags','FFlags','Sort','SortDir','ShowCmd','FolderType','Mode','Rev'):
|
||||
pb.Write(attr, template_pb.Read(attr))
|
||||
pb=None
|
||||
os.path.walk(template_folder,update_colinfo,None)
|
||||
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
# A little sample that walks from the desktop into child
|
||||
# items.
|
||||
from win32com.shell import shell, shellcon
|
||||
|
||||
def walk(folder, depth=2, indent=""):
|
||||
try:
|
||||
pidls = folder.EnumObjects(0, shellcon.SHCONTF_FOLDERS)
|
||||
except shell.error:
|
||||
# no items
|
||||
return
|
||||
for pidl in pidls:
|
||||
dn = folder.GetDisplayNameOf(pidl,
|
||||
shellcon.SHGDN_NORMAL)
|
||||
print(indent, dn)
|
||||
if depth:
|
||||
try:
|
||||
child = folder.BindToObject(pidl, None,
|
||||
shell.IID_IShellFolder)
|
||||
except shell.error:
|
||||
pass
|
||||
else:
|
||||
walk(child, depth-1, indent+" ")
|
||||
|
||||
walk(shell.SHGetDesktopFolder())
|
||||
BIN
Lib/site-packages/win32comext/shell/shell.pyd
Normal file
BIN
Lib/site-packages/win32comext/shell/shell.pyd
Normal file
Binary file not shown.
1488
Lib/site-packages/win32comext/shell/shellcon.py
Normal file
1488
Lib/site-packages/win32comext/shell/shellcon.py
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,37 @@
|
|||
from win32com.shell import shell, shellcon
|
||||
import win32api
|
||||
import os
|
||||
|
||||
def testSHFileOperation(file_cnt):
|
||||
temp_dir=os.environ['temp']
|
||||
orig_fnames=[win32api.GetTempFileName(temp_dir,'sfo')[0] for x in range(file_cnt)]
|
||||
new_fnames=[os.path.join(temp_dir,'copy of '+os.path.split(orig_fnames[x])[1]) for x in range(file_cnt)]
|
||||
|
||||
pFrom='\0'.join(orig_fnames)
|
||||
pTo='\0'.join(new_fnames)
|
||||
|
||||
shell.SHFileOperation((0, shellcon.FO_MOVE, pFrom, pTo, shellcon.FOF_MULTIDESTFILES|shellcon.FOF_NOCONFIRMATION))
|
||||
for fname in orig_fnames:
|
||||
assert not os.path.isfile(fname)
|
||||
|
||||
for fname in new_fnames:
|
||||
assert os.path.isfile(fname)
|
||||
shell.SHFileOperation((0, shellcon.FO_DELETE, fname, None, shellcon.FOF_NOCONFIRMATION|shellcon.FOF_NOERRORUI))
|
||||
|
||||
def testSHNAMEMAPPINGS(file_cnt):
|
||||
## attemps to move a set of files to names that already exist, and generated filenames should be returned
|
||||
## as a sequence of 2-tuples created from SHNAMEMAPPINGS handle
|
||||
temp_dir=os.environ['temp']
|
||||
orig_fnames=[win32api.GetTempFileName(temp_dir,'sfo')[0] for x in range(file_cnt)]
|
||||
new_fnames=[win32api.GetTempFileName(temp_dir,'sfo')[0] for x in range(file_cnt)]
|
||||
pFrom='\0'.join(orig_fnames)
|
||||
pTo='\0'.join(new_fnames)
|
||||
rc, banyaborted, NameMappings=shell.SHFileOperation((0, shellcon.FO_MOVE, pFrom, pTo,
|
||||
shellcon.FOF_MULTIDESTFILES|shellcon.FOF_NOCONFIRMATION|shellcon.FOF_RENAMEONCOLLISION|shellcon.FOF_WANTMAPPINGHANDLE))
|
||||
|
||||
for old_fname, new_fname in NameMappings:
|
||||
print('Old:',old_fname, 'New:', new_fname)
|
||||
assert len(NameMappings)==file_cnt
|
||||
testSHFileOperation(10)
|
||||
testSHFileOperation(1)
|
||||
testSHNAMEMAPPINGS(5)
|
||||
21
Lib/site-packages/win32comext/shell/test/testShellFolder.py
Normal file
21
Lib/site-packages/win32comext/shell/test/testShellFolder.py
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
from win32com.shell import shell
|
||||
from win32com.shell.shellcon import *
|
||||
|
||||
sf = shell.SHGetDesktopFolder()
|
||||
print("Shell Folder is", sf)
|
||||
|
||||
names = []
|
||||
for i in sf: # Magically calls EnumObjects
|
||||
name = sf.GetDisplayNameOf(i, SHGDN_NORMAL)
|
||||
names.append(name)
|
||||
|
||||
# And get the enumerator manually
|
||||
enum = sf.EnumObjects(0, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN)
|
||||
num = 0
|
||||
for i in enum:
|
||||
num += 1
|
||||
if num != len(names):
|
||||
print("Should have got the same number of names!?")
|
||||
print("Found", len(names), "items on the desktop")
|
||||
for name in names:
|
||||
print(name)
|
||||
62
Lib/site-packages/win32comext/shell/test/testShellItem.py
Normal file
62
Lib/site-packages/win32comext/shell/test/testShellItem.py
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
# Test IShellItem and related interfaces
|
||||
from win32com.shell import shell, shellcon, knownfolders
|
||||
import unittest
|
||||
|
||||
class TestShellItem(unittest.TestCase):
|
||||
def assertShellItemsEqual(self, i1, i2):
|
||||
n1 = i1.GetDisplayName(shellcon.SHGDN_FORPARSING)
|
||||
n2 = i2.GetDisplayName(shellcon.SHGDN_FORPARSING)
|
||||
self.assertEqual(n1, n2)
|
||||
|
||||
def test_idlist_roundtrip(self):
|
||||
pidl = shell.SHGetSpecialFolderLocation(0, shellcon.CSIDL_DESKTOP)
|
||||
item = shell.SHCreateItemFromIDList(pidl, shell.IID_IShellItem)
|
||||
pidl_back = shell.SHGetIDListFromObject(item)
|
||||
self.assertEqual(pidl, pidl_back)
|
||||
|
||||
def test_parsing_name(self):
|
||||
sf = shell.SHGetDesktopFolder()
|
||||
flags = shellcon.SHCONTF_FOLDERS | shellcon.SHCONTF_NONFOLDERS
|
||||
children = sf.EnumObjects(0, flags)
|
||||
child_pidl = next(children)
|
||||
name = sf.GetDisplayNameOf(child_pidl, shellcon.SHGDN_FORPARSING)
|
||||
|
||||
item = shell.SHCreateItemFromParsingName(name, None, shell.IID_IShellItem)
|
||||
# test the name we get from the item is the same as from the folder.
|
||||
self.assertEqual(name, item.GetDisplayName(shellcon.SHGDN_FORPARSING))
|
||||
|
||||
def test_parsing_relative(self):
|
||||
desktop_pidl = shell.SHGetSpecialFolderLocation(0, shellcon.CSIDL_DESKTOP)
|
||||
desktop_item = shell.SHCreateItemFromIDList(desktop_pidl, shell.IID_IShellItem)
|
||||
|
||||
sf = shell.SHGetDesktopFolder()
|
||||
flags = shellcon.SHCONTF_FOLDERS | shellcon.SHCONTF_NONFOLDERS
|
||||
children = sf.EnumObjects(0, flags)
|
||||
child_pidl = next(children)
|
||||
name_flags = shellcon.SHGDN_FORPARSING | shellcon.SHGDN_INFOLDER
|
||||
name = sf.GetDisplayNameOf(child_pidl, name_flags)
|
||||
|
||||
item = shell.SHCreateItemFromRelativeName(desktop_item, name, None,
|
||||
shell.IID_IShellItem)
|
||||
# test the name we get from the item is the same as from the folder.
|
||||
self.assertEqual(name, item.GetDisplayName(name_flags))
|
||||
|
||||
def test_create_in_known_folder(self):
|
||||
item = shell.SHCreateItemInKnownFolder(knownfolders.FOLDERID_Desktop, 0,
|
||||
None, shell.IID_IShellItem)
|
||||
# this will do for now :)
|
||||
|
||||
def test_create_item_with_parent(self):
|
||||
desktop_pidl = shell.SHGetSpecialFolderLocation(0, shellcon.CSIDL_DESKTOP)
|
||||
desktop_item = shell.SHCreateItemFromIDList(desktop_pidl, shell.IID_IShellItem)
|
||||
|
||||
sf = shell.SHGetDesktopFolder()
|
||||
flags = shellcon.SHCONTF_FOLDERS | shellcon.SHCONTF_NONFOLDERS
|
||||
children = sf.EnumObjects(0, flags)
|
||||
child_pidl = next(children)
|
||||
item1 = shell.SHCreateItemWithParent(desktop_pidl, None, child_pidl, shell.IID_IShellItem)
|
||||
item2 = shell.SHCreateItemWithParent(None, sf, child_pidl, shell.IID_IShellItem)
|
||||
self.assertShellItemsEqual(item1, item2)
|
||||
|
||||
if __name__=='__main__':
|
||||
unittest.main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue