当Directory.GetFiles()发现一个不符合要求的文件名时抛出异常,如何处理异常?

3
在一个拥有有效路径C:\Users\David的Vista机器上,以David用户身份运行Directory.GetFiles(@"C:\Users\David")会抛出以下ArgumentException异常。在Windows资源管理器中,David用户可以正常查看该目录的内容。
System.ArgumentException message: Illegal characters in path.
Argument: ""
Stack trace:
   at System.IO.Path.CheckInvalidPathChars(String path)
   at System.IO.Path.InternalCombine(String path1, String path2)
   at System.IO.Directory.InternalGetFileDirectoryNames(String path, String userPathOriginal, String searchPattern, Boolean includeFiles, Boolean includeDirs, SearchOption searchOption)
   at System.IO.Directory.GetFiles(String path, String searchPattern, SearchOption searchOption)
   at System.IO.Directory.GetFiles(String path)
   at Microsoft.Samples.XFileExplorer.ContentView.CreateContentDataTable(String CurrentFolder) in C:\Users\david\Downloads\MEF Preview 5\MEF Preview 5\Samples\XFileExplorer\XFileExplorer\ContentView.xaml.cs:line 108

这台Vista机器被一台运行MacFuse的Mac访问过,所以该目录中包含一个看起来名为“._Icon”的文件,但实际上可能包含一些非法字符。我认为这就是错误的根源。当Directory.GetFiles()遇到不喜欢的文件名时抛出异常后,我面临解决问题的困境。是否有其他列出文件内容的替代方法,不会抛出这样的异常呢?
至于这个特定的文件,我怀疑文件名可能包含Windows资源管理器或命令提示符未显示的某些字符。
   C:\Users\david>dir ._Icon
   Volume in drive C is Bootcamp
   Volume Serial Number is XXXX-XXX

   Directory of C:\Users\david

   File Not Found

最后提醒一下:
   C:\Users\david>dir ._Icon*
   Volume in drive C is Bootcamp
   Volume Serial Number is XXXX-XXX
   Directory of C:\Users\david

   05/25/2008  07:40 AM            43,296 ._Icon
           1 File(s)         43,296 bytes
           0 Dir(s)  58,950,623,232 bytes free

通过SMB查看文件,看起来该文件实际上命名为“._Icon?”。每次我尝试从Mac中删除该文件时,该文件似乎立即重新出现。

4个回答

1

你可以尝试使用PInvoke FindFirstFile- 在这里查看列出文件。它是否会导致类似的问题?

文件信息将在WIN32 FIND DATA结构中返回。然后,您调用FindNextFile来获取每个文件,直到它不返回0为止。在结构中,您可以使用cFileName成员获取文件名。检查一下里面有什么无效的东西。

我的观点是,这可能会返回Directory.GetFiles输出的文件信息。

以下是其使用示例:

public const int MAX_PATH = 260;
 public const int MAX_ALTERNATE = 14;

[StructLayout(LayoutKind.Sequential)]
    public struct FILETIME {
    public uint dwLowDateTime;
    public uint dwHighDateTime;
 }; 

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] 
public struct WIN32_FIND_DATA {
    public FileAttributes dwFileAttributes;
    public FILETIME ftCreationTime; 
    public FILETIME ftLastAccessTime; 
    public FILETIME ftLastWriteTime; 
    public int nFileSizeHigh;
    public int nFileSizeLow;
    public int dwReserved0;
    public int dwReserved1;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst=MAX_PATH)] 
    public string cFileName; 
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst=MAX_ALTERNATE)] 
    public string cAlternate; 
}

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

[DllImport("kernel32", CharSet=CharSet.Unicode)] 
public static extern bool FindNextFile(IntPtr hFindFile, out WIN32_FIND_DATA lpFindFileData);

private long RecurseDirectory(string directory, int level, out int files, out int folders) {
    IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
    long size = 0;
    files = 0;
    folders = 0;
    Kernel32.WIN32_FIND_DATA findData;

    IntPtr findHandle;

    // please note that the following line won't work if you try this on a network folder, like \\Machine\C$
    // simply remove the \\?\ part in this case or use \\?\UNC\ prefix
    findHandle = Kernel32.FindFirstFile(@"\\?\" + directory + @"\*", out findData);
    if (findHandle != INVALID_HANDLE_VALUE) {

        do {
            if ((findData.dwFileAttributes & FileAttributes.Directory) != 0) {

                if (findData.cFileName != "." && findData.cFileName != "..") {
                    folders++;

                    int subfiles, subfolders;
                    string subdirectory = directory + (directory.EndsWith(@"\") ? "" : @"\") + 
                        findData.cFileName;
                    if (level != 0)  // allows -1 to do complete search.
                        {
                    size += RecurseDirectory(subdirectory, level - 1, out subfiles, out subfolders);

                    folders += subfolders;
                    files += subfiles;
                    }
                }
            }
            else {
                // File
                files++;

                size += (long)findData.nFileSizeLow + (long)findData.nFileSizeHigh * 4294967296;
            }
        } 
        while (Kernel32.FindNextFile(findHandle, out findData));
        Kernel32.FindClose(findHandle);

    }

    return size;
}

// [Sample by Kåre Smith] // [Minor edits by Mike Liddell]

我喜欢这个例子:http://www.dotnet247.com/247reference/msgs/22/112844.aspx - flipdoubt
那么...我可以调用这个方法,没有问题,但是当它返回一个整数时,我该如何获取结果呢?如何使用该整数列出所有实际文件名? - flipdoubt

1

仅供参考...您所看到的是资源叉。这些与Windows资源迥然不同,大致相当于NTFS备用数据流。

http://en.wikipedia.org/wiki/Resource_fork

资源派生文件在Macintosh上用于系统驱动器的所有文件系统中实现(MFS、HFS和HFS Plus)。资源派生文件的存在使得存储各种附加信息变得容易,例如允许系统显示文件的正确图标并在文件名中不需要文件扩展名的情况下打开它。虽然对数据派生文件的访问类似于在任何其他操作系统上的文件访问-选择一个文件,选择一个字节偏移量,读取一些数据,但对资源派生文件的访问更像从数据库中提取结构化记录。

来自Microsoft sysinternals网站:

NTFS文件系统提供了创建替代数据流的功能。默认情况下,所有的数据都存储在一个文件的主未命名数据流中。但是通过使用语法“file:stream”,您可以读写替代数据流。并非所有应用程序都编写为访问备用数据流,但您可以非常简单地演示数据流。首先,在命令提示符内更改到NTFS驱动器上的目录。接下来,键入“echo hello > test:stream”。您刚刚创建了一个名为“stream”的流,它与文件“test”关联。请注意,当查看test的大小时,报告为0,并且在任何文本编辑器中打开的文件看起来为空。要查看流,请输入“more < test:stream”(type命令不接受流语法,因此必须使用more)。

0

我知道这已经有点过时了,但我刚刚遇到了从Windows访问OSX文件系统的完全相同的问题。 DotNet会给我“非法文件名”错误,导致这个错误的原因是一个名为icon但文件名末尾带有方块的零字节Mac文件。

我的解决方案是使用一些VB6代码和interop.scripting filesystemobject以那种方式列出文件。

FSO不会因为这些非法字符而垮掉。

希望能对某人有所帮助。


0

要不就直接刪除文件,或者重新命名它怎麼樣?

說真的,這種情況發生得足夠頻繁嗎?值得你寫特殊代碼來“修復”嗎?


2
通常情况下,“某种方式”会创建一个“非法”的文件名。我正在寻找一般解决方案,以便在目录包含非法文件名时如何查询目录内容。由于文件名非法,无法从Windows中删除或重命名该文件: C:\ Users \ david> del ._Icon? C:\ Users \ david \ ._Icon 文件名、目录名或卷标语法不正确。 - flipdoubt
1
我听到你了。我在问你是否值得解决一般情况,而不是通过删除文件来修复特定情况。解决一般情况(并维护代码)需要更多时间,还是删除或重命名所有会在代码生命周期内导致此问题的文件需要更多时间?如果Mac每天都会创建这样的文件,则解决一般情况。如果这种情况每年只发生一次,那么你可能有更好的事情要做。 - John Saunders

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接