如何在Windows中使用boost asioasynchronous读取命令行input?

我发现这个问题 ,询问如何asynchronous读取input,但只能使用POSIXstream描述符,这将不会在Windows上工作。 所以,我发现这个教程显示,而不是使用POSIXstream描述符,我可以使用boost::asio::windows::stream_handle

在这两个例子之后,我提出了下面的代码。 当我运行它时,我不能在命令提示符下键入任何内容,因为程序立即终止。 我希望它捕获来自用户的任何input,可能是一个std::string ,同时允许我的程序中的其他逻辑执行(即从Windows控制台执行asynchronousI / O)。

从本质上讲,我试图避免阻止我的程序,当它试图从stdin读取。 我不知道这是否可能在Windows中,因为我也发现这个post ,详细说明另一个用户在尝试做同样的事情时遇到的问题。

 #define _WIN32_WINNT 0x0501 #define INPUT_BUFFER_LENGTH 512 #include <cstdio> #include <iostream> #define BOOST_THREAD_USE_LIB // For MinGW 4.5 - (https://svn.boost.org/trac/boost/ticket/4878) #include <boost/bind.hpp> #include <boost/asio.hpp> class Example { public: Example( boost::asio::io_service& io_service) : input_buffer( INPUT_BUFFER_LENGTH), input_handle( io_service) { // Read a line of input. boost::asio::async_read_until( input_handle, input_buffer, "\r\n", boost::bind( &Example::handle_read, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); } 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::streambuf input_buffer; boost::asio::windows::stream_handle input_handle; }; void Example::handle_read( const boost::system::error_code& error, std::size_t length) { if (!error) { // Remove newline from input. input_buffer.consume(1); input_buffer.commit( length - 1); std::istream is(&input_buffer); std::string s; is >> s; std::cout << s << std::endl; boost::asio::async_read_until(input_handle, input_buffer, "\r\n", boost::bind( &Example::handle_read, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); } else if( error == boost::asio::error::not_found) { std::cout << "Did not receive ending character!" << std::endl; } } void Example::handle_write( const boost::system::error_code& error) { if (!error) { // Read a line of input. boost::asio::async_read_until(input_handle, input_buffer, "\r\n", boost::bind( &Example::handle_read, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); } } int main( int argc, char ** argv) { try { boost::asio::io_service io_service; Example obj( io_service); io_service.run(); } catch( std::exception & e) { std::cout << e.what() << std::endl; } std::cout << "Program has ended" << std::endl; getchar(); return 0; } 

我只花了一两个小时来调查这个话题,所以决定发帖以防止别人浪费时间。

Windows不支持标准输入/输出句柄的IOCP。 当您通过GetStdHandle(STD_INPUT_HANDLE)处理句柄时,句柄没有设置FILE_FLAG_OVERLAPPED因此它不支持重叠(异步)IO。 但即使你

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

WinAPI只是忽略dwFlagsAndAttributes并再次返回不支持重叠IO的句柄。 获得控制台输入/输出的异步IO的唯一方法是使用具有0超时的WaitForSingleObject的句柄,以便检查是否有任何内容读取非阻塞。 不完全异步IO,但可以避免多线程,如果这是一个目标。

有关控制台API的更多详细信息,请访问: https : //msdn.microsoft.com/en-us/library/ms686971(v=VS.85).aspx

GetStdHandleCreateFile返回的句柄之间的区别在这里描述: https : GetStdHandle 。 简而言之,只有在CreateFile可以访问其控制台输入缓冲区时(即使它在父进程中被重定向),子进程之间的区别也是如此。

您需要调用io_service::run()来启动异步操作的事件处理循环。

 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; } 

您需要将您的stream_handle初始化为控制台输入句柄。 您不能使用相同的stream_handle输入和输出,因为这是两个不同的句柄。

对于输入:

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

对于输出你可以使用CONSOLE_OUTPUT_HANDLE 。 但是,这可能是矫枉过正的,你不太可能将这么多的数据推送到窗口的标准输出,你需要使用异步写入。