C# 如何避免 System.IO.PathTooLongException?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/530109/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 06:58:09  来源:igfitidea点击:

How to avoid System.IO.PathTooLongException?

c#.net

提问by theKing

We constantly run into this problem...

我们经常遇到这个问题......

Example:

例子:

if I have a file that I want to copy it into an another directory or UNC share and if the length of the path exceeds 248 (if I am not mistaken), then it throws PathTooLongException. Is there any workaround to this problem?

如果我有一个文件想要将其复制到另一个目录或 UNC 共享中,并且路径的长度超过 248(如果我没有记错的话),那么它会抛出 PathTooLongException。这个问题有什么解决方法吗?

PS: Is there any registry setting to set this path to a longer char set?

PS:是否有任何注册表设置可以将此路径设置为更长的字符集?

采纳答案by Mark Hurd

As described in Jeremy Kuhne's blog, .NET Framework 4.6.2removes the MAX_PATHlimitation where possible, without breaking backwards compatibility.

正如 Jeremy Kuhne 的博客 中所述,.NET Framework 4.6.2MAX_PATH在不破坏向后兼容性的情况下尽可能消除了限制。

回答by Brandon

Only 1 workaround that I've seen on this one... this might be helpful

我在这个上看到的只有一种解决方法......这可能会有所帮助

http://www.codeproject.com/KB/files/LongFileNames.aspx

http://www.codeproject.com/KB/files/LongFileNames.aspx

回答by ShuggyCoUk

This has been discussed in depth by the BCL team, see the blog entries

BCL 团队对此进行了深入讨论,请参阅博客条目

In essence there is no way to do this within .Net codeand stick to the BCL. Too many functions rely on being able to canonicalize the path name (which immediately triggers the use of functions expecting MAX_PATH to be obeyed).

本质上,没有办法在 .Net 代码中做到这一点并坚持 BCL。太多的函数依赖于能够规范化路径名(这会立即触发期望遵守 MAX_PATH 的函数的使用)。

You could wrap all the win32 functions that support the "\\?\" syntax, with these you would be able to implement a suite of long path aware functionality but this would be cumbersome.

您可以包装所有支持“\\?\”语法的 win32 函数,通过这些函数,您将能够实现一套长路径感知功能,但这会很麻烦。

Since a vast number of tools (including explorer[1]) cannot handle long path names it is inadvisable to go down this route unless you are happy that allinteraction with the resulting file system goes through your library (or the limited number of tools that are built to handle it like robocopy)

由于大量工具(包括资源管理器 [1])无法处理长路径名,因此不建议沿着这条路线走下去,除非您很高兴与生成的文件系统的所有交互都通过您的库(或有限数量的工具)旨在像 robocopy 一样处理它)

In answer to your specific need I would investigate whether the use of robocopy directly would be sufficient to perform this task.

为了满足您的特定需求,我将调查直接使用 robocopy 是否足以执行此任务。

[1] Vista has ways to mitigate the issue with some fancy renaming under the hood but this is fragile at best)

[1] Vista 有办法通过一些花哨的重命名来缓解这个问题,但这充其量是脆弱的)

回答by Jason S

The problem is with the ANSI versions of the Windows APIs. One solution that needs to be tested carefully is to force the use of Unicode versions of the Windows API. This can be done by prepending "\\?\" to the path being queried.

问题在于 Windows API 的 ANSI 版本。需要仔细测试的一种解决方案是强制使用 Unicode 版本的 Windows API。这可以通过\\?\在要查询的路径前加上“ ”来完成。

Great information, including work arounds can be found in the following blog posts from Microsoft's Base Class Library (BCL) Team titled "Long Paths in .NET":

可以在以下来自 Microsoft 基类库 (BCL) 团队的名为“.NET 中的长路径”的博客文章中找到包括变通方法在内的重要信息:

回答by A. Harald

回答by CodingYourLife

In C# for me this is a workaround:

在 C# 中,对我来说这是一种解决方法:

/*make long path short by setting it to like cd*/
string path = @"\godDamnLong\Path\";
Directory.SetCurrentDirectory(path);

回答by JeffRSon

This library might be helpful: Zeta Long Paths

这个库可能会有所帮助: Zeta Long Paths

回答by Elshan

Try This : Delimon.Win32.I?O Library (V4.0)This Libarary is written on .NET Framework 4.0

试试这个:Delimon.Win32.I?O 库 (V4.0)这个库是在 .NET Framework 4.0 上编写的

Delimon.Win32.IO replaces basic file functions of System.IO and supports File & Folder names up to up to 32,767Characters.

Delimon.Win32.IO 取代了 System.IO 的基本文件功能,支持最多32,767 个字符的文件和文件夹名称。

https://gallery.technet.microsoft.com/DelimonWin32IO-Library-V40-7ff6b16c

https://gallery.technet.microsoft.com/DelimonWin32IO-Library-V40-7ff6b16c

This Library is written specifically to overcome the limitation of the .NET Framework to use long Path & File Names. With this Library you can programmatically browse, access, write, delete, etc Files and Folders that are not accessible by the System.IO namespace.Library

该库是专门为克服 .NET Framework 使用长路径和文件名的限制而编写的。使用此库,您可以以编程方式浏览、访问、写入、删除等 System.IO 命名空间无法访问的文件和文件夹。

Usage

用法

  1. First add a reference to the Delimon.Win32.IO.dll to your project (Browse to the Delimon.Win32.IO.dll file)

  2. In your Code File add "using Delimon.Win32.IO"

  3. Use normal File & Directory Objects as if you are working with System.IO

  1. 首先在您的项目中添加对 Delimon.Win32.IO.dll 的引用(浏览至 Delimon.Win32.IO.dll 文件)

  2. 在您的代码文件中添加“使用 Delimon.Win32.IO”

  3. 像使用 System.IO 一样使用普通的文件和目录对象

回答by alex

My Drive-Mapping solution works fine and stable using ?NetWorkDrive.cs“ and ?NetWorkUNCPath.cs“ which are listed below.

我的 Drive-Mapping 解决方案使用下面列出的“NetWorkDrive.cs”和“NetWorkUNCPath.cs”运行良好且稳定。

Test example:

测试示例:

if (srcFileName.Length > 260)
{
   string directoryName = srcFileName.Substring(0, srcFileName.LastIndexOf('\'));

   var uncName = GetUNCPath(srcFileName.Substring(0, 2)) + directoryName.Substring(2);

   using (NetWorkDrive nDrive = new NetWorkDrive(uncName))
   {
     drvFileName = nDrive.FullDriveLetter + Path.GetFileName(sourceFileName)
     File.Copy(drvFileName, destinationFileName, true);
   }
}
else
{
   File.Copy(srcFileName, destinationFileName, true);
}

NetWorkDrive.cssouce code:

NetWorkDrive.cs源代码:

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.ComponentModel;
namespace SeekCopySupportTool.Business
{

public class NetWorkDrive : IDisposable
{
    #region private fields
    private string m_DriveLetter = string.Empty;
    private string m_FullDriveLetter = string.Empty;
    private bool m_Disposed = false;
    //this list specifies the drive-letters, whitch will be used to map networkfolders
    private string[] possibleDriveLetters = new string[] 
    { 
        "G:\", 
        "H:\", 
        "I:\", 
        "J:\", 
        "K:\", 
        "L:\", 
        "M:\", 
        "N:\", 
        "O:\", 
        "P:\", 
        "Q:\", 
        "R:\", 
        "S:\", 
        "T:\", 
        "U:\", 
        "V:\", 
        "W:\", 
        "X:\", 
        "Y:\", 
        "Z:\" 
    };
    #endregion

    #region public properties
    public string DriveLetter
    {
        get { return m_DriveLetter; }
        set { m_DriveLetter = value; }
    }
    public string FullDriveLetter
    {
        get { return m_FullDriveLetter; }
        set { m_FullDriveLetter = value; }
    }
    #endregion

    #region .ctor
    public NetWorkDrive(string folderPath)
    {
        m_FullDriveLetter = MapFolderAsNetworkDrive(folderPath);
        if (string.IsNullOrEmpty(m_FullDriveLetter))
        {
            throw new Exception("no free valid drive-letter found");
        }
        m_DriveLetter = m_FullDriveLetter.Substring(0,2);
    }
    #endregion

    #region private methods
    /// maps a given folder to a free drive-letter (f:\)
    /// <param name="folderPath">the folder to map</param>
    /// <returns>the drive letter in this format: "(letter):\" -> "f:\"</returns>
    /// <exception cref="Win32Exception">if the connect returns an error</exception>
    private string MapFolderAsNetworkDrive(string folderPath)
    {
        string result = GetFreeDriveLetter();
        NETRESOURCE myNetResource = new NETRESOURCE();
        myNetResource.dwScope = ResourceScope.RESOURCE_GLOBALNET;
        myNetResource.dwType = ResourceType.RESOURCETYPE_ANY;
        myNetResource.dwDisplayType = ResourceDisplayType.RESOURCEDISPLAYTYPE_SERVER;
        myNetResource.dwUsage = ResourceUsage.RESOURCEUSAGE_CONNECTABLE;
        myNetResource.lpLocalName = result.Substring(0,2);
        myNetResource.lpRemoteName = folderPath;
        myNetResource.lpProvider = null;
        int errorcode = WNetAddConnection2(myNetResource, null, null, 0);
        if(errorcode != 0)
        {
            throw new Win32Exception(errorcode);
        }
        return result;
    }
    private void DisconnectNetworkDrive()
    {
        int CONNECT_UPDATE_PROFILE = 0x1;
        int errorcode = WNetCancelConnection2(m_DriveLetter, CONNECT_UPDATE_PROFILE, true);
        if (errorcode != 0)
        {
            throw new Win32Exception(errorcode);
        }
    }

    private string GetFreeDriveLetter()
    {
        //first get the existing driveletters
        const int size = 512;
        char[] buffer = new char[size];
        uint code = GetLogicalDriveStrings(size, buffer);
        if (code == 0)
        {                
            return "";
        }
        List<string> list = new List<string>();
        int start = 0;
        for (int i = 0; i < code; ++i)
        {
            if (buffer[i] == 0)
            {
                string s = new string(buffer, start, i - start);
                list.Add(s);
                start = i + 1;
            }
        }            
        foreach (string s in possibleDriveLetters)
        {                
            if (!list.Contains(s))
            {
                return s;
            }
        }
        return null;
    }        
    #endregion

    #region dll imports
    /// <summary>
    /// to connect to a networksource
    /// </summary>
    /// <param name="netResource"></param>
    /// <param name="password">null the function uses the current default password associated with the user specified by the username parameter ("" the function does not use a password)</param>
    /// <param name="username">null the function uses the default user name (The user context for the process provides the default user name)</param>
    /// <param name="flags"></param>
    /// <returns></returns>
    [DllImport("mpr.dll")]
    //public static extern int WNetAddConnection2(ref NETRESOURCE netResource, string password, string username, int flags);
    private static extern int WNetAddConnection2([In] NETRESOURCE netResource, string password, string username, int flags);
    /// <summary>
    /// to disconnect the networksource
    /// </summary>
    /// <param name="lpName"></param>
    /// <param name="dwFlags"></param>
    /// <param name="bForce"></param>
    /// <returns></returns>
    [DllImport("mpr.dll")]
    private static extern int WNetCancelConnection2(string lpName, Int32 dwFlags, bool bForce);
    /// <param name="nBufferLength"></param>
    /// <param name="lpBuffer"></param>
    /// <returns></returns>
    [DllImport("kernel32.dll")]
    private static extern uint GetLogicalDriveStrings(uint nBufferLength, [Out] char[] lpBuffer);
    #endregion

    #region enums/structs
    /// <example>
    /// NETRESOURCE myNetResource = new NETRESOURCE();
    /// myNetResource.dwScope = 2;
    /// myNetResource.dwType = 1;
    /// myNetResource.dwDisplayType = 3;
    /// myNetResource.dwUsage = 1;
    /// myNetResource.LocalName = "z:";
    /// myNetResource.RemoteName = @"\servername\sharename";
    /// myNetResource.Provider = null;
    /// </example>
    [StructLayout(LayoutKind.Sequential)]
    public class NETRESOURCE
    {
        public ResourceScope dwScope = 0;
        public ResourceType dwType = 0;
        public ResourceDisplayType dwDisplayType = 0;
        public ResourceUsage dwUsage = 0;
        public string lpLocalName = null;
        public string lpRemoteName = null;
        public string lpComment = null;
        public string lpProvider = null;
    };
    public enum ResourceScope : int
    {
        RESOURCE_CONNECTED = 1,
        RESOURCE_GLOBALNET,
        RESOURCE_REMEMBERED,
        RESOURCE_RECENT,
        RESOURCE_CONTEXT
    };
    public enum ResourceType : int
    {
        RESOURCETYPE_ANY,
        RESOURCETYPE_DISK,
        RESOURCETYPE_PRINT,
        RESOURCETYPE_RESERVED
    };
    public enum ResourceUsage
    {
        RESOURCEUSAGE_CONNECTABLE = 0x00000001,
        RESOURCEUSAGE_CONTAINER = 0x00000002,
        RESOURCEUSAGE_NOLOCALDEVICE = 0x00000004,
        RESOURCEUSAGE_SIBLING = 0x00000008,
        RESOURCEUSAGE_ATTACHED = 0x00000010,
        RESOURCEUSAGE_ALL = (RESOURCEUSAGE_CONNECTABLE | RESOURCEUSAGE_CONTAINER | RESOURCEUSAGE_ATTACHED),
    };
    public enum ResourceDisplayType
    {
        RESOURCEDISPLAYTYPE_GENERIC,
        RESOURCEDISPLAYTYPE_DOMAIN,
        RESOURCEDISPLAYTYPE_SERVER,
        RESOURCEDISPLAYTYPE_SHARE,
        RESOURCEDISPLAYTYPE_FILE,
        RESOURCEDISPLAYTYPE_GROUP,
        RESOURCEDISPLAYTYPE_NETWORK,
        RESOURCEDISPLAYTYPE_ROOT,
        RESOURCEDISPLAYTYPE_SHAREADMIN,
        RESOURCEDISPLAYTYPE_DIRECTORY,
        RESOURCEDISPLAYTYPE_TREE,
        RESOURCEDISPLAYTYPE_NDSCONTAINER
    };
    #endregion

    #region IDisposable Members
    public void Dispose()
    {
        Dispose(true);
    }
    #endregion

    #region overrides/virtuals
    public override string ToString()
    {
        return m_FullDriveLetter;
    }
    /// <param name="disposing"></param>
    protected virtual void Dispose(bool disposing)
    {
        if (!m_Disposed)
        {
            if (disposing)
            {
                DisconnectNetworkDrive();
            }
            m_Disposed = true;
        }
    }
    #endregion
}

}

NetWorkUNCPath.cssource code:

NetWorkUNCPath.cs源代码:

using System;
using System.Management;
namespace SeekCopySupportTool.Business
{

public class NetWorkUNCPath
{
    // get UNC path
    public static string GetUNCPath(string path)
    {
        if (path.StartsWith(@"\"))
        {
            return path;
        }

        ManagementObject mo = new ManagementObject();
        mo.Path = new ManagementPath(String.Format("Win32_LogicalDisk='{0}'", path));

        // DriveType 4 = Network Drive
        if (Convert.ToUInt32(mo["DriveType"]) == 4)
        {
            return Convert.ToString(mo["ProviderName"]);
        }
        // DriveType 3 = Local Drive
        else if (Convert.ToUInt32(mo["DriveType"]) == 3)
        {
            return "\\" + Environment.MachineName + "\" + path.Substring(0,1) + "$";
        }
        else
        {
            return path;
        }
    }
}

}