File.Exists() incorrectly returns false when path is too long

This is ugly and inefficient, but it DOES get around the MAX_PATH limitation:

const int MAX_PATH = 260;

private static void checkPath(string path)
{
    if (path.Length >= MAX_PATH)
    {
        checkFile_LongPath(path);
    }
    else if (!File.Exists(path))
    {
        Console.WriteLine("   *  File: " + path + " does not exist.");
    }
}

And here is the checkFile_LongPath function:

private static void checkFile_LongPath(string path)
{
    string[] subpaths = path.Split('\\');
    StringBuilder sbNewPath = new StringBuilder(subpaths[0]);
    // Build longest subpath that is less than MAX_PATH characters
    for (int i = 1; i < subpaths.Length; i++)
    {
        if (sbNewPath.Length + subpaths[i].Length >= MAX_PATH)
        {
            subpaths = subpaths.Skip(i).ToArray();
            break;
        }
        sbNewPath.Append("\\" + subpaths[i]);
    }
    DirectoryInfo dir = new DirectoryInfo(sbNewPath.ToString());
    bool foundMatch = dir.Exists;
    if (foundMatch)
    {
        // Make sure that all of the subdirectories in our path exist.
        // Skip the last entry in subpaths, since it is our filename.
        // If we try to specify the path in dir.GetDirectories(), 
        // We get a max path length error.
        int i = 0;
        while(i < subpaths.Length - 1 && foundMatch)
        {
            foundMatch = false;
            foreach (DirectoryInfo subDir in dir.GetDirectories())
            {
                if (subDir.Name == subpaths[i])
                {
                    // Move on to the next subDirectory
                    dir = subDir;
                    foundMatch = true;
                    break;
                }
            }
            i++;
        }
        if (foundMatch)
        {
            foundMatch = false;
            // Now that we've gone through all of the subpaths, see if our file exists.
            // Once again, If we try to specify the path in dir.GetFiles(), 
            // we get a max path length error.
            foreach (FileInfo fi in dir.GetFiles())
            {
                if (fi.Name == subpaths[subpaths.Length - 1])
                {
                    foundMatch = true;
                    break;
                }
            }
        }
    }
    // If we didn't find a match, write to the console.
    if (!foundMatch)
    {
        Console.WriteLine("   *  File: " + path + " does not exist.");
    }
}

From MSDN - Naming Files, Paths, and Namespaces:

In the Windows API (with some exceptions discussed in the following paragraphs), the maximum length for a path is MAX_PATH, which is defined as 260 characters.

...

The Windows API has many functions that also have Unicode versions to permit an extended-length path for a maximum total path length of 32,767 characters. This type of path is composed of components separated by backslashes, each up to the value returned in the lpMaximumComponentLength parameter of the GetVolumeInformation function (this value is commonly 255 characters). To specify an extended-length path, use the "\\?\" prefix. For example, "\\?\D:\very long path".

...

Because you cannot use the "\\?\" prefix with a relative path, relative paths are always limited to a total of MAX_PATH characters.

(Emphasis added)

If all your paths are full paths, you could update your code to use the extended-length path specifier as follows:

const longPathSpecifier = @"\\?";

private void checkFile(string path)
{
    // Add the long-path specifier if it's missing
    string longPath = (path.StartsWith(longPathSpecifier) ? path : longPathSpecifier  + path);

    if (!File.Exists(longPath))
    {
        // Print the original path
         Console.WriteLine("   *  File: " + path + " does not exist.");
    }
}

Update:

For file I/O, the "\?\" prefix to a path string tells the Windows APIs to disable all string parsing and to send the string that follows it straight to the file system. For example, if the file system supports large paths and file names, you can exceed the MAX_PATH limits that are otherwise enforced by the Windows APIs.

At least on my system (using Windows 7), long file names are not supported, so I can't verify if the above solution will work for you.

Update: I found a solution that does work, but it is fairly ugly. Here's what I did in pseudo-code:

  1. Split the path into an array of directories
  2. Get the longest portion of your path that is less than 260 characters (MAX_PATH).
  3. Create a DirectoryInfo for that portion of your path ("dir" for future reference).
  4. For the remaining directories in your path:
    a. Call dir.GetDirectories() and check if the next directory is contained in the results
    b. if so, set dir to that DirectoryInfo and keep digging
    c. if not, then the path doesn't exist
  5. Once we've gone through all of the directories leading up to our file, call dir.GetFiles() and see if our file exists in the returned FileInfo objects.

Never had the issue myself, someone on another SO post suggests opening a handle to the file, thus avoiding the whole 'exists' check in the first place. Not sure if this still has the 'long filename' issue:

It's the second answer here:

Check if a file/directory exists: is there a better way?

Not sure if that's useful :P

Tags:

C#

.Net