Delphi: Почему я иногда получаю ошибку 103 ввода-вывода с этим кодом?

В нескольких моих приложениях у меня есть код, похожий на следующий:

if ForceDirectories(ExtractFilePath(lLogName)) then
  begin
    AssignFile(lLog, lLogName);
    try
      if FileExists(lLogName) then
        Append(lLog)
      else
        Rewrite(lLog);
      Writeln(lLog, lLogLine);
    finally
      {$I-}CloseFile(lLog);{$I+}
    end;
  end;

В одном приложении, в первый раз, когда я пытаюсь выполнить это, я последовательно получаю исключение 103 ошибки ввода-вывода в строке с оператором Append (файл существует до вызова этого). Все последующие попытки операции будут работать нормально - пока я не перезапущу приложение.

Все документы, которые я нашел об этой ошибке, указали, что это вызвано вызовом CloseFile без предшествующих Reset или Rewrite (Append обычно не упоминается) или если файл находился в использование другим процессом. Поскольку исключение происходит до вызова CloseFile, оно, очевидно, не может быть первым.

Я уже пробовал вставить Reset сразу после AssignFile для хорошей меры, но затем я получаю исключение в этой строке.

Также нет другого приложения, открыто доступного для этого файла. Я говорю "открыто", потому что у меня есть небольшое подозрение, что антивирус (TrendMicro в моем случае) может быть куклитом здесь (так что, возможно, используется файл ). Если это действительно проблема, что было бы лучше всего вокруг нее? Жесткое кодирование автоматической повторной попытки на самом деле не похоже на чистое решение для меня...


Другим случаем, когда я иногда получаю ошибку 103, является этот код, который я использую для создания пустого файла (или чаще всего для удаления существующего файла):

AssignFile(lFile, AFileName);
try
  Rewrite(lFile);
finally
  CloseFile(lFile);
end;

В этом случае это намного сложнее воспроизвести. Это происходит гораздо реже. В большинстве случаев это похоже на первый запуск после того, как я перекомпилировал приложение. Может ли это снова стать антивирусом? Я только когда-либо видел это на моей машине разработки и никогда не получал отчет от клиента. Как и в случае с первым сценарием, это всегда случается один раз на один сеанс приложения (если вообще). Последующие попытки всегда успешны.

Любые предложения по другому, потенциально более безопасному подходу к созданию пустых файлов или опорожнению существующих?

Ответ 1

Я не вижу, что не так с автоматической попыткой. Я не вижу, что ты можешь сделать что-нибудь еще. Если какой-либо другой процесс читает файл, то ваш Append/Rewrite завершится с ошибкой. И поскольку файл является журналом, есть хорошая вероятность, что что-то, например, просмотрщик журналов или текстовый редактор, будет читать его в тот момент, когда вы попытаетесь его открыть.

Попробуйте открыть файл несколько раз с задержкой между попытками до окончательного завершения. Вы можете использовать экспоненциальный откат, если хотите, чтобы вы были фантазии.

Ответ 2

Хорошо, это на год, но я собираюсь добавить свой комментарий к этому, потому что это объясняет, почему это происходит.

У меня была такая же проблема в многопоточном приложении с кодом, почти идентичным фрагменту выше, и у меня были критические разделы, защищающие код.

Проблема произошла наиболее легко, когда одна операция регистрации быстро последовала за другой. Вторая операция потерпит неудачу по вышеуказанной причине.

Я думал, что это тоже антивирусное программное обеспечение, но ошибка произошла на одном компьютере, а не на другом, где оба были установлены Norton 360. Машина с проблемой была совершенно новая Windows 7, а одна без Windows XP. У коллеги также возникла проблема с запуском системы под виртуализированной машиной Windows Vista без установленного антивируса.

Итак, мой вопрос: "Почему эта машина XP была такой разной?".

Во-первых, он не был девственником, и это ответ кажется:

Оппортунистическая блокировка и кэширование NT были отключены. Большинство (зрелых) разработчиков Delphi будут знать, что при использовании BDE они отключены, чтобы поддерживать целостность файлов DBF и DB в многопользовательских ситуациях. Эти настройки не были отключены на новых машинах, потому что мы больше не разрабатываем файлы данных Paradox!

Отложенное кэширование записи, похоже, оставляет блокировку чтения/записи в файле до тех пор, пока ОС не выполнит свою работу, и это может быть несколько миллисекунд позже.

Итак, в моем случае второе событие журнала блокировалось первым.

Хорошо, я не предлагаю отключить оппортунистическую блокировку + NT Caching. Одна из основных причин, по которым мы отошли от BDE, заключалась в том, чтобы избежать убеждения клиентов возиться с такими настройками. Итак, есть четыре практических решения:

1) Повторить попытку в течение приемлемого периода времени, как указано dangph.

2), чтобы открыть файл, когда приложение загружается и удерживает его открытым в течение всего срока действия приложения. Не очень полезно, если вы используете несколько экземпляров приложения.

3) Летите положить спать (1) перед кодом регистрации и надеяться, что это достаточно долго, чтобы блокировка была выпущена. Но это может замедлить работу вашей системы, если вы делаете много протоколирования.

или 4) Положите попытку... кроме.. вокруг вашего кода. Но тогда вам, вероятно, гарантированно пропустите 100% вторых сообщений (со ссылкой на мой случай).

Ответ 3

Помимо антивируса, он также может быть индексирующим программное обеспечение или программное обеспечение для управления файлами, например, Google Desktop. Однако настоящая проблема заключается в том, что сообщение об ошибке не поможет вам решить проблему. Я предлагаю вам переписать код для использования TFileStream, вместо этого, чтобы улучшить ваши сообщения об ошибках.

Ответ 4

Обычно вы должны открыть файл перед попыткой try..finally:

if fileexists then
  append(..)
else
  rewrite(..);
try
  // do something with the file
finally
  CloseFile(..);
end;

и

AssignFile(lFile, AFileName);
Rewrite(lFile);
CloseFile(lFile);

(попробуйте, наконец, не имеет никакого смысла в последнем случае)

В противном случае закрытие файла может завершиться неудачно, потому что оно не может быть открыто, и это замаскирует реальную ошибку.

Но я не думаю, что это проблема.

Ответ 5

Является ли ваше приложение многопоточным? У меня когда-то была такая же проблема, как и при одновременном вызове кода регистрации из обоих потоков. Если это так, используйте TCriticalSection для контроля доступа.

Ответ 6

Не могли бы вы взглянуть на блуждающую ошибку из чего-то еще, скомпилированного в состоянии $I?

Ответ 7

Ваш примерный код должен работать вообще, эти ошибки, похоже, являются ошибками доступа. Чтобы обнаружить случай этого, вы можете попытаться отключить TrendMicro и посмотреть, не исчезла ли проблема.

Ответ 8

Если я правильно понимаю, ваше назначение файла не выполняется. Вы уверены, что FileChecks находятся во время вызова AssignFile? Это довольно необычно, но вы можете проверить это, используя:

{$IFOPT I-}

if IOResult <> 0 then
begin
  // Error handling
end;
{$ENDIF}

Я соглашаюсь использовать TFileStream (или что-то вроде функции доступа к низкоуровневому файлу). Это критично для производительности и может стать огромной проблемой при переходе на Unicode.

Ответ 9

Я ненавижу Windows... вижу, почему:

Это код, который решает проблему ReWrite (без сообщений):

AssignFile(MyFileHandler,RouteToWritableExistantFile);
try
   ReWrite(MyFileHandler); // This sometimes fails
except
      ReWrite(MyFileHandler); // When prior fails, this runs OK
end;

Да, это абсурдно... если ReWrite терпит неудачу, сделайте Rewrite... но он работает для меня в 100% случаев.

Я ДЕБЮГ, что проблема, связанная с большим количеством ShowMessage... была немного фантазии после попытки не абсурдных вещей (файл был ранее открыт и т.д.)... что произойдет, если я попробую ReWrite дважды? Сюрприз... если первый провал, вторая попытка срабатывает!

Я знаю, что это абсурд, но он работает!

Я знал это, потому что я писал много сообщений, например:

          ShowMessage('1: - pre - AssignFile - ');
AssignFile(MyFileHandler,RouteToWritableExistantFile);
          ShowMessage('2: - post - AssignFile - ');
try
          ShowMessage('3: - pre - ReWrite - ');
   ReWrite(MyFileHandler); // This sometimes fails
          ShowMessage('4: - pre - ReWrite - ');
except
          ShowMessage('5: - pre - ReWrite - ');
      ReWrite(MyFileHandler); // When prior fails, this allways runs OK (it is absurd, but works)
          ShowMessage('6: - pre - ReWrite - ');
end;

И я получил две сообщения:

  • 1,2,3,4 при работе fisrt rewrite
  • 1,2,3,5,6, когда переписывание fisrt не выполняется, обратите внимание, что существует 6, поэтому вторая перезапись работала

Надеюсь, что это поможет другим, не настолько безумным!!!

P.D.: Такая же проблема возникает с Reset... теперь я всегда инкапсулирую их в такую ​​попытку... кроме блока и избавлюсь от проблемы!

Ответ 10

Привет. Возможно, слишком поздно для ответа на исходный вопрос, но я хочу добавить свою часть, и, несмотря на то, что это не прямой ответ на исходный вопрос, я хочу внести свой вклад, поскольку я думаю, что это может помочь некоторым другим, кто ищет такой вид ответа.

У меня получился такой же ответ на ошибку, но в полнофункциональном программном обеспечении, в частности, анализаторе EIS, больше раз я пытался открыть файл, но через некоторое время его можно было открыть. Мне это было любопытно, поэтому я схватился за интернет. И действительно, как маловероятно, казалось (потому что значимая программа довольно мала), я нашел такой вызов ошибки. Я снимал эти ответы, и тот, у которого есть две проверки с зеленым чеком, я сразу последовал, просто интуитивно, и это так: Когда я открыл файл в другой программе, он не позволит мне перейти к откройте его как файл в анализаторе EIS.

Итак, общий вывод: когда вы открыли файл в одной программе, он не позволит вам открывать его где-нибудь еще, и вы получите сообщение об ошибке ввода/вывода ошибки 103.

PS: Немного смелый и лёгкий и легкий вывод (без правильного чтения (но у меня нет времени, извините) вышеупомянутые ответы), но я надеюсь, что у вас есть точка.:) Приветствия.

Ответ 11

Я случайно сделал CloseFile дважды и имел эту ошибку.