如何使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的拦截流程,且稳定性有保障。
具体实现方案
- 准备Detours库:从微软开源仓库获取Detours并编译,或者直接使用预编译的库文件。
- 用全局事件实现同步:创建一个手动重置事件,初始为无信号状态;LockFileThread在完成锁定逻辑后,将事件置为有信号状态,允许ReadFile继续。
- 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
相关产品推荐
相关产品推荐

