开发者

ReadDirectoryChangesW blocks deleting the watched directory

I am trying to watch a directory for create/delete/rename changes on windows with python using the ReadDirectoryChangesW API. This is my code and it is working fine:

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(fu开发者_如何学Pythonll_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)

However, when I try to delete the watched folder from python or from windows explorer, I get:

WindowsError: [Error 32] The process cannot access the file because it is being used by another process: 'c:\users\user\appdata\local\temp\new_dir'

I believe this makes sense, but how should I solve this? Because my application should allow the user to remove a watched folder. I tried the solution for the asynchronous method http://www.themacaque.com/?p=859, but it didn't help.

Thanks in advance!


From this blog post:

Another potential pitfall of [ReadDirectoryChangesW] is that the referenced directory itself is now "in use" and so can't be deleted. To monitor files in a directory and still allow the directory to be deleted, you would have to monitor the parent directory and its children.

The post also provides some more details on proper use of ReadDirectoryChangesW


Deleting the watched folder IS possible under ReadDirectoryChangesW

"Understanding ReadDirectoryChangesW - Part 2" by Jim Beveridge is (as Artomegus mentioned) a very good background for this problem, but the statement explaining FILE_SHARE_DELETE usage is misleading.

I my tests, use of FILE_SHARE_DELETE actually allows deletion / rename of the watched folder. (In other words, you don't need to "watch parent folder" as the only option.)

Here is the working snippet (edited and borrowed heavily from this otherwise excellent "Watch a Directory for Changes" by Tim Golden

# 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


Ok, to this is not simple to solve... In my case (http://www.themacaque.com/?p=859) I ignored the fact of allowing to rename or remove the directory.

What you could do to allow the user to rename the watch folder is to use the ReadDirectoryChangesW on the ancestor of the path to watch and filter the events according to the paths you are watching. I have implemented a new way to perform the watching using twisted to perform the processing of the events. With that solution you could be wathching the ancestors if:

  1. You folder does not have too many brothers to ignore. You do not want to be performing lots and lots of operations to filter events you are no interested in.
  2. There is no problem if the user cannot remove the ancestor.

In the code of Ubuntu One on windows we have been dealing with this problem and we have implemented a nice solution that you can take a look at. It follows a little the implementation of pyinotify on linux with a processor that will allow you to hook an object with callbacks that will be called according to the event in the twisted reactors main loop. Take a look at that code, it might help you.

Any problem late me know either in my blog or in irc (in freenode at #ubuntuone or #pyar) my nickname is mandel ;)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜