近日在讨论到之前我发布的“文件同步工具”时,有朋友建议用ReadDirectoryChangesW方法去做监控。我个人觉得,如果在C#中做监控的话,首选还是.NET Framework封装好的FileSystemWatcher去做。但我以为,监控也有监控的烦恼,就是说要一直监控。而一旦中途停止监控,又自然会涉及到一个状态保存的问题。
我刚才将该函数看了一下,做了一个范例,如下。如果有兴趣的朋友,可以参考一下
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess, uint dwShareMode,
IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes,
IntPtr hTemplateFile);
[DllImport("kernel32.dll")]
static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
static extern bool ReadDirectoryChangesW(IntPtr hDirectory, IntPtr lpBuffer, uint nBufferLength,
bool bWatchSubtree, uint dwNotifyFilter, out uint lpBytesReturned, IntPtr lpOverlapped,
IntPtr lpCompletionRoutine);
static void Main(string[] args)
{
const uint FILE_LIST_DIRECTORY = 0x1;
const uint FILE_SHARE_READ = 0x1;
const uint FILE_SHARE_WRITE = 0x2;
const uint FILE_SHARE_DELETE = 0x4;
const uint OPEN_EXISTING = 3;
const uint FILE_FLAG_BACKUP_SEMANTICS = 0x2000000;
const uint FILE_NOTIFY_CHANGE_FILE_NAME = 0x1;
const uint FILE_NOTIFY_CHANGE_DIR_NAME = 0x2;
const uint FILE_NOTIFY_CHANGE_LAST_WRITE = 0x10;
const uint BUFSIZE = 2048;
string myDocs = @"E:\Temp";
Console.WriteLine("Monitoring name changes in {0} and subdirectories.", myDocs);
IntPtr hDir = CreateFile(myDocs, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE |
FILE_SHARE_DELETE, IntPtr.Zero, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, IntPtr.Zero);
if (hDir == IntPtr.Zero)
{
Console.WriteLine("CreateFile failed. " + Marshal.GetLastWin32Error());
return;
}
IntPtr pBuf = IntPtr.Zero;
try
{
pBuf = Marshal.AllocHGlobal((int)BUFSIZE);
uint bytesReturned;
while (ReadDirectoryChangesW(hDir, pBuf, BUFSIZE, true, FILE_NOTIFY_CHANGE_FILE_NAME |
FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE, out bytesReturned,
IntPtr.Zero, IntPtr.Zero))
{
string[] actions = new string[] { "(unknown action) ", "Added ", "Removed ",
"Modified ", "Old name ", "New name " };
IntPtr pCurrent = pBuf;
while (pCurrent != IntPtr.Zero)
{
// Read file length (in bytes) at offset 8
int fileLen = Marshal.ReadInt32(pCurrent, 8);
// Read file name (fileLen/2 characters) from offset 12
string file = Marshal.PtrToStringUni((IntPtr)(12 + (int)pCurrent), fileLen / 2);
// Read action at offset 4
int action = Marshal.ReadInt32(pCurrent, 4);
if (action < 1 || action >= actions.Length) action = 0;
Console.WriteLine(actions[action] + file);
// Read NextEntryOffset at offset 0 and move pointer to next structure if needed
int inc = Marshal.ReadInt32(pCurrent);
pCurrent = inc != 0 ? (IntPtr)(inc + (int)pCurrent) : IntPtr.Zero;
}
}
//else
//Console.WriteLine("ReadDirectoryChangesW failed. " + Marshal.GetLastWin32Error());
}
finally
{
if (pBuf != IntPtr.Zero) Marshal.FreeHGlobal(pBuf);
CloseHandle(hDir);
}
Console.Read();
}