How to asynchronously read input from command line using boost asio in Windows?

我们两清 提交于 2019-11-30 07:29:53

I just spent an hour or two investigating this topic so decided to post to prevent others to waste their time.

Windows doesn't support IOCP for standard input/output handles. When you take the handle by GetStdHandle(STD_INPUT_HANDLE), the handle doesn't have FILE_FLAG_OVERLAPPED set so it doesn't support overlapped (async) IO. But even if you

CreateFile(L"CONIN$",
    GENERIC_READ,
    FILE_SHARE_READ,
    NULL,
    OPEN_EXISTING,
    FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING,
    NULL);

WinAPI just ignore dwFlagsAndAttributes and again returns the handle that doesn't support overlapped IO. The only way to get async IO of console input/output is to use the handle with WaitForSingleObject with 0 timeout so you can check if there's anything to read non-blocking. Not exactly async IO but can avoid multithreading if it's a goal.

More details about console API: https://msdn.microsoft.com/en-us/library/ms686971(v=VS.85).aspx

What's the difference between handles returned by GetStdHandle and CreateFile is described here: https://msdn.microsoft.com/en-us/library/windows/desktop/ms682075(v=vs.85).aspx. In short the difference is only for a child processes when CreateFile can give access to its console input buffer even if it was redirected in the parent process.

You need to invoke io_service::run() to start the event processing loop for asynchronous operations.

class Example {
    public:
        Example( boost::asio::io_service& io_service )
            : io_service(io_service), input_buffer( INPUT_BUFFER_LENGTH), input_handle( io_service)
        {
        }
        void start_reading();
        void handle_read( const boost::system::error_code& error, std::size_t length);
        void handle_write( const boost::system::error_code& error);
    private:
        boost::asio::io_service& io_service;
        boost::asio::streambuf input_buffer;
        boost::asio::windows::stream_handle input_handle;
};

int main( int argc, char * argv)
{
    boost::asio::io_service io_service;
    Example obj( io_service );
    obj.start_reading();

    io_service.run();

    return 0;
}
SoapBox

You need to initialize your stream_handle to the console input handle. You can't use the same stream_handle for input and for output because those are two different handles.

For input:

    Example()
        : /* ... */ input_handle( io_service, GetStdHandle(STD_INPUT_HANDLE) )

For output you would use CONSOLE_OUTPUT_HANDLE. But that is probably overkill, you're unlikely to be pushing that much data into stdout on windows that you'd need to use an async write.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!