Copy Windows files with Python using Shell API CopyItem
See the question and my original answer on StackOverflowHere is some sample code that supports Windows 10 and Windows 7, however not in the same manner.
The WPDShext.dll component shipped in Windows which is reponsible for handling the portable devices hierarchy is more limited in Windows 7 than in Window 10. It does not support interfaces such as IStream
or IStorage
from an IShellItem::BindToHandler implementation, which is probably what IFileOperation
tries to do.
All it seems it can do is support an IStream when requested through its root folder's IShellFolder::BindToObject method implementation.
import pythoncom
import platform
from win32comext.shell import shell, shellcon
from win32com import storagecon
# this points to my IPhone's root, adapt to your code or enumerate from root
folderPath = "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\\\\\?\\usb#vid_05ac&pid_12a8&mi_00#6&211e6304&0&0000#{6ac27878-a6fa-4155-ba85-f98f491d4f33}"
# this relative path points to some image in that IPhone (same path as shown in Explorer)
itemPath = "Internal Storage\\DCIM\\202308__\\IMG_2047.JPG"
# get the root folder (IPhone, etc.)
folderItem = shell.SHCreateItemFromParsingName(folderPath, None, shell.IID_IShellItem)
if (int(platform.release()) < 8): # release is hopefully reliable enough for Windows 7 detection...
# get WPDShext.dll's namespace extension IShellFolder implementation
folder = folderItem.BindToHandler(None, shell.BHID_SFObject, shell.IID_IShellFolder)
# get the relative child item
ret = folder.ParseDisplayName(None, None, itemPath, 0)
# get the item's stream using the relative pidl, from IShellFolder
inputStream = folder.BindToObject(ret[1], None, pythoncom.IID_IStream)
# get its size, don't ask for name in this code (avoid releasing mem)
inputStat = inputStream.Stat(1) # STATFLAG_NONAME
# open a file for writing & copy from input to output
outputStream = shell.SHCreateStreamOnFileEx("c:\\temp\\IMG_2047.JPG", storagecon.STGM_CREATE | storagecon.STGM_WRITE, 0, True, None)
inputStream.CopyTo(outputStream, inputStat[2])
else:
src = shell.SHCreateItemFromRelativeName(folderItem, itemPath, None, shell.IID_IShellItem)
dst = shell.SHCreateItemFromParsingName("c:\\temp", None, shell.IID_IShellItem)
fo = pythoncom.CoCreateInstance(shell.CLSID_FileOperation, None, pythoncom.CLSCTX_ALL, shell.IID_IFileOperation)
fo.CopyItem(src, dst)
fo.PerformOperations()
Note you can also use the WPD API if you want but 1) it's not super easy to use and 2) not sure it's easy to do in python.
Here is some extra sample code that allows you to determine what to use for the folderPath
variable used above:
import pythoncom
from win32comext.shell import shell
desktopItem = shell.SHCreateItemFromParsingName("", None, shell.IID_IShellItem)
for item in desktopItem.BindToHandler(None, shell.BHID_EnumItems, shell.IID_IEnumShellItems):
print(item.GetDisplayName(0) + " parsing name: " + item.GetDisplayName(0x80018001)) # SIGDN_DESKTOPABSOLUTEPARSING
It will display something like this:
Downloads parsing name: ::{088E3905-0323-4B02-9826-5D99428E115F}
3D Objects parsing name: ::{0DB7E03F-FC29-4DC6-9020-FF41B59E513A}
Pictures parsing name: ::{24AD3AD4-A569-4530-98E1-AB02F9417AA8}
Music parsing name: ::{3DFDF296-DBEC-4FB4-81D1-6A3438BCF4DE}
Desktop parsing name: ::{B4BFCC3A-DB2C-424C-B029-7FE99A87C641}
Documents parsing name: ::{D3162B92-9365-467A-956B-92703ACA08AF}
Videos parsing name: ::{F86FA3AB-70D2-4FC7-9C99-FCBF05467F3A}
Apple iPhone parsing name: \\?\usb#vid_05ac&pid_12a8&mi_00#6&211e6304&0&0000#{6ac27878-a6fa-4155-ba85-f98f491d4f33}
Local Disk (C:) parsing name: C:
DATA (D:) parsing name: D:
And {20D04FE0-3AEA-1069-A2D8-08002B30309D}
is CLSID_MyComputer
(aka "This PC" or "Computer" or whatever in localized Windows version)