在 LockFileEx 之后调用 ReadFile 时崩溃

Posted

技术标签:

【中文标题】在 LockFileEx 之后调用 ReadFile 时崩溃【英文标题】:Crash when calling ReadFile after LockFileEx 【发布时间】:2014-12-29 10:42:09 【问题描述】:

我有几个进程尝试读取和写入同一个文件。我希望他们每个人都锁定文件,以便一次只有一个人可以访问它。

我试过这个(编辑:这次是完整的测试代码):

#include "stdafx.h"
#include "Windows.h"


bool test()

        const char* path = "test.txt";

        HANDLE hFile = CreateFileA(path,
                        GENERIC_READ | GENERIC_WRITE,
                        FILE_SHARE_READ | FILE_SHARE_WRITE,
                        NULL,
                        OPEN_ALWAYS,
                        FILE_ATTRIBUTE_NORMAL,
                        NULL);

        if (hFile == INVALID_HANDLE_VALUE)
        
                printf("ERROR: Cannot open file %s\n", path);
                return false;
        

        // Lock the file
        
                OVERLAPPED overlapped = 0;
                BOOL res = LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, ~0, ~0, &overlapped);
                if (!res)
                
                        printf("ERROR: Cannot lock file %s\n", path);
                        return false;
                
        

        DWORD fileSize = GetFileSize(hFile, NULL);
        if (fileSize > 0)
        
                char* content = new char[fileSize+1];

                // Read the file
                BOOL res = ReadFile(hFile, content, fileSize, NULL, NULL);
                if (!res)
                
                        printf("ERROR: Cannot read file %s\n", path);
                

                delete[] content;
        


        const char* newContent = "bla";
        int newContentSize = 3;

        // Write the file
        BOOL res = WriteFile(hFile, newContent, newContentSize, NULL, NULL);
        if (!res)
        
                //int err = GetLastError();
                printf("ERROR: Cannot write to file\n");
        

        // Unlock the file
        
                OVERLAPPED overlapped = 0;
                UnlockFileEx(hFile, 0, ~0, ~0, &overlapped);
        

        CloseHandle(hFile);

        return true;


int _tmain(int argc, _TCHAR* argv[])

        bool res = test();

        return 0;

这在装有 Windows 8 的计算机上运行良好。但在装有 Windows 7 的同事的计算机上,它崩溃了。具体来说,对 ReadFile 和 WriteFile 的调用总是会崩溃。

请注意,它永远不会进入带有错误 printfs 的代码路径。除了在 ReadFile 中的位置 0x00000000 写入之外,此代码不会触发任何错误(在 Windows 7 上运行时)。

我们还尝试将重叠的结构体传递给 ReadFile 和 WriteFile 调用。它可以防止崩溃但锁不再起作用,文件全部加扰(不是用这个测试代码,用真实代码)。

我做错了什么?

【问题讨论】:

不检查返回结果和GetLastError()。 包括完整的代码并且是具体的。它在ReadFileWriteFile 上崩溃?它不能同时崩溃。 @RichardCritten 我没有在这里包含它,但我确实检查了返回结果。在 Windows 8 上,没有错误。我正在再次检查 Window 7 以确保。 @Rohan 如果我评论对 ReadFile 的调用,它会在 WriteFile 中崩溃。我将尝试制作一个最小的重现代码。 “崩溃”不是合适的错误描述。此外,正如 Richard 所建议的,如果函数的文档表明您需要,请在调用失败后检查 GetLastError() 【参考方案1】:

看起来你的问题是:

lpNumberOfBytesRead [out, optional] 参数在您的调用中为空。

只有当lpOverlapped参数为NULL时,该参数才可以为NULL。

http://msdn.microsoft.com/en-us/library/windows/desktop/aa365467%28v=vs.85%29.aspx

【讨论】:

非常感谢,这就是我错过的!它还解释了为什么它在添加 overalapped 参数时起作用。【参考方案2】:

这是你的问题:

您缺少必要的结构成员

0~00 都是糟糕的代码,像这样的常量表达式总是会产生意想不到的结果——WINAPI 不像 libc 那样工作,参数并不总是与常量比较,相反,它们是针对/通过宏和其他预处理器定义本身进行测试的,因此传递常量值或使用常量初始化 WINAPI 结构通常会导致此类错误

经过多年的实验,我发现只有一种万无一失的方法可以避免它们,我将用更正的代码表示:

OVERLAPPED overlapped;
overlapped.hEvent = CreateEvent( ........... ); // put valid parameters here!
UnlockFileEx(hFile, 0 /*"reserved"*/, ULONG_MAX, ULONG_MAX, &overlapped);

请仔细阅读:http://msdn.microsoft.com/en-us/library/windows/desktop/aa365716%28v=vs.85%29.aspx

【讨论】:

嗯,文档实际上说我应该将成员初始化为 0。对于 hEvent,它特别指出:您必须将 hEvent 成员初始化为有效句柄或零。我会尝试你的方法,因为我没有其他想法,但我没有尝试做异步 I/O,所以我不确定它是否相关。 尝试 NULL 而不是零 - 并扔掉 0 .... 因为他没有写任何东西我想我们可以假设他的错误已经解决了:-) 我刚吃午饭。不,你的解决方案不起作用。 具体来说OVERLAPPED overlapped = 0; 有什么不好?你认为OVERLAPPED overlapped; 更合适吗?为什么?

以上是关于在 LockFileEx 之后调用 ReadFile 时崩溃的主要内容,如果未能解决你的问题,请参考以下文章

fileapi.h里的API函数(包括LockFileEx和FindFirstChangeNotification函数)

windows c webserver上的独占锁文件

Node.js 中的回调地狱

微信公众号 过滤 typescript cheerio

在 Node 中使用 fs.readFile 串行读取文件?

使用windows c++ LockFIle() 锁定文件然后从中获取流?