如何用CreateFile为只读文件打开读写句柄
问题解答
核心原因
你遇到的错误5(拒绝访问)是因为文件设置了只读属性,即使程序拥有管理员权限,Windows文件系统默认也会阻止带GENERIC_WRITE的CreateFile调用,除非通过特定方式绕过属性检查。
两种实现方法
方法一:临时移除只读属性(最常用、兼容性好)
这是最直接可靠的方案:先读取文件当前属性,移除只读标记,打开文件完成操作后再恢复原属性。需注意异常处理,确保即使操作失败也能恢复属性。
示例代码:
#include <windows.h> #include <QString> #include <QDir> bool ModifyReadOnlyFile(const QString& targetFileString) { LPCWSTR filePath = QDir::toNativeSeparators(targetFileString).toStdWString().c_str(); // 1. 获取当前文件属性 DWORD fileAttr = GetFileAttributesW(filePath); if (fileAttr == INVALID_FILE_ATTRIBUTES) { DWORD err = GetLastError(); // 处理错误逻辑 return false; } bool wasReadOnly = (fileAttr & FILE_ATTRIBUTE_READONLY) != 0; BOOL restoreSuccess = TRUE; // 2. 移除只读属性(如果文件原本是只读) if (wasReadOnly) { if (!SetFileAttributesW(filePath, fileAttr & ~FILE_ATTRIBUTE_READONLY)) { DWORD err = GetLastError(); // 处理错误逻辑 return false; } } // 3. 打开文件进行读写 HANDLE hTargetFile = CreateFileW( filePath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if (hTargetFile == INVALID_HANDLE_VALUE) { DWORD err = GetLastError(); // 失败时恢复原属性 if (wasReadOnly) { restoreSuccess = SetFileAttributesW(filePath, fileAttr); } // 处理错误逻辑 return false; } // 4. 执行读写操作(此处添加你的业务代码) // ... // 5. 关闭文件句柄 CloseHandle(hTargetFile); // 6. 恢复原文件属性 if (wasReadOnly) { restoreSuccess = SetFileAttributesW(filePath, fileAttr); } return restoreSuccess; }
方法二:使用备份/恢复特权(无需修改文件属性)
Windows提供SE_BACKUP_NAME和SE_RESTORE_NAME特权,拥有这两个特权的进程可绕过文件属性的访问限制,配合FILE_FLAG_BACKUP_SEMANTICS标志调用CreateFile,即可打开只读文件的读写句柄。
示例代码:
#include <windows.h> #include <QString> #include <QDir> // 启用指定系统特权 BOOL EnablePrivilege(LPCWSTR privilegeName) { HANDLE hToken; TOKEN_PRIVILEGES tp; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { return FALSE; } LookupPrivilegeValueW(NULL, privilegeName, &tp.Privileges[0].Luid); tp.PrivilegeCount = 1; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; BOOL result = AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL); CloseHandle(hToken); return result; } HANDLE OpenReadOnlyFileForWrite(const QString& targetFileString) { // 启用备份和恢复特权 if (!EnablePrivilege(SE_BACKUP_NAME) || !EnablePrivilege(SE_RESTORE_NAME)) { DWORD err = GetLastError(); return INVALID_HANDLE_VALUE; } LPCWSTR filePath = QDir::toNativeSeparators(targetFileString).toStdWString().c_str(); // 打开只读文件获取读写句柄 HANDLE hTargetFile = CreateFileW( filePath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL ); // 可选:操作完成后关闭特权(将Attributes改为SE_PRIVILEGE_REMOVED即可) // ... return hTargetFile; }
针对你现有代码的问题
你已经添加了FILE_FLAG_BACKUP_SEMANTICS标志,但未启用备份/恢复特权,Windows仍会因文件的只读属性拒绝写入请求,这就是错误5的直接原因。
内容的提问来源于stack exchange,提问作者riverofwind
相关产品推荐
相关产品推荐

