Skip to content

Commit 24b7e69

Browse files
committed
Add Windows FileSystemWatcher
1 parent fd1dcb3 commit 24b7e69

2 files changed

Lines changed: 201 additions & 0 deletions

File tree

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.ComponentModel;
4+
using System.IO;
5+
using System.Runtime.InteropServices;
6+
using System.Text;
7+
using Oxide.Core;
8+
9+
namespace Oxide.IO.Windows
10+
{
11+
internal class WindowsFileSystem : IFileSystem
12+
{
13+
private const int MAX_BUFFERS = 5;
14+
15+
#region API
16+
17+
private static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
18+
19+
private const uint FILE_READ_EA = 0x0008;
20+
private const uint FILE_FLAG_BACKUP_SEMANTICS = 0x2000000;
21+
22+
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
23+
private static extern uint GetFinalPathNameByHandle(
24+
IntPtr hFile,
25+
[MarshalAs(UnmanagedType.LPTStr)] StringBuilder lpszFilePath,
26+
uint cchFilePath,
27+
uint dwFlags);
28+
29+
[DllImport("kernel32.dll", SetLastError = true)]
30+
[return: MarshalAs(UnmanagedType.Bool)]
31+
private static extern bool CloseHandle(IntPtr hObject);
32+
33+
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
34+
public static extern IntPtr CreateFile(
35+
[MarshalAs(UnmanagedType.LPTStr)] string filename,
36+
[MarshalAs(UnmanagedType.U4)] uint access,
37+
[MarshalAs(UnmanagedType.U4)] FileShare share,
38+
IntPtr securityAttributes, // optional SECURITY_ATTRIBUTES struct or IntPtr.Zero
39+
[MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
40+
[MarshalAs(UnmanagedType.U4)] uint flagsAndAttributes,
41+
IntPtr templateFile);
42+
43+
#endregion
44+
45+
private Queue<StringBuilder> StrBuffers { get; } = new Queue<StringBuilder>();
46+
47+
public bool IsSymbolicLink(string path)
48+
{
49+
if (File.Exists(path) || Directory.Exists(path))
50+
{
51+
return (File.GetAttributes(path) & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint;
52+
}
53+
54+
return false;
55+
}
56+
57+
public string ResolvePath(string path)
58+
{
59+
if (!IsSymbolicLink(path))
60+
{
61+
path = Path.GetFullPath(path);
62+
return path;
63+
}
64+
65+
IntPtr handle = CreateFile(path, FILE_READ_EA, FileShare.ReadWrite | FileShare.Delete,
66+
IntPtr.Zero, FileMode.Open, FILE_FLAG_BACKUP_SEMANTICS, IntPtr.Zero);
67+
68+
if (handle == INVALID_HANDLE_VALUE)
69+
{
70+
throw new Win32Exception(Marshal.GetLastWin32Error());
71+
}
72+
73+
StringBuilder str;
74+
75+
lock (StrBuffers)
76+
{
77+
str = StrBuffers.Count == 0 ? new StringBuilder(1024) : StrBuffers.Dequeue();
78+
}
79+
80+
str.Length = 0;
81+
try
82+
{
83+
uint fl = GetFinalPathNameByHandle(handle, str, 1024, 0);
84+
85+
if (fl == 0)
86+
{
87+
throw new Win32Exception(Marshal.GetLastWin32Error());
88+
}
89+
90+
path = str.ToString().TrimStart('\\', '?');
91+
Interface.Oxide.LogDebug($"Symbolic: {path}");
92+
return path;
93+
}
94+
finally
95+
{
96+
CloseHandle(handle);
97+
98+
lock (StrBuffers)
99+
{
100+
if (StrBuffers.Count < MAX_BUFFERS)
101+
{
102+
str.Length = 0;
103+
StrBuffers.Enqueue(str);
104+
}
105+
}
106+
}
107+
}
108+
}
109+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
using System;
2+
using System.IO;
3+
4+
namespace Oxide.IO.Windows
5+
{
6+
internal class WindowsFileSystemWatcher : CachedFileSystemWatcher
7+
{
8+
private readonly FileSystemWatcher watcher;
9+
private int currentSub = -1;
10+
11+
public WindowsFileSystemWatcher(IFileSystem fs, string directory, bool subDirs, NotifyMask filter, StringComparison stringComparison = StringComparison.OrdinalIgnoreCase) : base(directory, subDirs, filter, fs, stringComparison)
12+
{
13+
watcher = new FileSystemWatcher(Directory, "*")
14+
{
15+
EnableRaisingEvents = false
16+
};
17+
18+
watcher.IncludeSubdirectories = IncludeSubDirectories;
19+
watcher.NotifyFilter = NotifyFilters.LastAccess
20+
| NotifyFilters.LastWrite
21+
| NotifyFilters.FileName
22+
| NotifyFilters.DirectoryName;
23+
24+
watcher.Deleted += OnDeleted;
25+
watcher.Changed += OnChanged;
26+
watcher.Created += OnCreated;
27+
watcher.Renamed += OnRenamed;
28+
watcher.Error += OnError;
29+
}
30+
31+
public override void EndInit()
32+
{
33+
watcher.EnableRaisingEvents = true;
34+
base.EndInit();
35+
}
36+
37+
protected override int SubscribeTo(string directory)
38+
{
39+
return currentSub++;
40+
}
41+
42+
protected override bool UnsubscribeFrom(int id)
43+
{
44+
return true;
45+
}
46+
47+
protected override void OnDispose()
48+
{
49+
watcher.Dispose();
50+
}
51+
52+
private void OnRenamed(object sender, RenamedEventArgs args)
53+
{
54+
bool isDirectory = System.IO.Directory.Exists(args.FullPath);
55+
CleanName(args.OldFullPath, out string directory, out string name);
56+
OnFileSystemEvent(directory, name, isDirectory ? NotifyMask.OnMovedFrom | NotifyMask.DirectoryOnly : NotifyMask.OnMovedFrom);
57+
CleanName(args.FullPath, out directory, out name);
58+
OnFileSystemEvent(directory, name, isDirectory ? NotifyMask.OnMovedTo | NotifyMask.DirectoryOnly : NotifyMask.OnMovedTo);
59+
}
60+
61+
private void OnDeleted(object sender, FileSystemEventArgs args)
62+
{
63+
bool isDirectory = args.FullPath.EndsWith(Path.DirectorySeparatorChar.ToString()) || !Path.HasExtension(args.FullPath);
64+
CleanName(args.FullPath, out string directory, out string name);
65+
OnFileSystemEvent(directory, name, isDirectory ? NotifyMask.OnDeleted | NotifyMask.DirectoryOnly : NotifyMask.OnDeleted);
66+
}
67+
68+
private void OnError(object sender, ErrorEventArgs args) => OnFileSystemError(args.GetException());
69+
70+
private void OnCreated(object sender, FileSystemEventArgs args)
71+
{
72+
bool isDirectory = System.IO.Directory.Exists(args.FullPath);
73+
CleanName(args.FullPath, out string directory, out string name);
74+
OnFileSystemEvent(directory, name, isDirectory ? NotifyMask.OnCreated | NotifyMask.DirectoryOnly : NotifyMask.OnCreated);
75+
}
76+
77+
private void OnChanged(object sender, FileSystemEventArgs args)
78+
{
79+
bool isDirectory = System.IO.Directory.Exists(args.FullPath);
80+
CleanName(args.FullPath, out string directory, out string name);
81+
OnFileSystemEvent(directory, name, isDirectory ? NotifyMask.OnModified | NotifyMask.DirectoryOnly : NotifyMask.OnModified);
82+
}
83+
84+
protected override void OnFileSystemEvent(string directory, string name, NotifyMask mask)
85+
{
86+
directory = FileSystem.ResolvePath(directory);
87+
string fullPath = Path.Combine(directory, name);
88+
CleanName(fullPath, out directory, out name);
89+
base.OnFileSystemEvent(directory, name, mask);
90+
}
91+
}
92+
}

0 commit comments

Comments
 (0)