Reputation: 1636
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?
PS: Is there any registry setting to set this path to a longer char set?
Upvotes: 62
Views: 73169
Reputation: 7693
Try This : Delimon.Win32.IO Library (V4.0) This library is written on .NET Framework 4.0
Delimon.Win32.IO
replaces basic file functions of System.IO
and supports File & Folder names up to up to 32,767 Characters.
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
Usage
First add a reference to the Delimon.Win32.IO.dll to your project (Browse to the Delimon.Win32.IO.dll file)
In your Code File add "using Delimon.Win32.IO"
Use normal File & Directory Objects as if you are working with System.IO
Upvotes: 12
Reputation: 3715
I have been surprise to discover that solution of Jason work perfectly well on my PC using Visual Studio 2019 and .Net Framework 4.7.2
Initially, I have written something similar to this code
Dim sBaseDir = "D:\Documents\+Informatique\Application\@Visual Basic.NET\GetTestAchatsPosts\Site\communication-multimedia\ordinateurs"
Dim sLastDir = "2638.pc-acer-ne-charge-plus-malgre-un-retour-en-garantie-reviens-charge-mais-ne-charge-toujours-pas-garantie-passee-plus-dintervention-gra"
System.IO.Directory.CreateDirectory(sBaseDir & "\" & sLastDir)
This code generates an error 429-PathTooLongException
I tested in creating only last directory
System.IO.Directory.SetCurrentDirectory(sBaseDir)
System.IO.Directory.CreateDirectory(sLastDir)
This code return same error 429-PathTooLongException
I tested then using a short base directory to see if problem is linked to length of last directory or full directory
System.IO.Directory.SetCurrentDirectory("D:\Documents")
System.IO.Directory.CreateDirectory(sLastDir)
This code work. But directory is created in bad location.
I have then tested is reducing name's length of last directory.
System.IO.Directory.SetCurrentDirectory(sBaseDir)
System.IO.Directory.CreateDirectory(sLastDir.Substring(0, 100))
This code works, but last directory name is reducted !
I have then tested using proposal of Jason in 2 steps
System.IO.Directory.SetCurrentDirectory(sBaseDir)
System.IO.Directory.CreateDirectory("\\?\" & sLastDir)
This code crashes indicating that directory syntax is not correct !
I have then also tested same syntax in adding "." string
System.IO.Directory.SetCurrentDirectory(sBaseDir)
System.IO.Directory.CreateDirectory("\\?\.\" & sLastDir)
This code crashes indicating that directory syntax is not correct !
I have then tested using a simple command prefixing full directory with "\\?"
System.IO.Directory.CreateDirectory("\\?\" & sBaseDir & "\" & sLastDir)
This code work perfectly and it is the best solution for me.
The only inconvenience is that directory must be a full directory.
Warning ! You cannot not mix "\" and "/" characters !
I have written following code
sQuestionDir = "\\?\" & sArticleDir & "/" & sQuestionDir
If Not System.IO.Directory.Exists(sQuestionDir) Then
System.IO.Directory.CreateDirectory(sQuestionDir)
End If
and program crashes when executing CreateDirectory()
function !
The correct code that is working is
sQuestionDir = "\\?\" & sArticleDir & "\" & sQuestionDir
If Not System.IO.Directory.Exists(sQuestionDir) Then
System.IO.Directory.CreateDirectory(sQuestionDir)
End If
I hope that all these tests can help others to solve their problem.
Upvotes: 0
Reputation: 385
My Drive-Mapping solution works fine and stable using „NetWorkDrive.cs“ and „NetWorkUNCPath.cs“ which are listed below.
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.cs souce code:
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 source code:
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;
}
}
}
}
Upvotes: 2
Reputation: 10931
As described in Jeremy Kuhne's blog, .NET Framework 4.6.2 removes the MAX_PATH
limitation where possible, without breaking backwards compatibility.
Upvotes: 29
Reputation: 8616
In C# for me this is a workaround:
/*make long path short by setting it to like cd*/
string path = @"\\godDamnLong\Path\";
Directory.SetCurrentDirectory(path);
Upvotes: -2
Reputation: 21
I used the "subst" command to work around the problem... http://www.techrepublic.com/article/mapping-drive-letters-to-local-folders-in-windows-xp/5975262
Upvotes: 2
Reputation: 1149
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.
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":
Upvotes: 3
Reputation: 36458
This has been discussed in depth by the BCL team, see the blog entries
In essence there is no way to do this within .Net code and 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).
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.
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 all interaction with the resulting file system goes through your library (or the limited number of tools that are built to handle it like robocopy)
In answer to your specific need I would investigate whether the use of robocopy directly would be sufficient to perform this task.
[1] Vista has ways to mitigate the issue with some fancy renaming under the hood but this is fragile at best)
Upvotes: 9
Reputation: 14196
Only 1 workaround that I've seen on this one... this might be helpful
http://www.codeproject.com/KB/files/LongFileNames.aspx
Upvotes: 4