如何在无竞争条件的情况下修改文件属性?
原子修改Windows文件属性的可行方案
Windows并没有提供你期望的那种ModifyFileAttributes(DWORD addAttributes, DWORD rmAttributes)风格的直接原子API,但可以通过两种可靠方式规避查询-修改的竞争条件:
1. 独占打开文件后操作句柄
通过独占模式打开文件,确保操作期间其他进程无法修改文件属性,以此模拟原子性:
- 调用
CreateFileW时,设置共享模式为0(不允许其他进程读写或修改),同时请求FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES权限。 - 拿到句柄后,用
GetFileInformationByHandle获取当前属性,调整后调用SetFileInformationByHandle更新。 - 因为文件被独占锁定,这段操作期间其他进程无法干预属性,相当于实现了原子修改逻辑。
示例代码:
HANDLE hFile = CreateFileW(L"C:\\test.txt", FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES, 0, // 独占访问,无共享权限 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile != INVALID_HANDLE_VALUE) { FILE_BASIC_INFO fileInfo; if (GetFileInformationByHandle(hFile, &fileInfo)) { // 添加只读属性,移除隐藏属性 fileInfo.FileAttributes |= FILE_ATTRIBUTE_READONLY; fileInfo.FileAttributes &= ~FILE_ATTRIBUTE_HIDDEN; SetFileInformationByHandle(hFile, FileBasicInfo, &fileInfo, sizeof(fileInfo)); } CloseHandle(hFile); }
2. 使用NT原生API实现内核级原子更新
通过NtSetInformationFile这个NT内核API,可以直接原子性地写入目标属性值:
- 该API允许你构造
FILE_BASIC_INFORMATION结构,其中的FileAttributes字段会被原子性地写入文件元数据,无需担心中间被其他进程修改。 - 注意:这是未公开的原生接口,需要链接
ntdll.lib,且路径需使用NT格式(如\\?\C:\test.txt)或自行转换。
示例代码:
#include <ntdll.h> typedef struct _FILE_BASIC_INFORMATION { LARGE_INTEGER CreationTime; LARGE_INTEGER LastAccessTime; LARGE_INTEGER LastWriteTime; LARGE_INTEGER ChangeTime; ULONG FileAttributes; } FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION; int main() { HANDLE hFile = CreateFileW(L"\\\\?\\C:\\test.txt", FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile != INVALID_HANDLE_VALUE) { FILE_BASIC_INFORMATION fileInfo = {0}; IO_STATUS_BLOCK ioStatusBlock; // 可选:先查询当前属性 NtQueryInformationFile(hFile, &ioStatusBlock, &fileInfo, sizeof(fileInfo), FileBasicInformation); // 修改属性 fileInfo.FileAttributes |= FILE_ATTRIBUTE_READONLY; fileInfo.FileAttributes &= ~FILE_ATTRIBUTE_HIDDEN; // 原子写入属性 NTSTATUS status = NtSetInformationFile(hFile, &ioStatusBlock, &fileInfo, sizeof(fileInfo), FileBasicInformation); CloseHandle(hFile); } return 0; }
关键提示
- 独占访问方式兼容性更好,适用于绝大多数用户态场景,无需依赖未公开接口。
- NT API方式是真正的内核级原子操作,但需注意系统版本兼容性,微软不保证该接口长期稳定。
- 两种方式都需要处理文件打开失败的情况(如文件被其他进程占用)。
内容的提问来源于stack exchange,提问作者lb90
相关产品推荐
相关产品推荐

