如何在c ++中读取异步文件?

时间:2016-06-25 08:30:51

标签: c++ winapi asynchronous

我需要异步读取文件

string read(string path) {
            DWORD readenByte;
            int t;
            char* buffer = new char[512];
            HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, "read");
            OVERLAPPED overlap;
            overlap.hEvent = hEvent;
            HANDLE hFile = CreateFile(path.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
            if(!hFile) {
                Debug::error(GetLastError(), "fileAsync.cpp::read - ");
            }
            t = ReadFile(hFile, buffer, MAX_READ - 1, &readenByte, &overlap);
            if(!t) {
                Debug::error(GetLastError(), "fileAsync.cpp::read - ");
            }
            t = WaitForSingleObject(hEvent, 5000);
            if(t == WAIT_TIMEOUT) {
                Debug::error("fail to read - timeout, fileAsync.cpp::read");
            }
            buffer[readenByte] = '\0';
            string str = buffer;
            return str;
        }

我在ReadFile上遇到错误 - 38:到达文件末尾

如何使用winapi在c ++中读取asynchroneusly文件?

2 个答案:

答案 0 :(得分:5)

您的代码中有几个需要解决的错误,一些导致失败,另一些导致灾难性失败。

  • 第一个错误导致您获得错误代码:您有一个未初始化的OVERLAPPED结构,指示以下ReadFile调用从中存储的随机文件位置读取偏移 OffsetHigh 成员。要解决此问题,请初始化数据:OVERLAPPED overlap = {0};

  • 接下来,您不打开文件进行异步访问。要随后从文件中异步读取,您需要为{em> dwFlagsAndAttributes 调用CreateFile传递FILE_FLAG_OVERLAPPED。如果你几个月没有去寻找一个错误(见What happens if you forget to pass an OVERLAPPED structure on an asynchronous handle?)。

  • ReadFile的文档解释说, lpNumberOfBytesRead 参数不用于异步I / O,您应该通过NULL。这应该是显而易见的,因为异步ReadFile调用在传输的字节数已知之前返回。要获得传输的有效负载的大小,请在异步I / O完成后调用GetOverlappedResult

  • 下一个错误只会导致内存泄漏。您正在动态分配buffer,但从不致电delete[] buffer;。删除缓冲区,或分配具有自动存储持续时间(char buffer[MAX_READ] = {0};)的缓冲区,或使用C ++容器(例如std::vector<char> buffer(MAX_READ);)。

  • 另一个错误是,您尝试从std::string构建buffer:您选择的构造函数无法处理嵌入的NUL字符。它会截断你拥有的任何东西。您需要调用std::string constructor采用明确的长度参数。但即便如此,如果文件的字符编码与std::string不一致,您可能会厌倦垃圾。

  • 最后,发出一个异步读取,然后是WaitForSingleObject本质上是一个同步读取,并没有给你买任何东西。我假设这只是用于测试,而不是你的最终代码。完成此操作时请记住,只要异步读取操作正在进行,OVERLAPPED结构就需要保持活动状态。

其他建议,不会立即解决错误:

  • 您正在将std::string传递给您在read电话中使用的CreateFile功能。 Windows始终使用UTF-16LE编码,在使用Visual Studio(以及可能还有其他Windows编译器)时映射到wchar_t / std::wstring。传递std::string / const char*有两个直接的缺点:

    1. 调用ANSI API会导致字符串从MBCS转换为UTF-16(反之亦然)。这不必要地浪费资源,并且以非常微妙的方式失败,因为它依赖于当前的语言环境。
    2. 并非每个Unicode代码点都可以使用MBCS编码表示。这意味着,使用MBCS字符编码时无法打开某些文件。
    3. 始终使用Unicode API(CreateFileW)和UTF-16字符串(std::wstring / wchar_t)。您还可以在编译器的命令行中定义预处理器符号UNICODE(对于Windows API)和_UNICODE(对于CRT),以免意外调用任何ANSI API。

    4. 您正在创建一个只能通过其HANDLE值访问的事件对象,而不是其名称。您可以将NULL作为 lpName 参数传递给CreateEvent。这可以防止潜在的名称冲突,这对于名称为"read"的通用名称来说更为重要。

答案 1 :(得分:2)

1)您需要在CreateFile调用的第6个参数(FILE_FLAG_OVERLAPPED)中包含标记dwFlagsAndAttributes。这就是为什么重叠读取很可能失败的原因。

2)MAX_READ的价值是多少?我希望它小于513,否则如果文件大于512字节会发生坏事。

3)重叠结构指针不是ReadFile的{​​{1}}将为您提供预期的错误代码997(NULL),因此您无法评估ERROR_IO_PENDING致电t后。

4)在异步操作的情况下,ReadFile函数不存储您在调用中传递的指针中读取的字节,您必须在操作完成后自己查询重叠的结果。

这是一个小工作片段,我希望你能从中积累起来:

ReadFile