8

I am creating a simple function that creates a random file. To be thread safe, it creates the file in a retry loop and if the file exists it tries again.

while (true)
{
    fileName = NewTempFileName(prefix, suffix, directory);

    if (File.Exists(fileName))
    {
        continue;
    }

    try
    {
        // Create the file, and close it immediately
        using (var stream = new FileStream(fileName, FileMode.CreateNew, FileAccess.Write, FileShare.Read))
        {
            break;
        }
    }
    catch (IOException e)
    {
        // If the error was because the file exists, try again
        if ((e.HResult & 0xFFFF) == 0x00000050)
        {
            continue;
        }

        // else rethrow it
        throw;
    }
}

According to MSDN, the HResult value is derived from COM which would seem to indicate it will only work on Windows, and it specifically lists them as "Win32 codes". But this is in a library which targets .NET Standard and ideally it should work on every platform .NET Standard supports.

What I am wondering is whether I can rely on the above approach that uses the value from HResult to be cross-platform? The documentation is not clear on this point.

If not, how do I determine what HResult values to expect on other platforms?

NOTE: There is a similar question Does .NET define common HRESULT values?, but it was asked before .NET Standard (and cross-platform support for .NET) existed, so I cannot rely on that answer for this purpose.

For now, our codebase only uses:

  1. 0x00000020 - ERROR_SHARING_VIOLATION
  2. 0x00000021 - ERROR_LOCK_VIOLATION
  3. 0x00000050 - ERROR_FILE_EXISTS

We are targeting .NET Standard 1.5.

NOTE: While the accepted answer does satisfy what I asked here, I have a follow-up question How do I make catching generic IOExceptions reliably portable across platforms?

NightOwl888
  • 55,572
  • 24
  • 139
  • 212
  • I guess you have to be more specific about the scenario or specific error codes you want to rely on.. There are many places were lists of predefined codes are used (you can search for `hresults.cs` in both CoreCLR and fullfx reference source code) and for some cases there are normalisation functions [like this one for file errors](https://github.com/dotnet/coreclr/blob/5c07c5aa98f8a088bf25099f1ab2d38b59ea5478/src/pal/src/file/file.cpp#L184-L194). – Martin Ullrich Sep 23 '17 at 15:52
  • Thanks Martin. I added the 3 errors I am currently interested in. Was kinda hoping for a more general answer, though. Generally, we are only interested in file IO errors though. – NightOwl888 Sep 23 '17 at 16:12
  • You might want to link to [this documentation](https://learn.microsoft.com/en-us/dotnet/api/system.exception.hresult?view=netstandard-1.5) instead of the msdn one as it is the official .net standard 1.5 API docs. (Fairly sure it is a copy and paste of the MSDN, but it is good to point at the right spot) – Scott Chamberlain Sep 23 '17 at 17:48
  • I updated the link. I have also asked a follow up question https://stackoverflow.com/questions/46382637 – NightOwl888 Sep 23 '17 at 18:00
  • @MartinUllrich - Could you please answer specifically whether the `ERROR_SHARING_VIOLATION` and `ERROR_LOCK_VIOLATION` are cross platform? I tried analyzing the code you linked, but I can't seem to find the actual location where those constants are defined. We have a less-efficient fallback implementation, but I would prefer not to use it or at least limit it to less popular platforms. – NightOwl888 Oct 08 '17 at 18:17
  • @NightOwl888 I believe Jan Kotas who answered is more qualified to correctly answer this. I'd only go through the same code like you did now. – Martin Ullrich Oct 08 '17 at 18:36

1 Answers1

7

Exception.HResult values are not standardized across platforms.

For I/O errors, .NET Core will return platform specific error code as HResult. The value of HResult property for file already exist in your example is going be 17 on Linux, and it may be different value for other Unix systems.

The relevant code that maps IO errors to exceptions on Unix is here: https://github.com/dotnet/corefx/blob/master/src/Common/src/Interop/Unix/Interop.IOErrors.cs

Jan Kotas
  • 676
  • 6
  • 5
  • Thanks. Unfortunately, the link you provided shows only the code, but not the resources file that provides the values. There doesn't seem to be a corresponding file for other platforms in that directory. Am I to take away from this that the above approach cannot be made reliable? Or is there some way to obtain all of the values I need for all of the platforms? Do note I only need the 3 errors in my question for each platform .NET Standard 1.5 supports. – NightOwl888 Sep 23 '17 at 17:00
  • This approach cannot be made reliably portable. HResult in this case is what the underlying operating system returns. It can differ between Linux, macOS, FreeBSD, ... . I will edit the post to make it clear. – Jan Kotas Sep 23 '17 at 17:14