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

如何使Windows下针对特定NTFS文件的ReadFile调用阻塞直至其他线程显式允许其执行

如何使Windows下针对特定NTFS文件的ReadFile调用阻塞直至其他线程显式允许其执行

你遇到的问题其实是Windows IO子系统的默认行为导致的:当你用LockFileEx给文件加了独占字节范围锁后,同步模式下的ReadFile(也就是你main函数里不带重叠IO的调用)遇到锁冲突时,会直接返回ERROR_LOCK_VIOLATION,而不会阻塞等待锁释放。这是Windows设计的既定规则——只有重叠IO操作才会在遇到锁冲突时等待。

由于你明确要求main函数的代码不能修改,我们可以通过API Hook技术来拦截ReadFile调用,在真正执行文件读取前加入同步等待逻辑,直到另一个线程发出“允许继续”的信号。这里推荐使用微软官方开源的Detours库来实现Hook,它能简化Windows API的拦截流程,且稳定性有保障。

具体实现方案

  1. 准备Detours库:从微软开源仓库获取Detours并编译,或者直接使用预编译的库文件。
  2. 用全局事件实现同步:创建一个手动重置事件,初始为无信号状态;LockFileThread在完成锁定逻辑后,将事件置为有信号状态,允许ReadFile继续。
  3. Hook ReadFile函数:拦截main函数中的ReadFile调用,先等待同步事件,再执行原始的ReadFile操作。

完整代码示例

#include <windows.h>
#include <stdio.h>
#include <detours.h>

// 全局同步事件,初始为无信号状态
HANDLE g_hAllowReadEvent = NULL;

// 保存原始的ReadFile函数指针
BOOL(WINAPI* OriginalReadFile)(HANDLE, LPVOID, DWORD, LPDWORD, LPOVERLAPPED) = ReadFile;

// Hook后的ReadFile实现:先等待事件,再调用原始函数
BOOL WINAPI HookedReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped)
{
    // 等待允许读取的信号
    WaitForSingleObject(g_hAllowReadEvent, INFINITE);
    
    // 调用原始的ReadFile
    return OriginalReadFile(hFile, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, lpOverlapped);
}

DWORD WINAPI LockFileThread(LPVOID lpParam)
{
    // 初始化事件(这里确保只创建一次,实际可以移到main前初始化)
    if (!g_hAllowReadEvent)
    {
        g_hAllowReadEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
    }

    HANDLE hFile = CreateFileW(L"1.txt", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile != INVALID_HANDLE_VALUE)
    {
        OVERLAPPED overlapped = { 0 };
        // 加独占锁(作为额外文件保护,也可以移除)
        LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, MAXDWORD, MAXDWORD, &overlapped);
        
        printf("LockThread: 已锁定文件,等待10秒后允许读取\n");
        Sleep(10000);
        
        // 释放锁
        UnlockFileEx(hFile, 0, MAXDWORD, MAXDWORD, &overlapped);
        CloseHandle(hFile);
    }

    // 发出允许读取的信号
    SetEvent(g_hAllowReadEvent);
    printf("LockThread: 已允许读取\n");

    return 0;
}

int main()
{
    // 初始化Detours并Hook ReadFile
    DetourTransactionBegin();
    DetourUpdateThread(GetCurrentThread());
    DetourAttach(&(PVOID&)OriginalReadFile, HookedReadFile);
    LONG error = DetourTransactionCommit();
    if (error != NO_ERROR)
    {
        printf("Hook ReadFile失败,错误码: %ld\n", error);
        return 1;
    }

    // 你的原始main代码,完全未修改
    HANDLE hFile = CreateFileW(L"1.txt", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    HANDLE hThread = CreateThread(NULL, 0, LockFileThread, NULL, 0, NULL);
    Sleep(1000);
    char buffer[100];
    DWORD bytesRead;
    if (ReadFile(hFile, buffer, sizeof(buffer) - 1, &bytesRead, NULL)) {
        puts("Main: Read successful");
        buffer[bytesRead] = '\0';
        printf("读取内容: %s\n", buffer);
    } else {
        printf("Main: Read failed, error code: %d\n", GetLastError());
    }

    // 清理Hook
    DetourTransactionBegin();
    DetourUpdateThread(GetCurrentThread());
    DetourDetach(&(PVOID&)OriginalReadFile, HookedReadFile);
    DetourTransactionCommit();

    CloseHandle(hFile);
    CloseHandle(hThread);
    if (g_hAllowReadEvent) CloseHandle(g_hAllowReadEvent);
    return 0;
}

方案说明

  • 核心逻辑是通过Hook拦截ReadFile,在真正执行读取前等待g_hAllowReadEvent事件,直到LockFileThread调用SetEvent释放信号。
  • 你原来的LockFileEx锁逻辑可以保留(作为额外的文件保护),也可以直接去掉——事件同步已经能完全实现你需要的“阻塞ReadFile直到允许”的效果。
  • 如果你不想依赖Detours库,也可以手动实现Inline Hook,但这需要处理指令集、内存权限等细节,复杂度较高,Detours是更高效可靠的选择。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.07 07:53:04