【问题标题】:DirectoryNotFoundException when using long paths in .NET 4.7在 .NET 4.7 中使用长路径时出现 DirectoryNotFoundException
【发布时间】:2017-07-03 15:03:58
【问题描述】:

我已将本地组策略编辑器中的Enable Win32 Long Paths 设置为Enabled 并重新启动计算机。

这是代码:

string path = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
for (int i = 0; i < 10; i++)
    path += "\\" + new string('z', 200);
Directory.CreateDirectory(path);

我收到了错误:

System.IO.DirectoryNotFoundException: '找不到一部分 路径 'C:\Users...\Desktop\zzzzzzzzzz...

(这实际上是一条奇怪的错误消息。)

app.config 已经有:

<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7" />

更多信息(可能不重要)

我尝试在configuration 下的app.config 中添加this post 和其他地方中提到的(尽管在cmets 中指出,使用.net 4.7 时不需要):

<runtime>
  <AppContextSwitchOverrides value="Switch.System.IO.UseLegacyPathHandling=false;Switch.System.IO.BlockLongPaths=false" />
</runtime>

还是一样的错误。

如果我只使用一个zzzzzz...,它会在桌面上创建它而不会出错。

我使用的是 VS2017,Windows 10。我尝试过 Winforms 和 WPF。

【问题讨论】:

  • 这不是 4.7 的错误 这是 Windows 中可用文件路径的限制。默认情况下,它硬编码为 260 个字符。
  • @eocron 没有将 UseLegacyPathHandling 设置为 false。
  • 您确实意识到 UseLegacyPathHandling 开关适用于根据链接文章低于 4.6.2 的应用程序,对吧?
  • @CamiloTerevinto 是的。 &lt;supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7" /&gt;.

标签: c# .net wpf .net-4.6.2 .net-4.7


【解决方案1】:

周年更新 (RS1) 有一个错误,允许长路径在没有清单的情况下工作。对于任何更新的 Windows,您必须将 Application Manifest File 项添加到您的项目中。否则将无法正常工作。

<application xmlns="urn:schemas-microsoft-com:asm.v3">
  <windowsSettings>
    <longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
  </windowsSettings>
</application>

【讨论】:

  • 谢谢。 +50 (+25)。如果你有这个来源,那也很好。但无论如何 - 这解决了问题。这就解释了为什么有些人似乎没有这个问题,而我有。因为我正在运行 Windows 10 Creators 更新。
  • ASP .Net 应用程序怎么样?
【解决方案2】:

这可能无法回答您的问题,但会为您提供解决方法的提示。我在 Ubuntu Linux 下使用 mono 4.5 测试了你的 sn-p,效果很好,但在 Windows 中,情况可能会有所不同。在这里,应该归咎于 .NET Framework 本身,关于 this articlethis other article,不支持长路径。

因此,@Anastasiosyal 在this StackOverflow answer 中建议的解决方案是依赖 Windows Api 本身。有两种方式:直接绕过或Api调用。

Directory.CreateDirectory(@"\\?\" + veryLongPath);

Api 调用(代码不是我的,从@Anastasiosyal 回答中得到):

// This code snippet is provided under the Microsoft Permissive License.
using System;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;

[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern SafeFileHandle CreateFile(
    string lpFileName,
    EFileAccess dwDesiredAccess,
    EFileShare dwShareMode,
    IntPtr lpSecurityAttributes,
    ECreationDisposition dwCreationDisposition,
    EFileAttributes dwFlagsAndAttributes,
    IntPtr hTemplateFile);

public static void TestCreateAndWrite(string fileName) {

    string formattedName = @"\\?\" + fileName;
    // Create a file with generic write access
    SafeFileHandle fileHandle = CreateFile(formattedName,
        EFileAccess.GenericWrite, EFileShare.None, IntPtr.Zero,
        ECreationDisposition.CreateAlways, 0, IntPtr.Zero);

    // Check for errors
    int lastWin32Error = Marshal.GetLastWin32Error();
    if (fileHandle.IsInvalid) {
        throw new System.ComponentModel.Win32Exception(lastWin32Error);
    }

    // Pass the file handle to FileStream. FileStream will close the
    // handle
    using (FileStream fs = new FileStream(fileHandle,
                                    FileAccess.Write)) {
        fs.WriteByte(80);
        fs.WriteByte(81);
        fs.WriteByte(83);
        fs.WriteByte(84);
    }
}

另外,我建议你使用Path.Combine 而不是path + "\\" + subpath

【讨论】:

  • 谢谢。但 .net 确实 支持自 .net 4.6.2 以来的长路径。至于Path.Combine,a)这是示例中最快的。 b) 虽然我普遍同意使用方法更好 - Path.Combine 是一个例外。见stackoverflow.com/questions/53102/…
【解决方案3】:

我有一个经验:

1) 在桌面应用程序 (.NET 4.7) 中你不需要更多,然后使用前缀为 @"\?\ 的路径名(不需要清单,在 app.confing 中设置 UseLegacyPathHandling),一切正常

2) 在 web 应用程序中你必须设置这个:

  bool legacyPaths;
  if (AppContext.TryGetSwitch("Switch.System.IO.UseLegacyPathHandling", out legacyPaths) && legacyPaths)
  {
    var switchType = Type.GetType("System.AppContextSwitches"); 
    if (switchType != null)
    {
      AppContext.SetSwitch("Switch.System.IO.UseLegacyPathHandling", false);   
      var legacyField = switchType.GetField("_useLegacyPathHandling", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
      legacyField?.SetValue(null, (Int32)0); 
      AppContext.TryGetSwitch("Switch.System.IO.UseLegacyPathHandling", out legacyPaths);
      Assert.IsFalse(legacyPaths, "Long pathnames are not supported!");
    }
  }

希望对你有所帮助!

【讨论】:

  • 你的意思是前缀@"\\?\"(注意字符串开头的两个斜杠)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-06-16
  • 1970-01-01
  • 1970-01-01
  • 2018-08-16
  • 1970-01-01
  • 2010-09-18
  • 1970-01-01
相关资源
最近更新 更多