Можно ли разумно обойти антивирусное сканирование рабочего каталога?
Мое приложение Win32 во время работы выполняет множество операций с дисками в назначенной временной папке, и о серьезной переработке ее не может быть и речи.
Некоторые клиенты имеют антивирусное программное обеспечение, которое сканирует один и тот же временный каталог (оно просто сканирует все). Мы пытались уговорить их отключить его - это не работает, так что об этом тоже не может быть и речи.
Время от времени (что-то вроде одного раза на каждую тысячу файловых операций) мое приложение пытается выполнить операцию с файлом, который в это же время открывается антивирусом и поэтому блокируется операционной системой. Нарушение обмена происходит и вызывает ошибку в моем приложении. Это происходит в среднем раз в три минуты.
Временная папка может содержать до 100 тыс. Файлов в большинстве типичных сценариев, поэтому мне не нравится идея открывать их постоянно, потому что это может привести к нехватке ресурсов в некоторых пограничных условиях.
Есть ли у моего приложения разумная стратегия реагирования на ситуации, когда нужный файл заблокирован? Может как то так?
for( int i = 0; i < ReasonableNumber; i++ ) {
try {
performOperation(); // do useful stuff here
break;
} catch( ... ) {
if( i == ReasonableNumber - 1 ) {
throw; //not to hide errors if unlock never happens
}
}
Sleep( ReasonableInterval );
}
Это жизнеспособная стратегия? Если да, сколько раз и как часто мое приложение должно повторяться? Какие есть лучшие идеи, если таковые имеются?
6 ответов
У меня был опыт работы с антивирусным программным обеспечением Symantec и AVG, в результате чего файлы были недоступны для открытия.
Распространенная проблема, с которой мы столкнулись в 2002 году в Symantec, была с MSDev6, когда файл обновлялся в следующей последовательности:
- файл открыт
- содержимое изменяется в памяти
- приложение должно зафиксировать изменения
- Приложение создает новый файл TMP с новой копией файла + изменения
- приложение удаляет старый файл
- приложение копирует файл tmp в старое имя файла
- приложение удаляет файл tmp
Проблема возникнет между этапами 5 и 6. Symantec сделает что-то, чтобы замедлить удаление, предотвращая создание файла с тем же именем (CreateFile вернул ERROR_DELETE_PENDING). MSDev6 не заметит этого, что означает, что шаг 6 провалился. Шаг 7 все же произошел. Удаление оригинала в конечном итоге закончится. Таким образом, файл больше не существует на диске!
С AVG у нас периодически возникали проблемы с открытием файлов, которые были только что изменены.
Наше решение было попыткой / уловом в разумной петле, как в вопросе. Наш счетчик циклов равен 5.
Вирусный сканер, который блокирует файлы во время их сканирования, довольно плох. Клиенты, у которых есть плохие вирусы, должны заменить свои мозги...;-)
Хорошо, хватит разглагольствовать. Если файл заблокирован каким-либо другим процессом, вы можете использовать стратегию "попробуйте снова", как вы предлагаете. OTOH, вам действительно нужно закрыть, а затем снова открыть эти файлы? Разве вы не можете держать их открытыми, пока ваш процесс не закончится? Один совет: добавьте задержку (спящий режим) при повторной попытке открыть файл. Около 100 мс должно быть достаточно. Если virusscanner хранит файл так долго, значит это очень плохой сканер. Клиенты с плохими сканерами заслуживают сообщения об исключении, которое они увидят. Как правило, попробуйте до трех раз... -> Открыть, при сбое попробуйте еще раз, при втором сбое попробуйте еще раз, при третьем сбое просто сбой.
Не забудьте сбой в удобном для пользователя виде.
Зависит от того, насколько велики ваши файлы, но для 10–100 с килобайт я считаю, что 5 попыток с 100 мс (0,1 секунды) будет достаточно. Если вы все еще время от времени сталкивались с ошибкой, удвойте время ожидания, но YMMV.
Если у вас есть несколько мест в коде, которые должны это сделать, позвольте мне предложить функциональный подход:
using System;
namespace Retry
{
class Program
{
static void Main(string[] args)
{
int i = 0;
Utils.Retry(() =>
{
i = i + 1;
if (i < 3)
throw new ArgumentOutOfRangeException();
});
Console.WriteLine(i);
Console.Write("Press any key...");
Console.ReadKey();
}
}
class Utils
{
public delegate void Retryable();
static int RETRIES = 5;
static int WAIT = 100; /*ms*/
static public void Retry( Retryable retryable )
{
int retrys = RETRIES;
int wait = WAIT;
Exception err;
do
{
try
{
err = null;
retryable();
}
catch (Exception e)
{
err = e;
if (retrys != 1)
{
System.Threading.Thread.Sleep(wait);
wait *= 2;
}
}
} while( --retrys > 0 && err != null );
if (err != null)
throw err;
}
}
}
Если существует вероятность, что какой-то другой процесс - будь то антивирусное программное обеспечение, утилита резервного копирования или даже сам пользователь - может открыть файл, то вы должны написать код для этой возможности.
Ваше решение, пусть и не самое элегантное, безусловно, будет работать до тех пор, пока ReasonableNumber
достаточно велико - в прошлом я использовал 10 в качестве разумного числа. Я, конечно, не пошел бы выше, и вы могли бы уйти с более низким значением, таким как 5.
Ценность сна? Не более 100 мс или 200 мс
Имейте в виду, что в большинстве случаев ваше приложение все равно получит файл впервые.
Не могли бы вы изменить свое приложение, чтобы не выпускать дескриптор файла? Если вы сами заблокируете файл, антивирусное приложение не сможет его отсканировать.
В противном случае такая стратегия, как ваша, немного поможет, потому что она только уменьшает вероятность, но не решает проблему.
Сложная проблема. Большинство идей, которые у меня есть, идут в направлении, которое вам не нужно (например, редизайн).
Я не знаю, сколько файлов у вас есть в вашем каталоге, но если это не так много, вы можете обойти свою проблему, оставив все файлы открытыми и заблокированными во время работы вашей программы.
Таким образом, антивирусный сканер не сможет больше прерывать ваши обращения к файлам.