不,你不知道还有多少缓冲区。
它是隐藏在名为
FSWAsyncResult
因为该字节数组仅作为调用的结果的保留内存
ReadDirectoryChangesW
在这个答案的底部可以找到一个精简的反向工程版本,用于监视文件夹中的文件更改。它的逻辑和代码与您在真正的FileSystemWatcher中找到的内容相匹配。我没有费心用它们的正确含义来替换神奇常数。
。不要忘记更改生成设置
不安全的
因为代码经常篡改指针和本机结构。我去掉了所有的错误处理。。。
worker and I/O threads
我理解
ReadDirectoryChangesW
回调调度在托管线程池线程上。有时,你会得到与之前相同的托管id,当它很忙时,你会得到几个。在那条线上
CompletionStatusChanged
sizeused
Monitor
再次使用
相同字节[]缓冲区
完成状态已更改
正在执行的由操作系统保留,并在下次发送
被称为。
tl;博士
以下是对您的问题的回答:
-
-
哪个线程将清除与观察程序相关的内部缓冲区,以及何时?
-
我在很多地方读到,处理程序方法应该花费尽可能少的时间,否则我们可以得到InternalBufferOverflow异常。那么,假设只有当正在处理处理程序方法的线程(我不能说一个或全部,但想问您)已经处理了该方法时,才清理观察程序的内部缓冲区,这样安全吗?
ReadDirectoryChangesW
再一次在此期间,它将跟踪文件更改。当这些filechange事件不适合缓冲区时,它将在下次调用完成方法时引发InternalBufferOverflow。
安装程序
static object instance = new object(); // HACK
static SafeFileHandle hndl; // holds our filehandle (directory in this case)
static void Main(string[] args)
{
// the folder to watch
hndl = NativeMethods.CreateFile(@"c:\temp\delete", 1, 7, IntPtr.Zero, 3, 1107296256, new SafeFileHandle(IntPtr.Zero, false));
// this selects IO completion threads in the ThreadPool
ThreadPool.BindHandle(hndl);
// this starts the actual listening
Monitor(new byte[4096]);
Console.ReadLine();
}
班长
该方法负责创建本机结构和助手类的实例,作为IAsyncResult实现。
ReadDirectoryChangesW
它选择将其设置为异步完成的参数组合,以及IOCompletinPorts。有关这些选项的更多背景信息,请参阅
Understanding ReadDirectoryChangesW - Part 1
static unsafe void Monitor(byte[] buffer)
{
Overlapped overlapped = new Overlapped();
// notice how the buffer goes here as instance member on AsyncResult.
// Arrays are still Reference types.
overlapped.AsyncResult = new AsyncResult { buffer = buffer };
// CompletionStatusChanged is the method that will be called
// when filechanges are detected
NativeOverlapped* statusChanged = overlapped.Pack(new IOCompletionCallback(CompletionStatusChanged), buffer);
fixed (byte* ptr2 = buffer)
{
int num;
// this where the magic starts
NativeMethods.ReadDirectoryChangesW(hndl,
new HandleRef(instance, (IntPtr)((void*)ptr2)),
buffer.Length,
1,
(int)(NotifyFilters.FileName | NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.Attributes),
out num,
statusChanged,
new HandleRef(null, IntPtr.Zero));
}
}
一旦检测到文件更改,操作系统就会调用CompletionStatusChanged方法。在重叠结构中,解包后,我们将发现我们早期的ResultAsync实例具有填充的缓冲区。然后,该方法的其余部分通过读取数组中任何后续事件的偏移量以及标志和文件名来解码字节数组。
// this gets called by a ThreadPool IO Completion thread
static unsafe void CompletionStatusChanged(uint errorCode, uint numBytes, NativeOverlapped* overlappedPointer)
{
var sb = new StringBuilder();
Overlapped overlapped = Overlapped.Unpack(overlappedPointer);
var result = (AsyncResult) overlapped.AsyncResult;
var position = 0;
int offset;
int flags;
int sizeused = 0;
string file;
// read the buffer,
// that can contain multiple events
do
{
fixed (byte* ptr = result.buffer)
{
// process FILE_NOTIFY_INFORMATION
// see https://msdn.microsoft.com/en-us/library/windows/desktop/aa364391(v=vs.85).aspx
offset = ((int*)ptr)[position / 4];
flags = ((int*)ptr + position / 4)[1];
int len = ((int*)ptr + position / 4)[2];
file = new string((char*)ptr + position / 2 + 6, 0, len / 2);
sizeused = position + len + 14;
}
sb.AppendFormat("#thread {0}, event: {1}, {2}, {3}, {4}\r\n", Thread.CurrentThread.ManagedThreadId, position, offset, flags, file);
// in the real FileSystemWatcher here the several events are raised
// so that uses the same thread this code is on.
position += offset;
} while (offset != 0);
// my own logging
sb.AppendFormat(" === buffer used: {0} ==== ", sizeused);
Console.WriteLine(sb);
// start again, reusing the same buffer:
Monitor(result.buffer);
}
}
助手方法
NativeMethods正是它们所称的:WinAPI的本机调用的入口点。
class AsyncResult : IAsyncResult
{
internal byte[] buffer;
// default implementation of the interface left out
// removed default implementation for brevity
}
static class NativeMethods
{
[DllImport("kernel32.dll", BestFitMapping = false, CharSet = CharSet.Auto)]
public static extern SafeFileHandle CreateFile(string lpFileName, int dwDesiredAccess, int dwShareMode, IntPtr lpSecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, SafeFileHandle hTemplateFile);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public unsafe static extern bool ReadDirectoryChangesW(SafeFileHandle hDirectory, HandleRef lpBuffer, int nBufferLength, int bWatchSubtree, int dwNotifyFilter, out int lpBytesReturned, NativeOverlapped* overlappedPointer, HandleRef lpCompletionRoutine);
}