ReadDirectoryChangesW 阻塞删除被监视目录
我正在尝试用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 个回答
好吧,这个问题不太好解决……在我的情况下(http://www.themacaque.com/?p=859),我忽略了允许重命名或删除文件夹这件事。
如果你想让用户能够重命名监视的文件夹,可以使用一个叫做 ReadDirectoryChangesW 的方法,去监视文件夹路径的上级目录,并根据你要监视的路径来过滤事件。我已经实现了一种新的监视方式,使用 twisted 来处理这些事件。通过这个方法,你可以监视上级目录,前提是:
- 你的文件夹没有太多兄弟文件夹需要忽略。你不想进行很多操作来过滤那些你不感兴趣的事件。
- 如果用户不能删除上级目录,那也没关系。
在 Windows 上的 Ubuntu One 代码中,我们也遇到了这个问题,并且实现了一个不错的解决方案,你可以看看。这个方案有点像 Linux 上的 pyinotify 实现,配有一个处理器,可以让你连接一个对象,并根据事件在 twisted 的主循环中调用回调函数。看看那段代码,可能会对你有帮助。
如果有任何问题,随时在我的博客或者 IRC 上告诉我(在 freenode 的 #ubuntuone 或 #pyar 频道),我的昵称是 mandel ;)
在使用 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
来自这篇博客:
使用[ReadDirectoryChangesW]时,还有一个可能的陷阱就是被监控的目录会被标记为“正在使用中”,因此无法删除。如果你想监控一个目录里的文件,同时又想删除这个目录,你就需要监控这个目录的上级目录以及它里面的所有文件。
这篇文章还提供了一些关于如何正确使用ReadDirectoryChangesW的更多细节。