File.Exists returns true after File.Delete - c#

I have the following method to delete a file with a provided path
private void DestroyFile(string path)
{
try
{
if (File.Exists(path))
{
File.Delete(path);
}
if (File.Exists(path))
{
throw new IOException(string.Format("Failed to delete file: '{0}'.", path));
}
}
catch (Exception ex)
{
throw ex;
}
}
I am getting the IOException that is thrown if the file exists after the File.Delete method. Specifically
System.IO.IOException): Failed to delete file: 'C:\Windows\TEMP\[FILE NAME]'.
I have also confirmed that the file does not exist at the location in the path variable after the execution is complete. I am wondering if I am running up against a race condition between the file system updating after File.Delete and checking against it again with File.Exists. Is there a better way to smoothly delete? I know that File.Delete won't return an error if the file doesn't exist so maybe these checks are a bit redundant. Should I check if the file is in use rather than if it exists at all?
Some important additional information:
The program can and does run successfully often but this particular error has been frequently seen recently.

File.Delete will mark file for deletion. File really will be deleted only when all handles to it are closed (if there are no such handles - it will always be deleted after File.Delete returns). As documented for DeleteFile winapi function (which is used by C# File.Delete):
The DeleteFile function marks a file for deletion on close. Therefore,
the file deletion does not occur until the last handle to the file is
closed
Usually there are no open handles to files you delete. Or, if there are open handles - they usually don't have "delete" share (this share allows another process to mark file for deletion), so when you try to delete such file - it either gets deleted (no open handles) or access denied or similar exception is thrown (some handles, but without delete share).
However, sometimes some software, such as antivirus or search indexer, might open arbitrary files with "delete" share and hold them for some time. If you try to delete such file - it will go without errors and file really will be deleted when that software closes its handle. However, File.Exists will return true for such "pending delete" file.
You can reproduce this issue with this simple program:
public class Program {
public static void Main() {
string path = #"G:\tmp\so\tmp.file";
// create file with delete share and don't close handle
var file = new FileStream(path, FileMode.Create, FileAccess.ReadWrite, FileShare.Delete);
DestroyFile(path);
GC.KeepAlive(file);
}
private static void DestroyFile(string path) {
try {
if (File.Exists(path)) {
// no error
File.Delete(path);
}
// but still exists
if (File.Exists(path)) {
throw new IOException(string.Format("Failed to delete file: '{0}'.", path));
}
}
catch (Exception ex) {
throw ex;
}
}
}
You can retry File.Exists check forever in the program above - file will exist until you close the handle.
So that's what happens in your case - some program has open handle to this file with FileShare.Delete.
You should expect such situation. For example - just remove that File.Exists check, since you marked file for deletion and it will be deleted anyway.

while its not documented in the API, File.Delete WILL return before the file is completely deleted.
This is why you are running into the case you are having. Delete call will check for all the things that would make the delete fail (existing handle, lock, permission ect) and it will return after the initiation of Delete request
So its relatively safe to just put a while loop right after to wait until the file is gone or use a FileSystemWatcher to watch for Deleted event

File.Delete and generally most methods from System.IO are dependent on filesystem/streams/etc, whom a bit live their own lives, and are not managed resources, hence File.Delete can return before file is physically deleted, but after it's marked for deletion.
After File.Delete returns, you can be sure file will be deleted, if not this method will throw exception by itself, so second check with File.Exists and throwing IOException is unnecessary.
If you want custom exception, catch exceptions from File.Delete.
And in code attached, remember that throw ex; is diffrenent from throw; and changes stack trace to current line.

Related

Replacing PDF fails. Even with File.Delete() success when opened in Edge-Browser

The goal is to replace a PDF-File which is currently saved on disk.
I am deleting the current PDF file from disk, then recreating a new one. This works fine unless the PDF is currently opened in the Microsoft Edge Browser.
// Try delete PDF-File (which is opened in Edge Browser)
var info = new FileInfo(pathToPdf);
if (info.Exists)
{
try
{
info.Delete();
// Same thing with the File.Delete call
//File.Delete(path);
Console.WriteLine("Success.");
}
catch (Exception)
{
Console.WriteLine("Failed.");
return;
}
}
We get a "Success" print out even though the file is opened in Edge. If it were opened in Adobe Reader it would throw an exception (File in use).
Let's create a new file. (For demonstration purposes a text file with a .pdf ending)
try
{
using (var writer = File.CreateText(pathToPdf))
{
writer.Write("Foo");
writer.Flush();
Console.WriteLine("Success.");
}
}
catch (Exception e)
{
Console.WriteLine("Failed.");
return;
}
I expected to be able to create a new file, since the Delete() didn't fail. Yet I get an UnauthorizedAccessException: "Access to the path 'XYZ' is denied."
As a workaround I can recheck if the file exists after deleting it.
var newInfo = new FileInfo(pathToPdf);
if (newInfo.Exists)
// Delete failed
But why would I need to do this? Shouldn't FileInfo.Delete() or File.Delete(path) fail in the first place?
Notes:
Tested on Windows 10 Pro with .Net Framework 4.5.1
The file is still visible in the File-Explorer with its original filesize after it was deleted by code (while opened in Edge).
When closing the Edge Browser after deleting the file by code, the file vanishes from the File-Explorer and I can create a new file programatically.
This problem occurs only with PDFs being opened in Edge. When using a Text-File instead the Text-File gets deleted properly.
Any clarification and help is appreciated.
Best Chris
If the file does not exist, FileInfo.Delete() does nothing.
From msdn
WinNt4Family
Delete does not delete a file that is open for normal I/O or a file that is memory-mapped.
You get an UnauthorizedAccessException when the path is a directory.
If FILE_SHARE_DELETE is set on the handle by Edge, then File.delete() can be called with success by another process even when the handle exists. The file is then marked for deletion and deleted after the handle is closed. Until then, it is still visible in the Explorer, but not accessible anymore.
For a more detailed explanation, see this SO post:
Odd behaviour when deleting Files with Files.delete()

File is being used by another process in c#

I am trying to delete a file in C#, however I am receiving a message that the file is used from another process. What I want to do is to check if the files exists and close it. I am using the following function in order to check if the file is open:
public static bool IsFileInUse(string path)
{
if (string.IsNullOrEmpty(path))
throw new ArgumentException("'path' cannot be null or empty.", "path");
try
{
using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read)) { }
}
catch (IOException)
{
return true;
}
return false;
}
and I am trying when the file is in use to close it:
bool checking = IsFileInUse(file );
File.Create(file ).Close();
if (File.Exists(file))
{
File.Delete(file );
}
I got issues in File.Create line, I am receiving the message:
File is being used by another process.
EDIT: I am trying to use lock approach in order to delete the file. Am I suppose to delete the file inside a lock statement? How Can I use properly the lock statement?
Why do you suppose that a reading operation will fail if file is in use while a writing operation will not? File.Create() will fail exactly as new FileStream() failed before...
See also IOException: The process cannot access the file 'file path' because it is being used by another process.
Note that your check will fail if the other process didn't open that file exclusively (check FileShare enumeration): file may be open for shared reading, writing and sometimes even for deleting (for example you may be able to read concurrently but not writing however the other process may let you delete that file...).
To close an open file can be really disruptive for the other process, it may crash, nicely handle the problem or...anything else (silently ignore that error and produce random output, open file again and so on...) Is it possible to do it in C#? Yes with some P/Invoke...
1) Let's find the handle for the file you want to unlock. Use NtQuerySystemInformation() and enumerate all handles until you find the one that refers to that file.
2) Duplicate that handle to be valid in your own process using DuplicateHandle().
3) Close just create handle specifying DUPLICATE_CLOSE_SOURCE, it will close both your handle and the original one (of course if your process has enough permissions).
4) Check if file is really closed calling NtQuerySystemInformation() again, if not then you may need to directly close its parent process.
In your code, you don't do anything with the IsFileInUse result.
This File.Create(file ).Close(); will also not close a file that is opened by another process. You need to close the process that has the file open, and if it is your own app, close the file handle before trying to delete the file.
bool checking = IsFileInUse(file );
File.Create(file ).Close();
if (!checking)
{
if (File.Exists(file))
{
File.Delete(file );
}
}
You have no need to check if the file exists, just try do delete it:
https://msdn.microsoft.com/en-us/library/system.io.file.delete(v=vs.110).aspx
If the file to be deleted does not exist, no exception is thrown.
Try and check the exception
try {
File.Delete(file);
}
catch (IOException) {
// File in use and can't be deleted; no permission etc.
}

File.Replace - File is being used by another process error

Question background:
I am attempting to overwrite the contents of one specified file with the contents of another specified file within a folder on my C drive using the following 'File.Replace' method:
//'null' has been set to the 'backup file' parameter as I do not need this.
File.Replace(fileOnesLocation, filesTwosLocation, null);
The error:
I have the above method wrapped in a try catch and am currently receiving the following error:
System.IO.IOException: The process cannot access the file
because it is being used by another process.
Can anyone point me in the right direction of whats going wrong here?
If you want to avoid this errors, you could try doing something like this answer, create a method to check whether your file is open or not.
protected virtual bool IsFileLocked(FileInfo file)
{
FileStream stream = null;
try
{
stream = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None);
}
catch (IOException)
{
//the file is unavailable because it is:
//still being written to
//or being processed by another thread
//or does not exist (has already been processed)
return true;
}
finally
{
if (stream != null)
stream.Close();
}
//file is not locked
return false;
}
If the file is open either by you or another logged in user then you may not be able to open it.
check in task manager for processes by users and close the file.
This error is often caused when the file being replaced or written to is open by you or someone/thing while the code is running.

UnauthorizedAccessException StreamWriter

I have the following code:
public WriteToFile(string path, List<string> text)
{
File.Delete(path);
using (TextWriter writer = new StreamWriter(path, true))
{
foreach(string t in text)
{
writer.WriteLine(text);
}
}
}
Most of the time it works fine, the file is deleted and then created again with the text inside. However every so often the using statement throws an UnauthorizedAccessException. Any idea why? I have admin rights and the program is run as admin.
This is normal, it became undiagnosable because you used File.Delete(). Which is unnecessary, just use the StreamWriter(string) constructor.
This goes wrong because deleting a file doesn't provide a guarantee that the file will actually be deleted. It may be opened by another process. Which has opened the file with delete sharing, programs like virus scanners and file indexers commonly do this. Which makes the Delete() call succeed but the file doesn't disappear until all handles on the file are closed. You got the UnauthorizedAccessException exception because the file didn't get deleted yet.
Get ahead by removing the File.Delete() call. You still need to assume that the StreamReader() constructor can fail. Less often, it is bound to happen sooner or later. You'll get a better exception message. Such are the vagaries of a multi-tasking operating system.

File.Delete fails on attempting to delete an opened file but deletes on process exit?

While calling File.Delete(file_path) for a file which is opened in another process, the attempt fails with cannot access error, but on exiting the app (from where the attempt was made) the file gets deleted automatically. After File.Delete attempt, the path gets inaccessible for any other operation.
I need to either fail on delete and let the path be accessible through the app or delete the file completely, not on app exit.
Well my code is this :
private bool DeleteFilesAsync(string FileToDelete)
{
try
{
//Set file's attribute to normal if it is ReadOnly file
File.SetAttributes(FileToDelete, FileAttributes.Normal);
File.Delete(FileToDelete);
//Some bussiness logic to update file's status in database
return true;
}
catch (Exception ex)
{
//log the error
return false;
}
}
Something wrong here?
Windows does have a delete-on-last-close feature. All deletes are handled that way. The only reason that this almost never occurs in practice is because most apps do not open the file with FILE_SHARE_DELETE so it is not possible to delete a used file. But at the Kernel level all deletes are delete-on-close.
There might be a way to clear the delete flag by calling NtSetInformationFile to set the FileDispositionInfo class. This assumes that it is possible to clear the delete flag. It might well be.
Anyway, a more sane approach would be to open the file before deleting it to ensure that exclusive access is available:
using(new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) { }
After this line passes without exception, we know that the file was unused at the point of opening it. Of course, it might be opened by someone else immediately after this line, but maybe this solution is enough for you.

Categories