2011-10-21 8 views
8

मुझे this question मिला जो पूछता है कि इनपुट को असीमित रूप से कैसे पढ़ा जाए, लेकिन केवल POSIX स्ट्रीम डिस्क्रिप्टर के साथ काम करेगा, जो विंडोज पर काम नहीं करेगा। इसलिए, मुझे this tutorial मिला जो दिखाता है कि एक POSIX स्ट्रीम डिस्क्रिप्टर का उपयोग करने के बजाय मैं boost::asio::windows::stream_handle का उपयोग कर सकता हूं।विंडोज में बूस्ट एएसओ का उपयोग करके कमांड लाइन से इनपुट को अतुल्यकालिक रूप से कैसे पढ़ा जाए?

दोनों उदाहरणों के बाद मैं नीचे दिए गए कोड के साथ आया था। जब मैं इसे चलाता हूं, तो मैं कमांड प्रॉम्प्ट में कुछ भी टाइप नहीं कर सकता, क्योंकि कार्यक्रम तुरंत समाप्त हो जाता है। मैं इसे उपयोगकर्ता से किसी भी इनपुट को कैप्चर करना चाहता हूं, संभवतः std::string में, जबकि मेरे प्रोग्राम के भीतर अन्य तर्क निष्पादित करने की अनुमति देता है (यानी विंडोज कंसोल से एसिंक्रोनस I/O निष्पादित करता है)।

अनिवार्य रूप से, मैं stdin से पढ़ने का प्रयास करते समय अपने प्रोग्राम को अवरुद्ध करने से बचने की कोशिश कर रहा हूं। मुझे नहीं पता कि यह विंडोज़ में संभव है, क्योंकि मुझे 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

मैं विंडोज उपयोगकर्ता नहीं हूं, लेकिन क्या यह \ r \ n को एक नई लाइन सूचक के रूप में उपयोग नहीं करता है? –

+0

हां, लेकिन क्या यह इसे काम करने से रोक देगा, क्योंकि \ n अभी भी न्यूलाइन अनुक्रम में है? मैंने डेलीमीटर स्ट्रिंग को "\ r \ n" में बदल दिया, वही परिणाम। – nickb

+0

जहां io_service :: run() का आह्वान करते हैं? –

उत्तर

3

मैंने इस विषय की जांच करने में एक या दो घंटे बिताए इसलिए दूसरों को अपना समय बर्बाद करने के लिए पोस्ट करने का फैसला किया।

विंडोज मानक इनपुट/आउटपुट हैंडल के लिए आईओसीपी का समर्थन नहीं करता है। जब आप GetStdHandle(STD_INPUT_HANDLE) द्वारा हैंडल लेते हैं, तो हैंडल में FILE_FLAG_OVERLAPPED सेट नहीं है, इसलिए यह ओवरलैप्ड (async) IO का समर्थन नहीं करता है। लेकिन, यदि आपके

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

WinAPI सिर्फ dwFlagsAndAttributes ध्यान न दें और फिर से संभाल कि ओवरलैप आईओ का समर्थन नहीं करता देता है। कंसोल इनपुट/आउटपुट के एसिंक आईओ को प्राप्त करने का एकमात्र तरीका WaitForSingleObject के साथ हैंडल का उपयोग 0 टाइमआउट के साथ करना है ताकि आप जांच सकें कि गैर-अवरुद्ध पढ़ने के लिए कुछ भी है या नहीं। वास्तव में async IO नहीं है लेकिन यह एक लक्ष्य है तो multithreading से बच सकते हैं।

अधिक कंसोल एपीआई के बारे में विवरण: 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 अपने कंसोल इनपुट बफर तक पहुंच प्रदान कर सकता है भले ही इसे मूल प्रक्रिया में रीडायरेक्ट किया गया हो।

3

आपको कंसोल इनपुट हैंडल में अपनी स्ट्रीम_हेन्डल प्रारंभ करने की आवश्यकता है। आप इनपुट और आउटपुट के लिए उसी स्ट्रीम_हेंडल का उपयोग नहीं कर सकते क्योंकि ये दो अलग-अलग हैंडल हैं।

इनपुट के लिए:

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

उत्पादन आप CONSOLE_OUTPUT_HANDLE का प्रयोग करेंगे के लिए। लेकिन शायद यह अधिक है, आप उन डेटा पर stdout में इतना डेटा दबाए जाने की संभावना नहीं है कि आपको एसिंक लेखन का उपयोग करने की आवश्यकता होगी।

+2

यह समझ में आता है, लेकिन यह एक अपवाद फेंकने का कारण बनता है - "असाइन करें: हैंडल अमान्य है"। इसके अलावा, मुझे संकलन करने के लिए, "MSSN प्रति प्रति' '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

संबंधित मुद्दे