Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c#

.net

I am currently working on a program that traverses through various directories to ensure that specific files are present by using File.Exists().

The application has been claiming that certain files do not exist when they actually do, and I recently discovered that this error was due to the path being too long.

I realize there are questions on SO that address File.Exists() returning incorrect values, but none seem to solve this particular issue.

Renaming the directories and files to shorten the path is not really an option, so I'm not sure what to do at this point. Is there a work-around that would solve this problem?

The code in use is nothing special (I've cut out some irrelevant code), but I will include it below just in case it helps.

    private void checkFile(string path)
    {
        if (!File.Exists(path))
            Console.WriteLine("   *  File: " + path + " does not exist.");
    }
like image 639
Keplah Avatar asked Jun 26 '12 15:06

Keplah


People also ask

Why is file exists false?

The Exists method returns false if any error occurs while trying to determine if the specified file exists.

How to check whether file exists in php?

The file_exists() function checks whether a file or directory exists.

Does file exist Java?

To test to see if a file or directory exists, use the “ exists() ” method of the Java java. io. File class. If the exists() method returns true then the file or directory does exist and otherwise does not exists.


2 Answers

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.
like image 198
Jon Senchyna Avatar answered Nov 09 '22 02:11

Jon Senchyna


You need to P/Invoke Win32 APIs to get this to work properly:

    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    static extern uint GetFileAttributes(string lpFileName);

    public static bool DirectoryExists(string path)
    {
        uint attributes = GetFileAttributes(path.StartsWith(@"\\?\") ? path : @"\\?\" + path);
        if (attributes != 0xFFFFFFFF)
        {
            return ((FileAttributes)attributes).HasFlag(FileAttributes.Directory);
        }
        else
        {
            return false;
        }
    }

    public static bool FileExists(string path)
    {
        uint attributes = GetFileAttributes(path.StartsWith(@"\\?\") ? path : @"\\?\" + path);
        if (attributes != 0xFFFFFFFF)
        {
            return !((FileAttributes)attributes).HasFlag(FileAttributes.Directory);
        }
        else
        {
            return false;
        }
    }
like image 5
Alexandru Avatar answered Nov 09 '22 02:11

Alexandru