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

时间:2018-04-12 14:24:53

标签: python python-2.7 winapi pywin32

早上好,

我遇到了一个我在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!

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

1 个答案:

答案 0 :(得分: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
  3. 无论如何,我把你的代码改成了#34;有点"。

    code.py

    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()
    

    备注

    • 尽可能使用常数
    • 将代码拆分为函数,使其模块化(并避免重复)
    • 添加 print 语句以增加输出
    • 添加了异步功能(如果目录中没有活动,脚本不会永久挂起)
    • 添加了一种在用户按下 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
    

    <强>说明

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

    底线是没有避免丢失事件的方法。采取的每一项措施都可以被打败&#34;通过增加生成事件的数量。

    尽量减少损失:

    • 缓冲区大小。这是您案件中的(主要)问题。不幸的是,文档不太清楚,没有关于它应该有多大的指导。浏览 C 论坛我注意到 64K 是一个常见值。但是:

      • 不可能有一个巨大的缓冲区,并且在成功之前无法减小其大小,因为这意味着在计算缓冲区大小时会丢失所有生成的事件

        < / LI>
      • 即使 64k 足以容纳(多次)我在测试中生成的所有事件,但有些事件仍然丢失。也许那是因为&#34;魔法&#34;我在开头谈到的缓冲区

    • 尽可能减少事件数量。在您的情况下,我注意到您只对添加和删除事件感兴趣( 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

      • 摆脱所有 print 之类的陈述

      • 而不是例如win32con.FILE_NOTIFY_CHANGE_FILE_NAME在脚本开头使用from win32con import FILE_NOTIFY_CHANGE_FILE_NAME,并且只在循环中使用 FILE_NOTIFY_CHANGE_FILE_NAME (以避免模块中的变量查找)

      • 不要使用功能(因为调用 / ret 就像说明一样) - 不确定

      • 尝试使用win32file.GetQueuedCompletionStatus方法获取结果(仅 async

      • 由于时间往往会变得更好(当然也有例外),尝试切换到较新的 Python 版本。也许它会跑得更快

      • 使用 C - 这可能是不受欢迎的,但它可能有一些好处:

        • python32 执行 Python C 之间的来回转换 - 但我没有& #39;使用分析器检查花费的时间

        • lpCompletionRoutine pywin32 不提供)也可以使用,也许它更快

        • 作为替代方案,可以使用 ctypes 调用 C ,但这需要一些工作,我觉得它不值得