Retrieve path information from PathTooLongExceptio

2019-05-14 07:04发布

问题:

I am using DirectoryInfo and FileInfo in .Net 4.0 to enumerate files in a directory tree and I am hitting PathTooLongException. Simplified version is below

public static class Test
{
    public static void Search(DirectoryInfo base)
    {
        foreach(var file in base.GetFiles())
        {
            try
            {
                Console.WriteLine(file.FullName);
            } catch(PathTooLongException ex)
            {
                // What path was this?
            }
        }
        foreach(var dir in base.GetDirectories())
        {
            Search(dir);
        }
    }
}

When the error is thrown, I want to know what file path caused the problem. Obviously I can't ask for FullName as that is what errored. I can get name from file.Name, but if I can't get the rest of the path as file.Directory gives a PathTooLongException even though the DirectoryInfo that the file was found from worked fine! (I can't use that though as the actual code is much more complex).

Looking through the stack trace it seems that it's using an internal path (I see a protected file.FullPath from debug), and trying to tear the directory from the full (oversized) path. Most of the problems seem to involve System.IO.Path.NormalizePath, with I hear went through a few changes in .Net 4.0. I have not tried on previous versions of the framework.

My questions:

  1. How can I get the full path from this exception; it is seemingly passed without any useful information.
  2. Why would the framework need to limit characters in a path to chop off the filename?

Thanks in advance for any help,
Andy

回答1:

I can't think of any other way off the top of my head other than using reflection, or using a library or P/Invoke to use the Windows APIs that support long paths and manually check length. The full path is stored in a protected string field called FullPath

foreach(var dir in new DirectoryInfo (@"D:\longpaths")
                     .GetFileSystemInfos("*.*", SearchOption.AllDirectories))
{
    try
    {
        Console.WriteLine(dir.FullName);
    }
    catch (PathTooLongException)
    {
                FieldInfo fld = typeof(FileSystemInfo).GetField(
                                        "FullPath", 
                                         BindingFlags.Instance | 
                                         BindingFlags.NonPublic);
                Console.WriteLine(fld.GetValue(dir));  // outputs your long path
    }
}

If you're trying to actually do something with the files and not just check file length, I would suggest using a library like this one from the BCL team at Microsoft, however it doesn't create DirectoryInfos, FileInfo or FileSystemInfo, only strings. So it's might not be a drop-in replacement for your code it seems.

As for the answer to your second question, I recommend reading this blog post dealing with long paths in .NET. This is a quote from it that explains why they haven't been quick to add long path support in .NET.

Very few people complain about a 32K limit, so, problem solved? Not quite. There are several reasons we were reluctant to add long paths in the past, and why we’re still careful about it, related to security, inconsistent support in the Windows APIs of the \?\ syntax, and app compatibility.

It's a 3 part series and explains a few reasons why the API is the way it is and why the limitation is there.