Patrick Hughes 0 Posted April 15, 2020 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
aehimself 396 Posted April 15, 2020 (edited) 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 April 15, 2020 by aehimself Share this post Link to post
Patrick Hughes 0 Posted April 15, 2020 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
aehimself 396 Posted April 15, 2020 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
Remy Lebeau 1396 Posted April 15, 2020 (edited) 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 April 15, 2020 by Remy Lebeau 2 Share this post Link to post
Anders Melander 1783 Posted April 15, 2020 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. 1 Share this post Link to post
Patrick Hughes 0 Posted April 15, 2020 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
Remy Lebeau 1396 Posted April 15, 2020 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
David Heffernan 2345 Posted April 16, 2020 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. 2 Share this post Link to post
Rollo62 536 Posted April 16, 2020 Maybe some kind of file monitoring .... But also then I would use notifications instead of polling. Share this post Link to post
timfrost 78 Posted April 16, 2020 Are you attempting to monitor file existence on a network path? If so, Windows defaults to misleading you about this. Look at the description of the 'cache lifetime' registry keys at https://docs.microsoft.com/en-us/windows-server/administration/performance-tuning/role/file-server/. Share this post Link to post
Patrick Hughes 0 Posted April 16, 2020 (edited) 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 April 16, 2020 by Patrick Hughes Share this post Link to post
Patrick Hughes 0 Posted April 16, 2020 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
Patrick Hughes 0 Posted April 16, 2020 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
timfrost 78 Posted April 16, 2020 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