Reputation: 9469
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?
Upvotes: 12
Views: 49191
Reputation: 540
For me it was caused by triggering the function working with a file multiple times by repeated timer events. So the second timer event was called prior to the previous timer call was completed. This caused multiple openings of the same file and resulting in 103 Error code.
Solution was to temporary disable timer inside of timer call processing until the processing completes.
Upvotes: 0
Reputation: 311
I am answering to the original question about the 103 IOResult error that sometimes occurs when runnning a code that seems to be ok. I summarize the solution which I found here in this archive: https://de.comp.lang.delphi.misc.narkive.com/o1LNZylQ/assignfile-reset-und-ioresult-103
Basically, Delphi still contains some global flags inherited since more than 20 years or so. One is filemode, and the other is IOResult. IOresult gets reset to 0 if the file operation was ok regarding an open file. As a kind of weird interpretation of this rule, closefile() outs IOResult to 103, "since" the file is obviously closed. Then trying to open the same OR ANOTHER file can fail. The cure is simple: put this line after closefile():
r := IOResult;
It resets IOResult, and the code works as expected. Note, that multithreading would need additional precautions.
Upvotes: 0
Reputation: 121
Okay, it's over a year late, but I'm going to add my comment to this, as it explains why this is happening.
I had the exact same problem in a multi-threaded application with code almost identical to the snippet above and I had critical sections protecting the code.
The problem occurred most readily when one logging operation swiftly followed another. The second operation would fail for the above reason.
I thought it was anti-virus software too, but the error happened on one machine and not the other, where both had Norton 360 installed. The machine with the problem was brand new Windows 7 and the one without was Windows XP. A colleague also had the problem running the system under a virtualised Windows Vista machine with no virus checker installed.
So my question was, "why was this XP machine so different?".
For one, it wasn't virgin, and that is the answer it seems:
Opportunistic locking and NT caching were turned off. Most (mature) Delphi developers will know that when using BDE, these are turned off in order to maintain DBF and DB file integrity in multi-user situations. These settings were not disabled on the newer machines because we no longer develop for Paradox data files!
Delayed write caching seems to leave a read/write lock on the file until the OS has done its business, which could be several milliseconds later.
So, in my case, the second log event was being blocked by the first.
Okay, I'm not suggesting that you turn off opportunistic locking + NT Caching. One of the major reasons we moved away from BDE was to avoid persuading customers to tinker with such settings. So, there are four practical solutions:
1) To retry for an acceptable period of time, as mentioned by dangph.
2) to open the file when the application loads and hold it open for the full duration of the application. Not so useful if you are running multiple instances of the application.
3) Lazily put a sleep(1) before the logging code and hope that is long enough for the lock to be released. But that risks slowing your system down if you are doing lots of logging.
or 4) Put a try...except..end around your code. But then you are probably guaranteed to miss 100% of the second messages (referring to my case).
Upvotes: 12
Reputation: 1
Way hello. It's probably too late for answering to the original question, but I want to add my piece, and despite it isn't an direct answer to the original question, I want to contribute, as I think it may help some others looking for this kind of answer.
I got this same error answer, but in an fully diffrent software, particaularly EIS analyser, more times I was trying to open a file, but some time it could have had been opened. This was curious to me, so I grabbed for internet. And really, as improbable, it seemed (because the meantioned programm is quite small), I found such a error call. I was skinning the above answers and the one which has two checks with the green check tic, I right followed, just intuitively, and it is so that: When I have opened the file in another programm, it won't let me go to open it as a file in EIS analyser.
So the general conclusion: when you have opened a file in one programm, it won't let you open it anywhere else and you get an error I/O Error 103.
PS: A bit daring and lax and easy conclusion (without reading properly(but haven't have time, sorry) the above answeres), but I hope you have the point. :) Cheers.
Upvotes: 0
Reputation: 1
I hate Windows... see why:
This is the code that solves the problem of ReWrite (without messages):
AssignFile(MyFileHandler,RouteToWritableExistantFile);
try
ReWrite(MyFileHandler); // This sometimes fails
except
ReWrite(MyFileHandler); // When prior fails, this runs OK
end;
Yes, it is absurd... if ReWrite fails, the do a Rewrite ... but it works for me at 100% of times.
I DEBUG that problem putting a lot of ShowMessage... had a little imagination after trying the non absurd things (file was previously oppened, etc)... what would happen if i try the ReWrite twice? Surprise... if first fails, the second try works!
I know it is absurd, but it works!
I knew that because i was putting a lot of messages, just like this:
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;
And i got theese two secuences of messages:
Hope this helps other not getting so mad!!!
P.D.: Same problem happens to Reset... now i allways encapsulate them into such try...except block and get rid of the problem!
Upvotes: 0
Reputation: 104
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.
Upvotes: 0
Reputation: 9093
Could you be looking at a stray error from something else compiled in a $I- state?
Upvotes: 1
Reputation: 16939
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.
Upvotes: 2
Reputation: 11252
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.
Upvotes: 4
Reputation: 4211
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.
Upvotes: 1
Reputation: 49731
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.
Upvotes: 0
Reputation: 8573
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.
Upvotes: 3