在Windows上取消python中的停滞文件副本

在Windows上,我想用Python将一大堆文件复制到networking上。 有时,networking没有响应,副本停滞。 我想检查,如果发生这种情况,跳过有问题的文件,当发生这种情况。 通过在这里询问这个相关的问题,我发现了关于CopyFileEx函数,它允许使用callback函数,可以中止文件复制。

Python中的实现如下所示:

import win32file def Win32_CopyFileEx( ExistingFileName, NewFileName, Canc = False): win32file.CopyFileEx( ExistingFileName, # PyUNICODE | File to be copied NewFileName, # PyUNICODE | Place to which it will be copied Win32_CopyFileEx_ProgressRoutine, # CopyProgressRoutine | A python function that receives progress updates, can be None Data = None, # object | An arbitrary object to be passed to the callback function Cancel = Canc, # boolean | Pass True to cancel a restartable copy that was previously interrupted CopyFlags = win32file.COPY_FILE_RESTARTABLE, # int | Combination of COPY_FILE_* flags Transaction = None # PyHANDLE | Handle to a transaction as returned by win32transaction::CreateTransaction ) 

从CopyFileEx函数的文档中,我可以看到取消正在运行的副本的两种可能性。

pbCancel [in,optional]如果在复制操作期间将此标志设置为TRUE ,操作将被取消。 否则,复制操作将继续完成。

我无法弄清楚如何做到这一点。 我试着用相同的文件句柄再次调用相同的函数,但是将cancel标志设置为TRUE ,但是由于有问题的文件正被另一个进程使用,导致出现错误。

另一种可能性似乎是callback函数:

lpProgressRoutine [in,optional] LPPROGRESS_ROUTINEtypes的callback函数的地址,每当文件的另一部分被复制时被调用。 该参数可以是NULL。 有关进度callback函数的更多信息,请参阅CopyProgressRoutine函数。

这个ProgressRoutine的文档指出,这个callback要么在拷贝开始时要么被调用,或者当文件的一个垃圾完成拷贝时。 如果返回12 (取消,停止),callback函数可以取消复制过程。 但是,这个callback函数似乎没有被调用,当一个垃圾的副本停滞。

所以我的问题是:如何在每个文件停止的情况下取消此副本?

Solutions Collecting From Web of "在Windows上取消python中的停滞文件副本"

win32file.CopyFileEx不允许传递Cancel作为布尔值或整数值。 在API中,它是一个LPBOOL指针,它允许调用者在另一个线程中同时设置它的值。 你将不得不使用ctypes,Cython或C扩展来获得这个级别的控制。 下面我写了一个使用ctypes的例子。

如果由于线程在同步I / O上被阻塞而取消复制不起作用,则可以尝试在进度例程中传递的文件句柄上调用CancelIoEx ,或者使用CancelSynchronousIo取消该线程的所有同步I / O 。 这些I / O取消功能是在Windows Vista中添加的。 它们在Windows XP中不可用,万一你还在支持它。

 import ctypes from ctypes import wintypes kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) COPY_FILE_FAIL_IF_EXISTS = 0x0001 COPY_FILE_RESTARTABLE = 0x0002 COPY_FILE_OPEN_SOURCE_FOR_WRITE = 0x0004 COPY_FILE_ALLOW_DECRYPTED_DESTINATION = 0x0008 COPY_FILE_COPY_SYMLINK = 0x0800 COPY_FILE_NO_BUFFERING = 0x1000 CALLBACK_CHUNK_FINISHED = 0 CALLBACK_STREAM_SWITCH = 1 PROGRESS_CONTINUE = 0 PROGRESS_CANCEL = 1 PROGRESS_STOP = 2 PROGRESS_QUIET = 3 ERROR_REQUEST_ABORTED = 0x04D3 if not hasattr(wintypes, 'LPBOOL'): wintypes.LPBOOL = ctypes.POINTER(wintypes.BOOL) def _check_bool(result, func, args): if not result: raise ctypes.WinError(ctypes.get_last_error()) return args LPPROGRESS_ROUTINE = ctypes.WINFUNCTYPE( wintypes.DWORD, # _Retval_ wintypes.LARGE_INTEGER, # _In_ TotalFileSize wintypes.LARGE_INTEGER, # _In_ TotalBytesTransferred wintypes.LARGE_INTEGER, # _In_ StreamSize wintypes.LARGE_INTEGER, # _In_ StreamBytesTransferred wintypes.DWORD, # _In_ dwStreamNumber wintypes.DWORD, # _In_ dwCallbackReason wintypes.HANDLE, # _In_ hSourceFile wintypes.HANDLE, # _In_ hDestinationFile wintypes.LPVOID) # _In_opt_ lpData kernel32.CopyFileExW.errcheck = _check_bool kernel32.CopyFileExW.argtypes = ( wintypes.LPCWSTR, # _In_ lpExistingFileName wintypes.LPCWSTR, # _In_ lpNewFileName LPPROGRESS_ROUTINE, # _In_opt_ lpProgressRoutine wintypes.LPVOID, # _In_opt_ lpData wintypes.LPBOOL, # _In_opt_ pbCancel wintypes.DWORD) # _In_ dwCopyFlags @LPPROGRESS_ROUTINE def debug_progress(tsize, ttrnsfr, stsize, sttrnsfr, stnum, reason, hsrc, hdst, data): print('ttrnsfr: %d, stnum: %d, stsize: %d, sttrnsfr: %d, reason: %d' % (ttrnsfr, stnum, stsize, sttrnsfr, reason)) return PROGRESS_CONTINUE def copy_file(src, dst, cancel=None, flags=0, cbprogress=None, data=None): if isinstance(cancel, int): cancel = ctypes.byref(wintypes.BOOL(cancel)) elif cancel is not None: cancel = ctypes.byref(cancel) if cbprogress is None: cbprogress = LPPROGRESS_ROUTINE() kernel32.CopyFileExW(src, dst, cbprogress, data, cancel, flags) 

 if __name__ == '__main__': import os import tempfile import threading src_fd, src = tempfile.mkstemp() os.write(src_fd, os.urandom(16 * 2 ** 20)) os.close(src_fd) dst = tempfile.mktemp() cancel = wintypes.BOOL(False) t = threading.Timer(0.001, type(cancel).value.__set__, (cancel, True)) t.start() try: copy_file(src, dst, cancel, cbprogress=debug_progress) except OSError as e: print(e) assert e.winerror == ERROR_REQUEST_ABORTED finally: if os.path.exists(src): os.remove(src) if os.path.exists(dst): os.remove(dst)