Mark Williams 14 Posted May 19, 2019 I have used the following function for checking if a file is in use for many years (it is recommended on numerous sites): function IsFileInUse(FileName: String): Boolean; var HFileRes: HFILE; hdl:THandle; begin Result := False; if not FileExists(FileName) then Exit; HFileRes := CreateFile(PChar(FileName), GENERIC_READ or GENERIC_WRITE, 0, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); Result := (HFileRes = INVALID_HANDLE_VALUE); if not Result then CloseHandle(HFileRes); end; It still works fine for 32Bit, but not 64 bit. On 64 bit It always returns a valid handle even when the file is in use and thus reports that the file is not in use even when it is. Also, when run from the IDE in 64 bit it crashes with an EExternalException: "External exception C0000008" when trying to close the handle. I have found some posts on this via Google and it seems to be a known issue, the answer being to switch off some debug options. But I am not particularly troubled by the IDE bug. I am majorly concerned by the failure to detect that a file is in use! I've tried with CreateFileA and CreateFileW, but with the exactly the same result. Share this post Link to post
MarkShark 27 Posted May 19, 2019 (edited) HFileRes should be a THandle instead of HFILE (which is a LongWord). In 64bit CreateFile returns a 64bit value and you are assigning it to a 32bit one. On a side note I just checked Delphi RIO and still no compiler hint for this type of thing... Seems like that would be useful. -Mark Edited May 19, 2019 by MarkShark Share this post Link to post
Mark Williams 14 Posted May 19, 2019 5 minutes ago, MarkShark said: HFileRes should be a THandle instead of HFILE (which is a LongWord). In 64bit CreateFile returns a 64bit value and you are assigning it to a 32bit one. Noted and thanks, but still returns false when the file is actually in use in 64 bit. Any idea why? Share this post Link to post
David Heffernan 2345 Posted May 19, 2019 (edited) Why do you call FileExists? A single call to CreateFile can tell you what you need. The code as it stands won't tell you whether or not the file is in use. What is hdl? Use THandle as stated above. As for the problem, debug it! Use GetLastError if CreateFile fails. If CreateFile succeeds then there's presumably something wrong with your logic. Edited May 19, 2019 by David Heffernan Share this post Link to post
Mark Williams 14 Posted May 19, 2019 (edited) 1 hour ago, David Heffernan said: Why do you call FileExists? A single call to CreateFile can tell you what you need. The code as it stands won't tell you whether or not the file is in use. What is hdl? Use THandle as stated above. I copied the code from I believe Mike Lischke's site and found it worked exactly as I wanted in 32 bit. Never thought about the reason for the fileExists, but I suppose if the file doesn't exist then the answer to isFileInUse is "No". If it doesn't exist why bother creating it? So to my mind, it makes sense. However, it certainly isn't the cause of the problem. As for using THandle, please read my post to which you have responded where I say "Noted and thanks, but still returns false...". I was unaware of the problem with FileHdl in64 bit, but I have now changed it and it makes no difference whatsoever. In 64 bit when I run the function, it creates the file even though it is in use in another process and returns no error at least not on the CreateFile process. The only error it returns is on the call to CloseHandle, but that only happens in the IDE so, as I stated in my original post, I don't particularly care. Anyway, It's not hard to imagine that the reason CloseHandle bugs out is because the file is open in another process already and the handle should never have been created. But ignoring the CloseHandle problem, which would not exist if the function worked as expected (and as it does work in 32 bit), why doesn't it work in 64 bit? It would be helpful if someone else tried it in a 64 bit app. All you need is a button and an openDialog, the above function (obviously with HFileRes, changed to THandle) and the following code in your button's onClick: if OpenDialog1.Execute then if isFileInUse(openDialog1.FileName) then Showmessage('in use') else Showmessage('not in use'); Edited May 19, 2019 by Mark Williams Share this post Link to post
David Heffernan 2345 Posted May 19, 2019 The reason you don't call FileExists is that CreateFile will fail and GetLastError will tell you that the file does not exist. You have tell us why you believe that CreateFile should fail in the scenario that concerns you. Another program already opened the file? With what flags? Rather than have us spend time trying to guess how to recreate the problem, can you provide a complete console app that reproduces the issue. Share this post Link to post
Mark Williams 14 Posted May 19, 2019 6 minutes ago, David Heffernan said: The reason you don't call FileExists is that CreateFile will fail and GetLastError will tell you that the file does not exist. Ok. Noted, but not really my issue. However, for some reason the function is now working as expected in 64 bit and I have no idea why. Thanks anyway. Share this post Link to post
Remy Lebeau 1398 Posted May 20, 2019 (edited) I would suggest rewriting the function to something more like this: function IsFileInUse(const FileName: String): Boolean; var hf: THandle; begin hf := CreateFile(PChar(FileName), GENERIC_READ or GENERIC_WRITE, 0, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); if hf <> INVALID_HANDLE_VALUE then begin CloseHandle(hf); Result := False; end else begin Result := (GetLastError() = ERROR_SHARING_VIOLATION); end; end; Edited May 20, 2019 by Remy Lebeau 2 Share this post Link to post
Mark Williams 14 Posted May 20, 2019 3 hours ago, Remy Lebeau said: I would suggest rewriting the function to something more like this: Thanks. Will do. Share this post Link to post
Steve Maughan 26 Posted May 20, 2019 I had the same problem. Here's the fix: https://stackoverflow.com/questions/28363917/delphi-2010-is-file-in-use/54138875?noredirect=1#comment99036951_54138875 Share this post Link to post
KodeZwerg 54 Posted February 22, 2021 Sorry to bring up this old topic but I do have a problem. On normal folders all works sweet. On Windows protected folders it fail. (Always return false.) Is there a way to make it compatible to work for protected folder content? (C:\Program Files\ is a protected folder for example.) My addition to above code was to add a SetFileAttributes call to try if FILE_ATTRIBUTE_NORMAL can be set. If it cannot, folder is protected. Now I run out of Ideas. Share this post Link to post
FredS 138 Posted February 22, 2021 1 hour ago, KodeZwerg said: Now I run out of Ideas. For protected folders you are fighting the builtin security system.. it's like a builtin Firewall, you only know your call didn't work and most likely froze you never get an explanation why.. Share this post Link to post
KodeZwerg 54 Posted February 22, 2021 (edited) I did thought such, thanks. If someone find it usefull, here is my modificated variant. function IsFileInUse(const FileName: string): Boolean; // found on Delphi-Praxis forum // code provided by Remy Lebeau // slightly modified by KodeZwerg // (now you will get "True" for files that need administrative rights or on write protected media) var hFile: THandle; Attr: DWORD; begin Result := False; SetLastError(ERROR_SUCCESS); Attr := GetFileAttributes(PChar(FileName)); if (Attr <> $FFFFFFFF) and (Attr and FILE_ATTRIBUTE_DIRECTORY <> 0) then Exit(False); // only continue if we can normalize attributes if SetFileAttributes(PChar(FileName), FILE_ATTRIBUTE_NORMAL) then begin hFile := CreateFile(PChar(FileName), GENERIC_READ or GENERIC_WRITE, 0, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); if (hfile <> INVALID_HANDLE_VALUE) then CloseHandle(hFile) else Result := (GetLastError() = ERROR_SHARING_VIOLATION); // restore original attributes SetFileAttributes(PChar(FileName), Attr); end else Result := True; end; Edited February 22, 2021 by KodeZwerg Share this post Link to post
FredS 138 Posted February 22, 2021 11 hours ago, KodeZwerg said: I did thought such Remembered I added this for a self updater. But I can't say its fully tested yet the remark sounds correct: class function TDirectoryHelper.HaveControlledFolderAccess(const Path: string): Boolean; {$REGION 'History'} // 04-Jan-2019 - 'Controlled Folder Access' won't return an error nor an Invalid Handle // those checks are here in case the path is bad or doesn't exist. // The only indication is that the file is not created {$ENDREGION} var LFile: string; h: THandle; begin LFile := TPath.Combine(Path, TGuid.NewGuid.ToString + PERIOD + TGuid.NewGuid.ToString); h := CreateFile(LFile.ToPchar, GENERIC_READ or GENERIC_WRITE, 0, nil, CREATE_NEW, FILE_ATTRIBUTE_HIDDEN or FILE_FLAG_DELETE_ON_CLOSE, 0); if (h = INVALID_HANDLE_VALUE) then Exit(False); Result := FileExists(LFile, False); CloseHandle(h); end; Share this post Link to post