2011-10-21 77 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

相關問題