Как в C# проверить, используется ли файл другим процессом?
Проверка блокировки файла через попытку открытия с монопольным доступом
Прямого метода, который бы возвращал true/false без риска изменить состояние файла, в .NET не существует. Однако есть надёжный и широко используемый подход — попытаться открыть файл с флагом FileShare.None (монопольный доступ). Если файл занят, вы получите исключение IOException, которое и укажет на блокировку.
Главное преимущество этого метода — он работает на всех версиях .NET и не требует внешних вызовов WinAPI. Вот реализация:
public static bool IsFileInUse(string filePath)
{
try
{
using (FileStream fs = File.Open(filePath,
FileMode.Open,
FileAccess.ReadWrite,
FileShare.None))
{
// Если дошли сюда — файл свободен
return false;
}
}
catch (IOException)
{
// Файл недоступен — скорее всего, заблокирован
return true;
}
catch (UnauthorizedAccessException)
{
// Нет прав на чтение/запись — тоже считаем "занятым"
return true;
}
}
Как это работает:
FileMode.Open— открываем существующий файл.FileAccess.ReadWrite— запрашиваем полный доступ.FileShare.None— запрещаем доступ другим процессам на время проверки.
Если файл уже открыт другим процессом, вызов File.Open выбросит IOException, и мы вернём true. Если файл свободен — поток будет создан и сразу закрыт (благодаря using), вернётся false.
Важные нюансы
Этот метод не является «чистой» проверкой — он на мгновение блокирует файл. В многопоточной среде между проверкой и фактическим использованием файла его может занять другой процесс. Поэтому логичнее сразу пытаться открыть файл с нужными правами и обрабатывать исключение, а не проверять отдельно.
Пример правильного подхода с ожиданием освобождения:
public static string ReadFileWhenAvailable(string filePath, int maxRetries = 5, int delayMs = 500)
{
for (int i = 0; i < maxRetries; i++)
{
try
{
using (FileStream fs = new FileStream(filePath,
FileMode.Open,
FileAccess.Read,
FileShare.Read))
using (StreamReader reader = new StreamReader(fs))
{
return reader.ReadToEnd();
}
}
catch (IOException) when (i < maxRetries - 1)
{
Thread.Sleep(delayMs);
}
}
throw new IOException($"Файл {filePath} не освободился после {maxRetries} попыток.");
}
Такой код не просто проверяет, а пытается прочитать файл с повторными попытками через паузу. Это решает проблему «гонки» между проверкой и использованием.
Для продвинутых: использование WMI
Если нужно узнать, какой именно процесс заблокировал файл, придётся вызывать WinAPI через P/Invoke (функция NtQuerySystemInformation), либо использовать WMI-запросы. Но для 99% задач простой попытки открытия с FileShare.None более чем достаточно.
Альтернативный подход: проверка через FileSystemWatcher и таймер
Если ваша задача — не просто проверить, а дождаться освобождения файла (например, для мониторинга логов), можно использовать комбинацию FileSystemWatcher и таймера. Однако это сложнее и не гарантирует моментального обнаружения.
public static void WaitForFileRelease(string filePath, Action onReleased)
{
var watcher = new FileSystemWatcher(Path.GetDirectoryName(filePath))
{
Filter = Path.GetFileName(filePath),
EnableRaisingEvents = true
};
Timer checkTimer = null;
checkTimer = new Timer(_ =>
{
if (!IsFileInUse(filePath)) // используем метод из первого ответа
{
watcher.Dispose();
checkTimer.Dispose();
onReleased?.Invoke();
}
}, null, 0, 1000);
}
Недостатки этого подхода:
- Ресурсоёмкость (постоянный опрос каждую секунду).
- Задержка до 1 секунды между освобождением и реакцией.
- Ненужная сложность для простых сценариев.
Поэтому рекомендую придерживаться первого варианта — прямой попытки открытия с повторными попытками. Это проще, надёжнее и эффективнее.
Ответов пока нет.
Войдите чтобы оставить ответ
Блоги