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

Nik*_*ita 0 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;
        }
Run Code Online (Sandbox Code Playgroud)

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

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

IIn*_*ble 5

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

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

  • 接下来,您不打开异步访问文件.要随后从文件中异步读取,您需要为dwFlagsAndAttributes调用CreateFile传递.如果你不这样做几个月就要找错了(如果忘记在异步句柄上传递OVERLAPPED结构,会发生什么?).FILE_FLAG_OVERLAPPED

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

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

  • 另一个错误是,你尝试std::string从你buffer的构造中构建一个:你选择的构造函数不能处理嵌入NUL字符的内容.它会截断你拥有的任何东西.您需要使用显式长度参数调用std :: string构造函数.但即便如此,如果文件的字符编码std::string并不同意,你可能会厌倦垃圾.

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

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

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

    1. 调用ANSI API会导致字符串从MBCS转换为UTF-16(反之亦然).这不必要地浪费资源,并且以非常微妙的方式失败,因为它依赖于当前的语言环境.
    2. 并非每个Unicode代码点都可以使用MBCS编码表示.这意味着,使用MBCS字符编码时无法打开某些文件.

    始终使用Unicode API(CreateFileW)和UTF-16字符串(std::wstring/ wchar_t).您还可以在编译器的命令行中定义预处理器符号UNICODE(对于Windows API)和_UNICODE(对于CRT),以免意外调用任何ANSI API.

  • 您正在创建一个事件对象,该事件对象只能通过其HANDLE值访问,而不能通过其名称访问.您可以通过NULLlpName参数CreateEvent.这可以防止潜在的名称冲突,这对于名称一般来说更为重要"read".