我希望在修改给定目录下(直接或间接)任何文件时通知我的代码.通过"修改",我的意思是我希望每当文件的内容被更改,重命名或删除时都会通知我的代码; 或者是否添加了新文件.对于我的应用程序,可能有数千个文件.
我看起来像FSEvents,但其技术概述部分地说:
需要注意的重点是通知的粒度在目录级别.它只告诉您目录中的某些内容已更改,但不会告诉您更改的内容.
它还说:
文件系统事件API也不是为了查找特定文件何时更改而设计的.出于这种目的,kqueues机制更合适.
但是,为了在给定文件上使用kqueue,必须打开文件以获取文件描述符.管理数千个文件描述符是不切实际的(并且可能会超过最大允许的打开文件描述符数).
奇怪的是,在Windows下,我可以使用该ReadDirectoryChangesW()功能,它正是我想要的.
那么如何在Mac OS X下做我想做的事情呢?或者,问另一种方式:如何ReadDirectoryChangesW()在用户空间中编写等效的Mac OS X(并且非常有效地执行此操作)?
当有大量文件活动时,互联网上有很多关于ReadDirectoryChangesW API函数丢失文件的帖子.大多数人都指责调用ReadDirectoryChangesW函数循环的速度.这是一个不正确的假设.我看到的最好的解释是在下面的帖子,评论于2008年4月14日星期一下午2:15:27
http://social.msdn.microsoft.com/forums/en-US/netfxbcl/thread/4465cafb-f4ed-434f-89d8-c85ced6ffaa8/
摘要是ReadDirectoryChangesW函数报告文件更改,因为它们离开文件后写队列,而不是添加它们.如果在提交之前添加了太多,则会忽略其中一些.如果您只是编写一个程序来快速生成目录中的1000多个文件,您可以通过实现看到这一点.只需计算您获得的文件事件通知数量,您就会看到有时您不会收到所有这些通知.
问题是,有没有人找到一种可靠的方法来使用ReadDirectoryChangesW函数而不必每次都刷新卷?如果用户不是管理员并且还可能需要一些时间来完成,则不允许这样做.
我想ReadDirectoryChangesW()在异步模式下使用函数,并提供I/O完成例程.
问题是我不知道如何在完成例程(CALLBACK函数)中检索有关更改的确切信息.完成例程定义如下:
VOID CALLBACK FileIOCompletionRoutine(
[in] DWORD dwErrorCode,
[in] DWORD dwNumberOfBytesTransfered,
[in] LPOVERLAPPED lpOverlapped
);
Run Code Online (Sandbox Code Playgroud)
我想知道这些信息是否包含在LPOVERLAPPED结构中.但我不知道如何得到它.
我使用ReadDirectoryChangesW来监视指定的目录,并在检测到更改时更新索引结构.我使用以下代码(粗略)
var
InfoPointer : PFileNotifyInformation;
NextOffset : DWORD;
...
while (not Terminated) do begin
if ReadDirectoryChangesW (FDirHandle, FBuffer, FBufferLength, True,
FFilter, @BytesRead, @FOverlap, nil) then
begin
WaitResult := WaitForMultipleObjects (2, @FEventArray, False, INFINITE);
if (WaitResult = waitFileChange) then
begin
InfoPointer := FBuffer;
repeat
NextOffset := InfoPointer.NextEntryOffset;
...
PByte (InfoPointer) := PByte (InfoPointer) + NextOffset;
until NextOffset = 0;
end;
end;
end;
Run Code Online (Sandbox Code Playgroud)
过滤器是
FFilter := FILE_NOTIFY_CHANGE_FILE_NAME or
FILE_NOTIFY_CHANGE_DIR_NAME or
FILE_NOTIFY_CHANGE_SIZE or
FILE_NOTIFY_CHANGE_LAST_WRITE;
Run Code Online (Sandbox Code Playgroud)
和目录句柄是这样获得的:
FDirHandle := CreateFile (PChar (FDirectoryWatch.WatchedDirectory),
FILE_LIST_DIRECTORY or GENERIC_READ, …Run Code Online (Sandbox Code Playgroud) windows filesystems delphi readdirectorychangesw delphi-2009
正如它听起来一样,我试图ReadDirectoryChangesW与IO完成异步并且它不工作,特别是,GetLastError重复返回258(GetQueuedCompletionStatus超时).
我有结构:
typedef struct dirinfo_struct
{
HANDLE hDirFH; // directory handle
OVERLAPPED Overlapped; // overlapped storage
int len_buffer; // buffer length
wchar_t* buffer; // buffer itself
wchar_t* directory_name; // target name
} dirinfo_t;
typedef struct dirmon_struct
{
HANDLE hDirOPPort; // handle to the IO port.
dirinfo_t* dirinfo; // pointer to the struct above.
} dirmon_t;
Run Code Online (Sandbox Code Playgroud)
用于存储相关信息.这是初始化的:
dirinfo_t* t = malloc(1*sizeof(dirinfo_t));
dirmon_t* d = malloc(1*sizeof(dirmon_t));
dirinfo_init(t); // does t->buffer = malloc(8192*sizeof(wchar_t));
Run Code Online (Sandbox Code Playgroud)
然后我创建我的目录句柄和COM端口:
t->hDirFH = CreateFile(L"C:\\test", …Run Code Online (Sandbox Code Playgroud) 我正在开发一段C代码,它使用ReadDirectoryChangesW()来监视Windows中目录下的更改.我已经阅读了ReadDirectoryChangesW()和FILE_NOTIFY_INFORMATION结构的相关MSDN条目,以及其他几个文档.此时,我已成功监控多个目录,监控本身没有明显问题.问题是此函数放在FILE_NOTIFY_INFORMATION结构中的文件名不是规范的.
根据MSDN,它们可以是长形式也可以是短形式.我发现了几个帖子,建议缓存短路径名和长路径名来处理这种情况.不幸的是,根据我自己在Windows 7系统上的测试,这还不足以消除这个问题,因为每个文件名不只有两种选择.问题是在路径名中,每个组件可以是长形式还是短形式.以下路径名都可以引用同一个文件:
C:\ PROGRA〜1\MYPROG〜1\MYDATA〜1.TXT
C:\ PROGRA〜1\MYPROG〜1\MyDataFile.txt
C:\ PROGRA〜1\MyProgram\MYDATA〜1.TXT
C:\ PROGRA〜1\MyProgram\MyDataFile.txt
c:\ Program Files\MYPROG~1\MYDATA~1.TXT
...
从我使用cmd.exe的测试中我可以看出它们都是完全可以接受的.实质上,每个文件的有效路径名数量随其路径名中的组件数量呈指数上升.
不幸的是,ReadDirectoryChangesW()似乎用提供给导致每个操作的系统调用的文件名填充其输出缓冲区.例如,如果使用cmd.exe命令创建,重命名,删除等文件,则FILE_NOTIFY_INFORMATION将包含命令行中指定的文件名.
现在,在大多数情况下,我可以使用GetLongPathName()和朋友来获取我使用的唯一路径.遗憾的是,删除文件时无法完成 - 当我收到通知时,文件已经消失,Get*PathName()函数将无法正常工作.
目前我正在考虑使用更广泛的缓存来确定应用程序为每个文件使用哪些备用路径名,这将处理任何情况,除了有人决定使用看不见的混合路径名突然删除文件的那个.我正在考虑从父目录修改事件中创建数据挖掘,然后回到检查该情况的实际目录.
有什么建议可以更方便地做到这一点吗?
PS1:虽然Change Journals会有效地处理这个问题(我希望),但由于它们与NTFS的关系以及我的应用程序缺乏管理员权限,我不相信我可以使用它们.除非我绝对被迫,否则我宁愿不去那里.
PS2:请记住,我主要在Unix上编码,所以要温柔......
我有一个线程用于ReadDirectoryChangesW在文件夹中添加或删除文件时通知我.
对于每个新图像,我打开文件并创建图像的缩略图.但是,在将文件完全复制到目标文件夹之前,我会收到通知,在这种情况下,我只会获得部分缩略图.(文件从远程位置复制到中央服务器,网络在高峰时间可能会变慢.)
我确实检查文件是否正在使用,但这似乎不适用于图像文件.
HFileRes := CreateFile(pchar(Filename), GENERIC_READ or GENERIC_WRITE, 0, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0) ;
Result := (HFileRes = INVALID_HANDLE_VALUE);
if (not Result) then
CloseHandle(HFileRes) ;
Run Code Online (Sandbox Code Playgroud)
我的问题是:有没有办法检测文件何时被完全复制,或者我只是等到文件大小或上次修改时间自上次检查后没有改变?
以下是使用的最小程序ReadDirectoryChangesW.我遇到的问题是只有第一次GetQueuedCompletionStatus回复.无论对目录进行了多少更改,第二次循环都会永久阻塞.
我也尝试使用同步版本,并有完全相同的问题.
#include <array>
#include <cassert>
#include <iostream>
#include <Windows.h>
int main() {
// Open the directory to monitor.
HANDLE dir = ::CreateFileA(
"G:\\Program Files (x86)\\Steam\\steamapps\\common\\eve online"
, FILE_LIST_DIRECTORY
, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE
, NULL
, OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED
, NULL
);
if (dir == INVALID_HANDLE_VALUE) {
std::cout << "Failed to open directory for change notifications!\n";
return 1;
}
// Setup IOCP.
HANDLE iocp = ::CreateIoCompletionPort(
dir
, NULL
, NULL
, …Run Code Online (Sandbox Code Playgroud) 我在单独的线程应用程序配置文件中进行监视,在某些情况下,该文件可能是另一个XML或另一个XML中的INI.线程监控目录代码(在Delphi中)是这样的:
procedure TWatcherThread.Execute;
type
PFileNotifyInformation = ^TFileNotifyInformation;
TFileNotifyInformation = record
NextEntryOffset: DWORD;
Action: DWORD;
FileNameLength: DWORD;
FileName: WideChar;
end;
const
BufferLength = 65536;
var
Filter, BytesRead: DWORD;
InfoPointer: PFileNotifyInformation;
Offset, NextOffset: DWORD;
Buffer: array[0..BufferLength - 1] of byte;
Overlap: TOverlapped;
Events: array[0..1] of THandle;
WaitResult: DWORD;
FileName, s: string;
begin
if fDirHandle <> INVALID_HANDLE_VALUE then begin
Filter := FILE_NOTIFY_CHANGE_LAST_WRITE;
FillChar(Overlap, SizeOf(TOverlapped), 0);
Overlap.hEvent := fChangeHandle;
Events[0] := fChangeHandle;
Events[1] := fShutdownHandle;
while not Terminated do begin
FillChar(Buffer,SizeOf(Buffer),0);
if ReadDirectoryChangesW (fDirHandle, …Run Code Online (Sandbox Code Playgroud) 我想知道特定目录的任何更改文件。所以,我想出了 ReadDirectoryChangesW() 和 FindFirstChangeNotification() - FindNextChangeNotification() API。
然后,我使用 ReadDirectoryChangesW() 函数实现。但是,我不知道为什么会有 FindFirst...blabla API。我认为 ReadDirectoryChangesW() 函数可以完成 FindFirst... API 的所有工作。
有什么不同?
windows ×4
delphi ×3
winapi ×3
asynchronous ×2
c ×2
.net ×1
c++ ×1
delphi-2009 ×1
file ×1
file-in-use ×1
filenames ×1
filesystems ×1
fsevents ×1
kqueue ×1
macos ×1