我发现this question询问如何异步读取输入,但只能用于POSIX流描述符,这在Windows上不起作用。所以,我发现this tutorial表明我可以使用boost::asio::windows::stream_handle
而不是使用POSIX流描述符。
根据这两个例子,我想出了下面的代码。当我运行它时,我无法在命令提示符中输入任何内容,因为程序会立即终止。我希望它能捕获用户的任何输入,可能是std::string
,同时允许程序中的其他逻辑执行(即从Windows控制台执行异步I / O)。
基本上,我试图避免在尝试从stdin
读取时阻止我的程序。我不知道这是否可以在Windows中使用,因为我还发现this 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;
}
答案 0 :(得分:5)
您需要为异步操作调用io_service::run()
到start事件处理循环。
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;
}
答案 1 :(得分:5)
我只花了一两个小时调查这个话题,所以决定张贴以防止别人浪费时间。
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的唯一方法是使用WaitForSingleObject
的句柄,超时为0,这样您就可以检查是否有任何内容可以读取非阻塞。不完全是异步IO,但如果它是一个目标,可以避免多线程。
有关控制台API的更多详细信息:https://msdn.microsoft.com/en-us/library/ms686971(v=VS.85).aspx
此处描述了GetStdHandle
和CreateFile
返回的句柄之间的区别:https://msdn.microsoft.com/en-us/library/windows/desktop/ms682075(v=vs.85).aspx。简而言之,差异仅适用于CreateFile
可以访问其控制台输入缓冲区的子进程,即使它在父进程中被重定向也是如此。
答案 2 :(得分:3)
您需要将stream_handle初始化为控制台输入句柄。您不能将相同的stream_handle用于输入和输出,因为它们是两个不同的句柄。
输入:
Example()
: /* ... */ input_handle( io_service, GetStdHandle(STD_INPUT_HANDLE) )
对于输出,您将使用CONSOLE_OUTPUT_HANDLE
。但这可能是矫枉过正的,你不太可能将那么多数据推送到你需要使用异步写入的Windows上的stdout。