Can't figure out whats the issue with CreateFilePrivate
.
called from PowerShell, with:
$path = "D:\temporal\t1.txt"
$item = Get-Item -LiteralPath $path -Force
[Win32]::setFileDetails($path, $item.CreationTimeUtc.ToFileTime(), $item.LastAccessTimeUtc.ToFileTime(), $item.LastWriteTimeUtc.AddDays(1).ToFileTime())
using System; // required for some of it's content .
using System.Runtime.InteropServices; // DllImport, Marshal
using Microsoft.Win32.SafeHandles; // SafeFileHandle
using System.IO; // FileShare, FileMode
public class Win32 {
struct FILE_BASIC_INFO {
internal long CreationTime;
internal long LastAccessTime;
internal long LastWriteTime;
internal long ChangeTime;
internal uint FileAttributes; // binary
};
[Flags]
enum AccessRights: uint { // System.Messaging.GenericAccessRights doesn't work in Windows Powershell 's `Add-Type` .
// # GenericAccessRights
Read = 0x80000000, // 2147483648 "-2147483648"
Write = 0x40000000, // 1073741824
Execute = 0x20000000, // 536870912
All = 0x10000000, // 268435456
None = 0x00000000 // 0
};
[Flags]
enum CreateFile_Options: uint {
// # Flags
ReparsePoint = 0x00200000, // do not follow if a symbolic link . "OPEN_REPARSE_POINT"
BACKUP_SEMANTICS = 0x02000000,
DELETE_ON_CLOSE = 0x04000000,
NO_BUFFERING = 0x20000000,
OPEN_NO_RECALL = 0x00100000,
OVERLAPPED = 0x40000000,
POSIX_SEMANTICS = 0x01000000,
RANDOM_ACCESS = 0x10000000,
SESSION_AWARE = 0x00800000,
SEQUENTIAL_SCAN = 0x08000000,
WRITE_THROUGH = 0x80000000
};
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool SetFileInformationByHandle(
SafeFileHandle item,
int Type_v, // `enum FILE_INFO_BY_HANDLE_CLASS`
ref FILE_BASIC_INFO sho_v, // "Pointer"
uint size_v
);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool GetFileInformationByHandleEx(
SafeFileHandle item,
int Type_v, // `enum FILE_INFO_BY_HANDLE_CLASS`
ref FILE_BASIC_INFO sho_v, // "Pointer"
uint size_v
);
[DllImport("kernel32.dll", EntryPoint = "CreateFileW", SetLastError = true)]
static extern SafeFileHandle CreateFilePrivate(
string path,
AccessRights v_Access,
FileShare v_Share,
IntPtr v_Security, // `null` for default . `ref (SECURITY_ATTRIBUTES {})`
FileMode a,
CreateFile_Options v_Options,
IntPtr Template // `(SafeFileHandle) null` : "SafeHandle cannot be null" .
);
public static void setFileDetails (
string path,
long CreationEventTime,
long ReadingEventTime,
long WritingEventTime
// System.IO.FileAttributes FileAttributes
) {
SafeFileHandle item = CreateFilePrivate(path , AccessRights.Read | AccessRights.Write , FileShare.Read , IntPtr.Zero , FileMode.Open , CreateFile_Options.ReparsePoint , IntPtr.Zero);
Console.WriteLine(path) // the output path 100% targets a Symbolic Link
Console.WriteLine(Marshal.GetLastWin32Error()); // "2"/"ERROR_FILE_NOT_FOUND"
var BasicInfo = new FILE_BASIC_INFO() {};
GetFileInformationByHandleEx(item, (int) 0, ref BasicInfo, (uint) Marshal.SizeOf(BasicInfo));
BasicInfo = new FILE_BASIC_INFO() {
CreationTime = CreationEventTime,
LastAccessTime = ReadingEventTime,
LastWriteTime = WritingEventTime,
ChangeTime = BasicInfo.ChangeTime,
FileAttributes = (uint) 0 // `(uint) 0` to not change any
};
SetFileInformationByHandle(item, (int) 0, ref BasicInfo, (uint) Marshal.SizeOf(BasicInfo));
}
}
edited with fixes for issues pointed out by @Dai .
Dllimport by default converts parameters passed to the Method, to some specific encoding, so for Unicode <<name>>W
Win32 commands it requires it's CharSet being set .
[DllImport("kernel32.dll", EntryPoint = "CreateFileW", CharSet = CharSet.Unicode, SetLastError = true)]
IntPtr works as Pointer, so when:
IntPtr Type and IntPtr.Zero can be used .
thanks to everyone who tried to help, especially @SimonMourier @Dai .