在windows上取消python中已暂停的文件副本



在windows上,我想用Python通过网络复制一堆文件。有时,网络没有响应,并且拷贝被搁置。如果发生这种情况,我想检查一下,当发生这种情况时,跳过有问题的文件。通过在这里问这个相关的问题,我发现了CopyFileEx函数,它允许使用回调函数,可以中止文件复制。

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,可选]如果在复制操作期间将此标志设置为TRUE,则操作将被取消。否则,副本操作将继续完成。

我想不出该怎么做。我再次尝试用相同的文件句柄调用相同的函数,但取消标志设置为TRUE,但这导致了一个错误,因为另一个进程正在使用有问题的文件。

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

lpProgressRoutine[in,可选]的回调函数的地址键入LPPROGRESS_ROUTINE,每次文件已被复制。此参数可以为NULL。了解更多信息有关进度回调函数的信息,请参阅CopyProgressRoutine函数。

此ProgressRoutine的文档声明,此回调要么在复制开始时调用,要么在文件的垃圾复制完成时调用。如果回调函数返回12(cancel,stop),则可以取消复制过程。然而,当垃圾的副本被搁置时,这个回调函数似乎没有被调用。

所以我的问题是:当这个副本被搁置时,我如何按文件取消它?

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)

相关内容

  • 没有找到相关文章

最新更新