6

I'm stuck with a weird problem (which is probably my lack of knowledge), I present the offending code:

try
{
    f.Delete();
    fTemp.MoveTo(f.FullName);
    Console.WriteLine("INFO: Old file deleted new file moved in > {0}", f.FullName);
}
catch (IOException ex)
{
    Console.WriteLine("ERROR: Output file has IO exception > {0}", f.FullName);
    Environment.ExitCode = 1;
}

f and fTemp are FileInfo objects. So if I run this with code where f is a video file playing in a mediaplayer it throws the exception. That works fine and as expected. Now when I close the mediaplayer it deletes the file!? Even though my application is long closed. Even when I close Visual Studio it still deletes the file, when I close the mediaplayer. As if some callback is being setup somewhere to make sure the file gets deleted at some point. This offcourse in unwanted behaviour. But I can't figure out what exactly goes wrong...

Result for now :

if (!IsFileLocked(f))
{
    try
    {
        f.Delete();
        fTemp.MoveTo(f.FullName);
        Console.WriteLine("INFO: Old file deleted new file moved in > {0}", f.FullName);
    }
    catch (IOException ex)
    {
        Console.WriteLine("ERROR: Output file has IO exception > {0}", f.FullName);
        Environment.ExitCode = 1;

    }

    catch (UnauthorizedAccessException ex)
    {
        Environment.ExitCode = 2;
        Console.WriteLine("ERROR: Output file is locked > {0}", f.FullName);

    }
}
else
{
    Environment.ExitCode = 3;
    Console.WriteLine("ERROR: Couldn't delete file was locked");

}

I know I still can do better between Delete and MoveTo, but I'll take my changes for now, shotgun coding.....

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
JHN
  • 1,018
  • 9
  • 12
  • 5
    The media player most likely opened the file with FILE_SHARE_DELETE, which means "let people delete the file while I'm using it." The file is marked for deletion and the deletion completes when the media player closes the file. – Raymond Chen Aug 01 '12 at 14:55
  • 2
    What is the message given with your IOException ? – Sam Axe Aug 01 '12 at 14:57
  • But then I would expect that the exception wouldn't be raised. Plus I opened it with Windows Media Player, which in my book is infamously locking files... or is that not the same as the flag you mean. – JHN Aug 01 '12 at 14:59
  • Again, what is the actual message of your IOException? All you've written to the console is that you received an IOException, but there's potentially-useful information in the exception's Message property. Also, is the IOException being raised by f.Delete() or fTemp.MoveTo(...)? Both lines could raise an IOException, and you're assuming it's the first. – Chris Aug 01 '12 at 15:11
  • I didn't pursue the Exception (for now), because the behaviour I need is to know if a file can be deleted or not. So I took the isFileLocked method below and it works for my needs. Whenever I need to dive further into this I will do a better debug. – JHN Aug 01 '12 at 15:39

3 Answers3

3

You are getting the IOException because the file cannot immediately be deleted or written to. However, when you call Delete(), it seems the file is getting called for deletion.

Though the media player stops the file from being deleted while it is open, the file is still marked for deletion when it closes, regardless of whether your program is running. So when the media player closes the file is deleted.

You can check if the file is in use with the following code, taken from here. Make the Delete and Copy conditional on it not being locked, and you should be good.

try
{
    if(!IsFileLocked(f))
    {
        f.Delete();
        fTemp.MoveTo(f.FullName);
        Console.WriteLine("INFO: Old file deleted new file moved in > {0}", f.FullName);
    }
}

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;
}
Community
  • 1
  • 1
Rob Volgman
  • 2,104
  • 3
  • 18
  • 28
  • Can I not "clear the flag" or whatever machanisme was in place. Or better yet, is there a check that can be done if the file can be deleted in the first place, this behaviour is unpredictable for me now. – JHN Aug 01 '12 at 15:04
  • I see, it took me too long to write my answer ... :) – Hinek Aug 01 '12 at 15:11
  • If the file is being marked for deletion but is still there (for now), it's most likely your call to fTemp.MoveTo(f.FullName) that's raising the exception. If you want a workaround, you could delete the file, then check whether it still exists - if it does, you know the file's only been *marked* for deletion rather than actually deleted. – Chris Aug 01 '12 at 15:13
  • As asawyer points out, this is a race condition. Between `IsFileLocked` and `Delete` the file can become locked. So it's best to keep the try/catch in place. – Matt Greer Aug 01 '12 at 15:42
  • Yes, I tried it and this is exactly how I need my program to behave. Delete when possible, otherwise graciously fail (with an error code) but also keep the backup/temp file to manually correct it later. Thanks Rob and Hinek and everyone, awesome place this stackoverflow! – JHN Aug 01 '12 at 15:42
1

From the Windows SDK:

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. Subsequent calls to CreateFile to open the file fail with ERROR_ACCESS_DENIED.

001
  • 13,291
  • 5
  • 35
  • 66
0

As far as I understand, you want your program to wait until the file can be deleted and then delete it and move the other file.

To do this, you could check, if there is a handle open on the file, but this needs unmanaged code. Another way would be to use the methode from the answer of this question: Is there a way to check if a file is in use?

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; 
} 

Before you start to delete the file you could

A) loop until the file can be deleted

while(IsFileLocked(f)) { Thread.Sleep(100); }

or B) cancel

if (IsFileLocked(f)) { return; }

If you choose A or B depends on your requirements.

Community
  • 1
  • 1
Hinek
  • 9,519
  • 12
  • 52
  • 74