tags:

views:

5327

answers:

7

In several of my apps I have code similar to the following:

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;

In one application, the first time I try to execute this I consistently get an I/O Error 103 exception on the line with the Append statement (the file does exist prior to calling this). All subsequent attempts at the operation will work fine however - until I restart the app.

All the docs I found about this error so far indicated that this would either be caused by calling CloseFile without prior Reset or Rewrite (Append typically isn't mentioned) or if the file was in use by another process. As the exception occurs before the call to CloseFile it obviously couldn't be the former.

I already tried inserting a Reset right after the AssignFile for good measure but then I get the exception on that line.

There is also no other application overtly accessing that file. I say "overtly" because I do have a slight suspicion that anti-virus (TrendMicro in my case) might be the cuplrit here (so maybe the file is in use). If that was indeed the problem, what would be the best way around it? Hard-coding an automatic retry does not really feel like a clean solution to me...


Another case where I sometimes get the 103 error is this code, which I use to create an empty file (or more often to empty an existing file):

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

In this case it's much harder to reproduce. It happens a lot less often. Most of the time this seems to happen on the first run after I recompiled the application. Could this again be the anti-virus getting in the way? I have only ever seen this happen on my development machine and never gotten a report from a customer. As with the first scenario this only ever happens once per application session (if at all). Subsequent attempts are always successful.

Any suggestions for a different, potentially more fail-safe approach to creating empty files or emptying existing ones?

+2  A: 

Besides anti-virus it can also be indexing software or file management software, like Google Desktop. However, the real problem here is, that the error message doesn't help you solve the problem. I suggest that you rewrite the code to use TFileStream, instead, just in order to improve your error messages.

Lars D
+1 I strongly support the use of TFileStream. Once you make the switch, you won't want to go back.
skamradt
A: 

Your example code should work in general, those errors seem to be access errors. To detect the case of this, you could try to disable TrendMicro and look if the problem persists.

schnaader
+1  A: 

Is your app multi-threaded? I once had the same problem as this when the logging code was called simultaneously from both threads. If so use a TCriticalSection to control access.

Mike Sutton
The apps in question are multi-threaded but this code is always run from the main thread. A good hint nevertheless.
Oliver Giesen
+2  A: 

You should generally put opening the file before the try of a try..finally:

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

and

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

(try finally does not make any sense in the last case)

Otherwise closing the file might fail because it could not be opened and this would mask the real error.

But I don't think that is the issue here.

dummzeuch
You're right. AssignFile doesn't actually acquire any resources, so it' too early to enter a try-finally block right after that call. Only enter try-finally after Append, Reset, or Rewrite -- those are the functions that open a file.
Rob Kennedy
Yeah, sounds reasonable. I hadn't seen it that way so far...
Oliver Giesen
Can't append and rewrite fail then? I think it can.
Marco van de Voort
@Marco: Yes, it can fail, but if it fails, closing the file is not required because it has never been opened.
dummzeuch
+2  A: 

I don't see what is wrong with automatic retry. I don't see that you can do anything else. If some other process is reading the file, then your Append/Rewrite will fail. And since the file is a log, there is a good chance that something, such as a log viewer or a text editor, will be reading it at the instant you try to open it.

Try opening the file a few times with a delay in-between attempts before failing definitively. You could use an exponential backoff if you wanted to be fancy.

dangph
+1  A: 

Could you be looking at a stray error from something else compiled in a $I- state?

Loren Pechtel
A: 

If I understand this correctliy, your file assignment fails. Are you sure that FileChecks are on the time you call AssignFile? This is quite unusual, but you could verify this using:

{$IFOPT I-}

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

I agree on using TFileStream (or anything but the low-level file access function) instead. This is performance-critical and can be a huge issue when moving to Unicode.

kaeff