在C#/ .NET中访问MAX_PATH之外的文件

背景

我需要编写一个使用.NET 2.0版本的工具(由于政治,商业和机密性/信任原因,使用现成的东西不是这个客户的选项)通过网络将文件从一个服务器迁移到另一个服务器。 服务器是本地团队的文件服务器,需要将某些团队文件夹迁移到其他服务器以便于重组。 基本的想法是我们读取每个文件并在数小时内通过网络流式传输,几天后数据将被迁移。 需要保留文件权限。 由于这需要几天时间(我们正在谈论几千兆字节的数据,对于某些团队而言),我们需要每晚迭代文件并比较修改日期并更新已更改的日期。 理论上,最终新服务器将拥有最新的文件副本,用户可以切换到新服务器。 它当然不是那么简单,但我们有一个我们认为应该工作的设计:)

问题

所以从理论上讲,我们只需打开文件,通过网络流式传输,然后在另一端写入,对吧? 🙂

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

D:\ Data \ Team Shares \ DIVISION \ DEPARTMENT \ NAME OF TEAM – 可能会长篇大论

对于每个用户,此路径映射到驱动器,例如,它将作为\\ SERVER \ TEAMNAME共享并映射到T:驱动器。

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

我知道使用“\\?\”前缀指定路径的解决方法,该路径将路径视为UNC路径,并允许理论上最多32k个字符。

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

所以我的问题是……有没有一种方法我没有想到这将允许我解决这个问题,而不必完全重写几乎整个System.IO命名空间,加载P / Invoke调用,只是为了删除这个烦人的validation?

BCL团队分别完成了为什么做出这些选择以及解决方案的原因。 如果您还没有阅读,我建议您这样做,因为它是有关该主题的重要信息来源

我遇到了一个可能有用的第三方解决方案: AlphaFS 。

假设您的软件具有必要的权限,通过一些平台调用来解决此限制应该相当容易:

 [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 } } 

您可以尝试通过使用subst.exe(或其内部使用的任何API)映射父目录来缩短路径:

http://www.makeuseof.com/tag/how-to-map-a-local-windows-folder-to-a-drive-letter/

理想情况下,您可以尽可能多地绘制路径。

我已经成功使用下面的小脚本删除目录结构。 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