最初は、非常に単純なタスクに直面していると思いました。しかし、想像していたようにうまくいかないことに気付きました。今、私はほとんど立ち往生しているので、皆さんが私を助けてくれることを願っています.
私のシナリオはこれです(Windows 2008 R2サーバー上):
- ファイルは 1 日に 3 回、FTP ディレクトリにアップロードされます。ファイル名は常に同じです。つまり、既存のファイルは毎回上書きされます。
- FTP アップロード ディレクトリを監視する単純な C# サービスをプログラムしました。これには FileSystemWatcher クラスを使用しています。
- ファイルのアップロードには数分かかるため、File Watcher が変更を登録すると、ファイルがまだアップロードされている (またはロックされている) かどうかを確認するために、定期的にファイルを開こうとします。
- ファイルのロックが解除されたら、そのファイルを IIS 仮想ディレクトリに移動してみます。最初に古いファイルを削除してから、新しいファイルを移動する必要があります。これが私の問題の始まりです。ファイルは IIS (w3wp.exe プロセス) によって常にロックされているようです。
調査の結果、ファイルをロックしているプロセス (この場合は w3wp.exe) を強制終了する必要があることがわかりました。これを行うために、新しいアプリケーション プールを作成し、仮想ディレクトリをアプリケーションに変換しました。現在、私のディレクトリは別の w3wp.exe プロセスの下で実行されており、新しいファイルを安全に強制終了してそこに移動できると思われます。
次に、ターゲット ファイルをロックしている適切な w3wp.exe プロセス (合計 3 つの w3wp.exe プロセスが実行され、それぞれが別のアプリケーション プールで実行されます) を見つける必要があります。しかし、これは C# ではほとんど不可能な作業のようです。ここSOで「特定のファイルをロックしたプロセスの検索」に関する多くの質問を見つけましたが、どの回答も役に立ちませんでした。たとえば、Process Explorer は、どのプロセスがファイルをロックしているかを正確に教えてくれます。
次にわからないのは、Windows エクスプローラーを使用してターゲット ファイルを問題なく削除できることです。私のC#アプリケーションだけが「ファイルは別のプロセスで使用されています」というエラーを受け取ります。ここの違いは何だろうと思います...
ロックされたファイルと C# に関する SO に関する最も注目すべき質問は次のとおりです。
Win32: ミューテックスを所有するプロセス/スレッドを取得する方法は?
^^ ここのコード例は実際に動作しますが、これはアクティブなプロセスごとに開いているハンドル ID を出力します。特定のファイル名を検索する方法、または少なくともハンドル ID をファイル名に解決する方法がわかりません。この WinAPI のものは、私の頭のはるか上にあります。
C# を使用して、ファイルをロックしているプロセスをどのように特定するのでしょうか?
^^ ここにあるサンプル コードはまさに私が必要としているものですが、残念ながら動作させることができません。サンプル コードは WinAPI 呼び出しを多用しているため、常に "AccessViolationException" がスローされますが、これはわかりません。
簡単な作業、不可能ですか?助けていただければ幸いです。
編集 ここに私のサーバーコードのいくつかの関連部分があります:
ファイルがロックされているかどうかを検出するヘルパー関数:
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;
}
これは、FTP アップロード ディレクトリを監視している FileSystemWatcher イベントのコードです。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 分ごとに日付をチェックしています。そのため、ファイルの HTTP HEAD を継続的にチェックしているクライアントが存在するため、IIS によってファイルがロックされている可能性があります。それにもかかわらず、Windowsエクスプローラーを介してファイルを問題なく手動で削除/名前変更/移動できる理由がわかりません。これが機能するのはなぜですか? また、アプリケーションで「別のプロセスによってロックされています」という例外が発生するのはなぜですか?