长路径的问题 LongPath
topcss opened this issue · 1 comments
topcss commented
using Microsoft.Win32.SafeHandles;
using System;
using System.ComponentModel;
using System.IO;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
/// <summary>
/// 超长文件路径的处理
/// 参照
/// https://github.com/Dennis-Koch/ambeth/blob/9a13deba2f588b3826a3bfb75906680382fff8fe/ambeth/Ambeth.Util/ambeth/util/LongPath.cs
/// </summary>
public class LongPath
{
#region win32 api 处理错误消息
//// 这样使用,抛出异常
//int errCode = Marshal.GetLastWin32Error();
//throw new Exception(GetSysErrMsg(errCode));
[DllImport("kernel32.dll", EntryPoint = "GetProcAddress", SetLastError = true)]
public static extern IntPtr GetProcAddress(int hModule, [MarshalAs(UnmanagedType.LPStr)] string lpProcName);
[DllImport("kernel32.dll", EntryPoint = "FreeLibrary", SetLastError = true)]
public static extern bool FreeLibrary(int hModule);
[DllImport("Kernel32.dll")]
public extern static int FormatMessage(int flag, ref IntPtr source, int msgid, int langid, ref string buf, int size, ref IntPtr args);
/// <summary>
/// 获取系统错误信息描述
/// </summary>
/// <param name="errCode">系统错误码</param>
/// <returns></returns>
public static string GetSysErrMsg(int errCode)
{
IntPtr tempptr = IntPtr.Zero;
string msg = null;
FormatMessage(0x1300, ref tempptr, errCode, 0, ref msg, 255, ref tempptr);
return msg;
}
#endregion
#region win32 define
[StructLayout(LayoutKind.Sequential)]
public class SECURITY_ATTRIBUTES
{
public int nLength;
public IntPtr pSecurityDescriptor;
public int bInheritHandle;
}
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
internal static extern bool CopyFile(string lpExistingFileName, string lpNewFileName, bool bFailIfExists);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool CreateDirectory(String lpPathName, SECURITY_ATTRIBUTES lpSecurityAttributes);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern SafeFileHandle CreateFile(String lpFileName, FileAccess dwDesiredAccess,
FileShare dwShareMode,
SECURITY_ATTRIBUTES lpSecurityAttributes,
FileMode creationDisposition,
FileAttributes dwFlagsAndAttributes,
IntPtr hTemplateFile);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern bool DeleteFile(String lpFileName);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern FileAttributes GetFileAttributes(String lpPathName);
/// <summary>
/// Verifies that a path is a valid directory.
/// </summary>
/// <param name="pszPath">A pointer to a null-terminated string of maximum length MAX_PATH that contains the path to verify.</param>
/// <returns>Returns (BOOL)FILE_ATTRIBUTE_DIRECTORY if the path is a valid directory; otherwise, FALSE.</returns>
[DllImport("shlwapi.dll", EntryPoint = "PathIsDirectoryW", SetLastError = true, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool PathIsDirectory([MarshalAs(UnmanagedType.LPTStr)]string pszPath);
[DllImport("shell32.dll", CharSet = CharSet.Unicode)]
public static extern int SHCreateDirectoryEx(IntPtr hwnd, string pszPath, IntPtr psa);
#endregion
#region 长地址的处理
// http://www.cplusplus.com/forum/lounge/110597/
// max # of characters we support using the "\\?\" syntax
// (0x7FFF + 1 for NULL terminator)
const int PATHCCH_MAX_CCH = 0x8000;
const string LONG_PATH_ID = @"\\?\";
const string UNC_PREFIX = @"\\";
const string UNC_LONG_ID = @"\\?\UNC\";
const string CUR_DIR_REL_PATH_ID = @".\";
const char WILDCAR_CHAR_ASTER = '*';
const char WILDCAR_CHAR_QUEMARK = '?';
const string DIR_DOWN = "..";
const string DIR_UP = ".";
static Regex RegIP = new Regex(@"[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}"); // IP表达式
static Regex RegUNC = new Regex(@"\\[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}"); // UNC表达式
/// <summary>
/// 获取长路径的表达方式
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
static string GetLongPath(string path)
{
path = path.Replace('/', Path.DirectorySeparatorChar);
if (RegIP.Match(path).Success)// 包含 IP 地址的情况
{
if (!path.StartsWith(UNC_LONG_ID))// 还不存在UNC地址处理
{
if (RegUNC.Match(path).Success)// 如果双斜杠开头的情况
{
return UNC_LONG_ID + path.Substring(2);
}
else
{
return UNC_LONG_ID + path;// 如果直接IP的情况
}
}
}
else
{
if (!path.StartsWith(LONG_PATH_ID))// 不存在长地址处理的情况
{
return LONG_PATH_ID + path;
}
}
return path;
}
#endregion
#region 自定义方法
public static SafeFileHandle CreateFile(String fileName, FileAccess fileAccess, FileShare fileShare, FileMode fileMode)
{
String formattedName = GetLongPath(fileName);
int fileNameIndex = formattedName.LastIndexOf(Path.DirectorySeparatorChar);
if (fileNameIndex >= 0)
{
String dirName = formattedName.Substring(0, fileNameIndex);
CreateDir(dirName);
}
SafeFileHandle fileHandle = CreateFile(formattedName, fileAccess, fileShare, null, fileMode, 0, IntPtr.Zero);
if (fileHandle.IsInvalid)
{
int lastWin32Error = Marshal.GetLastWin32Error();
throw new Win32Exception(lastWin32Error);
}
return fileHandle;
}
public static void DeleteDir(String dir)
{
FileAttributes fileAttributes = GetFileAttributes(dir);
if (fileAttributes == 0)
{
return;
}
if (DeleteFile(dir))
{
return;
}
}
/// <summary>
/// 复制超长路径的文件
/// </summary>
/// <param name="existingFileName"></param>
/// <param name="newFileName"></param>
/// <returns></returns>
public static bool CopyFile(string existingFileName, string newFileName)
{
existingFileName = GetLongPath(existingFileName);
newFileName = GetLongPath(newFileName);
// 如果目标文件夹不存在则创建
int fileNameIndex = newFileName.LastIndexOf(Path.DirectorySeparatorChar);
if (fileNameIndex >= 0)
{
string dir = newFileName.Substring(0, fileNameIndex);
CreateDir(dir);
}
// 然后调用unicode版本的Windows API
return CopyFile(existingFileName, newFileName, false);
}
/// <summary>
/// 文件或文件夹是否存在
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public static bool IsExists(string path)
{
return GetFileAttributes(path) >= 0;
}
private static readonly Object StaticLockObj = new object();
/// <summary>
/// 创建长路径的多级目录
/// </summary>
/// <param name="dir"></param>
/// <returns></returns>
public static void CreateDir(String dir)
{
lock (StaticLockObj)
{
dir = GetLongPath(dir);
// 存在就返回
FileAttributes fileAttributes = GetFileAttributes(dir);
if (fileAttributes >= 0) { return; }
// 若不存在,就创建,
if (CreateDirectory(dir, null)) { return; }
// 创建失败,则尝试创建上级目录
int fileNameIndex = dir.LastIndexOf(Path.DirectorySeparatorChar);
if (fileNameIndex >= 0)
{
String parentDir = dir.Substring(0, fileNameIndex);
// 若上级目录不存在则创建
if (!IsExists(parentDir))
{
// 根路径不参加上级目录:c: -> c: | \\192.168.1.1\ -> \
if (parentDir.EndsWith(":") || @"\\?\\" == parentDir) { }
else
{
CreateDir(parentDir);
}
}
if (!CreateDirectory(dir, null))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
}
}
/// <summary>
/// 拷贝文件夹
/// </summary>
/// <param name="srcdir"></param>
/// <param name="desdir"></param>
public static void CopyDirectory(string srcdir, string desdir)
{
string folderName = srcdir.Substring(srcdir.LastIndexOf("\\") + 1);
string desfolderdir = desdir + "\\" + folderName;
if (desdir.LastIndexOf("\\") == (desdir.Length - 1))
{
desfolderdir = desdir + folderName;
}
string[] filenames = Directory.GetFileSystemEntries(srcdir);
foreach (string file in filenames)// 遍历所有的文件和目录
{
FileAttributes fileAttributes = GetFileAttributes(file);
if (fileAttributes >= 0)
{
if (PathIsDirectory(file))
{
string currentdir = desfolderdir + "\\" + file.Substring(file.LastIndexOf("\\") + 1);
CreateDir(currentdir);
CopyDirectory(file, desfolderdir);
}
else
{
string srcfileName = file.Substring(file.LastIndexOf("\\") + 1);
srcfileName = desfolderdir + "\\" + srcfileName;
if (!CopyFile(file, srcfileName))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
}
}
}
#endregion
}
topcss commented