如何从c#更新文件的更改时间
本文关键字:时间 文件 更新 | 更新日期: 2023-09-27 18:22:00
文件可以有更改日期。此日期与上次修改日期或上次访问日期不同。通过UI或.NET API无法看到更改日期。有两个Win32函数GetFileInformationByHandleEx用于读取文件信息,SetFileInformationByHandle用于写入文件信息。
我想读出更改日期,增加几个小时,然后将新日期写回文件的更改日期。
现在我有以下代码:
class Program
{
static void Main(string[] args)
{
using (var file = new FileStream(@"c:'path'to'file", FileMode.Open))
{
var fileInfo = new FILE_BASIC_INFO();
GetFileInformationByHandleEx(
file.Handle,
FILE_INFO_BY_HANDLE_CLASS.FileBasicInfo,
out fileInfo,
(uint)Marshal.SizeOf(fileInfo));
SetFileInformationByHandle(
file.Handle,
FILE_INFO_BY_HANDLE_CLASS.FileBasicInfo,
fileInfo,
(uint)Marshal.SizeOf(fileInfo));
}
}
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool GetFileInformationByHandleEx(
IntPtr hFile,
FILE_INFO_BY_HANDLE_CLASS infoClass,
out FILE_BASIC_INFO fileInfo,
uint dwBufferSize);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetFileInformationByHandle(
IntPtr hFile,
FILE_INFO_BY_HANDLE_CLASS infoClass,
FILE_BASIC_INFO fileInfo,
uint dwBufferSize);
private enum FILE_INFO_BY_HANDLE_CLASS
{
FileBasicInfo = 0
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct FILE_BASIC_INFO
{
public LARGE_INTEGER CreationTime;
public LARGE_INTEGER LastAccessTime;
public LARGE_INTEGER LastWriteTime;
public LARGE_INTEGER ChangeTime;
public uint FileAttributes;
}
[StructLayout(LayoutKind.Explicit, Size = 8)]
private struct LARGE_INTEGER
{
[FieldOffset(0)]
public Int64 QuadPart;
[FieldOffset(0)]
public UInt32 LowPart;
[FieldOffset(4)]
public Int32 HighPart;
}
}
我可以把更改日期读成那个糟糕的结构LARGE_INTEGER
。我想要的是一个函数,它可以将该类型转换为System.DateTime
,反之亦然。
我遇到的第二个问题是SetFileInformationByHandle方法的信号错误。我得到一个PInvokeStackImbalance与此附加信息:
附加信息:对PInvoke函数"Program::SetFileInformationByHandle"的调用使堆栈不平衡。这可能是因为托管PInvoke签名与非托管目标签名不匹配。请检查PInvoke签名的调用约定和参数是否与目标非托管签名匹配。
谁能帮我?
回答第一部分。。关于如何将"那个可怕的Large_Interger"转换为DateTime。。下面的代码片段应该会有所帮助。。
using (var file = new System.IO.FileStream(@"sample.log", System.IO.FileMode.Open))
{
var fileInfo = new FILE_BASIC_INFO();
GetFileInformationByHandleEx(
file.Handle,
FILE_INFO_BY_HANDLE_CLASS.FileBasicInfo,
out fileInfo,
(uint)System.Runtime.InteropServices.Marshal.SizeOf(fileInfo));
var changeTime = DateTime.FromFileTime(fileInfo.ChangeTime.QuadPart);
Console.WriteLine(changeTime);
System.TimeSpan changedForHowLong = DateTime.Now.Subtract(changeTime);
Console.WriteLine(changedForHowLong.Days);
}
我测试了上面的片段,它似乎工作得很好。。让我试着指责你在PInvokeStackImbalance中面临的问题。。
小心,
我在PInvoke 上找到了这个签名
[DllImport("Kernel32.dll", SetLastError = true)]
private static extern bool SetFileInformationByHandle(
IntPtr hFile,
int FileInformationClass,
IntPtr lpFileInformation,
Int32 dwBufferSize);
不知怎么的,这并没有奏效。我必须将参数lpFileInformation
的类型更改为FILE_BASIC_INFO
才能使其工作。
这是从PowerShell调用的完整C#示例:
$fu = @"
using System;
using System.IO;
using System.Runtime.InteropServices;
public class FileUtility
{
private struct FILE_BASIC_INFO
{
[MarshalAs(UnmanagedType.I8)]
public Int64 CreationTime;
[MarshalAs(UnmanagedType.I8)]
public Int64 LastAccessTime;
[MarshalAs(UnmanagedType.I8)]
public Int64 LastWriteTime;
[MarshalAs(UnmanagedType.I8)]
public Int64 ChangeTime;
[MarshalAs(UnmanagedType.U4)]
public UInt32 FileAttributes;
}
[DllImport("Kernel32.dll", SetLastError = true)]
private static extern bool SetFileInformationByHandle(
IntPtr hFile,
int FileInformationClass,
FILE_BASIC_INFO lpFileInformation,
Int32 dwBufferSize);
public void SetFileChangeTime()
{
using (FileStream fs = new FileStream(@"c:'path'to'file", FileMode.Open))
{
FILE_BASIC_INFO fileInfo = new FILE_BASIC_INFO();
fileInfo.ChangeTime = 943044610000000;
SetFileInformationByHandle(
fs.Handle,
0, // the same as FILE_INFO_BY_HANDLE_CLASS.FileBasicInfo
fileInfo,
Marshal.SizeOf(fileInfo));
}
}
}
"@
Add-Type -TypeDefinition $fu -IgnoreWarnings
$f = New-Object -TypeName FileUtility
$f.SetFileChangeTime()
我已经用其他日期属性运行了这个例子,因为它们显示在资源管理器中,并且有效。
编辑
此代码不在VS中以调试模式运行。如上所述,它引发异常。在命令行中运行EXE不会引发异常。但更改日期没有更新。但是,它只能在PowerShell中工作。奇怪的