programing

System.IO.PathTooLongException을 피하는 방법?

projobs 2021. 1. 16. 09:12
반응형

System.IO.PathTooLongException을 피하는 방법?


우리는 끊임없이이 문제에 부딪칩니다 ...

예:

다른 디렉터리 나 UNC 공유에 복사하려는 파일이 있고 경로 길이가 248을 초과하면 (잘못되지 않은 경우) PathTooLongException이 발생합니다. 이 문제에 대한 해결 방법이 있습니까?

추신 :이 경로를 더 긴 문자 집합으로 설정하는 레지스트리 설정이 있습니까?


Jeremy Kuhne의 블로그 에서 설명한대로 .NET Framework 4.6.2MAX_PATH이전 버전과의 호환성을 깨지 않고 가능한 경우 제한을 제거합니다 .


시도해보기 : Delimon.Win32.I O 라이브러리 (V4.0) 이 라이브러리는 .NET Framework 4.0에 작성되었습니다.

Delimon.Win32.IO는 System.IO의 기본 파일 기능을 대체하며 최대 32,767 자 까지 파일 및 폴더 이름을 지원합니다 .

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

이 라이브러리는 긴 경로 및 파일 이름을 사용하는 .NET Framework의 한계를 극복하기 위해 특별히 작성되었습니다. 이 라이브러리를 사용하면 System.IO 네임 스페이스에서 액세스 할 수없는 파일 및 폴더를 프로그래밍 방식으로 찾아보고, 액세스하고, 쓰고, 삭제할 수 있습니다.

용법

  1. 먼저 Delimon.Win32.IO.dll에 대한 참조를 프로젝트에 추가합니다 (Delimon.Win32.IO.dll 파일 찾아보기).

  2. 코드 파일에 "using Delimon.Win32.IO"를 추가하십시오.

  3. System.IO로 작업하는 것처럼 일반 파일 및 디렉터리 개체를 사용하십시오.


이것은 BCL 팀에 의해 심층적으로 논의되었습니다. 블로그 항목을 참조하십시오.

본질적으로 .Net 코드 내에서 이를 수행 하고 BCL을 고수 할 방법이 없습니다 . 너무 많은 함수가 경로 이름을 정규화 할 수있는 능력에 의존합니다 (이는 MAX_PATH가 준수 될 것으로 예상하는 함수 사용을 즉시 트리거 함).

"\\? \"구문을 지원하는 모든 win32 함수를 래핑 할 수 있습니다. 이러한 기능을 사용하여 긴 경로 인식 기능을 구현할 수 있지만 이는 번거 롭습니다.

수많은 도구 (explorer [1] 포함)는 긴 경로 이름을 처리 할 수 ​​없으므로 결과 파일 시스템과의 모든 상호 작용이 라이브러리 (또는 제한된 수의 도구)를 통과하는 것에 만족하지 않는 한이 경로로 이동하는 것은 바람직하지 않습니다. robocopy처럼 처리하도록 제작되었습니다)

귀하의 특정 요구에 대한 답변으로 robocopy를 직접 사용하는 것이이 작업을 수행하기에 충분한 지 조사 할 것입니다.

[1] Vista에는 내부적으로 멋진 이름을 변경하여 문제를 완화 할 수있는 방법이 있지만 기껏해야 깨지기 쉽습니다)


내가 본 해결 방법은 단 하나뿐입니다. 도움이 될 수 있습니다.

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


ANSI 버전의 Windows API에 문제가 있습니다. 주의 깊게 테스트해야하는 한 가지 솔루션은 Windows API의 유니 코드 버전을 강제로 사용하는 것입니다. \\?\쿼리되는 경로 앞에 " "을 추가하면됩니다 .

해결 방법을 포함한 훌륭한 정보는 ".NET의 긴 경로"라는 제목의 Microsoft BCL (Base Class Library) 팀의 다음 블로그 게시물에서 찾을 수 있습니다.


"subst"명령을 사용하여 문제를 해결했습니다 ... http://www.techrepublic.com/article/mapping-drive-letters-to-local-folders-in-windows-xp/5975262


이 라이브러리가 도움이 될 수 있습니다. Zeta Long Paths


내 드라이브 매핑 솔루션은 아래에 나열된 "NetWorkDrive.cs"및 "NetWorkUNCPath.cs"를 사용하여 잘 작동하고 안정적입니다.

테스트 예 :

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.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.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;
        }
    }
}

}

나를 위해 C #에서 이것은 해결 방법입니다.

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

ReferenceURL : https://stackoverflow.com/questions/530109/how-to-avoid-system-io-pathtoolongexception

반응형