Skip to content

Commit 57bbc77

Browse files
committed
updated dependencies and code style
1 parent 781f19d commit 57bbc77

13 files changed

+151
-171
lines changed

LICENSE

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
MIT License
1+
MIT License
22

3-
Copyright (c) 2022 Duong Dieu Phap
3+
Copyright (c) 2025 Duong Dieu Phap
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# FileWatcherEx for Windows
1+
# FileWatcherEx for Windows
22
A wrapper of `System.IO.FileSystemWatcher` to standardize the events and avoid false change notifications. It has been being used in [ImageGlass - A lightweight, versatile image viewer](https://github.com/d2phap/ImageGlass) project.
33

44
This project is based on the *VSCode FileWatcher*: https://github.com/Microsoft/vscode-filewatcher-windows
@@ -14,7 +14,7 @@ This project is based on the *VSCode FileWatcher*: https://github.com/Microsoft/
1414
## Features
1515
- Standardizes the events of `System.IO.FileSystemWatcher`.
1616
- No false change notifications when a file system item is created, deleted, changed or renamed.
17-
- Supports .NET 6.0, 7.0.
17+
- Supports .NET 6.0, 7.0, 8.0, 9.0
1818

1919
## Installation
2020
Run the command:
@@ -63,6 +63,5 @@ void FW_OnRenamed(object sender, FileChangedEvent e)
6363
- [GitHub sponsor](https://github.com/sponsors/d2phap)
6464
- [Patreon](https://www.patreon.com/d2phap)
6565
- [PayPal](https://www.paypal.me/d2phap)
66-
- [Wire Transfers](https://donorbox.org/imageglass)
6766

6867
Thanks for your gratitude and finance help!

Source/Demo/Demo.WinForms.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<OutputType>WinExe</OutputType>
5-
<TargetFramework>net8.0-windows</TargetFramework>
5+
<TargetFramework>net9.0-windows</TargetFramework>
66
<Nullable>enable</Nullable>
77
<UseWindowsForms>true</UseWindowsForms>
88
<ImplicitUsings>enable</ImplicitUsings>

Source/FileSystemEventRecorder/FileSystemEventRecorder.cs

Lines changed: 37 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
using System.Collections.Concurrent;
1+
using CsvHelper;
2+
using System.Collections.Concurrent;
23
using System.Diagnostics;
34
using System.Globalization;
4-
using CsvHelper;
55

66
namespace FileSystemEventRecorder;
77

88
// event received from C# FileSystemWatcher
99
internal record EventRecord(
10-
string FullPath,
11-
string EventName,
10+
string FullPath,
11+
string EventName,
1212
string? OldFullPath, // only provided by "rename" event
1313
long NowInTicks
1414
);
@@ -33,7 +33,7 @@ public static class FileSystemEventRecords
3333

3434
public static void Main(string[] args)
3535
{
36-
var (watchedDirectory, csvOutputFile) = ProcessArguments(args);
36+
var (watchedDirectory, csvOutputFile) = ProcessArguments(args);
3737

3838
var watcher = new FileSystemWatcher();
3939
watcher.Path = watchedDirectory;
@@ -48,7 +48,7 @@ public static void Main(string[] args)
4848
EventRecords.Enqueue(new EventRecord(ev.FullPath, "deleted", null, Stopwatch.GetTimestamp()));
4949

5050
watcher.Changed += (_, ev) =>
51-
EventRecords.Enqueue(new EventRecord(ev.FullPath, "changed", null, Stopwatch.GetTimestamp()));
51+
EventRecords.Enqueue(new EventRecord(ev.FullPath, "changed", null, Stopwatch.GetTimestamp()));
5252
watcher.Renamed += (_, ev) =>
5353
EventRecords.Enqueue(new EventRecord(ev.FullPath, "renamed", ev.OldFullPath, Stopwatch.GetTimestamp()));
5454
watcher.Error += (_, ev) =>
@@ -96,7 +96,7 @@ private static void ProcessQueueAndWriteToDisk(string csvOutputFile)
9696
else
9797
{
9898
Console.WriteLine($"Recorded {EventRecords.Count} file system events.");
99-
var records = MapToDiffTicks();
99+
var records = MapToDiffTicks;
100100

101101
Console.WriteLine($"Writing CSV to {csvOutputFile}.");
102102
using (var writer = new StreamWriter(csvOutputFile))
@@ -110,35 +110,39 @@ private static void ProcessQueueAndWriteToDisk(string csvOutputFile)
110110
}
111111

112112
// post-process queue. Calculate difference between previous and current event
113-
private static IEnumerable<EventRecordWithDiff> MapToDiffTicks()
113+
private static IEnumerable<EventRecordWithDiff> MapToDiffTicks
114114
{
115-
List<EventRecordWithDiff> eventsWithDiffs = new();
116-
long previousTicks = 0;
117-
foreach (var eventRecord in EventRecords)
115+
get
118116
{
119-
var diff = previousTicks switch
117+
List<EventRecordWithDiff> eventsWithDiffs = [];
118+
long previousTicks = 0;
119+
120+
foreach (var eventRecord in EventRecords)
120121
{
121-
0 => 0, // first run
122-
_ => eventRecord.NowInTicks - previousTicks
123-
};
124-
125-
previousTicks = eventRecord.NowInTicks;
126-
double diffInMilliseconds = Convert.ToInt64(new TimeSpan(diff).TotalMilliseconds);
127-
128-
var directory = Path.GetDirectoryName(eventRecord.FullPath) ?? "";
129-
var fileName = Path.GetFileName(eventRecord.FullPath);
130-
var oldFileName = Path.GetFileName(eventRecord.OldFullPath);
131-
132-
var record = new EventRecordWithDiff(
133-
directory,
134-
fileName,
135-
eventRecord.EventName,
136-
oldFileName,
137-
diff,
138-
diffInMilliseconds);
139-
eventsWithDiffs.Add(record);
140-
}
122+
var diff = previousTicks switch
123+
{
124+
0 => 0, // first run
125+
_ => eventRecord.NowInTicks - previousTicks
126+
};
127+
128+
previousTicks = eventRecord.NowInTicks;
129+
double diffInMilliseconds = Convert.ToInt64(new TimeSpan(diff).TotalMilliseconds);
130+
131+
var directory = Path.GetDirectoryName(eventRecord.FullPath) ?? "";
132+
var fileName = Path.GetFileName(eventRecord.FullPath);
133+
var oldFileName = Path.GetFileName(eventRecord.OldFullPath);
134+
135+
var record = new EventRecordWithDiff(
136+
directory,
137+
fileName,
138+
eventRecord.EventName,
139+
oldFileName,
140+
diff,
141+
diffInMilliseconds);
142+
eventsWithDiffs.Add(record);
143+
}
141144

142-
return eventsWithDiffs;
145+
return eventsWithDiffs;
146+
}
143147
}
144148
}

Source/FileSystemEventRecorder/FileSystemEventRecorder.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
5-
<TargetFramework>net8.0</TargetFramework>
5+
<TargetFramework>net9.0</TargetFramework>
66
<ImplicitUsings>enable</ImplicitUsings>
77
<Nullable>enable</Nullable>
88
</PropertyGroup>
@@ -12,7 +12,7 @@
1212
</ItemGroup>
1313

1414
<ItemGroup>
15-
<PackageReference Include="CsvHelper" Version="30.0.1" />
15+
<PackageReference Include="CsvHelper" Version="33.1.0" />
1616
</ItemGroup>
1717

1818
</Project>

Source/FileWatcherEx/FileSystemWatcherEx.cs

Lines changed: 15 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,28 @@
11

2+
using FileWatcherEx.Helpers;
23
using System.Collections.Concurrent;
34
using System.ComponentModel;
4-
using FileWatcherEx.Helpers;
55

66
namespace FileWatcherEx;
77

8+
89
/// <summary>
910
/// A wrapper of <see cref="FileSystemWatcher"/> to standardize the events
1011
/// and avoid false change notifications.
1112
/// </summary>
12-
public class FileSystemWatcherEx : IDisposable, IFileSystemWatcherEx
13+
/// <param name="folderPath"></param>
14+
/// <param name="logger">Optional Action to log out library internals</param>
15+
public class FileSystemWatcherEx(string folderPath = "", Action<string>? logger = null) : IDisposable, IFileSystemWatcherEx
1316
{
1417
#region Private Properties
1518

1619
private Thread? _thread;
1720
private EventProcessor? _processor;
18-
private readonly BlockingCollection<FileChangedEvent> _fileEventQueue = new();
21+
private readonly BlockingCollection<FileChangedEvent> _fileEventQueue = [];
1922

2023
private SymlinkAwareFileWatcher? _watcher;
2124
private Func<IFileSystemWatcherWrapper>? _fswFactory;
22-
private readonly Action<string> _logger;
25+
private readonly Action<string> _logger = logger ?? (_ => { });
2326

2427
// Define the cancellation token.
2528
private CancellationTokenSource? _cancelSource;
@@ -40,13 +43,13 @@ internal Func<IFileSystemWatcherWrapper> FileSystemWatcherFactory
4043
/// <summary>
4144
/// Gets or sets the path of the directory to watch.
4245
/// </summary>
43-
public string FolderPath { get; set; } = "";
46+
public string FolderPath { get; set; } = folderPath;
4447

4548

4649
/// <summary>
4750
/// Gets the collection of all the filters used to determine what files are monitored in a directory.
4851
/// </summary>
49-
public System.Collections.ObjectModel.Collection<string> Filters { get; } = new();
52+
public System.Collections.ObjectModel.Collection<string> Filters { get; } = [];
5053

5154

5255
/// <summary>
@@ -131,17 +134,6 @@ public string Filter
131134
#endregion
132135

133136

134-
/// <summary>
135-
/// Initialize new instance of <see cref="FileSystemWatcherEx"/>
136-
/// </summary>
137-
/// <param name="folderPath"></param>
138-
/// <param name="logger">Optional Action to log out library internals</param>
139-
public FileSystemWatcherEx(string folderPath = "", Action<string>? logger = null)
140-
{
141-
FolderPath = folderPath;
142-
_logger = logger ?? (_ => {}) ;
143-
}
144-
145137

146138
/// <summary>
147139
/// Start watching files
@@ -164,15 +156,13 @@ void InvokeChangedEvent(object? sender, FileChangedEvent fileEvent)
164156
{
165157
if (SynchronizingObject != null && SynchronizingObject.InvokeRequired)
166158
{
167-
SynchronizingObject.Invoke(new Action<object, FileChangedEvent>(InvokeChangedEvent), new object[] { SynchronizingObject, e });
159+
SynchronizingObject.Invoke(new Action<object, FileChangedEvent>(InvokeChangedEvent), [SynchronizingObject, e]);
168160
}
169161
else
170162
{
171163
OnChanged?.Invoke(SynchronizingObject, e);
172164
}
173165
}
174-
175-
176166
break;
177167

178168
case ChangeType.CREATED:
@@ -183,15 +173,13 @@ void InvokeCreatedEvent(object? sender, FileChangedEvent fileEvent)
183173
{
184174
if (SynchronizingObject != null && SynchronizingObject.InvokeRequired)
185175
{
186-
SynchronizingObject.Invoke(new Action<object, FileChangedEvent>(InvokeCreatedEvent), new object[] { SynchronizingObject, e });
176+
SynchronizingObject.Invoke(new Action<object, FileChangedEvent>(InvokeCreatedEvent), args: [SynchronizingObject, e]);
187177
}
188178
else
189179
{
190180
OnCreated?.Invoke(SynchronizingObject, e);
191181
}
192182
}
193-
194-
195183
break;
196184

197185
case ChangeType.DELETED:
@@ -202,15 +190,13 @@ void InvokeDeletedEvent(object? sender, FileChangedEvent fileEvent)
202190
{
203191
if (SynchronizingObject != null && SynchronizingObject.InvokeRequired)
204192
{
205-
SynchronizingObject.Invoke(new Action<object, FileChangedEvent>(InvokeDeletedEvent), new object[] { SynchronizingObject, e });
193+
SynchronizingObject.Invoke(new Action<object, FileChangedEvent>(InvokeDeletedEvent), [SynchronizingObject, e]);
206194
}
207195
else
208196
{
209197
OnDeleted?.Invoke(SynchronizingObject, e);
210198
}
211199
}
212-
213-
214200
break;
215201

216202
case ChangeType.RENAMED:
@@ -221,23 +207,21 @@ void InvokeRenamedEvent(object? sender, FileChangedEvent fileEvent)
221207
{
222208
if (SynchronizingObject != null && SynchronizingObject.InvokeRequired)
223209
{
224-
SynchronizingObject.Invoke(new Action<object, FileChangedEvent>(InvokeRenamedEvent), new object[] { SynchronizingObject, e });
210+
SynchronizingObject.Invoke(new Action<object, FileChangedEvent>(InvokeRenamedEvent), [SynchronizingObject, e]);
225211
}
226212
else
227213
{
228214
OnRenamed?.Invoke(SynchronizingObject, e);
229215
}
230216
}
231-
232-
233217
break;
234218

235219
default:
236220
break;
237221
}
238222
}, (log) =>
239223
{
240-
Console.WriteLine($"{Enum.GetName(typeof(ChangeType), ChangeType.LOG)} | {log}");
224+
Console.WriteLine($"{Enum.GetName(ChangeType.LOG)} | {log}");
241225
});
242226

243227
_cancelSource = new CancellationTokenSource();
@@ -278,7 +262,7 @@ void OnError(ErrorEventArgs e)
278262

279263

280264
internal void StartForTesting(
281-
Func<string, FileAttributes> getFileAttributesFunc,
265+
Func<string, FileAttributes> getFileAttributesFunc,
282266
Func<string, DirectoryInfo[]> getDirectoryInfosFunc)
283267
{
284268
Start();

Source/FileWatcherEx/FileWatcherEx.csproj

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks>
4+
<TargetFrameworks>net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
55
<ImplicitUsings>enable</ImplicitUsings>
66
<Nullable>enable</Nullable>
7-
<Copyright>Copyright © 2018-2023 Duong Dieu Phap</Copyright>
7+
<Copyright>Copyright © 2018-2025 Duong Dieu Phap</Copyright>
88
<PackageProjectUrl>https://github.com/d2phap/FileWatcherEx</PackageProjectUrl>
99
<PackageReadmeFile>README.md</PackageReadmeFile>
1010
<RepositoryUrl>https://github.com/d2phap/FileWatcherEx</RepositoryUrl>
@@ -13,14 +13,15 @@
1313
<Description>A wrapper of FileSystemWatcher to standardize the events and avoid false change notifications, used in ImageGlass project (https://imageglass.org). This project is based on the VSCode FileWatcher: https://github.com/Microsoft/vscode-filewatcher-windows.</Description>
1414
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
1515
<FileVersion>$(Version)</FileVersion>
16-
<VersionPrefix>2.6.0</VersionPrefix>
16+
<VersionPrefix>2.7.0</VersionPrefix>
1717
<PackageReleaseNotes>See https://github.com/d2phap/FileWatcherEx/releases</PackageReleaseNotes>
1818
<Authors>d2phap</Authors>
1919
<Title>FileWatcherEx - A file system watcher</Title>
2020
<IncludeSymbols>True</IncludeSymbols>
2121
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
2222
<PackageLicenseFile>LICENSE</PackageLicenseFile>
2323
<SignAssembly>False</SignAssembly>
24+
<LangVersion>latest</LangVersion>
2425
</PropertyGroup>
2526

2627
<ItemGroup>

Source/FileWatcherEx/Helpers/EventNormalizer.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -128,15 +128,15 @@ internal static bool IsParent(FileChangedEvent e, List<string> deletedPaths)
128128
internal static bool IsParent(string path, string candidatePath)
129129
{
130130
// if exists, remove trailing "\" for both paths
131-
candidatePath = candidatePath.TrimEnd('\\');
131+
candidatePath = candidatePath.TrimEnd('\\');
132132
path = path.TrimEnd('\\');
133-
return path.IndexOf(candidatePath + '\\', StringComparison.Ordinal) == 0;
133+
return path.StartsWith(candidatePath + '\\', StringComparison.Ordinal);
134134
}
135135

136136

137137
private class FileEventRepository
138138
{
139-
private readonly Dictionary<string, FileChangedEvent> _mapPathToEvents = new();
139+
private readonly Dictionary<string, FileChangedEvent> _mapPathToEvents = [];
140140

141141
public void AddOrUpdate(FileChangedEvent newEvent)
142142
{
@@ -166,7 +166,7 @@ public void Remove(FileChangedEvent ev)
166166

167167
public List<FileChangedEvent> Events()
168168
{
169-
return _mapPathToEvents.Values.ToList();
169+
return [.. _mapPathToEvents.Values];
170170
}
171171
}
172172
}

0 commit comments

Comments
 (0)