起初我以为我面临着一项非常简单的任务。但是现在我意识到它并没有像我想象的那样起作用,所以现在我希望你们有人可以帮助我,因为我现在几乎陷入困境。
我的方案是这样的(在Windows 2008 R2服务器上):
经过一番研究,我发现我必须杀死锁定文件的进程(在这种情况下是w3wp.exe)。为此,我创建了一个新的应用程序池,并将虚拟目录转换为一个应用程序。现在我的目录在一个单独的w3wp.exe进程下运行,据说我可以安全地杀死并在那里移动新文件。
现在我只需要找到正确的w3wp.exe进程(总共运行3个w3wp.exe进程,每个进程在一个单独的应用程序池下运行),该进程锁定了我的目标文件。但这似乎是C#中几乎不可能完成的任务。我在这里发现了许多关于“查找锁定特定文件的过程”的问题,但没有一个答案对我有帮助。 例如,Process Explorer正好告诉我哪个进程正在锁定我的文件。
接下来我不明白的是,我可以通过Windows资源管理器删除目标文件而没有任何问题。只是我的C#应用程序获取“文件被另一个进程使用”错误。我想知道这里有什么不同......
以下是有关锁定文件和C#的最值得注意的问题:
Win32: How to get the process/thread that owns a mutex?
^^ 这里的示例代码确实有效,但是这会为每个活动进程输出打开的句柄ID。我只是无法弄清楚如何搜索特定的文件名,或至少解析文件名的句柄ID。这个WinAPI的东西远远超出我的想象。
Using C#, how does one figure out what process locked a file?
^^ 这里的示例代码正是我所需要的,但不幸的是我无法让它工作。它始终抛出一个我无法弄清楚的“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;
}
这是我的FileSystemWatcher事件中的代码,它监视FTP上载目录。 filepath是新上传的文件,targetfilepath是我的IIS目录中的目标文件。
// here I'm waiting for the newly uploaded file to be ready
if (FileReadable(filepath, FWConfig.TimeOut))
{
// move uploaded file to IIS virtual directory
string targetfilepath = Path.Combine(FWConfig.TargetPath, FWConfig.TargetFileName);
if(File.Exists(targetfilepath))
{
m_log.LogLogic(4, "ProcessFile", "Trying to delete old file first: [{0}]", targetfilepath);
// targetfilepath is the full path to my file in my IIS directory
// always fails because file is always locked my w3wp.exe :-(
if(FileReadable(targetfilepath, FWConfig.TimeOut))
File.Delete(targetfilepath);
}
File.Move(filepath, targetfilepath);
}
EDIT2: 在客户端下载文件时杀死w3wp.exe进程对我们来说没有问题。我只是很难找到正确锁定文件的w3wp.exe进程。
此外,我在客户端上下载文件的客户端应用程序正在检查HTTP HEAD的Last-Modified日期。客户每10分钟检查一次日期。因此,IIS可能会锁定该文件,因为有些客户端不断检查文件的HTTP HEAD。尽管如此,我不明白为什么我可以通过Windows资源管理器手动删除/重命名/移动文件没有任何问题。为什么这样做,以及为什么我的应用程序会被“另一个进程锁定”异常?
答案 0 :(得分:3)
我遇到的一个问题是文件仍在被写入时存在,这意味着它也会被锁定。如果此时调用了FileReadable()函数,它将返回false。
我的解决办法是,在写入文件的proc中,将文件写入,例如OUTPUT1.TXT,然后在完全写入并关闭FileStream之后,将其重命名为OUTPUT2.TXT。这样,OUTPUT2.TXT的存在表明文件已被写入并且(希望)已解锁。只需在FileReadable()循环中检查OUTPUT2.TXT即可。
答案 1 :(得分:3)
每个人都说......
这是怎么回事。因为您提到了“我的客户端应用程序”,所以如果您无法控制阅读该文件的应用程序,那么您将无法拥有这个机会。
您可以控制程序读取和写入文件。在文件名中加上一个递增的#,让客户端选择最大的#(实际上是最新的日期,然后你的数字可以包围)。让编写器程序清理旧文件如果可以;如果没有,他们不会伤害任何东西。 IIS最终将放弃它们。如果没有,请每周打开资源管理器并自己动手!
使这项工作的其他关键是更新频率低(文件不会太差),以及FTP +网络服务器在同一驱动器上的事实(否则MOVE不是原子的,客户端可能会得到一个半复制的文件。如果FTP驱动器不同,解决方法是复制到网络服务器上的临时驱动器,然后移动)。
但如果您无法更改客户端或只需读取一个名称该怎么办?
使用脚本前端。让客户端点击一个ASPX,设置正确的HTTP头并具有“选择正确的文件”逻辑,并吐出文件内容。这是一个非常流行的技巧页面,用于将存储在数据库中的图像写入浏览器,而img标签似乎从文件中读取。 (google沿着这一行获取示例代码)。
听起来像是黑客,但事实并非如此。现代无锁内存缓存系统也做类似的事情。锁定或腐败是不可能发生的;直到'写'完成,读者才能看到旧版本。
plus ,这很简单,从剧本小家伙到穿孔卡片的每个人都会知道你到底要做什么。低技术!
答案 2 :(得分:0)
您正在排查问题的症状,而不是修复根本原因。如果你想沿着这条路走下去就是杀死进程的代码http://www.codeproject.com/Articles/20284/My-TaskManager - 但更好的想法是正确地做到这一点并找出错误的方法。我建议在FileReadable的Catch Exception中使用:
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;
}
答案 3 :(得分:0)
我在示例代码中没有看到您关闭文件流的位置。保持文件流打开将锁定文件。关闭流是个好主意。你可能不想像你在这里提到的那样杀死你的w3wp.exe进程。
答案 4 :(得分:0)
重新启动IIS可以解锁w3wp.exe所占用的文件。
cmd(以管理员身份运行) - &gt; iisreset / stop - &gt;更新/删除文件 Windows资源管理器 - &gt; iisreset / start