2013-02-19 12 views
5

Es tut mir leid, das ist nicht viel eine Frage, aber mehr von Menschen mit Problemen mit diesen besonderen Dingen zu helfen. Das Problem, an dem ich arbeite, erfordert die Verwendung von Serial I/O, wird jedoch hauptsächlich unter Windows CE 6.0 ausgeführt. Ich wurde jedoch kürzlich gefragt, ob die Anwendung auch unter Windows funktionieren könnte, also habe ich das Problem gelöst. Ich verbrachte ziemlich viel Zeit damit, mich umzusehen, ob jemand die Antworten hatte, nach denen ich suchte, und das alles kam als eine Menge Fehlinformationen und Dinge auf, die in manchen Fällen einfach falsch waren. Nachdem ich dieses Problem gelöst hatte, dachte ich, ich würde meine Ergebnisse mit allen teilen, damit jeder, der auf diese Schwierigkeiten stößt, Antworten hätte.Serielle E/A überlappen/nicht überlappen mit Windows/Windows CE

Unter Windows CE ist OVERLAPPED I/O NICHT unterstützt. Dies bedeutet, dass die bidirektionale Kommunikation über die serielle Schnittstelle sehr mühsam sein kann. Das Hauptproblem besteht darin, dass Sie keine Daten senden können, wenn Sie auf Daten von der seriellen Schnittstelle warten, da Ihr Hauptthread blockiert, bis der Lesevorgang abgeschlossen ist oder Timeouts (abhängig davon, ob Sie Timeouts eingerichtet haben)

Wie die meisten Leute, die serielle I/O machen, hatte ich ein Lesegerät seriellen Thread zum Lesen der seriellen Schnittstelle eingerichtet, die WaitCommEvent() mit einer EV_RXCHAR-Maske auf serielle Daten warten. Hier liegt die Schwierigkeit bei Windows und Windows CE.

Wenn ich einen einfachen Leser Thread wie diesen, als Beispiel: -

UINT SimpleReaderThread(LPVOID thParam) 
{ 
    DWORD eMask; 
    WaitCommEvent(thParam, &eMask, NULL); 
    MessageBox(NULL, TEXT("Thread Exited"), TEXT("Hello"), MB_OK); 
} 

Offensichtlich in dem obigen Beispiel, ich bin nicht die Daten von der seriellen Schnittstelle oder etwas zu lesen und ich gehe davon aus thatParam enthält das geöffnete Handle für den COM-Port usw. Jetzt ist das Problem unter Windows, wenn Ihr Thread ausgeführt wird und die WaitCommEvent() trifft, wird Ihr Leser-Thread in den Ruhezustand gehen und auf serielle Port-Daten warten. Okay, das ist in Ordnung und so wie es sein sollte, aber ... wie beende ich diesen Thread und lasse die MessageBox() erscheinen? Nun, wie sich herausstellt, ist es nicht so einfach und ist ein grundlegender Unterschied zwischen Windows CE und Windows in der Art, wie es seine seriellen I/O macht.

Unter Windows CE können Sie einige Dinge tun, die WaitCommEvent() durchfallen lassen, z. B. SetCommMask (COMMPORT_HANDLE, 0) oder sogar CloseHandle (COMMPORT_HANDLE). Dadurch können Sie Ihren Thread ordnungsgemäß beenden und den seriellen Anschluss freigeben, damit Sie mit dem erneuten Senden von Daten beginnen können. Keines dieser Dinge funktioniert jedoch unter Windows und beide veranlassen den Thread, von dem aus sie aufgerufen werden, in den Ruhezustand zu gehen und warten auf die Beendigung von WaitCommEvent(). Also, wie beenden Sie die WaitCommEvent() unter Windows? Nun, normalerweise würden Sie OVERLAPPED I/O verwenden und die Thread-Blockierung wäre kein Problem, aber da die Lösung auch mit Windows CE kompatibel sein muss, ist OVERLAPPED I/O keine Option. Es gibt eine Sache, die Sie unter Windows tun können, um die Funktion WaitCommEvent() zu beenden und die Funktion CancelSynchronousIo() aufzurufen. Dadurch wird Ihre WaitCommEvent() beendet, aber beachten Sie, dass dies geräteabhängig sein kann. Das Hauptproblem von CancelSynchronousIo() ist, dass es auch von Windows CE nicht unterstützt wird, also haben Sie kein Glück, das für dieses Problem zu verwenden!

Also, wie machst du es? Um dieses Problem zu lösen, können Sie WaitCommEvent() einfach nicht verwenden, da es keine Möglichkeit gibt, diese Funktion unter Windows zu beenden, das von Windows CE unterstützt wird. Das belässt Sie dann mit ReadFile(), das wieder blockiert, während es NICHT ÜBERLAPPEN I/O liest und WILL mit Comm Timeouts arbeiten.

Die Verwendung von ReadFile() und einer COMMTIMEOUTS-Struktur bedeutet, dass Sie eine enge Schleife auf Ihre seriellen Daten warten müssen, aber wenn Sie keine großen Mengen an seriellen Daten erhalten, sollte dies kein Problem sein. Auch ein Ereignis zum Beenden der Schleife mit einem kleinen Timeout stellt sicher, dass Ressourcen an das System zurückgegeben werden und Sie den Prozessor nicht zu 100% laden.Unten ist die Lösung, die ich mir ausgedacht habe, und ich würde mich über Feedback freuen, wenn Sie meinen, dass es verbessert werden könnte.

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); 

Also den Faden beenden Sie nur das Ereignis im event_handle Signal und das ermöglicht es Ihnen, den Faden richtig und Ressourcen bereinigen zu beenden und korrekt auf Windows und Windows CE arbeitet.

Hoffnung, die jedem hilft, den ich gesehen habe, hatte Schwierigkeiten mit diesem Problem.

+0

Warum nicht CancelSynchronousIo() zur Laufzeit dynamisch zu laden, und wenn Sie unter CE sind nur ein leeres Stub nennen? Hat sich die Bibliothek überlappend verwendet, wenn sie sich auf dem Desktop befindet, sich aber unter Windows CE nicht überlappt? Scheint, es gibt glattere Wege um dieses Problem. – ctacke

+0

Sie können CancelSynchronousIo() nicht dynamisch laden, es ist eine Windows-API-Funktion, die nicht Teil von Windows CE ist. Der Zweck davon ist, eine Lösung zu haben, die auf Windows CE und Windows ohne Änderung funktioniert. Es hat keinen Sinn, eine OVERLAPPED-Version für Windows und eine separate, NICHT ÜBERLAPPTE Version für Windows CE zu haben, wie Sie vielleicht auch, # ifdef/# endif und kompilieren Sie Ihren Code für die Zielplattform bedingt. –

+0

'SetCommMask' hat immer für mich bewirkt, dass' WaitCommEvent' früher beendet wurde. In meinem Code war es jedoch immer das Auslösen eines in Bearbeitung befindlichen überlappenden 'WaitCommEvent'. Willst du sagen, dass überlappende 'WaitCommEvent' wird von 'SetCommMask' abgeschlossen, aber synchrone nicht? –

Antwort

5

Da ich denke, es gab ein Missverständnis in meinem Kommentar oben, hier ist mehr Details zu zwei möglichen Lösungen, die keine enge Schleife verwenden. Beachten Sie, dass diese die Laufzeitbestimmung verwenden und daher unter beiden Betriebssystemen einwandfrei sind (obwohl Sie für jedes Ziel separat kompilieren müssen) und da beide keine #ifdef verwenden, ist es weniger wahrscheinlich, dass der Compiler auf der einen oder anderen Seite durchbricht, ohne dass Sie dies sofort bemerken.

Zuerst können Sie CancelSynchonousIo dynamisch laden und verwenden, wenn es im Betriebssystem vorhanden ist. Auch optional etwas statt der Cancel für CE (wie vielleicht den Griff zu schließen?);

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); 
    } 
} 

Die andere Option, die ein bisschen mehr Arbeit nimmt, wie Sie wahrscheinlich unterschiedliche Threading-Modelle haben werden zu (obwohl, wenn Sie C++ verwenden, wäre es ein ausgezeichnetes Argument für separate Klassen auf der Plattform basieren ohnehin) wäre die Plattform und die Nutzung überlappten auf dem Desktop zu bestimmen: versuchen

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

Okay, ich konvertiere deinen Punkt über die Existenz von CancelSynchronousIo() und benutze es, wenn verfügbar! Schön gemacht, ist mir einfach nie in den Sinn gekommen ... Ich wollte nur Code, der für beide Plattformen gleich ist. Verdammt, mein Oldschool-Byte spart Wege! –

+0

'cancelFcn (hPort);' ist falsch; Diese Funktion übernimmt das Handle des Threads, um die synchrone Ein-/Ausgabe abzubrechen. Liefern Sie entweder das Handle (wie es z. B. von 'CreateThread 'zurückgegeben wurde), oder verwenden Sie' CancelIoEx', das den Port-Handle übernimmt. –