win32file.ReadDirectoryChangesW找不到所有移动的文件

问题描述 投票:1回答:1

早上好,

我遇到了一个我在Python中创建的程序的一个特殊问题。看来,当我将文件从一个位置拖放到另一个位置时,并非所有文件都被模块注册为事件。

我一直在使用win32file和win32con来尝试获取与将文件从一个位置移动到另一个位置以进行处理相关的所有事件。

这是我的检测代码的一小部分:

import win32file
import win32con
def main():
    path_to_watch = 'D:\\'
    _file_list_dir = 1
    # Create a watcher handle
    _h_dir = win32file.CreateFile(
        path_to_watch,
        _file_list_dir,
        win32con.FILE_SHARE_READ |
        win32con.FILE_SHARE_WRITE |
        win32con.FILE_SHARE_DELETE,
        None,
        win32con.OPEN_EXISTING,
        win32con.FILE_FLAG_BACKUP_SEMANTICS,
        None
    )
    while 1:
        results = win32file.ReadDirectoryChangesW(
            _h_dir,
            1024,
            True,
            win32con.FILE_NOTIFY_CHANGE_FILE_NAME |
            win32con.FILE_NOTIFY_CHANGE_DIR_NAME |
            win32con.FILE_NOTIFY_CHANGE_ATTRIBUTES |
            win32con.FILE_NOTIFY_CHANGE_SIZE |
            win32con.FILE_NOTIFY_CHANGE_LAST_WRITE |
            win32con.FILE_NOTIFY_CHANGE_SECURITY,
            None,
            None
        )
        for _action, _file in results:
            if _action == 1:
                print 'found!'
            if _action == 2:
                print 'deleted!'

我拖放了7个文件,它只发现了4个。

# found!
# found!
# found!
# found!

如何检测所有丢弃的文件?

python python-2.7 winapi pywin32
1个回答
1
投票

[ActiveState.Docs]: win32file.ReadDirectoryChangesW(这是我能为[GitHub]: mhammond/pywin32 - Python for Windows (pywin32) Extensions找到的最好的文档)是[MS.Docs]: ReadDirectoryChangesW function的包装。这是它所说的(关于缓冲区):

  1. 一般 首次调用ReadDirectoryChangesW时,系统会分配一个缓冲区来存储更改信息。此缓冲区与目录句柄关联,直到它关闭并且其大小在其生命周期内不会更改。在对此函数的调用之间发生的目录更改将添加到缓冲区,然后在下一次调用时返回。如果缓冲区溢出,则丢弃缓冲区的全部内容,lpBytesReturned参数包含零,并且ReadDirectoryChangesW函数失败,错误代码为ERROR_NOTIFY_ENUM_DIR。 我的理解是,这是一个不同于作为参数传递的缓冲区(lpBuffer): 将前者传递给ReadDirectoryChangesW的每次调用(可以为每次调用传递不同的缓冲区(具有不同的大小)) 后者由系统分配,前者在功能调用之前明确地(由用户)分配 这是在函数调用之间存储数据(可能是某种原始格式)的函数,并且在调用函数时,缓冲区内容被复制(并格式化)为lpBuffer(如果不是在此期间过度使用(并丢弃))
  2. 同步 成功完成同步后,lpBuffer参数是一个格式化缓冲区,写入缓冲区的字节数在lpBytesReturned中可用。如果传输的字节数为零,则缓冲区要么太大而不能分配系统,要么太小而无法提供有关目录或子树中发生的所有更改的详细信息。在这种情况下,您应该通过枚举目录或子树来计算更改。 这有点证实了我之前的假设 “缓冲区要么太大而不能分配系统” - 也许当分配前一点的缓冲区时,它会考虑到nBufferLength?

无论如何,我拿了你的代码并“改变了一下”。

code.朋友:

import sys
import msvcrt
import pywintypes
import win32file
import win32con
import win32api
import win32event


FILE_LIST_DIRECTORY = 0x0001
FILE_ACTION_ADDED = 0x00000001
FILE_ACTION_REMOVED = 0x00000002

ASYNC_TIMEOUT = 5000

BUF_SIZE = 65536


def get_dir_handle(dir_name, async):
    flags_and_attributes = win32con.FILE_FLAG_BACKUP_SEMANTICS
    if async:
        flags_and_attributes |= win32con.FILE_FLAG_OVERLAPPED
    dir_handle = win32file.CreateFile(
        dir_name,
        FILE_LIST_DIRECTORY,
        (win32con.FILE_SHARE_READ |
         win32con.FILE_SHARE_WRITE |
         win32con.FILE_SHARE_DELETE),
        None,
        win32con.OPEN_EXISTING,
        flags_and_attributes,
        None
    )
    return dir_handle


def read_dir_changes(dir_handle, size_or_buf, overlapped):
    return win32file.ReadDirectoryChangesW(
        dir_handle,
        size_or_buf,
        True,
        (win32con.FILE_NOTIFY_CHANGE_FILE_NAME |
         win32con.FILE_NOTIFY_CHANGE_DIR_NAME |
         win32con.FILE_NOTIFY_CHANGE_ATTRIBUTES |
         win32con.FILE_NOTIFY_CHANGE_SIZE |
         win32con.FILE_NOTIFY_CHANGE_LAST_WRITE |
         win32con.FILE_NOTIFY_CHANGE_SECURITY),
        overlapped,
        None
    )


def handle_results(results):
    for item in results:
        print("    {} {:d}".format(item, len(item[1])))
        _action, _ = item
        if _action == FILE_ACTION_ADDED:
            print("    found!")
        if _action == FILE_ACTION_REMOVED:
            print("    deleted!")


def esc_pressed():
    return msvcrt.kbhit() and ord(msvcrt.getch()) == 27


def monitor_dir_sync(dir_handle):
    idx = 0
    while True:
        print("Index: {:d}".format(idx))
        idx += 1
        results = read_dir_changes(dir_handle, BUF_SIZE, None)
        handle_results(results)
        if esc_pressed():
            break


def monitor_dir_async(dir_handle):
    idx = 0
    buffer = win32file.AllocateReadBuffer(BUF_SIZE)
    overlapped = pywintypes.OVERLAPPED()
    overlapped.hEvent = win32event.CreateEvent(None, False, 0, None)
    while True:
        print("Index: {:d}".format(idx))
        idx += 1
        read_dir_changes(dir_handle, buffer, overlapped)
        rc = win32event.WaitForSingleObject(overlapped.hEvent, ASYNC_TIMEOUT)
        if rc == win32event.WAIT_OBJECT_0:
            bufer_size = win32file.GetOverlappedResult(dir_handle, overlapped, True)
            results = win32file.FILE_NOTIFY_INFORMATION(buffer, bufer_size)
            handle_results(results)
        elif rc == win32event.WAIT_TIMEOUT:
            #print("    timeout...")
            pass
        else:
            print("Received {:d}. Exiting".format(rc))
            break
        if esc_pressed():
            break
    win32api.CloseHandle(overlapped.hEvent)


def monitor_dir(dir_name, async=False):
    dir_handle = get_dir_handle(dir_name, async)
    if async:
        monitor_dir_async(dir_handle)
    else:
        monitor_dir_sync(dir_handle)
    win32api.CloseHandle(dir_handle)


def main():
    print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
    async = True
    print("Attempting {}ynchronous mode using a buffer {:d} bytes long...".format("As" if async else "S", BUF_SIZE))
    monitor_dir(".\\test", async=async)


if __name__ == "__main__":
    main()

笔记:

  • 尽可能使用常量
  • 将代码拆分为函数,使其模块化(并避免重复)
  • 添加了打印语句以增加输出
  • 添加了异步功能(如果dir中没有活动,脚本不会永久挂起)
  • 添加了一种在用户按下ESC时退出的方法(当然在同步模式下,dir中的事件也必须发生)
  • 针对不同的结果使用不同的值

输出:

e:\Work\Dev\StackOverflow\q049799109>dir /b test
0123456789.txt
01234567890123456789.txt
012345678901234567890123456789.txt
0123456789012345678901234567890123456789.txt
01234567890123456789012345678901234567890123456789.txt
012345678901234567890123456789012345678901234567890123456789.txt
0123456789012345678901234567890123456789012345678901234567890123456789.txt
01234567890123456789012345678901234567890123456789012345678901234567890123456789.txt
012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.txt
0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.txt

e:\Work\Dev\StackOverflow\q049799109>
e:\Work\Dev\StackOverflow\q049799109>"C:\Install\x64\HPE\OPSWpython\2.7.10__00\python.exe" code.py
Python 2.7.10 (default, Mar  8 2016, 15:02:46) [MSC v.1600 64 bit (AMD64)] on win32

Attempting Synchronous mode using a buffer 512 bytes long...
Index: 0
    (2, u'0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.txt') 104
    deleted!
Index: 1
    (2, u'012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.txt') 94
    deleted!
Index: 2
    (2, u'01234567890123456789012345678901234567890123456789012345678901234567890123456789.txt') 84
    deleted!
Index: 3
    (2, u'0123456789012345678901234567890123456789012345678901234567890123456789.txt') 74
    deleted!
    (2, u'012345678901234567890123456789012345678901234567890123456789.txt') 64
    deleted!
Index: 4
    (2, u'01234567890123456789012345678901234567890123456789.txt') 54
    deleted!
Index: 5
    (2, u'0123456789012345678901234567890123456789.txt') 44
    deleted!
    (2, u'012345678901234567890123456789.txt') 34
    deleted!
Index: 6
    (2, u'01234567890123456789.txt') 24
    deleted!
    (2, u'0123456789.txt') 14
    deleted!
Index: 7
    (1, u'0123456789.txt') 14
    found!
Index: 8
    (3, u'0123456789.txt') 14
Index: 9
    (1, u'01234567890123456789.txt') 24
    found!
Index: 10
    (3, u'01234567890123456789.txt') 24
    (1, u'012345678901234567890123456789.txt') 34
    found!
    (3, u'012345678901234567890123456789.txt') 34
    (1, u'0123456789012345678901234567890123456789.txt') 44
    found!
Index: 11
    (3, u'0123456789012345678901234567890123456789.txt') 44
    (1, u'01234567890123456789012345678901234567890123456789.txt') 54
    found!
    (3, u'01234567890123456789012345678901234567890123456789.txt') 54
Index: 12
Index: 13
    (1, u'01234567890123456789012345678901234567890123456789012345678901234567890123456789.txt') 84
    found!
Index: 14
Index: 15
    (1, u'0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.txt') 104
    found!
Index: 16
    (3, u'0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.txt') 104
Index: 17
    (1, u'a') 1
    found!
Index: 18
    (3, u'a') 1

e:\Work\Dev\StackOverflow\q049799109>
e:\Work\Dev\StackOverflow\q049799109>"C:\Install\x64\HPE\OPSWpython\2.7.10__00\python.exe" code.py
Python 2.7.10 (default, Mar  8 2016, 15:02:46) [MSC v.1600 64 bit (AMD64)] on win32

Attempting Synchronous mode using a buffer 65536 bytes long...
Index: 0
    (2, u'0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.txt') 104
    deleted!
Index: 1
    (2, u'012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.txt') 94
    deleted!
Index: 2
    (2, u'01234567890123456789012345678901234567890123456789012345678901234567890123456789.txt') 84
    deleted!
Index: 3
    (2, u'0123456789012345678901234567890123456789012345678901234567890123456789.txt') 74
    deleted!
Index: 4
    (2, u'012345678901234567890123456789012345678901234567890123456789.txt') 64
    deleted!
Index: 5
    (2, u'01234567890123456789012345678901234567890123456789.txt') 54
    deleted!
Index: 6
    (2, u'0123456789012345678901234567890123456789.txt') 44
    deleted!
Index: 7
    (2, u'012345678901234567890123456789.txt') 34
    deleted!
    (2, u'01234567890123456789.txt') 24
    deleted!
    (2, u'0123456789.txt') 14
    deleted!
Index: 8
    (1, u'0123456789.txt') 14
    found!
Index: 9
    (3, u'0123456789.txt') 14
Index: 10
    (1, u'01234567890123456789.txt') 24
    found!
Index: 11
    (3, u'01234567890123456789.txt') 24
Index: 12
    (1, u'012345678901234567890123456789.txt') 34
    found!
Index: 13
    (3, u'012345678901234567890123456789.txt') 34
Index: 14
    (1, u'0123456789012345678901234567890123456789.txt') 44
    found!
Index: 15
    (3, u'0123456789012345678901234567890123456789.txt') 44
Index: 16
    (1, u'01234567890123456789012345678901234567890123456789.txt') 54
    found!
    (3, u'01234567890123456789012345678901234567890123456789.txt') 54
Index: 17
    (1, u'012345678901234567890123456789012345678901234567890123456789.txt') 64
    found!
    (3, u'012345678901234567890123456789012345678901234567890123456789.txt') 64
    (1, u'0123456789012345678901234567890123456789012345678901234567890123456789.txt') 74
    found!
Index: 18
    (3, u'0123456789012345678901234567890123456789012345678901234567890123456789.txt') 74
    (1, u'01234567890123456789012345678901234567890123456789012345678901234567890123456789.txt') 84
    found!
    (3, u'01234567890123456789012345678901234567890123456789012345678901234567890123456789.txt') 84
    (1, u'012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.txt') 94
    found!
    (3, u'012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.txt') 94
    (1, u'0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.txt') 104
    found!
    (3, u'0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.txt') 104
Index: 20
    (2, u'a') 1
    deleted!

e:\Work\Dev\StackOverflow\q049799109>
e:\Work\Dev\StackOverflow\q049799109>"C:\Install\x64\HPE\OPSWpython\2.7.10__00\python.exe" code.py
Python 2.7.10 (default, Mar  8 2016, 15:02:46) [MSC v.1600 64 bit (AMD64)] on win32

Attempting Asynchronous mode using a buffer 512 bytes long...
Index: 0
Index: 1
    (2, u'0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.txt') 104
    deleted!
Index: 2
    (2, u'012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.txt') 94
    deleted!
Index: 3
    (2, u'01234567890123456789012345678901234567890123456789012345678901234567890123456789.txt') 84
    deleted!
Index: 4
    (2, u'012345678901234567890123456789012345678901234567890123456789.txt') 64
    deleted!
Index: 5
    (2, u'01234567890123456789012345678901234567890123456789.txt') 54
    deleted!
Index: 6
    (2, u'0123456789012345678901234567890123456789.txt') 44
    deleted!
Index: 7
    (2, u'012345678901234567890123456789.txt') 34
    deleted!
Index: 8
    (2, u'01234567890123456789.txt') 24
    deleted!
Index: 9
    (2, u'0123456789.txt') 14
    deleted!
Index: 10
Index: 11
Index: 12
    (1, u'0123456789.txt') 14
    found!
Index: 13
    (1, u'01234567890123456789.txt') 24
    found!
Index: 14
    (1, u'012345678901234567890123456789.txt') 34
    found!
Index: 15
    (3, u'012345678901234567890123456789.txt') 34
Index: 16
    (1, u'0123456789012345678901234567890123456789.txt') 44
    found!
    (3, u'0123456789012345678901234567890123456789.txt') 44
Index: 17
Index: 18
    (1, u'0123456789012345678901234567890123456789012345678901234567890123456789.txt') 74
    found!
Index: 19
Index: 20
    (1, u'012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.txt') 94
    found!
Index: 21
Index: 22
Index: 23
Index: 24

e:\Work\Dev\StackOverflow\q049799109>
e:\Work\Dev\StackOverflow\q049799109>"C:\Install\x64\HPE\OPSWpython\2.7.10__00\python.exe" code.py
Python 2.7.10 (default, Mar  8 2016, 15:02:46) [MSC v.1600 64 bit (AMD64)] on win32

Attempting Asynchronous mode using a buffer 65536 bytes long...
Index: 0
Index: 1
    (2, u'0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.txt') 104
    deleted!
Index: 2
    (2, u'012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.txt') 94
    deleted!
Index: 3
    (2, u'01234567890123456789012345678901234567890123456789012345678901234567890123456789.txt') 84
    deleted!
Index: 4
    (2, u'0123456789012345678901234567890123456789012345678901234567890123456789.txt') 74
    deleted!
Index: 5
    (2, u'012345678901234567890123456789012345678901234567890123456789.txt') 64
    deleted!
Index: 6
    (2, u'01234567890123456789012345678901234567890123456789.txt') 54
    deleted!
Index: 7
    (2, u'0123456789012345678901234567890123456789.txt') 44
    deleted!
Index: 8
    (2, u'012345678901234567890123456789.txt') 34
    deleted!
    (2, u'01234567890123456789.txt') 24
    deleted!
Index: 9
    (2, u'0123456789.txt') 14
    deleted!
Index: 10
Index: 11
Index: 12
    (1, u'0123456789.txt') 14
    found!
Index: 13
    (1, u'01234567890123456789.txt') 24
    found!
Index: 14
    (1, u'012345678901234567890123456789.txt') 34
    found!
Index: 15
    (3, u'012345678901234567890123456789.txt') 34
    (1, u'0123456789012345678901234567890123456789.txt') 44
    found!
    (3, u'0123456789012345678901234567890123456789.txt') 44
Index: 16
    (1, u'01234567890123456789012345678901234567890123456789.txt') 54
    found!
    (3, u'01234567890123456789012345678901234567890123456789.txt') 54
    (1, u'012345678901234567890123456789012345678901234567890123456789.txt') 64
    found!
    (3, u'012345678901234567890123456789012345678901234567890123456789.txt') 64
    (1, u'0123456789012345678901234567890123456789012345678901234567890123456789.txt') 74
    found!
Index: 17
    (3, u'0123456789012345678901234567890123456789012345678901234567890123456789.txt') 74
    (1, u'01234567890123456789012345678901234567890123456789012345678901234567890123456789.txt') 84
    found!
    (3, u'01234567890123456789012345678901234567890123456789012345678901234567890123456789.txt') 84
    (1, u'012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.txt') 94
    found!
    (3, u'012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.txt') 94
    (1, u'0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.txt') 104
    found!
    (3, u'0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.txt') 104
Index: 18
Index: 19

备注:

  • 使用包含10个不同名称的文件的dir测试(重复0123456789)
  • 有4个运行: 同步 512B缓冲区 64K缓冲区 异步 512B缓冲区 64K缓冲区
  • 对于每个(上面)运行,文件是(使用Windows Commander进行操作): 从dir移动(涉及删除) 移动(返回)到目录(涉及添加)
  • 这只是每个组合的一次运行,到目前为止不能作为基准测试,但我运行了几次脚本,模式趋于一致
  • 在运行期间删除文件的变化不会太大,这意味着事件均匀分布(很少)时间
  • 另一方面,添加文件取决于缓冲区大小。另一个值得注意的事情是,每增加一个,就有2个事件
  • 从性能的角度来看,异步模式并没有带来任何改进(正如我所期待的那样),相反,它往往会减慢速度。但它的最大优点是可以在超时时正常退出(异常中断可能会使资源锁定直到程序退出(有时甚至超出!))

底线是没有避免丢失事件的方法。采取的每项措施都可以通过增加生成事件的数量来“打败”。

减少损失:

  • 缓冲区大小。这是您案件中的(主要)问题。遗憾的是,文档不太清楚,没有关于它应该有多大的指导。浏览C论坛我注意到64K是一个常见的值。然而: 不可能有一个巨大的缓冲区,并且在成功之前无法减小其大小,因为这将意味着在计算缓冲区大小时丢失所有生成的事件 即使64k足以容纳(多次)我在测试中生成的所有事件,但仍有一些事件丢失了。也许这是因为我在开始时谈到的“神奇”缓冲
  • 尽可能减少事件的数量。在您的情况下,我注意到您只对添加和删除事件(FILE_ACTION_ADDED和FILE_ACTION_REMOVED)感兴趣。仅为ReadDirectoryChangesW指定适当的FILE_NOTIFY_CHANGE_ *标志(例如,您不关心FILE_ACTION_MODIFIED,但是在添加文件时您正在接收它)
  • 尝试在几个子目录中拆分目录内容并同时监视它们。例如,如果您只关心在一个目录和一堆子目录中发生的更改,则递归监视整个树是没有意义的,因为它很可能会产生大量无用的事件。无论如何,如果并行处理,不要因为GIL而使用线程! ([Python.Wiki]: GlobalInterpreterLock)。请改用[Python 2]: multiprocessing - Process-based “threading” interface
  • 提高在循环中运行的代码的速度,以便在ReadDirectoryChangesW之外花费尽可能少的时间(当生成的事件可能溢出缓冲区时)。当然,下面的一些项目可能会产生微不足道的影响,并且(也会产生一些不良副作用),但无论如何我都要列出它们: 做尽可能少的处理并尝试延迟它。也许在另一个过程中做(因为GIL) 摆脱所有类似的打印声明 而不是例如win32con.FILE_NOTIFY_CHANGE_FILE_NAME在脚本开头使用from win32con import FILE_NOTIFY_CHANGE_FILE_NAME,并且只在循环中使用FILE_NOTIFY_CHANGE_FILE_NAME(以避免模块中的变量查找) 不要使用函数(因为调用/ ret类似指令) - 不确定 尝试使用win32file.GetQueuedCompletionStatus方法获取结果(仅限异步) 事情往往会变得更好(当然也有例外),尝试切换到更新的Python版本。也许它会跑得更快 使用C - 这可能是不可取的,但它可能有一些好处: pywin32执行的Python和C之间不会有来回转换 - 但我没有使用分析器来检查它们花费了多少时间 lpCompletionRoutine(pywin32不提供)也可用,也许它更快 作为替代方案,可以使用ctypes调用C,但这需要一些工作,我觉得它不值得
© www.soinside.com 2019 - 2024. All rights reserved.