C# 无法以编程方式覆盖IIS虚拟目录/应用程序中的文件(文件始终处于锁定状态)

C# 无法以编程方式覆盖IIS虚拟目录/应用程序中的文件(文件始终处于锁定状态),c#,iis,file-locking,C#,Iis,File Locking,起初我以为我面临的是一项非常简单的任务。但现在我意识到这并不像我想象的那样有效,所以现在我希望你们能帮助我,因为我现在几乎陷入困境 我的场景如下(在Windows 2008 R2服务器上): 一个文件每天上传3次到FTP目录。文件名始终相同,这意味着每次都会覆盖现有文件 我已经编写了一个简单的C#服务,用于监视FTP上传目录,为此我使用FileSystemWatcher类 上传文件需要几分钟的时间,所以一旦文件观察者注册了更改,我会定期尝试打开文件,查看文件是否仍在上传(或锁定) 一旦文件不再被

起初我以为我面临的是一项非常简单的任务。但现在我意识到这并不像我想象的那样有效,所以现在我希望你们能帮助我,因为我现在几乎陷入困境

我的场景如下(在Windows 2008 R2服务器上):

  • 一个文件每天上传3次到FTP目录。文件名始终相同,这意味着每次都会覆盖现有文件
  • 我已经编写了一个简单的C#服务,用于监视FTP上传目录,为此我使用FileSystemWatcher类
  • 上传文件需要几分钟的时间,所以一旦文件观察者注册了更改,我会定期尝试打开文件,查看文件是否仍在上传(或锁定)
  • 一旦文件不再被锁定,我会尝试将文件移到我的IIS虚拟目录。我必须先删除旧文件,然后再移动新文件。这就是我的问题开始的地方。文件似乎总是被IIS(w3wp.exe进程)锁定
  • 经过一些研究,我发现我必须终止锁定文件的进程(本例中是w3wp.exe)。为此,我创建了一个新的应用程序池,并将虚拟目录转换为一个应用程序。现在,我的目录在一个单独的w3wp.exe进程下运行,我认为它可以安全地杀死并将新文件移动到那里

    现在我只需要找到合适的w3wp.exe进程(总共有3个w3wp.exe进程在运行,每个进程都在一个单独的应用程序池中运行),它锁定了我的目标文件。但在C#中,这似乎是一项几乎不可能完成的任务。我在这里发现了很多关于“锁定特定文件的查找过程”的问题,但没有一个答案对我有帮助。 例如,Process Explorer确切地告诉我哪个进程正在锁定我的文件

    接下来我不明白的是,我可以通过Windows资源管理器毫无问题地删除目标文件。只有我的C#应用程序收到“文件正被另一个进程使用”错误。我想知道这里有什么不同

    以下是关于锁定文件和C#的最值得注意的问题:

    ^^ 这里的示例代码确实有效,但这会输出每个活动进程的打开句柄ID。我只是不知道如何搜索特定的文件名,或者至少将句柄ID解析为文件名。这个WinAPI的东西太让我头疼了

    ^^ 这里的示例代码正是我需要的,但不幸的是,我无法让它工作。它总是抛出一个我无法理解的“AccessViolationException”,因为示例代码大量使用WinAPI调用

    简单的任务,不可能完成?谢谢你的帮助

    编辑 以下是我的服务器代码的一些相关部分:

    用于检测文件是否已锁定的帮助器函数:

        private bool FileReadable(string file, int timeOutSeconds)
        {
            DateTime timeOut = DateTime.Now.AddSeconds(timeOutSeconds);
    
            while (DateTime.Now < timeOut)
            {
                try
                {
                    if (File.Exists(file))
                    {
                        using (FileStream fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.None))
                        {
                            return true;
                        }
                    }
                    return false;
                }
                catch (Exception)
                {
                    Thread.Sleep(500);
                }
            }
    
            m_log.LogLogic(0, "FileReadable", "Timeout after [{0}] seconds trying to open the file {1}", timeOutSeconds, file);
            return false;
        }
    
    EDIT2: 在客户端下载文件时终止w3wp.exe进程对我们来说没有问题。我只是很难找到正确的w3wp.exe进程来锁定文件


    另外,我的客户端应用程序正在客户端上下载文件,它正在检查HTTP头的最后修改日期。客户每10分钟检查一次日期。因此,该文件可能被IIS锁定,因为有客户端在不断地检查HTTP头以查找该文件。尽管如此,我不明白为什么我可以通过windows资源管理器手动删除/重命名/移动文件而不会出现任何问题。为什么会这样,为什么我的应用程序会出现“被另一个进程锁定”异常?

    我遇到的一个问题是,文件在编写过程中仍然存在,这意味着它也会被锁定。如果此时调用了FileReadable()函数,它将返回false


    我的解决方案是,在写入文件的过程中,将文件写入(比如)OUTPUT1.TXT,然后在完全写入并关闭文件流后,将其重命名为OUTPUT2.TXT。这样,OUTPUT2.TXT的存在表示该文件已被写入并(希望)解锁。只需在FileReadable()循环中检查OUTPUT2.TXT

    您正在对问题的症状进行故障排除,而不是对根本原因进行修复。如果你想沿着这条路走下去,这里是终止进程的代码——但更好的办法是正确地完成它并找出错误所在。我建议在FileReadable的Catch异常中:

    catch (Exception ex) {
    if (ex is IOException && IsFileLocked(ex)) {
    //Confirm the code see's it as a FileLocked issue, not some other exception
    //its not safe to unlock files used by other processes, because the other process is likely reading/writing it. 
    }
    }
    
    private static bool IsFileLocked(Exception exception)
    {
        int errorCode = Marshal.GetHRForException(exception) & ((1 << 16) - 1);
        return errorCode == 32 || errorCode == 33;
    }
    
    catch(异常示例){
    if(ex是IOException&&IsFileLocked(ex)){
    //确认代码将其视为文件锁定问题,而不是其他异常
    //解锁其他进程使用的文件不安全,因为其他进程可能正在读取/写入它。
    }
    }
    私有静态bool IsFileLocked(异常)
    {
    
    int errorCode=Marshal.GetHRForException(异常)和((1在您的示例代码中,我看不到您正在关闭文件流的位置。保持文件流打开将保持对文件的锁定。关闭流是一个好主意。您可能不想杀死w3wp.exe进程,正如其他人所提到的那样。

    大家都说

    “做得更好” 没人说怎么办!!! 这就是方法。因为你提到了“我的客户端应用程序”,这里有一个关键的机会,如果你不能控制读取文件的应用程序,你就不会有这个机会

    每次只需使用新的文件名。 您可以控制程序读取和写入文件。在文件名中添加一个递增的#,让客户端选择最大的#(实际上是最晚的日期,然后您的数字可以环绕)。如果可以,让writer程序清理旧文件;如果不清理,则不会造成任何伤害。IIS最终会放弃这些文件。如果不清理,请每周打开资源管理器,自己动手

    其他关键是更新频率低(无法生成文件)
    catch (Exception ex) {
    if (ex is IOException && IsFileLocked(ex)) {
    //Confirm the code see's it as a FileLocked issue, not some other exception
    //its not safe to unlock files used by other processes, because the other process is likely reading/writing it. 
    }
    }
    
    private static bool IsFileLocked(Exception exception)
    {
        int errorCode = Marshal.GetHRForException(exception) & ((1 << 16) - 1);
        return errorCode == 32 || errorCode == 33;
    }