You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 10:35:18