Jump to content
Patrick Hughes

Using TFileStream to check if file is in use

Recommended Posts

I've tried a number of "FileInUse" routines and for one reason or another they fail to capture the status of a file that is in use within its native application. So I came up with my own which appears to function properly. But I'm wondering how safe it may be so that it doesn't damage the original file. here's my code:

 

function FileReallyIsInUse(fName: string): boolean;
var
  Stream: TFileStream;
begin
  result := false;
  try
    try     
      Stream := TFileStream.Create(fName, fmCreate or fmShareDenyNone);  //fmShareExclusive);
      //Stream.Seek(0, soFromBeginning);  //  (resulting file was 0 bytes)
    except on E:Exception do
      result := true;
    end;
  finally
    Stream.Free;
  end;

end;

 

What could possibly go wrong?

 

Thanks for your thoughts and/or warnings.

 

Patrick Hughes

Share this post


Link to post

fmCreate, according to Delphi help = Create a file with the given name. If a file with the given name exists, override the existing file and open it in write mode.

I don't use FileStreams that often, but won't you effectively null the contents...?

 

I would go the old-school way; however I have no idea if it would work:

Var
 f: File;
Begin
 If Not FileExists('myfile') Then Exit(False);
 {$I-}
 AssignFile(f, 'myfile');
 Append(f);
 Result := IOResult <> 0;
 If Not Result Then CloseFile(f);
End;

 

Edited by aehimself

Share this post


Link to post

When I included the Stream.Seek(0, soFromBeginning); line it did. When I commented it out the file remained intact. I'll give yours a tryr

Share this post


Link to post

Good to know; thanks for sharing, I'll try to keep this in mind if I'll have to use FileStream-s one day!

Share this post


Link to post

To use a TFileStream just to check if the file is in use, you need to use fmOpenRead instead of fmCreate, and you definitely need to use fmShareExclusive instead of fmShareDenyNone, and note that the file could fail to open for any number of reasons, not all of which are related to the file being in use, so you have to check for that, eg:

function FileReallyIsInUse(fName: string): boolean;
begin
  Result := False;
  try
    TFileStream.Create(fName, fmOpenRead or fmShareExclusive).Free;
  except
    on E: EFOpenError do
      Result := GetLastError() in [ERROR_SHARING_VIOLATION, ERROR_LOCK_VIOLATION];
  end;
end; 

However, the exception doesn't carry the error code, and using GetLastError() after the exception is raised does not guarantee the original error code is preserved.  You are better off simply calling the Win32 CreateFile() function directly instead (which avoids the need to allocate an object in memory, or invoke the overhead of exception handling):

function FileReallyIsInUse(fName: string): boolean;
var
  hFile: THandle;
begin
  hFile := CreateFile(PChar(fName), GENERIC_READ, 0, nil, OPEN_EXISTING, 0, 0);
  if hFile <> INVALID_HANDLE_VALUE then
  begin
    CloseHandle(hFile);
    Result := False;
  end else
    Result := GetLastError() in [ERROR_SHARING_VIOLATION, ERROR_LOCK_VIOLATION];
end; 

Or, the RTL's FileOpen() function:

function FileReallyIsInUse(fName: string): boolean;
var
  hFile: THandle;
begin
  hFile := FileOpen(fName, fmOpenRead or fmShareExclusive);
  if hFile <> INVALID_HANDLE_VALUE then
  begin
    FileClose(hFile);
    Result := False;
  end else
    Result := GetLastError() in [ERROR_SHARING_VIOLATION, ERROR_LOCK_VIOLATION];
end; 

 

Edited by Remy Lebeau
  • Like 2

Share this post


Link to post
1 hour ago, Patrick Hughes said:

function FileReallyIsInUse(fName: string): boolean;

Whatever function you come up with it will be susceptible to race conditions; After you have determined that the file isn't in use, but before you can open and lock it, another process can come in and open it - or vice versa.

 

Working from Remy's example you need to keep the file open after FileCreate. As soon as the file is closed the test result is stale.

  • Like 1

Share this post


Link to post

As far as a race condition, I'm not too concerned since I'm polling every second and I'm not doing anything to the file, not even reading its contents.

 

As for my original function it seemed to work several time but failed on subsequent attempts.

 

Regarding the other function options presented none provide accurate results. Seriously, try them on a known file open in another application, such as an .xlsx in Excel or .docx in Word. See what result you get while the file is open (should return true) then the result after it's closed (should return false).

 

I have working code that allows me to make a true determination but it involves retrieving all desktop handles and consumes massive page faults that I'm trying to eliminate.

 

In any event that you all for your input.

Share this post


Link to post
31 minutes ago, Patrick Hughes said:

As far as a race condition, I'm not too concerned since I'm polling every second and I'm not doing anything to the file, not even reading its contents.

Rather than polling, you might consider letting the OS notify you when the file is being used.

31 minutes ago, Patrick Hughes said:

Regarding the other function options presented none provide accurate results. Seriously, try them on a known file open in another application, such as an .xlsx in Excel or .docx in Word. See what result you get while the file is open (should return true) then the result after it's closed (should return false).

There is no guarantee that other apps will actually keep the file open while they are working on the data.  In fact, it is not unusual for an app to open a file, load the data, and then close the file, and then reopen the file later only if new data is being saved.

 

Share this post


Link to post

What I can't understand is why you would ever need to call such a function. Broadly, you never ask if a file is in use an any one moment. You try to do something with a file, and then handle the scenario that it fails due to being open. 

  • Like 2

Share this post


Link to post

Maybe some kind of file monitoring ....

But also then I would use notifications instead of polling.

Share this post


Link to post
8 hours ago, David Heffernan said:

What I can't understand is why you would ever need to call such a function. Broadly, you never ask if a file is in use an any one moment. You try to do something with a file, and then handle the scenario that it fails due to being open. 

I've already populated my data structure with file information that I'm monitoring including the filename, application that has opened it, the window handles (parent and child0 that contains the document, etc. I'm just trying to finalize my logging once the file is closed.

Edited by Patrick Hughes

Share this post


Link to post

Thanks for the heads up Tim, yes network files are included and can be a big part of my need. I'll check out the link.

 

Rollo62, it is indeed a monitoring application, a time logger.

Share this post


Link to post

Note, before arriving at the point in my program where this function is used I've already gone through several iterations of FindWindow, FindWindowEx, etc. The trouble arises in some particular applications that do not use an MDIClient window arrangement or other window variations.

Share this post


Link to post

Basically you will need to set the cache values to zero, otherwise the cache will show you the state of the file as it was several seconds ago.  Windows file handling has long been optimised for long-lifetime database files, not for files that come and go quickly.  If this is a new application you would be better off with one of the alternatives already suggested.

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×