几年来我们一直在使用以下代码。
/// <summary>
/// Opens a file and returns an exclusive handle. The file is deleted as soon as the handle is released.
/// </summary>
/// <param name="path">The name of the file to create</param>
/// <returns>A FileStream backed by an exclusive handle</returns>
/// <remarks>If another process attempts to open this file, they will recieve an UnauthorizedAccessException</remarks>
public static System.IO.FileStream OpenAsLock(string path)
{
var stream = TranslateIOExceptions(() => System.IO.File.Open(path, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.Write, System.IO.FileShare.Delete));
System.IO.File.Delete(path);
return stream;
}
从内存中,此代码使用”将文件保留在原位置,直到FileStream关闭。该技术被用作协作并发锁的一部分。
[我发现了许多其他问题,使我认为该行为曾经是注释所描述的:文件保留在原处,直到关闭返回的文件流。
Will we ever be able to delete an open file in Windows?
Can using FileShare.Delete cause a UnauthorizedAccessException?
但是,作为调查的一部分,我发现Windows并非如此。而是,一旦调用File.Delete,该文件即被删除。我还尝试重现Hans建议在上述链接中发生的错误,但未成功。
class Program
{
static void Main(string[] args)
{
File.Open("test", FileMode.OpenOrCreate, FileAccess.Write, FileShare.Delete);
File.Delete("test");
File.WriteAllText("test", "hello world");
Console.Write(File.ReadAllText("test"));
Console.ReadLine();
}
}
[不幸的是,我们可能已经捕获了这种行为变化的单元测试未正确配置为在我们的环境中每晚运行,所以我无法确定它是否曾经运行过绿色。
这是行为的真正改变吗?我们知道什么时候发生吗?是否故意(已记录)?
非常感谢Eryk的建议。
事实证明,我们确实有一些单元测试可以捕获这种行为上的变化,包括明确测试此行为的测试。我怀疑这些是在最初调查此奇怪行为时添加的。
单元测试尚未发出警报,因为我们的测试计算机运行的Windows 10版本比我的开发计算机版本的旧。
我审阅了list of build releases,不幸的是,这两个版本之间有两打以上的发行版。但是,我对build 17763.832, available Oct 15, 2019
中列出的“改进和修复”感到非常怀疑解决了共享存储在群集中的文件的问题在您执行完之后,带有备用数据流的卷(CSV)仍然存在尝试删除它们。您可能还会收到“访问被拒绝”消息在下一个尝试访问或删除文件。
我不确定为什么特定于CSV的更改会影响我的系统,但是说明与我看到的更改完全匹配。
关于特定的代码,结果返回“ FileStream”在我们的代码中从未使用过。相反,我们依靠IDisposable接口,在“关键部分”完成后关闭流,并解锁共享文件。
技术上是一项重大突破,现在我要执行以下操作:
// ...
public static IDisposable OpenAsLock(string path)
{
var stream = TranslateIOExceptions(() => System.IO.File.Open(path, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.Write, System.IO.FileShare.None));
return new FileBasedLock(stream, path);
}
// ...
internal class FileBasedLock : IDisposable
{
public FileBasedLock(FileStream stream, string path)
{
Stream = stream ?? throw new System.ArgumentNullException(nameof(stream));
Path = path ?? throw new System.ArgumentNullException(nameof(path));
}
public FileStream Stream { get; }
public string Path { get; }
public void Dispose()
{
Stream.Close();
try { File.Delete(Path); }
catch (IOException) { }
}
}