如何从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签名的调用约定和参数是否与目标非托管签名匹配。

谁能帮我?

如何从c#更新文件的更改时间

回答第一部分。。关于如何将"那个可怕的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中工作。奇怪的