2011-10-21 46 views
8

我找到了this question,它询问如何异步读取输入,但只适用于POSIX流描述符,这在Windows上不起作用。所以,我发现this tutorial这表明,而不是使用POSIX流描述符,我可以使用boost::asio::windows::stream_handle如何在Windows中使用boost asio异步读取命令行输入?

下面两个例子中,我想出了下面的代码。当我运行它时,我无法在命令提示符中输入任何内容,因为程序立即终止。我希望它能够捕获来自用户的任何输入,可能是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

我不是一个Windows用户,但是不会使用\ r \ n作为新的线路指示器吗? –

+0

是的,但会阻止它的工作,因为\ n仍然在换行符序列中?我将分隔符字符串更改为“\ r \ n”,结果相同。 – nickb

+0

哪里可以调用io_service :: run()? –

回答

3

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

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://msdn.microsoft.com/en-us/library/windows/desktop/ms682075(v=vs.85).aspx。简而言之,区别仅在于当子进程CreateFile可以访问其控制台输入缓冲区时,即使它在父进程中被重定向。

3

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

对于输入:

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

对于您可以使用CONSOLE_OUTPUT_HANDLE输出。但这可能是矫枉过正的,你不可能将那么多的数据推送到你需要使用异步写入的windows上的stdout。

+2

这是有道理的,但它会导致抛出异常 - “分配:句柄无效”。此外,我需要将每个MSDN的'CONSOLE_INPUT_HANDLE'更改为'STD_INPUT_HANDLE',以便编译它。 – nickb

+2

也许这是为什么:http://comments.gmane.org/gmane.comp.lib.boost.asio.user/2394 – nickb

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

感谢您的帮助山姆。我修改了上面的问题以包含我现在拥有的完整代码。我整合了你的答案,但现在当我调用该程序时,它立即返回并成功退出(不崩溃)。为什么当io_service.run()应该阻塞时它会返回? – nickb

+2

@nickb因为你从来没有分配过任何东西的句柄,所以你不能读取它。 – SoapBox

相关问题