文件名以句点结尾并使用 FileInfo C#



我遇到了一个问题,FileInfo看不到名称末尾包含句点的文件。我知道Windows阻止以这种方式命名文件,但是此数据来自不同的操作系统。

我能够使用命令行在 Windows 中创建问题文件:

echo "test" > "\?C:TestBadFileName."

这是我用来测试文件的代码:

DateTime origAccessDate;
DateTime OrigCreateDate;
long sizeBytes;
string path = @"\?C:TestBadFileName.";
FileInfo fi = new FileInfo(path);
try
{
origAccessDate = fi.LastAccessTime;
OrigCreateDate = fi.CreationTime;
sizeBytes = fi.Length;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}

在路径上调用文件信息时出现问题。Exists 属性为 false,即使您可以复制/粘贴路径以确认其有效也是如此。目标不是重命名文件以便读取,而是就地读取它(按原样)。

这是一种通过命令行执行文件操作的方法。不是最优雅的解决方案,但希望是一个有用的参考。

using System;
using System.IO;
using System.Diagnostics;
namespace StackOverflow_FileNameShenanigans
{
class Program
{
static void Main(string[] args)
{
string contents;
DateTime origAccessDate;
DateTime origCreateDate;
long sizeBytes;
string path = @"\?C:TestBadFileName.";
try
{
contents = CommandLineFileOps.ReadAllText(path);
origAccessDate = CommandLineFileOps.LastAccessTime(path);
origCreateDate = CommandLineFileOps.CreationTime(path);
sizeBytes = CommandLineFileOps.Length(path);
Console.WriteLine($"Contents: {contents}");
Console.WriteLine($"OrigAccessDate: {origAccessDate}");
Console.WriteLine($"OrigCreateDate: {origCreateDate}");
Console.WriteLine($"SizeBytes: {sizeBytes}");
Console.ReadKey();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
Console.ReadKey();
}
}
}
public static class CommandLineFileOps
{
public static string ReadAllText(string path)
{
string contents;
RunOnCommandLine($"type {path}", out contents);
contents = contents.Substring(0, contents.Length - 3);
return contents;
}
public static DateTime CreationTime(string path)
{
string output;
RunOnCommandLine($"dir /T:C {path}", out output);
string dateLine = output.Split('n')[5];
string dateStr = dateLine.Replace("                 ", "n").Split('n')[0];
return DateTime.Parse(dateStr);
}
public static DateTime LastAccessTime(string path)
{
string output;
RunOnCommandLine($"dir /T:A {path}", out output);
string dateLine = output.Split('n')[5];
string dateStr = dateLine.Replace("                 ", "n").Split('n')[0];
return DateTime.Parse(dateStr);
}
public static long Length(string path)
{
string output;
RunOnCommandLine($"dir {path}", out output);
string lengthLine = output.Split('n')[6];
string lengthStr = lengthLine.Replace("              ", "n").Split('n')[2].Split(' ')[0];
return long.Parse(lengthStr);
}
private static int RunOnCommandLine(string line)
{
Process cmd = new Process();
cmd.StartInfo.FileName = "cmd.exe";
cmd.StartInfo.RedirectStandardInput = true;
cmd.StartInfo.RedirectStandardOutput = true;
cmd.StartInfo.CreateNoWindow = true;
cmd.StartInfo.UseShellExecute = false;
cmd.Start();
cmd.StandardInput.WriteLine(line);
cmd.StandardInput.Flush();
cmd.StandardInput.Close();
cmd.WaitForExit();
int exitCode = cmd.ExitCode;
return exitCode;
}
private static int RunOnCommandLine(string line, out string output)
{
string tempPath = Path.GetTempFileName();
int exitCode = RunOnCommandLine($"{line} > {tempPath}");
output = File.ReadAllText(tempPath);
File.Delete(tempPath);
return exitCode;
}
}
}

方法调用:

string path = @"C:TestBadFileName.";
DateTime createDate = cmdGetCreateDate(path);
DateTime accessDate = cmdGetAccessDate(path);
long bytes = cmdGetSizeBytes(path);

方法:

private DateTime cmdGetCreateDate(string path)
{
DateTime createDate = new DateTime();
int lastSlash = path.LastIndexOf(Convert.ToChar("\"));
string file = path.Substring(lastSlash + 1);
string folder = path.Substring(0, lastSlash);
string cmdexe = @"C:WindowsSystem32cmd.exe";
string args = @"/c dir /T:C /A:-D """ + folder + """;
Process procCreateDate = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = cmdexe,
Arguments = args,
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
}
};
procCreateDate.Start();
string output = procCreateDate.StandardOutput.ReadToEnd();
if (!output.Contains(file))
{
return createDate; //File not found
}
string p = @"bd{2}/d{2}/d{4}bs+d{2}:d{2} ..";
Regex rx = new Regex(p);
Match m = rx.Match(output);
if (m.Success)
{
DateTime.TryParse(m.Value, out createDate);
}
return createDate;
}
private DateTime cmdGetAccessDate(string path)
{
DateTime accessDate = new DateTime();
int lastSlash = path.LastIndexOf(Convert.ToChar("\"));
string file = path.Substring(lastSlash + 1);
string folder = path.Substring(0, lastSlash);
string cmdexe = @"C:WindowsSystem32cmd.exe";
string args = @"/c dir /T:A /A:-D """ + folder + """;
Process procCreateDate = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = cmdexe,
Arguments = args,
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
}
};
procCreateDate.Start();
string output = procCreateDate.StandardOutput.ReadToEnd();
if (!output.Contains(file))
{
return accessDate; //File not found
}
string p = @"bd{2}/d{2}/d{4}bs+d{2}:d{2} ..";
Regex rx = new Regex(p);
Match m = rx.Match(output);
if (m.Success)
{
DateTime.TryParse(m.Value, out accessDate);
}
return accessDate;
}
private long cmdGetSizeBytes(string path)
{
long bytes = -1;
int lastSlash = path.LastIndexOf(Convert.ToChar("\"));
string file = path.Substring(lastSlash + 1);
string folder = path.Substring(0, lastSlash);
string cmdexe = @"C:WindowsSystem32cmd.exe";
string args = @"/c dir /A:-D """ + folder + """;

Process procCreateDate = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = cmdexe,
Arguments = args,
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
}
};
procCreateDate.Start();
string output = procCreateDate.StandardOutput.ReadToEnd();
if (!output.Contains(file))
{
return bytes; //File not found
}
string p = @"d+ " + file;
Regex rx = new Regex(p);
Match m = rx.Match(output);
if (m.Success)
{
string[] splitVal = m.Value.Split(Convert.ToChar(" "));
bytes = Convert.ToInt64(splitVal[0]);
}
return bytes;
}

由于这显然是不受支持的场景,因此我怀疑是否可以在不使用一些低级文件访问的情况下完成它。

您可以尝试做的是放下FileInfo然后继续File.Exists(path)File.ReadAllBytes(path). 这些可能能够规避这个问题。

使用安全文件句柄访问文件

以下内容未经测试

创建UnmanagedFileLoader的实例(下面的代码,取自 MSDN),允许您创建一个SafeFileHandle对象,该对象可以通过以下方式传递给FileStream构造函数:

UnmanagedFileLoader ufl = new UnmanagedFileLoader(path);
FileStream fs = new FileStream(ufl.Handle, FileMode.Open);

注意:记得打电话给ufl.Handle.Dispose()

这应该为您提供更多,可以说,直接访问文件,因此可以绕过Windows已到位的有效文件名的强制执行。

非托管文件加载程序代码

class UnmanagedFileLoader 
{
public const short FILE_ATTRIBUTE_NORMAL = 0x80;
public const short INVALID_HANDLE_VALUE = -1;
public const uint GENERIC_READ = 0x80000000;
public const uint GENERIC_WRITE = 0x40000000;
public const uint CREATE_NEW = 1;
public const uint CREATE_ALWAYS = 2;
public const uint OPEN_EXISTING = 3;
// Use interop to call the CreateFile function.
// For more information about CreateFile,
// see the unmanaged MSDN reference library.
[DllImport("kernel32.dll", SetLastError = true, CharSet=CharSet.Unicode)]
static extern SafeFileHandle CreateFile(string lpFileName, uint dwDesiredAccess,
uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition,
uint dwFlagsAndAttributes, IntPtr hTemplateFile);
private SafeFileHandle handleValue = null;
public UnmanagedFileLoader(string Path)
{
Load(Path);
}
public void Load(string Path)
{
if (Path == null || Path.Length == 0)
{
throw new ArgumentNullException("Path");
}
// Try to open the file.
handleValue = CreateFile(Path, GENERIC_WRITE, 0, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
// If the handle is invalid,
// get the last Win32 error 
// and throw a Win32Exception.
if (handleValue.IsInvalid)
{
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
}
}
public SafeFileHandle Handle
{
get
{
// If the handle is valid,
// return it.
if (!handleValue.IsInvalid)
{
return handleValue;
}
else
{
return null;
}
}
}
}

使用 Windows API 访问文件日期

下面的GetFileTimeSample类取自 www.pinvoke.net,使用另一个Windows API调用,特别是GetFileTime。 此实现只是一个示例,您肯定能够对其进行调整以仅获取所需的日期。在当前形式中,它将输出所有三个日期。

用法:

DateTime fileDateCreated;
DateTime fileDateAccessed;
DateTime fileDateModified;
GetFileTimeSample.GetFileTimes(path, out fileDateCreated, out fileDateAccessed, out fileDateModified);

从 C# 7.0 开始,可以直接在函数调用中声明out变量,如下所示:

GetFileTimeSample.GetFileTimes(path, out DateTime fileDateCreated, out DateTime fileDateAccessed, out DateTime fileDateModified);

获取文件时间示例

public class GetFileTimeSample
{
private const uint GENERIC_READ = 0x80000000;
private const uint FILE_SHARE_READ = 0x1;
private const uint FILE_ATTRIBUTE_NORMAL = 0x80;
private const int INVALID_HANDLE_VALUE = -1;
private const uint OPEN_EXISTING = 3;
[StructLayout(LayoutKind.Sequential)]
private struct FILETIME
{
public uint dwLowDateTime;
public uint dwHighDateTime;
}
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(
IntPtr hObject
);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
private static extern IntPtr CreateFile(
string lpFileName,
uint dwDesiredAccess,
uint dwShareMode,
IntPtr SecurityAttributes,
uint dwCreationDisposition,
uint dwFlagsAndAttributes,
IntPtr hTemplateFile
);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool GetFileTime(
IntPtr hFile,
ref FILETIME lpCreationTime,
ref FILETIME lpLastAccessTime,
ref FILETIME lpLastWriteTime
);
public static void GetFileTimes(string FileName, out DateTime CreationTime, out DateTime LastAccessTime, out DateTime LastWriteTime)
{
CreationTime = DateTime.MinValue;
LastAccessTime = DateTime.MinValue;
LastWriteTime = DateTime.MinValue;
IntPtr ptr = IntPtr.Zero;
FILETIME ftCreationTime = new FILETIME();
FILETIME ftLastAccessTime = new FILETIME();
FILETIME ftLastWriteTime = new FILETIME();
try
{
ptr = CreateFile(FileName, GENERIC_READ, FILE_SHARE_READ, IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);
if (ptr.ToInt32() == INVALID_HANDLE_VALUE)
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
if (GetFileTime(ptr, ref ftCreationTime, ref ftLastAccessTime, ref ftLastWriteTime) != true)
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
CreationTime = DateTime.FromFileTimeUtc((((long)ftCreationTime.dwHighDateTime) << 32) | ((uint)ftCreationTime.dwLowDateTime));
LastAccessTime = DateTime.FromFileTimeUtc((((long)ftLastAccessTime.dwHighDateTime) << 32) | ((uint)ftLastAccessTime.dwLowDateTime));
LastWriteTime = DateTime.FromFileTimeUtc((((long)ftLastWriteTime.dwHighDateTime) << 32) | ((uint)ftLastWriteTime.dwLowDateTime));
}
catch (Exception e)
{
throw (e);
}
finally
{
if (ptr !=IntPtr.Zero && ptr.ToInt32() != INVALID_HANDLE_VALUE) CloseHandle(ptr);
}
}
}

最新更新