Search code examples
c#fileiopinvokekernel32

SetFileTime returning error code 5


I am trying to P/Invoke SetFileTime, but I can't seem to get it to work. It always returns an error code of 5 for me (access denied), and I am not sure why. Here is the code I am using:

void Main()
{
    var pointer = CreateFile(@"C:\Users\User\Desktop\New folder\New Text Document.txt", FileAccess.ReadWrite, FileShare.None, IntPtr.Zero, FileMode.Open, FileAttributes.Normal, IntPtr.Zero);
    Console.WriteLine(pointer);
    var now = DateTime.Now.ToFileTime();
    long lpCreationTime = now;
    long lpLastAccessTime = now;
    long lpLastWriteTime = now;
    if (!SetFileTime(pointer, ref lpCreationTime, ref lpLastAccessTime, ref lpLastWriteTime))
        Console.WriteLine(GetLastError());
    CloseHandle(pointer);
}

[DllImport("kernel32.dll")]
static extern UInt32 GetLastError();

[DllImport("kernel32.dll", SetLastError = true)]
static extern Boolean SetFileTime(IntPtr hFile, ref long lpCreationTime, ref long lpLastAccessTime, ref long lpLastWriteTime);

[DllImport("kernel32.dll", SetLastError = true)]
static extern Boolean CloseHandle(IntPtr hObject);

[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
static extern IntPtr CreateFile(String fileName, [MarshalAs(UnmanagedType.U4)] FileAccess fileAccess, [MarshalAs(UnmanagedType.U4)] FileShare fileShare, IntPtr securityAttributes, [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition, [MarshalAs(UnmanagedType.U4)] FileAttributes flags, IntPtr template);

My pointer is valid (2376) but I see that SetFileTime has failed and has returned an error code of 5 (access denied). Now, I have ensured I am running as administrator and that my account has permissions to that path, but still, no cigar. Anyone have any ideas why this is happening?

Update

Marshal.GetLastWin32Error() also returns 5 after the call to SetFileTime. Also, my need for making this call work is so that I can SetFileTime on long paths in Windows, which CreateFile supports, but the current file libraries of .NET do not support long paths in Windows.


Solution

  • From the documentation of SetFileTime:

    A handle to the file or directory. The handle must have been created using the CreateFile function with the FILE_WRITE_ATTRIBUTES access right.

    Your code doesn't manage to do that. The .net FileAccess enumeration is not compatible with Win32 access flags. You'll need to define an enum specifically for use with CreateFile. Likewise your use of FileShare and FileMode are not correct.

    This p/invoke declaration should suffice: http://www.pinvoke.net/default.aspx/kernel32.createfile


    As Alexei said, don't call GetLastError because you may be picking up an error code for a framework call rather than the true error. Use SetLastError = true and Marshal.GetLastWin32Error().

    You also fail to check for errors in the return value if CreateFile.