tylerART
tylerART

Reputation: 123

Copy using the Windows copy dialog

I'm currently using shutil.copy2() to copy a large number of image files and folders (anywhere between 0.5 and 5 gigs). Shutil works fine, but it's so slow. I'm wondering if there is a way to pass this info over to Windows to make the copy and give me its standard transfer dialog box. You know, this guy...

http://www.top-windows-tutorials.com/images/file-copy.jpg

Many times, my script will take about twice the time the standard windows copy takes, and it makes me nervous that my python interpreter hangs while running the copy. I run the copy process multiple times and I'm looking to cut the time down.

Upvotes: 12

Views: 3809

Answers (4)

ewerybody
ewerybody

Reputation: 1590

*bump* Windows 10!

With all your help and Virgil Dupras' send2trash:
I just cooked a vanilla Python version only using ctypes:

import os
import ctypes
from ctypes import wintypes


class _SHFILEOPSTRUCTW(ctypes.Structure):
    _fields_ = [("hwnd", wintypes.HWND),
                ("wFunc", wintypes.UINT),
                ("pFrom", wintypes.LPCWSTR),
                ("pTo", wintypes.LPCWSTR),
                ("fFlags", ctypes.c_uint),
                ("fAnyOperationsAborted", wintypes.BOOL),
                ("hNameMappings", ctypes.c_uint),
                ("lpszProgressTitle", wintypes.LPCWSTR)]


def win_shell_copy(src, dst):
    """
    :param str src: Source path to copy from. Must exist!
    :param str dst: Destination path to copy to. Will be created on demand.
    :return: Success of the operation. False means is was aborted!
    :rtype: bool
    """
    if not os.path.exist(src):
        print('No such source "%s"' % src)
        return False

    src_buffer = ctypes.create_unicode_buffer(src, len(src) + 2)
    dst_buffer = ctypes.create_unicode_buffer(dst, len(dst) + 2)

    fileop = _SHFILEOPSTRUCTW()
    fileop.hwnd = 0
    fileop.wFunc = 2  # FO_COPY
    fileop.pFrom = wintypes.LPCWSTR(ctypes.addressof(src_buffer))
    fileop.pTo = wintypes.LPCWSTR(ctypes.addressof(dst_buffer))
    fileop.fFlags = 512  # FOF_NOCONFIRMMKDIR
    fileop.fAnyOperationsAborted = 0
    fileop.hNameMappings = 0
    fileop.lpszProgressTitle = None

    result = ctypes.windll.shell32.SHFileOperationW(ctypes.byref(fileop))
    return not result

✔ Tested on Python 3.7 and 2.7 also with long src and dst paths.

Upvotes: 1

frmdstryr
frmdstryr

Reputation: 21362

Update: See

Would be nice to have it wrapped in a library... With the help of the answers above, I was able to get it working on windows 7 as follows.

import pythoncom
from win32com.shell import shell,shellcon

def win_copy_files(src_files,dst_folder):           
        # @see IFileOperation
        pfo = pythoncom.CoCreateInstance(shell.CLSID_FileOperation,None,pythoncom.CLSCTX_ALL,shell.IID_IFileOperation)

        # Respond with Yes to All for any dialog
        # @see http://msdn.microsoft.com/en-us/library/bb775799(v=vs.85).aspx
        pfo.SetOperationFlags(shellcon.FOF_NOCONFIRMATION)

        # Set the destionation folder
        dst = shell.SHCreateItemFromParsingName(dst_folder,None,shell.IID_IShellItem)

        for f in src_files:
                src = shell.SHCreateItemFromParsingName(f,None,shell.IID_IShellItem)
                pfo.CopyItem(src,dst) # Schedule an operation to be performed


        # @see http://msdn.microsoft.com/en-us/library/bb775780(v=vs.85).aspx
        success = pfo.PerformOperations()

        # @see sdn.microsoft.com/en-us/library/bb775769(v=vs.85).aspx
        aborted = pfo.GetAnyOperationsAborted()
        return success and not aborted


files_to_copy = [r'C:\Users\jrm\Documents\test1.txt',r'C:\Users\jrm\Documents\test2.txt']
dest_folder = r'C:\Users\jrm\Documents\dst'
win_copy_files(files_to_copy,dest_folder)

The references here were also very helpful: http://timgolden.me.uk/pywin32-docs/html/com/win32com/HTML/QuickStartClientCom.html

Upvotes: 2

Nikita Nemkin
Nikita Nemkin

Reputation: 2820

If your goal is a fancy copy dialog, SHFileOperation Windows API function provides that. pywin32 package has a python binding for it, ctypes is also an option (google "SHFileOperation ctypes" for examples).

Here is my (very lightly tested) example using pywin32:

import os.path
from win32com.shell import shell, shellcon


def win32_shellcopy(src, dest):
    """
    Copy files and directories using Windows shell.

    :param src: Path or a list of paths to copy. Filename portion of a path
                (but not directory portion) can contain wildcards ``*`` and
                ``?``.
    :param dst: destination directory.
    :returns: ``True`` if the operation completed successfully,
              ``False`` if it was aborted by user (completed partially).
    :raises: ``WindowsError`` if anything went wrong. Typically, when source
             file was not found.

    .. seealso:
        `SHFileperation on MSDN <http://msdn.microsoft.com/en-us/library/windows/desktop/bb762164(v=vs.85).aspx>`
    """
    if isinstance(src, basestring):  # in Py3 replace basestring with str
        src = os.path.abspath(src)
    else:  # iterable
        src = '\0'.join(os.path.abspath(path) for path in src)

    result, aborted = shell.SHFileOperation((
        0,
        shellcon.FO_COPY,
        src,
        os.path.abspath(dest),
        shellcon.FOF_NOCONFIRMMKDIR,  # flags
        None,
        None))

    if not aborted and result != 0:
        # Note: raising a WindowsError with correct error code is quite
        # difficult due to SHFileOperation historical idiosyncrasies.
        # Therefore we simply pass a message.
        raise WindowsError('SHFileOperation failed: 0x%08x' % result)

    return not aborted

You can also perform the same copy operation in "silent mode" (no dialog, no confirmationsm, no error popups) if you set the flags above to shellcon.FOF_SILENT | shellcon.FOF_NOCONFIRMATION | shellcon.FOF_NOERRORUI | shellcon.FOF_NOCONFIRMMKDIR. See SHFILEOPSTRUCT for details.

Upvotes: 5

kirbyfan64sos
kirbyfan64sos

Reputation: 10727

See IFileCopy. IFileOperation may be available through ctypes and shell32.dll, I am not sure.

Upvotes: 1

Related Questions