Delphi:为什么我有时会使用此代码获得I / O错误103?

时间:2009-03-11 13:50:01

标签: delphi file-io

在我的几个应用中,我的代码类似于以下内容:

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;

在一个应用程序中,第一次尝试执行此操作时,我一直在使用Append语句的行上获得I / O错误103异常(该文件在调用之前确实存在)。然后,该操作的所有后续尝试都将正常工作 - 直到我重新启动应用程序。

到目前为止,我发现的有关此错误的所有文档都表明,这可能是因为CloseFile之前ResetRewrite Append通常不是CloseFile提到的)或者该文件是否被另一个进程使用。由于异常发生在调用Reset之前,它显然不可能是前者。

我已经尝试在AssignFile之后插入AssignFile(lFile, AFileName); try Rewrite(lFile); finally CloseFile(lFile); end; 以获得良好的衡量标准,但之后我就会在该行上获得例外。

也没有其他应用程序直接访问该文件。我说“公然”,因为我确实怀疑反病毒(在我的情况下是TrendMicro)可能是这里的杯子(因此可能使用的文件 )。如果这确实是问题,那么围绕它的最佳方法是什么?硬编码自动重试对我来说并不是一个干净的解决方案......


我有时会收到103错误的另一种情况是这段代码,我用它来创建一个空文件(或者更常用于清空现有文件):

{{1}}

在这种情况下,重现起来要困难得多。它发生的次数要少得多。大多数情况下,这似乎是在我重新编译应用程序后的第一次运行时发生的。这可能再次成为反病毒的阻碍吗?我从未见过这种情况发生在我的开发机器上,从未得到过客户的报告。与第一个场景一样,每个应用程序会话只发生一次(如果有的话)。后续尝试总是成功。

对于创建空文件或清空现有文件的其他可能更安全的故障安全方法的建议是什么?

13 个答案:

答案 0 :(得分:12)

好的,已经晚了一年多了,但是我要加上我的评论,因为它解释了为什么会发生这种情况。

我在多线程应用程序中遇到完全相同的问题,其代码几乎与上面的代码段相同,并且我有关键部分保护代码。

当一个记录操作迅速跟随另一个记录操作时,问题最容易发生。由于上述原因,第二次操作将失败。

我认为它也是防病毒软件,但错误发生在一台机器而不是另一台机器上,两者都安装了Norton 360。有问题的机器是全新的Windows 7和没有Windows XP的机器。一位同事也遇到了在虚拟化Windows Vista机器下运行系统的问题,没有安装病毒检查程序。

所以我的问题是,“为什么这台XP机器如此与众不同?”

首先,它不是处女,这就是答案:

机会锁定和NT缓存已关闭。大多数(成熟的)Delphi开发人员都知道,在使用BDE时,这些是关闭的,以便在多用户情况下维护DBF和DB文件的完整性。这些设置未在新机器上禁用,因为我们不再为Paradox数据文件开发!

延迟写入缓存似乎会对文件进行读/写锁定,直到操作系统完成其业务,这可能是几毫秒之后。

因此,在我的情况下,第二个日志事件被第一个阻止。

好的,我并不是说你关闭机会锁定+ NT缓存。我们摆脱BDE的一个主要原因是避免说服客户修改这些设置。因此,有四种实用的解决方案:

1)如dangph所述,重试一段可接受的时间。

2)在应用程序加载时打开文件,并在应用程序的整个持续时间内保持打开状态。如果您正在运行应用程序的多个实例,则不太有用。

3)懒惰地在日志代码之前放置一个sleep(1)并希望它足够长以释放锁。但是,如果您正在进行大量的日志记录,则可能会降低系统速度。

或4)尝试一下...除了...代码周围。但是你可能会保证错过100%的第二条消息(指我的情况)。

答案 1 :(得分:3)

除了反病毒,它还可以索引软件或文件管理软件,如Google桌面。但是,这里真正的问题是,错误消息无法帮助您解决问题。我建议您重写代码以使用TFileStream,而不仅仅是为了改进您的错误消息。

答案 2 :(得分:3)

您通常应该在尝试尝试之前打开文件。最终:

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

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

(在最后一种情况下最后尝试没有任何意义)

否则关闭文件可能会失败,因为它无法打开,这会掩盖真正的错误。

但我认为这不是问题所在。

答案 3 :(得分:2)

我没有看到自动重试有什么问题。我没有看到你可以做任何其他事情。如果某个其他进程正在读取该文件,则Append / Rewrite将失败。由于该文件是一个日志,因此很有可能在您尝试打开它时,某些内容(例如日志查看器或文本编辑器)读取它。

尝试打开文件几次,延迟中间尝试,然后才能最终失败。如果你想要花哨,你可以使用指数退避。

答案 4 :(得分:1)

您的应用是多线程的?当从两个线程同时调用日志代码时,我曾遇到过与此相同的问题。如果是这样,请使用TCriticalSection来控制访问。

答案 5 :(得分:1)

你能看到在$ I-状态下编译的其他东西出现的杂散错误吗?

答案 6 :(得分:0)

您的示例代码应该可以正常工作,这些错误似乎是访问错误。要检测此情况,您可以尝试禁用TrendMicro并查看问题是否仍然存在。

答案 7 :(得分:0)

如果我理解这是正确的,那么您的文件分配将失败。你确定FileChecks是在你调用AssignFile的时候吗?这很不寻常,但您可以使用以下方法验证:

{$IFOPT I-}

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

我同意使用TFileStream(或者除了低级文件访问功能之外的任何东西)。这对性能至关重要,在转向Unicode时可能是一个巨大的问题。

答案 8 :(得分:0)

我讨厌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的问题......在尝试了非荒谬的事情之后有一点想象力(文件以前是oppened等)......如果我尝试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重写时
  • 1,2,3,5,6当fisrt重写失败时,注意有6,所以第二次重写已经有效

希望这有助于其他人不要这么生气!

P.D。:同样的问题发生在重置......现在我总是将它们封装到这样的尝试中...除了块并摆脱问题!

答案 9 :(得分:0)

方式你好。回答原始问题可能为时已晚,但我想补充一下,尽管它不是原始问题的直接答案,但我想做出贡献,因为我觉得它可能有所帮助其他一些人正在寻找这种答案。

我得到了同样的错误答案,但是在一个完全不同的软件中,特别是EIS分析器,我尝试打开文件的次数更多,但有些时候它本来可以打开。这对我很好奇,所以我抓住了互联网。而且,实际上,似乎不太可能(因为有意思的程序非常小),我发现了这样一个错误调用。我正在给上面的答案换颜色,并且用绿色检查tic进行了两次检查,我正确地跟着,直观地说,它是这样的:当我在另一个程序中打开文件时,它不会让我将其作为EIS分析器中的文件打开。

所以一般的结论是:当你在一个程序中打开一个文件时,它不会让你在其他任何地方打开它,你会收到错误I / O错误103.

PS:有点大胆,松懈而且简单的结论(没有正确阅读(但是没有时间,对不起)上面的回答),但我希望你有这个观点。 :)干杯。

答案 10 :(得分:0)

我无意中做了两次CloseFile而且出现了这个错误。

答案 11 :(得分:0)

我正在回答有关103 IOResult错误的原始问题,该问题在运行似乎可以的代码时有时会发生。 我总结了在此存档中找到的解决方案: https://de.comp.lang.delphi.misc.narkive.com/o1LNZylQ/assignfile-reset-und-ioresult-103

基本上,Delphi仍然包含一些20多年来以来继承的全局标志。一个是文件模式,另一个是IOResult。 如果有关打开文件的文件操作正常,则IOresult将重置为0。作为对该规则的一种怪异解释,closefile()将IOResult限制为103,因为“显然”该文件已关闭。然后尝试打开相同或另一个文件可能会失败。 解决方法很简单:将这一行放在closefile()之后:

r := IOResult; 

它重置IOResult,并且代码按预期工作。 请注意,多线程将需要其他预防措施。

答案 12 :(得分:0)

对我来说,这是由重复的计时器事件触发多次处理文件的函数引起的。所以第二个定时器事件在前一个定时器调用完成之前被调用。 这导致多次打开同一文件并导致 103 错误代码。

解决方案是在计时器调用处理中临时禁用计时器,直到处理完成。