Skip to content

Commit 655c3bb

Browse files
committed
Added directory change notification support with async.
1 parent 3b466ea commit 655c3bb

2 files changed

Lines changed: 178 additions & 0 deletions

File tree

NtApiDotNet/NtFile.cs

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,28 @@ private NtResult<FileBasicInformation> QueryBasicInformation(bool throw_on_error
358358
return Query(FileInformationClass.FileBasicInformation, new FileBasicInformation(), throw_on_error);
359359
}
360360

361+
private static IEnumerable<DirectoryChangeNotification> ReadNotifications(SafeHGlobalBuffer buffer, IoStatus status)
362+
{
363+
List<DirectoryChangeNotification> ns = new List<DirectoryChangeNotification>();
364+
365+
// Change buffer size to reflect what's in the buffer.
366+
buffer.Initialize((uint)status.Information32);
367+
368+
int offset = 0;
369+
while (offset < buffer.Length)
370+
{
371+
var info = buffer.GetStructAtOffset<FileNotifyInformation>(offset);
372+
var result = info.Result;
373+
ns.Add(new DirectoryChangeNotification(result.Action, info.Data.ReadUnicodeString(result.FileNameLength / 2)));
374+
if (result.NextEntryOffset == 0)
375+
{
376+
break;
377+
}
378+
offset += result.NextEntryOffset;
379+
}
380+
return ns.AsReadOnly();
381+
}
382+
361383
#endregion
362384

363385
#region Static Methods
@@ -3249,6 +3271,99 @@ public IEnumerable<string> FindFilesBySid(Sid sid)
32493271
}
32503272
}
32513273

3274+
/// <summary>
3275+
/// Get change notifications.
3276+
/// </summary>
3277+
/// <param name="completion_filter">The filter of events to watch for.</param>
3278+
/// <param name="watch_subtree">True to watch all sub directories.</param>
3279+
/// <param name="throw_on_error">True to throw on error.</param>
3280+
/// <returns>The list of changes.</returns>
3281+
public NtResult<IEnumerable<DirectoryChangeNotification>> GetChangeNotification(
3282+
DirectoryChangeNotifyFilter completion_filter, bool watch_subtree, bool throw_on_error)
3283+
{
3284+
using (NtAsyncResult result = new NtAsyncResult(this))
3285+
{
3286+
using (var buffer = new SafeHGlobalBuffer(4096))
3287+
{
3288+
return result.CompleteCall(NtSystemCalls.NtNotifyChangeDirectoryFile(
3289+
Handle, result.EventHandle, IntPtr.Zero, IntPtr.Zero, result.IoStatusBuffer,
3290+
buffer, buffer.Length, completion_filter, watch_subtree))
3291+
.CreateResult(throw_on_error, () => ReadNotifications(buffer, result.IoStatusBuffer.Result));
3292+
}
3293+
}
3294+
}
3295+
3296+
/// <summary>
3297+
/// Get change notifications.
3298+
/// </summary>
3299+
/// <param name="completion_filter">The filter of events to watch for.</param>
3300+
/// <param name="watch_subtree">True to watch all sub directories.</param>
3301+
/// <returns>The list of changes.</returns>
3302+
public IEnumerable<DirectoryChangeNotification> GetChangeNotification(DirectoryChangeNotifyFilter completion_filter, bool watch_subtree)
3303+
{
3304+
return GetChangeNotification(completion_filter, watch_subtree, true).Result;
3305+
}
3306+
3307+
/// <summary>
3308+
/// Get change notifications.
3309+
/// </summary>
3310+
/// <param name="completion_filter">The filter of events to watch for.</param>
3311+
/// <param name="watch_subtree">True to watch all sub directories.</param>
3312+
/// <param name="throw_on_error">True to throw on error.</param>
3313+
/// <param name="token">Cancellation token.</param>
3314+
/// <returns>The list of changes.</returns>
3315+
public async Task<NtResult<IEnumerable<DirectoryChangeNotification>>> GetChangeNotificationAsync(
3316+
DirectoryChangeNotifyFilter completion_filter, bool watch_subtree, CancellationToken token, bool throw_on_error)
3317+
{
3318+
using (var buffer = new SafeHGlobalBuffer(4096))
3319+
{
3320+
var status = await RunFileCallAsync(result => NtSystemCalls.NtNotifyChangeDirectoryFile(
3321+
Handle, result.EventHandle, IntPtr.Zero, IntPtr.Zero, result.IoStatusBuffer,
3322+
buffer, buffer.Length, completion_filter, watch_subtree), token, throw_on_error);
3323+
return status.Map(r => ReadNotifications(buffer, r));
3324+
}
3325+
}
3326+
3327+
/// <summary>
3328+
/// Get change notifications.
3329+
/// </summary>
3330+
/// <param name="completion_filter">The filter of events to watch for.</param>
3331+
/// <param name="watch_subtree">True to watch all sub directories.</param>
3332+
/// <param name="throw_on_error">True to throw on error.</param>
3333+
/// <returns>The list of changes.</returns>
3334+
public Task<NtResult<IEnumerable<DirectoryChangeNotification>>> GetChangeNotificationAsync(
3335+
DirectoryChangeNotifyFilter completion_filter, bool watch_subtree, bool throw_on_error)
3336+
{
3337+
return GetChangeNotificationAsync(completion_filter, watch_subtree, CancellationToken.None, throw_on_error);
3338+
}
3339+
3340+
/// <summary>
3341+
/// Get change notifications.
3342+
/// </summary>
3343+
/// <param name="completion_filter">The filter of events to watch for.</param>
3344+
/// <param name="watch_subtree">True to watch all sub directories.</param>
3345+
/// <param name="token">Cancellation token.</param>
3346+
/// <returns>The list of changes.</returns>
3347+
public async Task<IEnumerable<DirectoryChangeNotification>> GetChangeNotificationAsync(
3348+
DirectoryChangeNotifyFilter completion_filter, bool watch_subtree, CancellationToken token)
3349+
{
3350+
var result = await GetChangeNotificationAsync(completion_filter, watch_subtree, token, true);
3351+
return result.Result;
3352+
}
3353+
3354+
/// <summary>
3355+
/// Get change notifications.
3356+
/// </summary>
3357+
/// <param name="completion_filter">The filter of events to watch for.</param>
3358+
/// <param name="watch_subtree">True to watch all sub directories.</param>
3359+
/// <param name="token">Cancellation token.</param>
3360+
/// <returns>The list of changes.</returns>
3361+
public Task<IEnumerable<DirectoryChangeNotification>> GetChangeNotificationAsync(
3362+
DirectoryChangeNotifyFilter completion_filter, bool watch_subtree)
3363+
{
3364+
return GetChangeNotificationAsync(completion_filter, watch_subtree, CancellationToken.None);
3365+
}
3366+
32523367
/// <summary>
32533368
/// Method to query information for this object type.
32543369
/// </summary>

NtApiDotNet/NtFileNative.cs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,19 @@ public static extern NtStatus NtReplacePartitionUnit(UnicodeString TargetInstanc
254254
[DllImport("ntdll.dll")]
255255
public static extern NtStatus NtCancelSynchronousIoFile(SafeKernelObjectHandle ThreadHandle,
256256
[In] SafeIoStatusBuffer IoRequestToCancel, [Out] IoStatus IoStatusBlock);
257+
258+
[DllImport("ntdll.dll")]
259+
public static extern NtStatus NtNotifyChangeDirectoryFile(
260+
SafeKernelObjectHandle FileHandle,
261+
SafeKernelObjectHandle Event,
262+
IntPtr ApcRoutine,
263+
IntPtr ApcContext,
264+
SafeIoStatusBuffer IoStatusBlock,
265+
SafeBuffer Buffer,
266+
int BufferSize,
267+
DirectoryChangeNotifyFilter CompletionFilter,
268+
bool WatchTree
269+
);
257270
}
258271

259272
public static partial class NtRtl
@@ -1408,5 +1421,55 @@ public struct FileCaseSensitiveInformation
14081421
public FileCaseSensitiveFlags Flags;
14091422
}
14101423

1424+
1425+
[Flags]
1426+
public enum DirectoryChangeNotifyFilter
1427+
{
1428+
None = 0,
1429+
FileName = 0x00000001,
1430+
DirName = 0x00000002,
1431+
Attributes = 0x00000004,
1432+
Size = 0x00000008,
1433+
LastWrite = 0x00000010,
1434+
LastAccess = 0x00000020,
1435+
Creation = 0x00000040,
1436+
Ea = 0x00000080,
1437+
Security = 0x00000100,
1438+
StreamName = 0x00000200,
1439+
StreamSize = 0x00000400,
1440+
StreamWrite = 0x00000800,
1441+
All = 0x00000FFF
1442+
}
1443+
1444+
public enum FileNotificationAction
1445+
{
1446+
Added = 1,
1447+
Removed = 2,
1448+
Modified = 3,
1449+
RenamedOldName = 4,
1450+
RenamedNewName = 5,
1451+
}
1452+
1453+
[StructLayout(LayoutKind.Sequential), DataStart("FileName")]
1454+
public struct FileNotifyInformation
1455+
{
1456+
public int NextEntryOffset;
1457+
public FileNotificationAction Action;
1458+
public int FileNameLength;
1459+
public char FileName;
1460+
}
1461+
1462+
public class DirectoryChangeNotification
1463+
{
1464+
public FileNotificationAction Action { get; }
1465+
public string FileName { get; }
1466+
1467+
internal DirectoryChangeNotification(FileNotificationAction action, string file_name)
1468+
{
1469+
Action = action;
1470+
FileName = file_name;
1471+
}
1472+
}
1473+
14111474
#pragma warning restore 1591
14121475
}

0 commit comments

Comments
 (0)