try/catch ブロックを使用せずにファイルがロックされているかどうかを確認する方法はありますか?
現在、私が知っている唯一の方法は、ファイルを開いて任意のSystem.IO.IOException
.
同様の問題に直面したとき、次のコードで終了しました。
public class FileManager
{
private string _fileName;
private int _numberOfTries;
private int _timeIntervalBetweenTries;
private FileStream GetStream(FileAccess fileAccess)
{
var tries = 0;
while (true)
{
try
{
return File.Open(_fileName, FileMode.Open, fileAccess, Fileshare.None);
}
catch (IOException e)
{
if (!IsFileLocked(e))
throw;
if (++tries > _numberOfTries)
throw new MyCustomException("The file is locked too long: " + e.Message, e);
Thread.Sleep(_timeIntervalBetweenTries);
}
}
}
private static bool IsFileLocked(IOException exception)
{
int errorCode = Marshal.GetHRForException(exception) & ((1 << 16) - 1);
return errorCode == 32 || errorCode == 33;
}
// other code
}
いいえ、残念ながら、それについて考えると、ファイルが次の1秒でロックされる可能性があるため、その情報はとにかく価値がありません(読み取り:短い期間)。
とにかくファイルがロックされているかどうかを具体的に知る必要があるのはなぜですか?それを知っていると、あなたに良いアドバイスを与える他の方法が得られるかもしれません。
コードが次のようになる場合:
if not locked then
open and update file
次に、2行の間で、別のプロセスがファイルを簡単にロックし、最初に回避しようとしていたのと同じ問題、つまり例外が発生する可能性があります。
また、プロセスがこのファイルを使用しているかどうかを確認し、インストーラーのように続行するために閉じる必要があるプログラムのリストを表示することもできます。
public static string GetFileProcessName(string filePath)
{
Process[] procs = Process.GetProcesses();
string fileName = Path.GetFileName(filePath);
foreach (Process proc in procs)
{
if (proc.MainWindowHandle != new IntPtr(0) && !proc.HasExited)
{
ProcessModule[] arr = new ProcessModule[proc.Modules.Count];
foreach (ProcessModule pm in proc.Modules)
{
if (pm.ModuleName == fileName)
return proc.ProcessName;
}
}
}
return null;
}
相互運用機能を使用する代わりに、.NET FileStream クラス メソッドの Lock と Unlock を使用できます。
FileStream.Lock http://msdn.microsoft.com/en-us/library/system.io.filestream.lock.aspx
FileStream.Unlock http://msdn.microsoft.com/en-us/library/system.io.filestream.unlock.aspx
DixonDの優れた答えのバリエーション(上記)。
public static bool TryOpen(string path,
FileMode fileMode,
FileAccess fileAccess,
FileShare fileShare,
TimeSpan timeout,
out Stream stream)
{
var endTime = DateTime.Now + timeout;
while (DateTime.Now < endTime)
{
if (TryOpen(path, fileMode, fileAccess, fileShare, out stream))
return true;
}
stream = null;
return false;
}
public static bool TryOpen(string path,
FileMode fileMode,
FileAccess fileAccess,
FileShare fileShare,
out Stream stream)
{
try
{
stream = File.Open(path, fileMode, fileAccess, fileShare);
return true;
}
catch (IOException e)
{
if (!FileIsLocked(e))
throw;
stream = null;
return false;
}
}
private const uint HRFileLocked = 0x80070020;
private const uint HRPortionOfFileLocked = 0x80070021;
private static bool FileIsLocked(IOException ioException)
{
var errorCode = (uint)Marshal.GetHRForException(ioException);
return errorCode == HRFileLocked || errorCode == HRPortionOfFileLocked;
}
使用法:
private void Sample(string filePath)
{
Stream stream = null;
try
{
var timeOut = TimeSpan.FromSeconds(1);
if (!TryOpen(filePath,
FileMode.Open,
FileAccess.ReadWrite,
FileShare.ReadWrite,
timeOut,
out stream))
return;
// Use stream...
}
finally
{
if (stream != null)
stream.Close();
}
}
ファイルのロックが解除されるまで待機する秒数を追加し、再試行する DixonD のコードのバリエーションを次に示します。
public bool IsFileLocked(string filePath, int secondsToWait)
{
bool isLocked = true;
int i = 0;
while (isLocked && ((i < secondsToWait) || (secondsToWait == 0)))
{
try
{
using (File.Open(filePath, FileMode.Open)) { }
return false;
}
catch (IOException e)
{
var errorCode = Marshal.GetHRForException(e) & ((1 << 16) - 1);
isLocked = errorCode == 32 || errorCode == 33;
i++;
if (secondsToWait !=0)
new System.Threading.ManualResetEvent(false).WaitOne(1000);
}
}
return isLocked;
}
if (!IsFileLocked(file, 10))
{
...
}
else
{
throw new Exception(...);
}
関心のあるファイルの領域で相互運用機能を介してLockFileを呼び出すことができます。これは例外をスローしません。成功した場合、ファイルのその部分 (プロセスによって保持されている) がロックされ、そのロックはUnlockFileを呼び出すか、プロセスが終了するまで保持されます。
次に、2行の間で、別のプロセスがファイルを簡単にロックし、最初に回避しようとしていたのと同じ問題、つまり例外が発生する可能性があります。
ただし、この方法では、問題が一時的なものであることがわかり、後で再試行できます。(たとえば、書き込み中にロックが発生した場合に、ロックが解除されるまで再試行を続けるスレッドを作成できます。)
一方、IOExceptionは、それ自体ではロックがIO障害の原因となるほど具体的ではありません。一時的ではない理由がある可能性があります。
ファイルがロックされているかどうかは、最初に自分で読み取りまたはロックを試みることで確認できます。
同じことですが、Powershellで
function Test-FileOpen
{
Param
([string]$FileToOpen)
try
{
$openFile =([system.io.file]::Open($FileToOpen,[system.io.filemode]::Open))
$open =$true
$openFile.close()
}
catch
{
$open = $false
}
$open
}
私がやったことは次のとおりです。
internal void LoadExternalData() {
FileStream file;
if (TryOpenRead("filepath/filename", 5, out file)) {
using (file)
using (StreamReader reader = new StreamReader(file)) {
// do something
}
}
}
internal bool TryOpenRead(string path, int timeout, out FileStream file) {
bool isLocked = true;
bool condition = true;
do {
try {
file = File.OpenRead(path);
return true;
}
catch (IOException e) {
var errorCode = Marshal.GetHRForException(e) & ((1 << 16) - 1);
isLocked = errorCode == 32 || errorCode == 33;
condition = (isLocked && timeout > 0);
if (condition) {
// we only wait if the file is locked. If the exception is of any other type, there's no point on keep trying. just return false and null;
timeout--;
new System.Threading.ManualResetEvent(false).WaitOne(1000);
}
}
}
while (condition);
file = null;
return false;
}