-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Description
Description
Before .NET 7, the FileSystemEventArgs constructor allowed an empty directory string to be specified without throwing. Starting in .NET 7, specifying an empty string for the directory throws an ArgumentException.
This breaks a scenario where a file name without a directory is passed to Path.GetDirectoryName, and that return value is passed into the FileSystemEventArgs or RenamedEventArgs.
Reproduction Steps
Minimal repro:
var eventArgs = new FileSystemEventArgs(WatcherChangeTypes.Created, "", null);Test cases that illustrate the change in behavior compared to .NET 6:
var paths = new List<(string directory, string? file)>();
paths.Add(("", null));
paths.Add(("", ""));
paths.Add(("", "foo.txt"));
paths.Add((@"D:\", null));
paths.Add((@"D:\", ""));
paths.Add((@"D:\", "foo.txt"));
paths.Add((@"D:\git", null));
paths.Add((@"D:\git", ""));
paths.Add((@"D:\git", "foo.txt"));
foreach ((string d, string? f) in paths)
{
string? argsName;
string argsFullPath;
try
{
var fileArgs = new FileSystemEventArgs(WatcherChangeTypes.Created, d, f);
argsName = $"\"{fileArgs.Name}\"";
argsFullPath = $"\"{fileArgs.FullPath}\"";
}
catch (Exception ex)
{
argsName = "throw";
argsFullPath = ex.GetType().Name;
}
Console.WriteLine($"{"\"" + d + "\"",-12}{"\"" + f + "\"",-12}{argsName,-12}{argsFullPath}");
}Expected behavior
In the minimal repro, the constructor should not throw.
eventArgs.Nameshould be""eventArgs.FullPathshould be the current process directory, with a trailing directory separator character.
With the test cases, the output should be the following, where ~ is a placeholder representing the process's current directory.
"" "" "" "~\"
"" "" "" "~\"
"" "foo.txt" "foo.txt" "~\foo.txt"
"D:\" "" "" "D:\"
"D:\" "" "" "D:\"
"D:\" "foo.txt" "foo.txt" "D:\foo.txt"
"D:\git" "" "" "D:\git\"
"D:\git" "" "" "D:\git\"
"D:\git" "foo.txt" "foo.txt" "D:\git\foo.txt"
Actual behavior
The constructor throws ArgumentException when the directory is "".
The output from the test is:
"" "" throw ArgumentException
"" "" throw ArgumentException
"" "foo.txt" throw ArgumentException
"D:\" "" "" "D:\"
"D:\" "" "" "D:\"
"D:\" "foo.txt" "foo.txt" "D:\foo.txt"
"D:\git" "" "" "D:\git\"
"D:\git" "" "" "D:\git\"
"D:\git" "foo.txt" "foo.txt" "D:\git\foo.txt"
Regression?
Yes, this is a regression from .NET 6. This regression was introduced in #63051. That change made a deliberate breaking change to the FileSystemEventArgs.FullPath property's value, but it wasn't intended for the constructor to throw when directory is an empty string.
Instead of calling Path.Join(Path.GetFullPath(directory), name), we likely need to call Path.GetFullPath(Path.Join(directory, name)), as this will gracefully handle the empty directory.
Known Workarounds
A possible, but frustrating workaround, would be to check if directory is an empty string, and specify "." instead.
Configuration
No response
Other information
This was reported by an internal team at Microsoft as a .NET 7 adoption blocker because they have a lot of code using this pattern.