在C#/.NET中访问超过MAX_PATH限制的文件

9

背景

我需要编写一个工具,使用.NET 2.0版本及以上(由于政治、商业和保密/信任原因,对于此客户来说,使用现有的工具不是一个选择),在网络上从一个服务器迁移文件到另一个服务器。这些服务器是本地团队的文件服务器,并且需要将某些团队文件夹迁移到其他服务器以便进行重新组织。基本想法是我们读取每个文件并在非工作时间通过网络流传输它,几天后数据将被迁移。需要保留文件权限。由于这将需要几天时间(对于一些团队,我们正在谈论数千兆字节的数据),因此我们需要每晚遍历文件并比较修改日期,并更新已更改的文件。理论上,最终新服务器将拥有文件的最新副本,用户可以切换到新服务器。当然,情况并不完全如此简单,但我们有一个我们认为应该起作用的设计 :)

问题

所以理论上,我们只需打开文件,通过网络流传输它,并在另一端写入,对吧? :)

不幸的是,在服务器本身上,文件共享是在文件夹路径上创建的,例如:

D:\Data\Team Shares\DIVISION\DEPARTMENT\NAME OF TEAM - COULD BE FAIRLY LONG\

对于每个用户,此路径映射到一个驱动器,例如以 \\SERVER\TEAMNAME 的形式共享,并映射到 T: 驱动器。

这导致了这样的情况:从 T: 驱动器可见的文件在 MAX_PATH 限制内,但是在服务器本身上查看时,它们远远超出了限制。我们无法使用网络共享访问文件,因为此工具需要通用性,在数百个这些服务器上运行,并且没有标准方法可以告诉我们应该移动哪些文件共享,哪些不是 - 甚至没有命名约定标准。此外,有时会有其他共享的子共享,因此我们超过了 MAX_PATH 限制两倍!

我知道一种解决方法是使用 "\\?\" 前缀指定路径,它将路径视为 UNC 路径并允许理论上的最大字符数为 32k。

此解决方法在 Win32 API 级别上实现,System.IO 命名空间(大多数)基本上只是本机 Win32 API 函数的薄包装器,但是 Microsoft 已经“有帮助地”在将调用传递给 API 之前实现了额外的(不正确的)验证。在这种情况下,.NET Framework 拒绝该路径,因为它声称“?”是无效的路径字符。

那么我的问题是...有没有我没有想到的方法可以让我解决这个问题,而无需完全重写几乎整个 System.IO 命名空间,进行大量 P/Invoke 调用,只是为了删除这个烦人的验证?

5个回答

5

1
是的,我看过这个。说实话,他们推荐的解决方案正是我想避免的。因为我们要处理文件和目录元数据、权限和文件内容,这会产生大量的 API 调用,我真的很希望有人能提供一个可行的解决方案,这样我就不必在 pinvoke.net 上花费下个月的时间了 ;) - Jon Grant
我自己和其他人的答案建议您使用一些包装库来处理长路径。这里 - rkagerer

4
我找到了一个可能有帮助的第三方解决方案:AlphaFS

1

如果您的软件具有必要的权限,使用一些平台调用应该可以很容易地解决这个限制问题:

[DllImport("kernel32.dll", SetLastError = true)]
static extern SafeFileHandle CreateFile(string lpFileName, uint dwDesiredAccess,
  uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition,
  uint dwFlagsAndAttributes, IntPtr hTemplateFile);

// Must close/dispose handle separately from FileStream since it's not owned by
// that object when passed to constructor.
using (SafeFileHandle h = CreateFile(longUncPath, GENERIC_WRITE, 0, IntPtr.Zero, 
       OPEN_EXISTING, 0, IntPtr.Zero))
{
    using (var fs = new FileStream(h, FileAccess.Read))
    {
        // operations with FileStream object
    }
}

1
当我的P/Invoke定义类达到了大约3000行的各种导入、结构和常量,而我仍然没有我需要的所有功能时,我想我应该先来这里看看是否有什么明显的遗漏……看起来似乎没有 :( - Jon Grant
明白了。没有现成的第三方解决方案?我很惊讶! - Ben M

0

-1

我使用以下小脚本成功删除目录结构。pushd使用UNC格式,可以获得32K而不是260的限制。

set "folder=\\SERVER\SHARE\DIVISION\DEPARTMENT\NAME OF TEAM - COULD BE FAIRLY LONG\" 
pushd "%folder%"
for /d %%i in ("*") do rmdir "%%i" /s /q
popd

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