ReadDirectoryChangesW 阻塞删除被监视目录

4 投票
3 回答
4625 浏览
提问于 2025-04-16 22:16

我正在尝试用Python通过ReadDirectoryChangesW这个API来监控一个文件夹的创建、删除和重命名变化。这是我的代码,运行得很好:

results = win32file.ReadDirectoryChangesW(self.hDir, 8192, True, self.type, None,
                                           None)
for action, file in results:
    full_filename = os.path.join (self.source_path, file)
    if   action == 1:                                    # Created
        self.fileCreated(full_filename)
    elif action == 2:                                    # Deleted
        self.fileDeleted(full_filename)
    elif action == 3:                                    # Updated
        self.fileUpdated(full_filename)
    elif action == 4:                                    # Renamed from something
        renamed_file = full_filename
    elif action == 5:                                    # Renamed to something
        self.fileRenamed(renamed_file, full_filename)

但是,当我尝试从Python或者Windows资源管理器中删除这个被监控的文件夹时,我遇到了这个错误:

WindowsError: [错误 32] 该进程无法访问文件,因为它正被另一个进程使用: 'c:\users\user\appdata\local\temp\new_dir'

我觉得这个错误是有道理的,但我该怎么解决呢?因为我的应用程序应该允许用户删除一个被监控的文件夹。我尝试了一个关于异步方法的解决方案,链接在这里http://www.themacaque.com/?p=859,但没有帮助。

提前谢谢你们!

3 个回答

0

好吧,这个问题不太好解决……在我的情况下(http://www.themacaque.com/?p=859),我忽略了允许重命名或删除文件夹这件事。

如果你想让用户能够重命名监视的文件夹,可以使用一个叫做 ReadDirectoryChangesW 的方法,去监视文件夹路径的上级目录,并根据你要监视的路径来过滤事件。我已经实现了一种新的监视方式,使用 twisted 来处理这些事件。通过这个方法,你可以监视上级目录,前提是:

  1. 你的文件夹没有太多兄弟文件夹需要忽略。你不想进行很多操作来过滤那些你不感兴趣的事件。
  2. 如果用户不能删除上级目录,那也没关系。

在 Windows 上的 Ubuntu One 代码中,我们也遇到了这个问题,并且实现了一个不错的解决方案,你可以看看。这个方案有点像 Linux 上的 pyinotify 实现,配有一个处理器,可以让你连接一个对象,并根据事件在 twisted 的主循环中调用回调函数。看看那段代码,可能会对你有帮助。

如果有任何问题,随时在我的博客或者 IRC 上告诉我(在 freenode 的 #ubuntuone 或 #pyar 频道),我的昵称是 mandel ;)

2

在使用 ReadDirectoryChangesW 的时候,删除被监视的文件夹 可能的。

Jim Beveridge 的文章 "Understanding ReadDirectoryChangesW - Part 2"(正如 Artomegus 提到的)对这个问题提供了很好的背景知识,但关于 FILE_SHARE_DELETE 的用法说明有点误导。

根据我的测试,使用 FILE_SHARE_DELETE 实际上是允许删除或重命名被监视的文件夹的。(换句话说,你并不需要把“父文件夹”作为唯一的选择来监视。)

下面是一个有效的代码片段(经过编辑,并大量借鉴了这篇很棒的文章:Tim Golden 的 "Watch a Directory for Changes"

# License is same as snippets on this page
# http://timgolden.me.uk/python/win32_how_do_i/watch_directory_for_changes.html
# In other words, bug Tim Golden to publish a license for his snippets
def windows_watch_path(watched_path):
    import win32file
    import win32con

    ACTIONS = {
        1 : "Created",
        2 : "Deleted",
        3 : "Updated",
        4 : "RenamedFrom",
        5 : "RenamedTo"
    }
    # Thanks to Claudio Grondi for the correct set of numbers
    FILE_LIST_DIRECTORY = 0x0001

    try:
        hDir = win32file.CreateFile (
            watched_path
            , FILE_LIST_DIRECTORY
            , win32con.FILE_SHARE_READ | 
              win32con.FILE_SHARE_WRITE | 
              win32con.FILE_SHARE_DELETE
            , None
            , win32con.OPEN_EXISTING
            , win32con.FILE_FLAG_BACKUP_SEMANTICS
            , None
        )
    except:
        # either it does not exist by this time, or some other issue... blah.
        # we'll just say "it 'changed' from 'some other expected state'"
        return [[watched_path, '', ACTIONS[2]]]

    results = win32file.ReadDirectoryChangesW (
        hDir,
        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
    )

    files_changed = []
    for action, fn in results:
        files_changed.append(
            [
                watched_path
                , fn
                , ACTIONS[action]
            ]
        )
        # print fullfn, ACTIONS.get(action, "Unknown")
    return files_changed
3

来自这篇博客

使用[ReadDirectoryChangesW]时,还有一个可能的陷阱就是被监控的目录会被标记为“正在使用中”,因此无法删除。如果你想监控一个目录里的文件,同时又想删除这个目录,你就需要监控这个目录的上级目录以及它里面的所有文件。

这篇文章还提供了一些关于如何正确使用ReadDirectoryChangesW的更多细节。

撰写回答