【问题标题】:Directory.EnumerateFiles search pattern does not work on file sharesDirectory.EnumerateFiles 搜索模式不适用于文件共享
【发布时间】:2020-10-28 00:08:10
【问题描述】:

根据Microsoft Docs site for Directory.EnumerateFiles,搜索模式参数将匹配以指定模式开头的任何扩展名,只要它正好是3 个字符。但是,这不适用于文件共享,仅适用于本地驱动器。

对于包含名为file.xlsx 的单个文件的\\share\folder\ 目录,第一个代码sn-p 不会返回它:

public static List<string> GetAllFilesFromDirectory(string directory) =>
   new[] { "*.csv", "*.xls", "*.txt" }.SelectMany(ext => Directory.EnumerateFiles(directory, ext)).ToList();

但是,如果我添加 *.xlsx 模式,它会返回它:

public static List<string> GetAllFilesFromDirectory(string directory) =>
   new[] { "*.csv", "*.xls", "*.xlsx", "*.txt" }.SelectMany(ext => Directory.EnumerateFiles(directory, ext)).ToList();

我还用C:\temp 目录中的同一个文件对此进行了测试,发现它以两种方式都返回了。

这是在 .NET Framework 4.7.2 控制台应用程序中运行的。

我是否在搜索模式中遗漏了什么?或者这不适用于文件共享与本地驱动器相同的方式?这是意料之中的吗?

【问题讨论】:

    标签: c# .net .net-4.7.2


    【解决方案1】:

    你一定是最倒霉的人,才会遇到这个错误。我可以确认它的行为符合您的观察,并且在互联网上的任何地方都找不到对此的任何引用。

    所以我跟踪了 .NET 源代码以了解 Directory.EnumerateFiles 是如何工作的,并且 - 在内心深处 - 最终遇到了 callFindFirstFile 和随后的 FindNextFile 调用。这些是直接从内核中 PInvoked,所以你不能比这更低。

    [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
    public static extern IntPtr FindFirstFile(string lpFileName, out WIN32_FIND_DATA lpFindFileData);
    

    那么必须测试一下。你猜怎么了?它在本地目录中捕获 XLSX 文件,但不在网络共享中。

    The doc for the function 也没有提到这种行为。是的。您刚刚遇到了一个未记录的“功能”:)

    编辑:这变得更好了。看起来在 .NET Core(从 2.0 一直到 .NET 5)中不再存在这种行为。这次他们实际上写了自己的pattern matcher*.xls 不会在任何文件夹中捕获 XLSX,无论是本地文件夹还是其他文件夹。然而their documentation 仍然表示应该这样做。

    Edit 2021:该文档现已成为 updated,并附有关于 .NET Framework 的怪癖的评论。


    这是我对FindFirstFile 调用的测试代码:

    public class Program
    {
        public static void Main(string[] args)
        {
            // Ensure these test folders only contain ONE file.
            // Name the file "Test.xlsx"
            Test(@"C:\Temp\*.xls"); // Finds the xlsx file just fine
            Test(@"\\Server\Temp\*.xls"); // But not here!
        }
    
        public static void Test(string fileName)
        {
            Win32Native.WIN32_FIND_DATA data;
            var hnd = Win32Native.FindFirstFile(fileName, out data);
    
            if (hnd == Win32Native.InvalidPtr) 
                Debug.WriteLine("Not found!!");
            else
                Debug.WriteLine("Found: " + data.cFileName);
        }
    }
    
    /** Windows native Pinvoke **/
    public class Win32Native
    {
        public static IntPtr InvalidPtr = new IntPtr(-1);
    
        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        public static extern IntPtr FindFirstFile(string lpFileName, out WIN32_FIND_DATA lpFindFileData);
    
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        public struct WIN32_FIND_DATA
        {
            public uint dwFileAttributes;
            public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime;
            public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime;
            public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime;
            public uint nFileSizeHigh;
            public uint nFileSizeLow;
            public uint dwReserved0;
            public uint dwReserved1;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
            public string cFileName;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
            public string cAlternateFileName;
        }
    }
    

    【讨论】:

    • 很好的调查。我想知道如果我们明确地使用广泛版本的调用,我们是否会看到不同之处。我怀疑根据使用哪个,它会调用不知道长文件名的遗留代码并以某种方式截断扩展名。
    猜你喜欢
    • 1970-01-01
    • 2020-07-12
    • 1970-01-01
    • 2015-12-09
    • 2017-05-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多