.NET 4.0 中超长路径超长文件名的问题
1. 昨天开发中遇到一个问题,场景如下:
客户端从服务器下载一个文件并解压,客户端在使用后需要做清理操作(删除解压出来的文件),结果删除失败,抛出如下异常:
The specified path, file name, or both are too long. The fully qualified file name must be less than 260 characters, and the directory name must be less than 248 characters.
手动在资源管理器也无法删除,各种搜索之后,找到了解决的办法,直接贴链接吧:
共有三部分,第一部分再扯蛋,第二部分给了个处理实际问题的workaround(我就用的这个),第三部分还是扯蛋
https://blogs.msdn.microsoft.com/bclteam/2007/02/13/long-paths-in-net-part-1-of-3-kim-hamilton/
https://blogs.msdn.microsoft.com/bclteam/2007/03/26/long-paths-in-net-part-2-of-3-long-path-workarounds-kim-hamilton/
https://blogs.msdn.microsoft.com/bclteam/2008/07/07/long-paths-in-net-part-3-of-3-redux-kim-hamilton/
这家伙说长路径在.NET 4.6.2里已经支持了,但我还在用4.0,并且.NET 4.6.2是在Win10上默认是不支持的,需要配置开启:
https://blogs.msdn.microsoft.com/jeremykuhne/2016/07/30/net-4-6-2-and-long-paths-on-windows-10/
2. 将上面提到的“第二部分”文章中的代码copy过来(摘自:https://blogs.msdn.microsoft.com/bclteam/2007/03/26/long-paths-in-net-part-2-of-3-long-path-workarounds-kim-hamilton/)
For now, our suggested workaround for users that encounter the MAX_PATH issue is to rearrange directories so that the names are shorter. This may sound like a cop out, but this is ultimately easier on users because of (1) limited tool support (i.e. Explorer doesn’t work with long paths) and (2) getting the full System.IO functionality for long paths results in a significant code delta for users. However, if you really want to work with paths longer than MAX_PATH you can, and this part of the series demonstrates how.
Recall from Part 1 that if you prefix the path with \\?\ and use the Unicode versions of the Win32 APIs, you can use paths up to 32K characters in length. These code samples will use that fact to show a few common file operations with long path files.
Deleting a File
Let’s start with the simplest example – deleting a file. Recall that Explorer won’t let you delete long path files, so you’ll need this to clean up the files you create in the subsequent section.
First, we look at the Win32 API docs for DeleteFile and confirm that it supports long paths. DeleteFile does according to this comment:
In the ANSI version of this function, the name is limited to MAX_PATH characters. To extend this limit to 32,767 wide characters, call the Unicode version of the function and prepend “\\?\” to the path. For more information, see Naming a File.
So we specify the PInvoke signature:
using System;
using System.Runtime.InteropServices;
[DllImport(“kernel32.dll”, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool DeleteFile(string lpFileName);
And then all we have to do is call it with a file name prefixed by \\?\:
// This code snippet is provided under the Microsoft Permissive License.
public static void Delete(string fileName) {
string formattedName = @”\\?\” + fileName;
DeleteFile(formattedName);
}
For some tasks such as deleting, moving, and renaming a file, you simply PInvoke to the Win32 APIs and you’re done. For other cases, such as writing to a file, you mix the Win32 calls with the System.IO APIs.
Writing to or Reading from a file
First you need to create or open a file with the Win32 CreateFile function. CreateFile returns a file handle, which you can pass to a System.IO.FileStream constructor. Then you simply work with the FileStream as normal.
// 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);
}
}
This sample shows writing a few bytes, but once you have the FileStream, you can do anything you would normally do: wrap it in a BinaryWriter, etc.
If you wanted to open a file instead of creating it, you would change the creation disposition from CreateAlways to OpenExisting. If you wanted to read a file instead of writing, you would change the file access from GenericWrite to GenericRead.
See the end of the article for definitions of the enums and structs in this example.
Finding Files and Directories
So far the workarounds have been fairly minor, but suppose you want to get the files and folders contained in a folder. Unfortunately, now you’re starting to rewrite the .NET libraries.
// This code snippet is provided under the Microsoft Permissive License.
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
[DllImport(“kernel32.dll”, CharSet = CharSet.Unicode)]
internal static extern IntPtr FindFirstFile(string lpFileName, out
WIN32_FIND_DATA lpFindFileData);
[DllImport(“kernel32.dll”, CharSet = CharSet.Unicode)]
internal static extern bool FindNextFile(IntPtr hFindFile, out
WIN32_FIND_DATA lpFindFileData);
[DllImport(“kernel32.dll”, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool FindClose(IntPtr hFindFile);
// Assume dirName passed in is already prefixed with \\?\
public static List<string> FindFilesAndDirs(string dirName) {
List<string> results = new List<string>();
WIN32_FIND_DATA findData;
IntPtr findHandle = FindFirstFile(dirName + @”\*”, out findData);
if (findHandle != INVALID_HANDLE_VALUE) {
bool found;
do {
string currentFileName = findData.cFileName;
// if this is a directory, find its contents
if (((int)findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
if (currentFileName != “.” && currentFileName != “..”)
{
List<string> childResults = FindFilesAndDirs(Path.Combine(dirName, currentFileName));
// add children and self to results
results.AddRange(childResults);
results.Add(Path.Combine(dirName, currentFileName));
}
}
// it’s a file; add it to the results
else {
results.Add(Path.Combine(dirName, currentFileName));
}
// find next
found = FindNextFile(findHandle, out findData);
}
while (found);
}
// close the find handle
FindClose(findHandle);
return results;
}
Related Resources
- http://msdn2.microsoft.com/en-us/library/w4byd5y4.aspx: MSDN docs providing more details on platform invokes; I brushed over these details.
- http://www.pinvoke.net/: A wiki of PInvoke signatures to help you look up how to declare Win32 functions in your managed apps
- http://blogs.msdn.com/bclteam/archive/2005/03/16/396900.aspx: If you’re curious about why these examples use safe handles instead of IntPtrs, check out this article. Note that most of the examples on PInvoke.net use IntPtrs, so keep that in mind if you’re comparing these signatures to those on PInvoke.net
Constants, Structs and Enums for the code samples
internal static IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
internal static int FILE_ATTRIBUTE_DIRECTORY = 0x00000010;
internal const int MAX_PATH = 260;
[StructLayout(LayoutKind.Sequential)]
internal struct FILETIME {
internal uint dwLowDateTime;
internal uint dwHighDateTime;
};
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct WIN32_FIND_DATA {
internal FileAttributes dwFileAttributes;
internal FILETIME ftCreationTime;
internal FILETIME ftLastAccessTime;
internal FILETIME ftLastWriteTime;
internal int nFileSizeHigh;
internal int nFileSizeLow;
internal int dwReserved0;
internal int dwReserved1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH)]
internal string cFileName;
// not using this
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
internal string cAlternate;
}
[Flags]
public enum EFileAccess : uint {
GenericRead = 0x80000000,
GenericWrite = 0x40000000,
GenericExecute = 0x20000000,
GenericAll = 0x10000000,
}
[Flags]
public enum EFileShare : uint {
None = 0x00000000,
Read = 0x00000001,
Write = 0x00000002,
Delete = 0x00000004,
}
public enum ECreationDisposition : uint {
New = 1,
CreateAlways = 2,
OpenExisting = 3,
OpenAlways = 4,
TruncateExisting = 5,
}
[Flags]
public enum EFileAttributes : uint {
Readonly = 0x00000001,
Hidden = 0x00000002,
System = 0x00000004,
Directory = 0x00000010,
Archive = 0x00000020,
Device = 0x00000040,
Normal = 0x00000080,
Temporary = 0x00000100,
SparseFile = 0x00000200,
ReparsePoint = 0x00000400,
Compressed = 0x00000800,
Offline = 0x00001000,
NotContentIndexed = 0x00002000,
Encrypted = 0x00004000,
Write_Through = 0x80000000,
Overlapped = 0x40000000,
NoBuffering = 0x20000000,
RandomAccess = 0x10000000,
SequentialScan = 0x08000000,
DeleteOnClose = 0x04000000,
BackupSemantics = 0x02000000,
PosixSemantics = 0x01000000,
OpenReparsePoint = 0x00200000,
OpenNoRecall = 0x00100000,
FirstPipeInstance = 0x00080000
}
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES {
public int nLength;
public IntPtr lpSecurityDescriptor;
public int bInheritHandle;
}
Update: The SizeConst of the WIN32_FIND_DATA.cAlternate member incorrectly stated 10 instead of 14 and has been revised.
.NET 4.0 中超长路径超长文件名的问题的更多相关文章
- vue2.0中配置文件路径
在build/webpack.base.conf.js中添加一些代码即可 module.exports = { resolve: { extensions: ['.js', '.vue', '.jso ...
- 使用OC swift 截取路径中的最后的文件名
使用 OC swift 截取路径中的最后的文件名 如何截取下面路径中最后的文件名 AppDelegate.swift /Users/XXX/Desktop/Swift/swift02/code/02- ...
- dirname basename 截取路径中的目录以及文件名
dirname basename 截取路径中的目录以及文件名 windows 下面: #include "shlwapi.h"#pragma comment(lib, &qu ...
- 下面的程序段创建了BufferedReader类的对象in,以便读取本机c盘my文件夹下的文件1.txt。File构造函数中正确的路径和文件名的表示是( )。
下面的程序段创建了BufferedReader类的对象in,以便读取本机c盘my文件夹下的文件1.txt.File构造函数中正确的路径和文件名的表示是( ). ./表示当前项目的路径../表示当 ...
- 在 SQL Server 中从完整路径提取文件名(sql 玩转文件路径)
四个函数: --1.根据路径获取文件名 -- ============================================= -- Author: Paul Griffin -- Crea ...
- git路径超长 及gitignore
1 忽略路径超长 git config --system core.longpaths true 2 比较全的gitignore https://www.gitignore.io/api/vim,no ...
- Java学习-043-获取文件在目录中的路径
我们在日常的电脑使用中,经常需要在当前目录或当期目录及其子目录中查找文件,并获取相应的文件路径名.在我们的自动化测试中,也经常需要确认文件在目录中是否成功生成或已存在,因而我写了一个小方法来实现. 获 ...
- find_first_of()和 find_last_of() 【获取路径、文件名】
find_first_of()和 find_last_of() [获取路径.文件名](2011-06-11 12:44:46)转载▼标签: 杂谈 分类: c string 类提供字符串处理函数,利用 ...
- Java JTable 表格 获取存储路径,文件名 ,导出excel表格
在做计量泵上位机软件时,需要将下位机传上来的数据,存入MYSQL数据库,显示在java 上位机界面上,并能导出至电脑指定位置. 选择存储路径和文件名: // 处理另存文件的菜单 public void ...
随机推荐
- php工作笔记8-并发和数据类型
1.mysql在进行数据的修改时,并发情况下: $RoundsRows=$modelRounds->where("id=$roundsID and (sendMoney + $amou ...
- ssh免密码登录机器(使用公钥和秘钥进行加密来实现)
ssh 无密码登录要使用公钥与私钥.linux下可以用用ssh-keygen生成公钥/私钥对,下面我以CentOS为例. 登录的原理: 有机器A(192.168.1.155),B(192.168.1. ...
- Tomcat 实现双向SSL认证
大概思路: 使用openssl生产CA证书,使用keytool生产密钥库 实验环境:RHEL6.4+Tomcat8 一.生成CA根证书,并自签名 1.生成CA密钥 # genrsa [产生密钥命令] ...
- Node.js 中MongoDB的基本接口操作
Node.js 中MongoDB的基本接口操作 连接数据库 安装mongodb模块 导入mongodb模块 调用connect方法 文档的增删改查操作 插入文档 方法: db.collection(& ...
- bash coding to changeNames
____通配符和正则表达式 此处的定义只针对linux 中的shell语言,对其它语言不适用 _正则表达式用来在文件中匹配符合条件的字符串,正则是包含匹配.grep.awk.sed等命令可以支持正则表 ...
- zepto的touch模块解决click延迟300ms问题以及点透问题的详解
大家都知道移动端的click事件会延迟300ms触发,这时大家可以使用zepto的touch模块,里面定义了一个tap事件,通过绑定tap事件,可以实现点击立即触发的功能. 那么,它的tap事件是怎么 ...
- easyui datagrid 点击列表头排序出现错乱的原因
之前我的导师,也就是带我的同事,使用datagrid,发现点击列表头排序出现乱序,按理说只有顺序和逆序两种排序结果.因为他比较忙,当时没解决,把排序禁掉了,后来又要求一定要排序,所以他交给我. 一开始 ...
- 几个opencv 的iOS的编译问题解决
一个iOS项目需要用到opencv,而且要支持arm64的,以前有个demo的,只支持32位的.到官网下载了最新支持64位库,结果编译无法通过. google了好久也没法解决,后来问了一个同事,找出原 ...
- 数据库一些常用的SQL语句
1.多表连接查询: 假设现在有三个表,One,Two,Three: One表字段:Code(主键),Name Two表字段:Birthday,T_code(One表Code的外键) Three表字段: ...
- MySQL数据库引擎介绍、区别、创建和性能测试的深入分析
本篇文章是对MySQL数据库引擎介绍.区别.创建和性能测试进行了详细的分析介绍,需要的朋友参考下 数据库引擎介绍 MySQL数据库引擎取决于MySQL在安装的时候是如何被编译的.要添加一个新的引擎 ...