大多數備份方案僅關注檔案內容,忽略檔案/資料夾的時間屬性(建立時間、最後存取時間、最後修改時間)。Windows 缺乏直接修改資料夾時間的工具,甚至備份程式也難以完整保留根目錄時間屬性。本文提供一套透過 Win32 API 實現資料夾時間屬性完整讀寫的解決方案。
Win32 API 僅提供檔案時間屬性操作函式(GetFileTime、SetFileTime),但 CreateFile 實際上支援以 FILE_FLAG_BACKUP_SEMANTICS 旗標開啟資料夾,取得的控制碼可視同檔案控制碼,進而套用檔案時間 API 於資料夾。
HANDLE CreateFile(
LPCTSTR lpFileName, // 資料夾路徑
DWORD dwDesiredAccess, // GENERIC_READ / GENERIC_WRITE
DWORD dwShareMode, // FILE_SHARE_READ | FILE_SHARE_DELETE
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition, // OPEN_EXISTING (不可建立)
DWORD dwFlagsAndAttributes, // 關鍵:FILE_FLAG_BACKUP_SEMANTICS
HANDLE hTemplateFile
);
FILETIME 結構為 UTC 時間,需轉換為本地時間再轉為 SYSTEMTIME 方便操作:
// 讀取流程
GetFileTime(hDir, &lpCreationTime, &lpLastAccessTime, &lpLastWriteTime);
FileTimeToLocalFileTime(&lpLastWriteTime, &localTime);
FileTimeToSystemTime(&localTime, &sysTime);
// 寫入流程
SystemTimeToFileTime(&sysTime, &lpCreationTime);
SystemTimeToFileTime(&sysTime, &lpLastAccessTime);
SystemTimeToFileTime(&sysTime, &lpLastWriteTime);
SetFileTime(hDir, &lpCreationTime, &lpLastAccessTime, &lpLastWriteTime);
BOOL CSetFolderTimeDlg::GetDirTime(CString DirName, SYSTEMTIME& stime)
{
HANDLE hDir = CreateFile(DirName, GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_DELETE,
NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
FILETIME ftCreate, ftAccess, ftWrite;
BOOL retval = FALSE;
if (GetFileTime(hDir, &ftCreate, &ftAccess, &ftWrite)) {
FILETIME localTime;
FileTimeToLocalFileTime(&ftWrite, &localTime);
FileTimeToSystemTime(&localTime, &stime);
retval = TRUE;
}
CloseHandle(hDir);
return retval;
}
BOOL CSetFolderTimeDlg::SetDirTime(CString DirName, SYSTEMTIME newTime)
{
HANDLE hDir = CreateFile(DirName, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_DELETE,
NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
FILETIME ftCreate, ftAccess, ftWrite;
SystemTimeToFileTime(&newTime, &ftCreate);
SystemTimeToFileTime(&newTime, &ftAccess);
SystemTimeToFileTime(&newTime, &ftWrite);
BOOL retval = SetFileTime(hDir, &ftCreate, &ftAccess, &ftWrite);
CloseHandle(hDir);
return retval;
}
SYSTEMTIME stime;
if (GetDirTime(folderPath, stime)) {
// 可選:修改 stime 內容
// stime.wYear = 2024; ...
SetDirTime(folderPath, stime); // 寫回
}