23

In trying to answer this question, I was surprised to discover that attempting to create a new file when that file already exists does not throw a unique exception type, it just throws a generic IOException.

I am therefore left wondering how to determine if the IOException is the result of an existing file, or some other IO error.

The exception has an HResult, but this property is protected, and thus unavailable to me.

The only other way I can see is to pattern match the message string which feels awful.

example:

try
{
    using (var stream = new FileStream("C:\\Test.txt", FileMode.CreateNew))
    using (var writer = new StreamWriter(stream))
    {
        //write file
    }
}
catch (IOException e)
{
    //how do I know this is because a file exists?
}
Community
  • 1
  • 1
GazTheDestroyer
  • 20,722
  • 9
  • 70
  • 103
  • 7
    Why don't you just check to see if the file exists? KISS. – Rex M Sep 12 '12 at 13:45
  • 7
    Because a file system is inherently unstable. Files can be created at any time (not just by me). – GazTheDestroyer Sep 12 '12 at 13:47
  • 1
    Lumping multiple errors into one exception with no way to tell them apart is probably of the worst feature of the .NET framework design. You have the same issue with disk full, network path not found, etc. IOExceptions. Even if you get to the HResult code, if your code needs to also run under Mono on Linux, that's not supported – jimvfr Feb 11 '13 at 23:33
  • Note for future readers that HResult is now available to in .Net4.5 onwards as pointed out by yurish below. – GazTheDestroyer Mar 21 '17 at 12:12
  • I wouldn't say that "a file system is inherently unstable". It's more accurate to say "for this software solution, it is legitimate for another process to write the same file I am trying to write (and in that case I do not want to overwrite it)". – ryanwebjackson Jun 16 '22 at 01:40

8 Answers8

14
try
{
    using (var stream = new FileStream("C:\\Test.txt", FileMode.CreateNew))
    using (var writer = new StreamWriter(stream))
    {
        //write file
    }
}
catch (IOException e)
{
    var exists = File.Exists(@"C:\Text.text"); // =)
}

Won't work for temp files etc which might have been deleted again.

Here are my exception best practices: https://coderr.io/exception-handling

jgauffin
  • 99,844
  • 45
  • 235
  • 372
9

Edit: there is another Hresult that is used when file already exists: 0x800700B7 (-2147024713) "Cannot create a file when that file already exists". Updated the code sample.


When you try to create a new file and it already exists IOException will have Hresult = 0x80070050 (-2147024816).

So you code could look like this:

try
{
    using (var stream = new FileStream("C:\\Test.txt", FileMode.CreateNew))
    using (var writer = new StreamWriter(stream))
    {
        //write file
    }
}
catch (IOException e)
{
    if (e.HResult == -2147024816 || 
        e.HResult == -2147024713)
    {
        // File already exists.
    }
}
yurish
  • 1,515
  • 1
  • 15
  • 16
  • 1
    HResult used to be protected (see my third paragraph) so this only looks to be valid in .Net4.5 onward, but useful to know it's now available, thanks. – GazTheDestroyer Mar 21 '17 at 12:10
  • @BrunoZell, sorry I do not work with Linux, so have no idea. – yurish Oct 13 '20 at 16:28
  • 2
    [more info](https://stackoverflow.com/questions/46380483/does-net-standard-normalize-hresult-values-across-every-platform-it-supports/46381756#46381756) – Bruno Zell Oct 13 '20 at 17:23
5

You can place this condition in your catch statement for IOException: if(ex.Message.Contains("already exists")) { ... }. It is a hack, but it will work for all cases that a file exists, even temporary files and such.

ThunderGr
  • 2,297
  • 29
  • 20
5

To modify @jgauffin, in C# 6, you can use the File.Exists inside of the when clause to avoid entering the catch block and thus behaving more like an actual dedicated exception:

try
{
    using (var stream = new FileStream("C:\\Test.txt", FileMode.CreateNew))
    using (var writer = new StreamWriter(stream))
    {
        //write file
    }
}
catch (IOException e) when (File.Exists(@"C:\Text.text"))
{
   //...
}
Arithmomaniac
  • 4,604
  • 3
  • 38
  • 58
3

It's not 100% foolproof (there are other reasons for an IOException), but you can at least exclude all derived exception types:

try
{
    ...
}
catch(IOException e)
{
    if (e is UnauthorizedAccessException) throw;
    if (e is DirectoryNotFoundException) throw;
    if (e is PathTooLongException) throw;
    // etc for other exceptions derived from IOException

    ... assume file exists
}

or the equivalent:

try
{
    ...
}
catch(UnauthorizedAccessException)
{
    throw;
}
catch(DirectoryNotFoundException)
{
    throw;
}
catch(PathTooLongException)
{
    throw;
}
catch(IOException e)
{
    ... assume file exists
}

As for the linked question, I'd just check for existence, prompt the user to overwrite, then use OpenOrCreate to overwrite if it exists. I think most apps work this way even if there is a theoretical risk of overwriting a file that's created just at the wrong moment.

Joe
  • 122,218
  • 32
  • 205
  • 338
2

In C# 6 and later:

const int WARN_WIN32_FILE_EXISTS = unchecked((int)0x80070050);

try
{
    ...
}
catch (IOException e) when (e.HResult == WARN_WIN32_FILE_EXISTS)
{
    ...
}

... or just when (e.HResult == -2147024816), if you're going for "quick and impenetrable". ;-)

(FWIW, the Windows-centric error code has been faithfully copied by Mono and also works on Mac/Linux.)

Søren Løvborg
  • 8,354
  • 2
  • 47
  • 40
  • 1
    Maybe Mono has copied it, but for .NET Standard, [HResult values are not portable across platforms](https://stackoverflow.com/a/46381756). – NightOwl888 Sep 20 '19 at 20:48
-1

You can't. Unfortunatly IOExceptions are not further specified for some reason beyond my comprehension in the .NET framework.

But in case of creating a new file it is common practice to check if the file exists first. Like so:

       try
        {
            if (File.Exists("C:\\Test.txt"))
            {
                //write file

                using (var stream = new FileStream("C:\\Test.txt", FileMode.CreateNew))
                using (var writer = new StreamWriter(stream))
                {
                    //The actual writing of file

                }

            }
        }
        catch (IOException ex)
        {
            //how do I know this is because a file exists?
            Debug.Print(ex.Message);
        }

Perhaps not the answer you were looking for. But, c'est ca.

Danny van der Kraan
  • 5,344
  • 6
  • 31
  • 41
  • Problem is: Another process *could* create the file between the check that it exists and trying to create it. Another solution could be to check if the file exists *after* the create fails. – Matthew Watson Sep 12 '12 at 14:10
-3

You should use

FileMode.Create

instead of

FileMode.CreateNew

It will override a file if its already exists.

Vytalyi
  • 1,637
  • 3
  • 20
  • 33