2013-02-19 69 views
5

के साथ ओवरलैप्ड/गैर-ओवरलैप मुझे खेद है कि यह एक प्रश्न का अधिक नहीं है, लेकिन इन विशेष चीज़ों के साथ लोगों को समस्या रखने में मदद करने के लिए अधिक से अधिक। जिस समस्या पर मैं काम कर रहा हूं उसे सीरियल I/O के उपयोग की आवश्यकता है, लेकिन मुख्य रूप से विंडोज सीई 6.0 के तहत चल रहा है। हालांकि, मुझे हाल ही में पूछा गया था कि क्या विंडोज़ के तहत भी काम करने के लिए आवेदन किया जा सकता है, इसलिए मैंने इस समस्या को हल करने के बारे में बताया। मैंने यह देखने के लिए काफी समय बिताया कि क्या किसी के पास जवाब हैं जो मैं ढूंढ रहा था और यह सब कुछ गलतफहमी और चीजों के रूप में सामने आया जो कुछ मामलों में मूल रूप से गलत थे। इसलिए इस समस्या को हल करने के बाद, मैंने सोचा कि मैं अपने निष्कर्षों को हर किसी के साथ साझा करूंगा ताकि इन कठिनाइयों का सामना करने वाले किसी भी व्यक्ति के पास जवाब हों।सीरियल I/O ओवरलैप/विंडोज/विंडोज सीई

विंडोज सीई के तहत, ओवरलैप्टेड I/O समर्थित नहीं है। इसका मतलब है कि धारावाहिक बंदरगाह के माध्यम से द्वि-दिशात्मक संचार काफी परेशानी हो सकती है। मुख्य समस्या यह है कि जब आप सीरियल पोर्ट से डेटा पर इंतजार कर रहे हैं, तो आप डेटा नहीं भेज सकते हैं क्योंकि ऐसा करने से आपके मुख्य थ्रेड को रीड ऑपरेशन पूर्ण होने या टाइमआउट तक अवरुद्ध कर दिया जाएगा (इस पर निर्भर करता है कि आपने टाइमआउट सेट किया है या नहीं)

सीरियल I/O करने वाले अधिकांश लोगों की तरह, मेरे पास सीरियल पोर्ट पढ़ने के लिए एक पाठक सीरियल थ्रेड स्थापित किया गया था, जो सीरियल डेटा की प्रतीक्षा करने के लिए एक EV_RXCHAR मास्क के साथ WaitCommEvent() का उपयोग करता था। अब यह वह जगह है जहां विंडोज और विंडोज सीई के साथ कठिनाई उत्पन्न होती है।

अगर मैं इस तरह एक साधारण पाठक धागा, एक उदाहरण के रूप है: -

UINT SimpleReaderThread(LPVOID thParam) 
{ 
    DWORD eMask; 
    WaitCommEvent(thParam, &eMask, NULL); 
    MessageBox(NULL, TEXT("Thread Exited"), TEXT("Hello"), MB_OK); 
} 
ऊपर के उदाहरण में

जाहिर है, मैं सीरियल पोर्ट या कुछ भी से डाटा पढ़ने नहीं कर रहा हूँ और मैं यह सोचते हैं रहा हूँ कि thParam में कॉम पोर्ट आदि के लिए खुले हैंडल शामिल हैं। अब, समस्या तब होती है जब आपका धागा WaitCommEvent() को निष्पादित करता है और हिट करता है, आपका पाठक थ्रेड सीरियल पोर्ट डेटा के लिए प्रतीक्षा करने के लिए सो जाएगा। ठीक है, यह ठीक है और जैसा होना चाहिए, लेकिन ... आप इस धागे को कैसे समाप्त करते हैं और संदेशबॉक्स() को प्रकट करने के लिए कैसे मिलता है? खैर, जैसा कि यह पता चला है, यह वास्तव में इतना आसान नहीं है और विंडोज सीई और विंडोज के बीच एक मौलिक अंतर है जिस तरह से यह सीरियल I/O करता है।

विंडोज सीई के तहत, आप WaitCommEvent() को SetCommMask (COMMPORT_HANDLE, 0) या यहां तक ​​कि CloseHandle (COMMPORT_HANDLE) के माध्यम से गिरने के लिए कुछ चीजें कर सकते हैं। यह आपको अपने धागे को सही तरीके से समाप्त करने की अनुमति देगा और इसलिए डेटा भेजने के लिए आपके लिए सीरियल पोर्ट जारी करेगा। हालांकि इन चीजों में से कोई भी विंडोज के तहत काम नहीं करेगा और दोनों थ्रेड का कारण बनेंगे जिन्हें आप उन्हें प्रतीक्षा करने के लिए प्रतीक्षा करेंगे, WaitCommEvent() के पूरा होने पर प्रतीक्षा कर रहे हैं। तो, आप विंडोज के तहत WaitCommEvent() को कैसे समाप्त करते हैं? खैर, आम तौर पर आप ओवरलैप्टेड I/O का उपयोग करेंगे और थ्रेड अवरोधन कोई मुद्दा नहीं होगा, लेकिन चूंकि समाधान को विंडोज सीई के साथ भी संगत होना है, इसलिए ओवरलैप्टेड I/O एक विकल्प नहीं है। WaitCommEvent() को समाप्त करने के लिए विंडोज़ के तहत एक चीज आप कर सकते हैं और यह CancelSynchronousIo() फ़ंक्शन को कॉल करना है और यह आपके WaitCommEvent() को समाप्त कर देगा, लेकिन ध्यान रखें कि यह डिवाइस निर्भर हो सकता है। CancelSynchronousIo() के साथ मुख्य समस्या यह है कि यह विंडोज सीई द्वारा समर्थित नहीं है, इसलिए आप इस समस्या के लिए भाग्य से बाहर हैं!

तो आप इसे कैसे करते हैं? तथ्य यह है कि, इस समस्या को हल करने के लिए, आप बस WaitCommEvent() का उपयोग नहीं कर सकते क्योंकि Windows पर इस फ़ंक्शन को समाप्त करने का कोई तरीका नहीं है जो Windows CE द्वारा समर्थित है। तब वह आपको रीडफाइल() से छोड़ देता है जो फिर से ब्लॉक नहीं करेगा, जबकि यह गैर ओवरलैप्ड I/O पढ़ रहा है और यह होगा कम टाइमआउट के साथ काम करेगा।

रीडफाइल() और एक COMMTIMEOUTS संरचना का उपयोग करने का मतलब यह है कि आपको अपने सीरियल डेटा के लिए एक कसौटी लूप इंतजार करना होगा, लेकिन यदि आपको बड़ी मात्रा में सीरियल डेटा नहीं मिल रहा है, तो यह कोई समस्या नहीं होनी चाहिए। इसके अलावा एक छोटे टाइमआउट के साथ अपने लूप को समाप्त करने के लिए एक कार्यक्रम यह भी सुनिश्चित करेगा कि संसाधन सिस्टम पर वापस आ जाएंगे और आप 100% लोड पर प्रोसेसर को हथियार नहीं दे रहे हैं।नीचे वह समाधान है जिसके साथ मैं आया था और कुछ फीडबैक की सराहना करता हूं, अगर आपको लगता है कि इसे बेहतर किया जा सकता है।

typedef struct 
{ 
    UINT8 sync; 
    UINT8 op 
    UINT8 dev; 
    UINT8 node; 
    UINT8 data; 
    UINT8 csum; 
} COMMDAT; 

COMSTAT cs = {0}; 
DWORD byte_count; 
COMMDAT cd; 

ZeroMemory(&cd, sizeof(COMMDAT)); 
bool recv = false; 
do 
{ 
    ClearCommError(comm_handle, 0, &cs); 
    if (cs.cbInQue == sizeof(COMMDAT)) 
    { 
     ReadFile(comm_handle, &cd, sizeof(COMMDAT), &byte_count, NULL); 
     recv = true; 
    } 
} while ((WaitForSingleObject(event_handle, 2) != WAIT_OBJECT_0) && !recv); 
ThreadExit(recv ? cd.data : 0xFF); 

तो धागा तुम सिर्फ event_handle में घटना का संकेत समाप्त करने के लिए और है कि आप धागा ठीक से बाहर निकलें और संसाधनों को साफ करने के लिए अनुमति देते हैं और Windows और विंडोज सीई पर सही ढंग से काम करता है।

आशा है कि जो भी मैंने देखा है, उसे इस समस्या से परेशानी हो रही है।

+0

क्यों गतिशील रनटाइम पर CancelSynchronousIo() लोड करने का प्रयास नहीं, और यदि आप सीई नीचे होते हैं तो सिर्फ एक खाली ठूंठ फोन:) निर्धारित करने के लिए मंच और उपयोग डेस्कटॉप पर ओवरलैप हो सकता है? पुस्तकालय का उपयोग डेस्कटॉप पर होने पर ओवरलैप किया गया है, लेकिन विंडोज सीई के तहत गैर-ओवरलैप किया गया है? ऐसा लगता है कि इस समस्या के आसपास चिकनी तरीके हैं। – ctacke

+0

आप डिसमैनिक रूप से CancelSynchronousIo() को लोड नहीं कर सकते हैं, यह एक विंडोज एपीआई फ़ंक्शन है जो कि विंडोज सीई का हिस्सा नहीं है। इसका मुद्दा एक ऐसा समाधान है जो संशोधन के बिना विंडोज सीई और विंडोज दोनों पर काम करता है। विंडोज के लिए एक ओवरलैप्ड संस्करण और Windows CE के लिए एक अलग गैर ओवरलैप्ड एक होने का कोई मतलब नहीं है, जैसा कि आप भी हो सकते हैं, बस # ifdef/# endif और लक्ष्य प्लेटफ़ॉर्म के लिए सशर्त रूप से अपना कोड संकलित करें। –

+0

'SetCommMask' ने हमेशा मेरे लिए' WaitCommEvent' को पूरा करने के लिए हमेशा काम किया है। हालांकि, मेरे कोड में यह हमेशा एक प्रगति को पूरा करने के लिए ट्रिगर कर रहा है 'WaitCommEvent' ओवरलैप किया गया। क्या आप कह रहे हैं कि 'WaitCommEvent' ओवरलैप' SetCommMask' द्वारा पूरा हो जाता है, लेकिन सिंक्रोनस कोई नहीं करता है? –

उत्तर

5

चूंकि मुझे लगता है कि उपर्युक्त मेरी टिप्पणी में कोई गलतफहमी थी, तो यहां दो संभावित समाधानों पर अधिक जानकारी दी गई है जो तंग लूप का उपयोग नहीं करते हैं। ध्यान दें कि इन रनटाइम निर्धारण का उपयोग करते हैं और ओएसई के तहत ठीक से ठीक है (हालांकि आपको प्रत्येक लक्ष्य को अलग से संकलित करना है) और चूंकि न तो #ifdef का उपयोग करें, इसलिए एक तरफ कंपेलर को तोड़ने के बाद इसे कम करने की संभावना कम हो जाती है।

सबसे पहले, आप गतिशील रूप से CancelSynchonousIo लोड कर सकते हैं और ओएस में मौजूद होने पर इसका उपयोग कर सकते हैं। सीई के लिए रद्द करने के बजाय वैकल्पिक रूप से कुछ भी कर रहा है (जैसे हैंडल बंद करना?);

typedef BOOL (WINAPI *CancelIo)(HANDLE hThread); 

HANDLE hPort; 

BOOL CancelStub(HANDLE h) 
{ 
    // stub for WinCE 
    CloseHandle(hPort); 
} 

void IoWithCancel() 
{ 
    CancelIo cancelFcn; 

    cancelFcn = (CancelIo)GetProcAddress(
     GetModuleHandle(_T("kernel32.dll")), 
     _T("CancelSynchronousIo")); 

    // if for some reason you want something to happen in CE 
    if(cancelFcn == NULL) 
    { 
     cancelFcn = (CancelIo)CancelStub; 
    } 

    hPort = CreateFile(/* blah, blah */); 

    // do my I/O 

    if(cancelFcn != NULL) 
    { 
     cancelFcn(hPort); 
    } 
} 

अन्य विकल्प है, जो थोड़ा अधिक काम लेता है के रूप में आप की संभावना अलग सूत्रण मॉडल के लिए (हालांकि अगर आप सी ++ का उपयोग कर रहे जा रहे हैं, यह मंच पर वैसे भी आधारित अलग वर्ग के लिए एक उत्कृष्ट मामला होगा

HANDLE hPort; 

void IoWithOverlapped() 
{ 
    DWORD overlapped = 0; 
    OSVERSIONINFO version; 

    GetVersionEx(&version); 
    version.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); 
    if((version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) 
     || (version.dwPlatformId == VER_PLATFORM_WIN32_NT)) 
    { 
     overlapped = FILE_FLAG_OVERLAPPED; 
    } 
    else 
    { 
     // create a receive thread 
    } 

    hPort = CreateFile(
     _T("COM1:"), 
     GENERIC_READ | GENERIC_WRITE, 
     FILE_SHARE_READ | FILE_SHARE_WRITE, 
     NULL, 
     OPEN_EXISTING, 
     overlapped, 
     NULL); 
} 
+0

ठीक है, मैं CancelSynchronousIo() के अस्तित्व को निर्धारित करने और उपलब्ध होने पर इसका उपयोग करने के बारे में आपके बिंदु को स्वीकार करता हूं! अच्छी तरह से किया, बस मेरे दिमाग को कभी पार नहीं किया ... मैं बस कोड के लिए गया जो दोनों प्लेटफार्मों के लिए समान था। अरे, मेरे पुराने स्कूल बाइट सेविंग तरीके! –

+0

'रद्द करें एफसीएन (एचपीओआरटी); 'गलत है; यह फ़ंक्शन सिंक्रोनस i/o को रद्द करने के लिए थ्रेड के हैंडल को लेता है। या तो उस संभाल को आपूर्ति करें (क्योंकि इसे उदाहरण के लिए 'CreateThread' द्वारा वापस किया गया था), या' CancelIoEx' का उपयोग करें जो पोर्ट हैंडल लेता है। –