由于所做的课题需要得到电脑中的所有文件,并且需要像everything一样能对文件进行搜索归类,所以我就去研究了一下everything的原理,其中,everything是通过读取usn文件来获取整个盘符的所有文件的,今天来整理一下它是怎么实现的。(以下代码只做逻辑示例,所以不会是优美的代码。。。)
USN日志(USN Journal)是NTFS的一个特性,全称更新序列号码日志(UpdateSequenceNumberJournal),或称更改日志(Change Journal),相当于NTFS的秘书,它会记录下NTFS盘符内一切的改动,并储存为USN_RECORD的格式。
整个实现过程一共分为以下五步:
一、判断盘符是否为NTFS格式
前面说了,USN日志是NTFS的一个特性,所以这也展示了它的局限性-----只有NTFS下才能使用。我们可以通过函数GetVolumeInformation()获取相关的信息进行判断:
使用可参考:https://docs.microsoft.com/zh-cn/windows/win32/api/fileapi/nf-fileapi-getvolumeinformationa
BOOL GetVolumeInformationA( //检索有关与指定根目录关联的文件系统和卷的信息。
LPCSTR lpRootPathName, // 磁盘驱动器代码字符串
LPSTR lpVolumeNameBuffer, //磁盘驱动器卷标名称
DWORD nVolumeNameSize, //磁盘驱动器卷标名称长度
LPDWORD lpVolumeSerialNumber, //磁盘驱动器卷标序列号
LPDWORD lpMaximumComponentLength, //系统允许的最大文件名长度
LPDWORD lpFileSystemFlags, //文件系统标识
LPSTR lpFileSystemNameBuffer, //文件操作系统名称-------------格式类型
DWORD nFileSystemNameSize //文件操作系统名称长度
);
可以看出,在上面的函数中,倒数第二项参数代表的就是盘符的格式类型,所以,我们可以通过使用这个函数的lpFileSystemNameBuffer参数来判断是否为NTFS格式,下面是代码示例:
char Root[] = "D:";
//判断驱动盘是否是NTFS格式
char sysNameBuf[MAX_PATH]={0};
int status= GetVolumeInformation(Root,
NULL, // 驱动盘名缓冲,这里我们不需要
0,
NULL,
NULL,
NULL,
sysNameBuf, // 驱动盘的系统名( FAT/NTFS)
MAX_PATH);
printf(" 文件系统名 : %sn" , Root);
if(0==strcmp(sysNameBuf,"NTFS")){
printf(" 该驱动盘是NTFS 格式 n" );
}
else{
printf(" 该驱动盘非 NTFS 格式 n" );
}
二、获取盘符句柄
获取盘符句柄使用CreateFileA函数,该函数创建或打开文件或I / O设备。最常用的I / O设备有:文件,文件流,目录,物理磁盘,卷,控制台缓冲区,磁带驱动器,通信资源,邮件槽和管道。该函数返回一个句柄,可用于根据文件或设备以及指定的标志和属性访问各种类型的I / O的文件或设备。
使用可参考:https://docs.microsoft.com/zh-cn/windows/win32/api/fileapi/nf-fileapi-createfilea
HANDLE CreateFileA(
LPCSTR lpFileName, //要创建或打开的文件或设备的名称
DWORD dwDesiredAccess, //请求访问文件或设备
DWORD dwShareMode, //请求的文件或设备共享模式
LPSECURITY_ATTRIBUTES lpSecurityAttributes, //指向SECURITY_ATTRIBUTES 结构的指针,可以为NULL。
DWORD dwCreationDisposition, //对存在或不存在的文件或设备执行的操作。对文件以外的设备,此参数通常设置为OPEN_EXISTING。
DWORD dwFlagsAndAttributes, //文件或设备属性和标志,FILE_ATTRIBUTE_NORMAL是文件的最常见默认值。
HANDLE hTemplateFile //具有GENERIC_READ访问权限的模板文件的有效句柄,可以为NULL。
);
通过使用CreateFileA()函数就可以返回盘符句柄,下面是参考代码:
char fileName[MAX_PATH];
fileName[0] = '0';
// 传入的文件名必须为.C:的形式
strcpy_s(fileName, ".");
//char Root[] = "C:";
strcat_s(fileName, Root);
// 为了方便操作,这里转为string进行去尾
string fi