在C#中获取Unicode目录名称?

3
我正在尝试使用以下代码获取我的USB驱动器上的所有目录
DirectoryInfo di = new DirectoryInfo("H:\\");
DirectoryInfo[] allDir = di.GetDirectories();

这段代码适用于ASCII名称的目录完美运行。但是,有一个目录的名称为" "(Unicode值U+00A0)。GetDirectories()无法获取该目录。是否有一种方法可以获取具有Unicode名称的目录?


1
发生了什么?它只是不返回吗? - Mike Perrenoud
也许这可以帮助:http://stackoverflow.com/questions/1298136/file-paths-with-non-ascii-characters-and-fileinfo-in-c-sharp - tantry2002
1
尝试使用 Directory.EnumerateDirectories() - Farhad Alizadeh Noori
只返回了 null :( - Zohaib Aslam
你的USB驱动器上使用的是什么文件系统?该文件系统必须支持Unicode。如果是FAT16,则不支持。 - Ben
显示剩余5条评论
1个回答

1

好的,你正在与.NET框架使用的FindFirstFile签名进行斗争:

[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false)]
internal static SafeFindHandle FindFirstFile(string fileName, [In, Out] Win32Native.WIN32_FIND_DATA data);

To then add insult to injury, you're fighting the language:
自动调整字符串以适应目标操作系统。在 Windows NT、Windows 2000、Windows XP 和 Windows Server 2003 系列上,默认为 Unicode; 在 Windows 98 和 Windows Me 上默认为 Ansi。尽管公共语言运行时的默认设置是 Auto,但语言可能会覆盖此默认设置。例如,默认情况下,C#将所有方法和类型标记为 Ansi。 (来自 CharSet 枚举文档强调添加)
手头的问题是 DllImport 上的 CharSet 参数。这意味着你只能采用一种方法:自己利用 P/Invoke。你需要很多东西。首先,你需要返回的数据结构:
[BestFitMapping(false)]
[Serializable]
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
internal class WIN32_FIND_DATA
{
  internal int dwFileAttributes;
  internal uint ftCreationTime_dwLowDateTime;
  internal uint ftCreationTime_dwHighDateTime;
  internal uint ftLastAccessTime_dwLowDateTime;
  internal uint ftLastAccessTime_dwHighDateTime;
  internal uint ftLastWriteTime_dwLowDateTime;
  internal uint ftLastWriteTime_dwHighDateTime;
  internal int nFileSizeHigh;
  internal int nFileSizeLow;
  internal int dwReserved0;
  internal int dwReserved1;
  [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
  internal string cFileName;
  [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
  internal string cAlternateFileName;

  [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
  public WIN32_FIND_DATA()
  {
  }
}

下一步,您需要正确的导入FindFirstFile
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true, BestFitMapping = false)]
internal static SafeFindHandle FindFirstFile(string fileName, [In, Out] Win32Native.WIN32_FIND_DATA data);

下一步,您需要使用GetLastWin32Error来检查错误;可以通过InteropServices命名空间中的{{link1:Marshal.GetLastWin32Error}}访问它。
接下来,您需要进行迭代,因此需要使用FindNextFile:
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true, BestFitMapping = false)]
internal static bool FindNextFile(SafeFindHandle hndFindFile, [MarshalAs(UnmanagedType.LPStruct), In, Out] Win32Native.WIN32_FIND_DATA lpFindFileData);

拥有这些知识,您可以构建自己的迭代器。但是,为了您的利益,以下是 Microsoft 的“init”操作:
[SecurityCritical]
private void CommonInit()
{
  string fileName = Path.InternalCombine(this.searchData.fullPath, this.searchCriteria);
  Win32Native.WIN32_FIND_DATA wiN32FindData = new Win32Native.WIN32_FIND_DATA();
  this._hnd = Win32Native.FindFirstFile(fileName, wiN32FindData);
  if (this._hnd.IsInvalid)
  {
    int lastWin32Error = Marshal.GetLastWin32Error();
    switch (lastWin32Error)
    {
      case 2:
      case 18:
        this.empty = this.searchData.searchOption == SearchOption.TopDirectoryOnly;
        break;
      default:
        this.HandleError(lastWin32Error, this.searchData.fullPath);
        break;
    }
  }
  if (this.searchData.searchOption == SearchOption.TopDirectoryOnly)
  {
    if (this.empty)
    {
      this._hnd.Dispose();
    }
    else
    {
      SearchResult searchResult = this.CreateSearchResult(this.searchData, wiN32FindData);
      if (!this._resultHandler.IsResultIncluded(searchResult))
        return;
      this.current = this._resultHandler.CreateObject(searchResult);
    }
  }
  else
  {
    this._hnd.Dispose();
    this.searchStack.Add(this.searchData);
  }
}

以下是针对您所需的“迭代”操作的具体步骤:

  if (this.searchData != null && this._hnd != null)
  {
    while (Win32Native.FindNextFile(this._hnd, wiN32FindData))
    {
      SearchResult searchResult = this.CreateSearchResult(this.searchData, wiN32FindData);
      if (this._resultHandler.IsResultIncluded(searchResult))
      {
        if (this.needsParentPathDiscoveryDemand)
        {
          this.DoDemand(this.searchData.fullPath);
          this.needsParentPathDiscoveryDemand = false;
        }
        this.current = this._resultHandler.CreateObject(searchResult);
        return true;
      }
    }
    int lastWin32Error = Marshal.GetLastWin32Error();
    if (this._hnd != null)
      this._hnd.Dispose();
    if (lastWin32Error != 0 && lastWin32Error != 18 && lastWin32Error != 2)
      this.HandleError(lastWin32Error, this.searchData.fullPath);
  }

注意:您不能直接使用此代码,您需要将其适应于您的解决方案,但这是API的巨大跳跃。获取dotPeek的副本以填补空白。
注意:由FindFirstFile接受的fileName参数将进入父目录。在您的情况下,是H:\

好的,我已经测试过了,在NTFS上至少有以下情况:1)普通的System.IO.Path方法可以很好地创建和返回Unicode文件名。2)您无法使用名称为(char)160CreateDirectory创建目录,但是您可以使用DirectoryInfo.Move命令将现有目录重命名为该名称。3)如果您有一个以单个字符(char)160命名的目录,则会被GetDirectories返回。 - Ben

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