2011-10-21 15 views
8

this question numaralı girdiyi buldum; bu, giriş eşzamansız olarak nasıl okunacağını sorar, ancak yalnızca Windows üzerinde çalışmayan POSIX akış tanımlayıcılarıyla çalışır. Bu yüzden, bir POSIX akış tanımlayıcısı kullanmak yerine boost::asio::windows::stream_handle kullanabileceğimi gösteren this tutorial'u buldum.Windows'taki yükseltmeyi kullanarak komut satırındaki giriş eşzamansız olarak nasıl okunur?

Her iki örneği de takiben aşağıdaki kodla geldim. Çalıştırdığımda, programın hemen sona ermesiyle, komut istemine herhangi bir şey yazamam. Programımdaki diğer mantığın çalıştırılmasına izin verirken (örneğin, Windows konsolundan eşzamansız I/O gerçekleştirin), kullanıcının herhangi bir girişini, muhtemelen bir std::string içine çekmesini istiyorum.

Temelde, stdin'dan okumaya çalışırken programımı engellemekten kaçınmaya çalışıyorum. Ben de aynı şeyi yapmaya çalışırken başka bir kullanıcı karşılaşılan sorunları ayrıntılar this post bulundu gibi, Windows mümkün olup olmadığını bilmiyorum.

#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 kullanıcısı değilim, ancak yeni bir satır göstergesi olarak \ r \ n kullanmıyor mu? –

+0

Evet, ancak çalışmayı engelleyecektir, çünkü \ n hala yeni satır sırasına mı giriyor? Aynı dizede "\ r \ n" için sınır dizesini değiştirdim. – nickb

+0

nerede io_service :: run()? –

cevap

3

Bu konuyu araştıran bir iki saat harcadım, bu nedenle başkalarının zamanlarını boşa harcamasını önlemek için yayınlamaya karar verdim.

Windows, standart giriş/çıkış tutamaçları için IOCP'yi desteklemez. Tutamağı GetStdHandle(STD_INPUT_HANDLE) aldığınızda, tanıtıcıda FILE_FLAG_OVERLAPPED ayarlanmamıştır, böylece üst üste (async) IO'yu desteklemez. Ama sen bile

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

WinAPI eğer sadece dwFlagsAndAttributes görmezden gelip tekrar çakışan IO desteklemediği tanıtıcısı döndürür. Konsol giriş/çıkışının asenkron IO'sunu almanın tek yolu, 0 zaman aşımıyla WaitForSingleObject ile kolu kullanmaktır, böylece engelleme olmayan bir şey olup olmadığını kontrol edebilirsiniz. Tam olarak IO değil, ancak bir hedef ise çoklu okumadan kaçınabilirsiniz. Konsol API hakkında

Diğer ayrıntılar: https://msdn.microsoft.com/en-us/library/ms686971(v=VS.85).aspx burada açıklanan GetStdHandle ve CreateFile tarafından döndürülen tutamaklar arasındaki fark nedir

: https://msdn.microsoft.com/en-us/library/windows/desktop/ms682075(v=vs.85).aspx. Kısacası, fark sadece ana süreçte yeniden yönlendirilse bile CreateFile konsol giriş arabelleğine erişebildiği zaman çocuk süreçleri içindir.

3

Stream_handle'ınızı konsol giriş tutamacına başlatmanız gerekir. Giriş ve çıkış için aynı stream_handle'ı kullanamazsınız çünkü bunlar iki farklı tutamaçtır. girişi için

: Eğer CONSOLE_OUTPUT_HANDLE kullanırsınız çıkışı için

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

. Ama bu muhtemelen çok fazla, o kadar fazla veriyi async yazımı kullanmanız gereken pencerelere stdout'a zorlamak mümkün değil.

+2

Bu, mantıklıdır, ancak bir istisnanın atılmasına neden olur - "atama: tanıtıcı geçersiz". Ayrıca, derlenmesini sağlamak için, CONSOLE_INPUT_HANDLE değerini her bir MSDN için 'STD_INPUT_HANDLE 'olarak değiştirmem gerekiyordu. – nickb

+2

Belki de bu yüzden: http://comments.gmane.org/gmane.comp.lib.boost.asio.user/2394 – nickb

5

Eşzamansız işlemler için olay işleme döngüsünü startio_service::run() numaralı telefona çağırmanız gerekir.

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

Yardımlarınız için teşekkürler Sam. Yukarıdaki soruyu, şu anda sahip olduğum kodun tamamını içerecek şekilde değiştirdim. Cevabınızı ekledim, ancak şimdi programı çağırdığımda, anında geri dönüyor ve başarıyla çıkıyor (çökmüyor). Io_service.run() ne zaman engellenmeli? – nickb

+2

@nickb Çünkü tutamağı hiçbir şeye atamadınız, bundan dolayı okuyamazsınız. – SoapBox

İlgili konular