【问题标题】:Creating file then setting timestamp without releasing file lock创建文件然后设置时间戳而不释放文件锁
【发布时间】:2016-07-25 14:28:18
【问题描述】:

我想知道是否有一种方法可以创建文件并设置上次写入时间(和其他时间戳信息),而不允许另一个进程在这两个操作之间获取对文件的锁定。

我想这样做的原因是为了解决一个问题,即防病毒软件在文件创建后立即获得对文件的锁定,并且在尝试设置文件属性时仍然拥有锁定。具体来说,我正在使用的代码是 SevenZipSharp(据我所知,不再维护)。

重现此问题的代码是:

var filePath = "test.txt";
using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite))
{
    var bytes = Encoding.ASCII.GetBytes("Hello fail.");
    fileStream.Write(bytes, 0, bytes.Length);
    var fileInfo = new FileInfo(filePath);
    fileInfo.CreationTime = DateTime.Now;
}

这在执行最后一条语句时会产生以下异常: System.IO.IOException “该进程无法访问文件 'c:\test.txt',因为它正被另一个进程使用。”

我正在考虑使用重试机制实现时间属性的设置,但想知道是否有更优雅的解决方案。

【问题讨论】:

  • 为什么在 " 之间不允许另一个进程获取文件的锁" ?为什么以后不能改时间?
  • 听起来像防病毒软件正在攻击它并阻止修改?
  • 您可以在 fileStream.Dispose() 之后更改 CreationTime。但不是介于两者之间。
  • 我可以稍后设置(这是当前 SevenZipSharp 代码所做的),但有时会失败,因为文件被防病毒锁定。
  • CreationTime 在您复制文件时不会改变。理论上(如果防病毒是问题),您可以在其他地方创建文件,它不会在其中查找,然后从那里复制它。或者干脆为防病毒添加一个例外。

标签: c# .net windows file


【解决方案1】:

正如@Damien_The_Unbeliever 提到的,您需要获取文件句柄。试试这个。

class Program {
    [DllImport("kernel32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool SetFileTime(SafeFileHandle hFile, ref long lpCreationTime, ref long lpLastAccessTime, ref long lpLastWriteTime);

    static void Main(string[] args) {
        var filePath = "test.txt";
        long when = DateTime.Now.AddDays(10).ToFileTime();
        using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite)) {
            if (!SetFileTime(fileStream.SafeFileHandle, ref when, ref when, ref when)) {
                throw new Win32Exception();
            }
            var bytes = Encoding.ASCII.GetBytes("Hello fail.");
            fileStream.Write(bytes, 0, bytes.Length);
        }
    }
}

【讨论】:

  • 完美。感谢您和@Damien_The_Unbeliever。
【解决方案2】:

问题是您正试图访问您的 using 语句正在使用的文件。这就是您收到错误的原因。您需要完成您的 using 语句,然后您可以在文件上分配创建时间。

如果文件被其他软件锁定,最好的办法是创建一个while循环来等待文件。

尝试以下方法:

var filePath = "test.txt";
DateTime creationTime;
using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite))
{
    var bytes = Encoding.ASCII.GetBytes("Hello fail.");
    fileStream.Write(bytes, 0, bytes.Length);
    creationTime = DateTime.Now;
}
int numTries = 0;
while (true)
{
    ++numTries;
    try
    {
        // Attempt to open the file exclusively.
        using (var fileInfo = new FileInfo(filePath))
        {
             // If we got this far the file is ready
            fileInfo.CreationTime = creationTime;
            break;
        }
    }
    catch (Exception ex)
    {
        if (numTries > 10)
        {
            // Get out of it
            Console.WriteLine("This joker still has your file, I'm out.");
            break;
        }

        // Wait for the lock to be released
        System.Threading.Thread.Sleep(500);
    }
}

【讨论】:

  • 这会停止异常,但不能解决另一个进程在创建和设置属性之间获取锁的问题。正如我在原帖中所说,我正在考虑重试,但想知道是否可以使用另一种不释放文件锁的方法。
  • 是的,我明白你的目标是什么,但你必须先释放文件,然后才能使用不同的库重新打开它。
  • 你应该检查异常是由于文件被锁定:stackoverflow.com/a/11060322
【解决方案3】:

不,您无法阻止其他进程锁定文件。

您可以尝试编写文件的临时版本,然后在设置属性时使用Process.Start 执行Robocopy 以复制文件。文件的原始副本变得无关紧要 - 您可以稍后清理它。

这将取决于 Robocopy 用于复制文件和设置属性的方法是否是原子的。

有人已经编写了一个包装器来避免从您的 .NET 代码调用命令行应用程序的丑陋 - 它被称为 RoboSharp 并且有一个 nuget package

【讨论】:

  • 如果 Robocopy 是原子的,那么原子性是可能的。如果原子性是可能的,那么大概有一种方法可以在我的代码中使用这种原子性。
  • 是的,它将使用一些较低级别的 API。我想知道是否有一个已经打包好的第 3 方库。现在我很好奇。我去看看...是的,有一个包裹 RoboCopy 的 nuget 包。
  • 但如果@aquinas 的答案有效,那么相比之下,这个解决方案确实很笨拙且值得怀疑。
猜你喜欢
  • 2012-03-01
  • 2012-03-08
  • 2018-12-19
  • 2011-09-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-20
相关资源
最近更新 更多