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 в моем случае) может быть cuplrit здесь (так что, возможно, файл is В использовать). Если это действительно проблема, то как лучше ее обойти? Жесткое кодирование автоматической повторной попытки на самом деле не кажется мне чистым решением...


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

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

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

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

11 ответов


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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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


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


как правило, вы должны положить открытие файла перед попыткой попробовать..наконец:--3-->

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

и

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

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

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

но я не думаю, что здесь проблема.


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


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


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


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

{$IFOPT I-}

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

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


Я ненавижу Windows... смотрите почему:

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

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

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

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

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

          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 переписать работает
  • 1,2,3,5,6 когда fisrt переписать не удается, обратите внимание, есть 6, так что второй переписать работал

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

Д. П.: такая же проблема происходит сброс... теперь я всегда инкапсулирую их в такую попытку...кроме блока и избавиться от проблемы!


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

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

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

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


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