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

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

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;

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

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

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

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


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

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

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

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

Ответы [ 11 ]

12 голосов
/ 19 ноября 2010

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

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

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

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

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

С одной стороны, это не было девственно, и вот ответ, который кажется:

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

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

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

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

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

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

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

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

3 голосов
/ 11 марта 2009

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

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

и

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

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

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

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

3 голосов
/ 11 марта 2009

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

2 голосов
/ 12 марта 2009

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

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

1 голос
/ 12 марта 2009

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

1 голос
/ 11 марта 2009

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

0 голосов
/ 30 сентября 2017

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

0 голосов
/ 27 апреля 2015

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

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

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

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

0 голосов
/ 23 мая 2014

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

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

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

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

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

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

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

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

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

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

0 голосов
/ 14 августа 2009

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

{$IFOPT I-}

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...