引言:
对于大多数的IT企业和绝大多数的个人用户而言,数据的备份主要是对数据内容的简单备份,而很少涉及对备份数据自身属性如文件属性、所在文件夹的时间属性等内容的备份。而在某些对数据管理有着非常严格要求的特殊行业中,对以上这些相关属性信息的备份也是同等重要的。但是在目前的Windows操作系统下,不论是直接通过手工进行数据备份还是通过某些数据备份软件来进行,都难以将这些信息完全保持原貌复制过来,尤其对于每时每刻均在发生变化的时间属性更是难以保持其原始信息。不仅如此,在Windows操作系统下甚至没有提供能直接修改此类属性的工具和手段。为此,笔者对该问题做了研究,并总结出一套比较简单的解决办法。其中,对于文件属性的备份与更改方法笔者已在电脑报天极网《在VC+ +下对文件属性的获取与更改》一文做了阐述,因此,本文在此就不再加以赘述,而着重对文件夹时间属性的获取与更改方法进行介绍。
设计思路
在Windows操作系统下并没有提供任何可供修改文件夹时间属性的方法和手段,即使是在Win32 API函数中,也只是提供了对文件时间属性进行修改的函数调用,而没有关于文件夹时间属性修改的只言片语。虽然Windows所提供的备份程序能够把待备份文件夹下的所有子文件夹的时间属性按照原样完整的复制过去,但是却无法保持根目录时间属性的恒定。由此,可以考虑采取备份的方式来进行,并从同备份相关的Win32 API函数入手。具体而言,可以先以打开文件的方式来打开文件夹,然后就可以通过原本用于处理文件时间属性的Win32 API函数GetFileTime()和SetFileTime()来获取原始时间属性并以其为参数来设置备份后的文件夹时间属性了,这样的处理可以确保文件夹在备份前后时间属性的一致。
根据前面的分析可以看出,按照打开文件的方式来打开文件夹是整个处理过程的关键,通常主要用于创建、打开文件的Win32 API函数CreateFile()并非只能用来创建和打开文件对象,实际上它还可以用来创建、打开管道、邮槽、通讯资源、磁盘驱动器(只对 Windows NT而言)、控制台和文件夹(只能打开)等。下面给出CreateFile()的原型:
HANDLE CreateFile( LPCTSTR lpFileName, // 文件名指针
DWORD dwDesiredAccess, // 访问模式
DWORD dwShareMode, // 共享模式
LPSECURITY_ATTRIBUTES lpSecurityAttributes, //安全属性
DWORD dwCreationDisposition, // 创建方式
DWORD dwFlagsAndAttributes, // 文件属性
HANDLE hTemplateFile // 指向待复制属性的文件指针
);
当用其进行文件夹打开操作时,第一个参数lpFileName应被设置成为待打开的文件夹的名称;至于访问模式可以根据需要灵活设置,对于本文而言,对源文件夹只进行读取操作故可以设置为GENERIC_READ,对于备份后的文件夹由于需要将属性信息写入,因此需要有GENERIC_WRITE的支持;共享模式参数dwShareMode的设置与进行文件处理时的设置没有什么区别,在此可以设置为FILE_SHARE_READ|FILE_SHARE_DELETE;由于CreateFile()函数在进行文件夹操作时,不能以创建方式进行,因此创建方式只能打开已经存在的对象,即dwCreationDisposition应当设置为OPEN_EXISTING;相比而言, dwFlagsAndAttributes参数的设置是比较重要的,正是通过将该参数设置为FILE_FLAG_BACKUP_SEMANTICS属性才使CreateFile()函数来进行打开文件夹的操作。
通常,文件、文件夹的时间属性指的是创建时间、最近访问时间和最近修改时间等几个具体属性。对于文件的上述属性可以通过GetFileTime()来获取,对于文件夹,在通过CreateFile()函数将其打开后,其获取得到的句柄可以当作文件句柄来使用。因此,通过GetFileTime()函数同样也可以得到文件夹的时间属性。GetFileTime()函数原型如下:
BOOL GetFileTime( HANDLE hFile, // 文件句柄
LPFILETIME lpCreationTime, // 创建时间的地址
LPFILETIME lpLastAccessTime, // 最近访问时间的地址
LPFILETIME lpLastWriteTime // 最近修改时间的地址
);
其中后三个参数均是指向FILETIME结构的指针,得到的也都是UTC时间,如果需要,可以通过FileTimeToLocalFileTime()函数将此UTC时间转化成本地时间。而且还可以进一步通过 FileTimeToSystemTime()函数将其从文件时间格式转化成系统时间格式,转化后的时间格式将保存在一个SYSTEMTIME结构对象中。类似的,在将时间信息写入到文件夹属性时,如果不是文件时间格式也应当通过SystemTimeToFileTime()函数将其从系统时间格式转换成文件时间格式,然后再通过SetFileTime()函数将指定的时间写入到文件夹的时间属性中去。这样,在进行数据备份和恢复的过程中,包括根目录在内的所有文件夹都可以保持时间属性的一致。
简单示例
下面根据前面的讲述而给出一个简单的应用示例,通过此示例可以从指定的文件夹读取时间属性信息并可在经过修改后再回写进去(在此仅对最近修改时间进行处理,对于其他的时间属性可用类似的方法加以实现)。这里通过两个函数 GetDirTime()和SetDirTime()来实现对文件夹时间信息的获取与更改处理,下面就以注释的形式对这两个函数的实现过程进行讲解:
// 获取指定文件夹的时间属性,入口参数DirName指定了待处理的文件夹,stime为一
// 指向SYSTEMTIME结构的指针
BOOL CSetForderTimeDlg::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 lpCreationTime; // 文件夹的创建时间
FILETIME lpLastAccessTime; // 对文件夹的最近访问时间
FILETIME lpLastWriteTime; // 文件夹的最近修改时间
// 获取文件夹时间属性信息
if (GetFileTime(hDir, &lpCreationTime, &lpLastAccessTime, &lpLastWriteTime)){
FILETIME ftime;
FileTimeToLocalFileTime(&lpLastWriteTime, &ftime); // 转换成本地时间
FileTimeToSystemTime(&ftime, &stime); // 转换成系统时间格式
}
CloseHandle(hDir); // 关闭打开过的文件夹
return retval;
}
// 设置指定文件夹的时间属性,入口参数DirName指定了待处理的文件夹,new_time
// 为一指向SYSTEMTIME结构的指针
BOOL CSetForderTimeDlg::SetDirTime(CString DirName, SYSTEMTIME new_stime){
// 打开目录的Win32 API调用
HANDLE hDir = CreateFile(DirName, GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_DELETE,
NULL, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS, NULL);
FILETIME lpCreationTime; // 文件夹的创建时间
FILETIME lpLastAccessTime; // 对文件夹的最近访问时间
FILETIME lpLastWriteTime; // 对文件夹的最近修改时间
SystemTimeToFileTime(&new_stime, &lpCreationTime); // 转换成文件时间格式
SystemTimeToFileTime(&new_stime, &lpLastAccessTime);
SystemTimeToFileTime(&new_stime, &lpLastWriteTime);
// 设置文件夹的时间属性
BOOL retval = SetFileTime(hDir, &lpCreationTime, &lpLastAccessTime, &lpLastWriteTime);
CloseHandle(hDir); // 关闭文件夹
return retval;
}
至此,可以很方便的通过调用GetDirTime()和SetDirTime()函数来实现对任意指定文件夹时间属性的获取与设置,具体为:
SYSTEMTIME stime; // 系统时间结构对象
if (GetDirTime(m_Path, stime))
{
// 如果获取文件夹时间属性成功,获取到的时间信息将保存在stime结构对象中
……
// 如果需要可以对获取到的时间属性进行修改,也可以保留不变
……
// 将修改后的时间属性回写到文件夹
SetDirTime(m_Path, stime);
}
小结
本文通过CreateFile()函数打开文件夹,并在以后的处理中将其以文件来对待,从而可以使用GetFileTime()、SetFileTime()等函数来对其时间属性进行获取与写入处理,可对包括根目录在内的任意文件夹进行时间属性设置。在数据的完整备份与复原等方面中有较好的应用前景。本文所述代码在Windows 2000 Professional下,由Microsoft Visual C++ 6.0编译通过。