我遇到的问题是FileInfo无法看到名称末尾包含句点的文件。我知道Windows会以这种方式阻止命名文件,但这些数据来自不同的操作系统。
我可以使用命令行在Windows中创建问题文件:
echo "test" > "\\?\C:\Test\BadFileName."
这是我用来测试文件的代码:
DateTime origAccessDate;
DateTime OrigCreateDate;
long sizeBytes;
string path = @"\\?\C:\Test\BadFileName.";
FileInfo fi = new FileInfo(path);
try
{
origAccessDate = fi.LastAccessTime;
OrigCreateDate = fi.CreationTime;
sizeBytes = fi.Length;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
在路径上调用FileInfo时会出现问题。即使您可以复制/粘贴路径以确认其有效,Exists属性也是false。目标不是重命名文件以便阅读,而是按原样(原样)阅读。
答案 0 :(得分:0)
由于这显然是不受支持的情况,我怀疑如果不使用某些低级文件访问就可以完成。
你可以尝试做的是放弃FileInfo
并继续
File.Exists(path)
和File.ReadAllBytes(path)
。
这些可能能够绕过这个问题。
使用SafeFileHandle进行文件访问
以下是未经测试的
创建UnmanagedFileLoader
的实例(下面的代码,取自MSDN),允许您创建一个SafeFileHandle
对象,该对象可以传递给FileStream
中的UnmanagedFileLoader ufl = new UnmanagedFileLoader(path);
FileStream fs = new FileStream(ufl.Handle, FileMode.Open);
构造函数。以下方式:
ufl.Handle.Dispose()
注意:请记得致电 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实施的有效文件名的执行。
UnmanagedFileLoader代码
GetFileTimeSample
使用Windows API访问文件日期
以下GetFileTime
类取自www.pinvoke.net,使用另一个Windows API调用,特别是DateTime fileDateCreated;
DateTime fileDateAccessed;
DateTime fileDateModified;
GetFileTimeSample.GetFileTimes(path, out fileDateCreated, out fileDateAccessed, out fileDateModified);
。
这个实现只是一个例子,你肯定能够调整它只获得你需要的日期。在目前的形式中,它将输出所有三个日期。
用法:
out
从C#7.0开始,可以直接在函数调用中声明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);
}
}
}
GetFileTimeSample
{{1}}
答案 1 :(得分:0)
这是通过命令行执行文件操作的方法。不是最优雅的解决方案,但希望是一个有用的参考。
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:\Test\BadFileName.";
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;
}
}
}
答案 2 :(得分:0)
方法调用:
string path = @"C:\Test\BadFileName.";
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:\Windows\System32\cmd.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 = @"\b\d{2}/\d{2}/\d{4}\b\s+\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:\Windows\System32\cmd.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 = @"\b\d{2}/\d{2}/\d{4}\b\s+\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:\Windows\System32\cmd.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;
}