Probleme mit WaitCommEvent (vermutlich wegen OVERLAPPED)



  • Hallo

    Ich verwende WaitCommEvent um auf ankommende Zeichen vom Com Port zu warten. ( ­čśâ Was sonst?) Bevor ich Overlapped verwendete funktionierte das wunderbar, nur wenn kein Zeichen kommt wartet WaitCommEvent ahlt bis in alle Ewigkeit, deshalb stellte ich auf overlapped um. Beim ersten Versuch kommt der Fehler 997, also IO_PENDING, ab dem 2. Versuch kommt 87 also INVALID_PARAMETER, womit vermutlich der letzte Parameter also &overlapped gemeint ist. Leider habe ich keine Ahnung was diese overlapped Struktur enth├Ąlt und was das beteutet. Ich habe einfach den Code von MSDN kopiert. Das F├╝llen der Struktur sieht so aus:

    overlapped.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
    		overlapped.Internal = 0;
    		overlapped.InternalHigh = 0;
    		overlapped.Offset = 0;
    		overlapped.OffsetHigh = 0;
    

    Komisch ist nur dass erst IO_PENDING kommt und erst dann INLAVID_PARAMETER (Wenn man von der Polizei aufgehalten wird sagt der Beamte ja auch nicht: 1. Ihr Bremslicht geht nicht und 2. Wo ist eigentlich die hintere H├Ąlfte Ihres Autos geblieben?) ­čśâ

    Wei├č jemand was dieses overlapped nun genau hei├čt bzw was in meinem Fall da hinein geh├Ârt? Danke!


  • Mod

    Setze nur beim ersten mal die Struktur auf 0 und setze das Handle ein. Ab dann ├Ąndere die Struktur nicht mehr!



  • Danke f├╝r den Beitrag, aber so ganz verstanden hab ich ihn nicht.

    Das Besetzten der Struktur findet nur unter WM_CREATE statt, also nur 1 mal. (vor dem Aufruf von WaitCommEvent)

    Welches Handle soll ich wo einsetzen? Das Handle der Schnittstelle in WaitCommEvent? Das hab ich schon.

    if(WaitCommEvent(RS232,&events,&overlapped))
    

    Was macht ├╝berhaupt CreateEvent? Was ist ein Event? Was ist ├╝berhaupt alles in overlapped enthalten?


  • Mod

    Das Handle von dem CreateEvent. Das Event dient dazu das Signal aufzunehmen, wenn die Operation beendet ist.



  • Das Handle ist eingesetzt, hab ich ja geschrieben:

    overlapped.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
    

    Das Event dient dazu das Signal aufzunehmen, wenn die Operation beendet ist.

    ­čśĽ Welches Signal?
    ­čśĽ Welche Operation?



  • Hast du ├╝berhaupt kapiert, was "overlapped" bedeutet? Die Funktion startet den Lesevorgang an und kehrt dann sofort zum Aufrufer zur├╝ck. Das hei├čt, w├Ąhrend dein Rechner auf die Eingabe wartet, kannst du etwas anderes machen. Irgendwann ist der Lesevorgang fertig und sagt dir Bescheid, indem er overlapped.hEvent signalisiert - das kannst du z.B. per WaitForSingleObject() feststellen.



  • Aha, aber was hat das alles mit WaitCommEvent zu tun? Warum entsteht dabei ein Fehler?



  • Also so ganz kapiert hab ich das noch nicht. Meiner Meinung nach gibt es 3 verschiedene Wege ein Programm auf ein Ereignis auf der seriellen Schnittstelle hin zu weisen:

    1. Der UART l├Âst beim Ankommen eines Zeichens einen Interupt aus. Windows registriert diesen und schickt eine Windows Message an das jeweilige Programm. (Das w├Ąre meiner Meinung nach das einfachste und beste, geht aber seit Win 3.1 offenbar nicht mehr.)

    2. Eine Funktion wartet auf diesen Interrupt und kehrt erst dann zur├╝ck wenn er eintritt (genau das macht WaitCommEvent wenn es nicht overlapped arbeitet.) Das hat allerdings den Nachteil, dass der Thread, der diese Funktion aufruft vollst├Ąndig blockiert wird bis das Zeichen eintrifft (und somit auch nicht mehr beendet werden kann)

    3. Eine Funktion fragt st├Ąndig die Schnittstelle ab ob etwas da ist und kehrt wieder zur├╝ck, das hat den Nachteil, dass der Prozessor die ganze Zeit auf 100% l├Ąuft.

    Also 1. gibt es nicht mehr, 2. und 3. sind f├╝r unser Zeitalter v├Âllig ungeeignet

    Wenn ich nun overlapped verwende muss ich ja erst recht WaitForSingleObject oder ├Ąhnliches einsetzen um auf den Event zu warten, der ├╝ber die OVERLAPPED Struktur festgelegt wurde, womit wir wieder bei 2. w├Ąren, wo liegt also da der Vorteil?



  • Vergiss mal Interrupts und das alles, ich denke du solltest lieber mal die Doku zum Thema OVERLAPPED IO nochmal lesen ­čśë
    Das ganze geht z.B. so:

    OVERLAPPED ov;
    
    // Vorbereitung:
    memset(&ov, 0, sizeof(ov));
    ov.hEvent = mein_event; // sollte manual-reset sein!
    ov.Offset = ...;
    ov.OffsetHigh = ...;
    
    // IO Request absetzen:
    BOOL rc = ::WaitCommEvent(file, mask, &ov);
    DWORD error = 0;
    
    if (!rc)
        error = ::GetLastError();
    
    // Gucken ob "pending", wenn ja auf Beendigung warten:
    if (error == ERROR_IO_PENDING)
    {
    	// WaitCommEvent konnte nicht unmittelbar beendet werden, der IO Request ist also noch ausst├Ąndig.
    	// "ov" geh├Ârt somit noch "dem system" und du darfst es nicht anr├╝hren - ausgenommen das hEvent,
    	// das darfst du LESEN und in einem WaitXXX call verwenden.
    
        // NOTE: hier nochmals WaitCommEvent (oder eine andere IO Funktion ausser GetOverlappedResult)
        //    mit der selben OVERLAPPEN Struktur aufzurufen w├Ąre ein FEHLER, da der Request noch nicht
        //    abgeschlossen ist.
    
        // Wir warten:
    
        while (1)
        {
            MachSonstwas();
    
            // Max. 100 msec auf den Event warten:
            DWORD wait_result = ::WaitForSingleObject(ov.hEvent, 100);
    
            // Gucken was Sache ist:
            if (wait_result == WAIT_TIMEOUT)
            {
                // noch nicht fertig -> weiter warten
            }
            else if (wait_timeout == WAIT_OBJECT_0)
            {
                // fertig :)
                break;
            }
            else
            {
                 // darf eigentlich nie vorkommen -> schwerer, unerwarteter fehler -> programm beenden
                 terminate();
            }
        }
    
    	// Wenn wir hier angekommen sind ist der IO Request sicher abgeschlossen, aber wir wissen noch
    	// nicht ob erfolgreich oder nicht. Also m├╝ssen wir das Ergebnis abholen:
    
        DWORD bytes_transferred = 0;
        rc = GetOverlappedResult(file, &ov, &bytes_transferred, FALSE);
    
    	// bytes_transferred ist bei WaitCommEvent bloss ein "Schrottwert", wenn der overlapped io request
    	// allerdings ├╝ber z.B. ReadFile, WriteFile oder DeviceIoControl gestartet wurde steht in
    	// bytes_transferred das was ReadFile/WriteFile/... bei sofortiger beendigung in das entsprechende
    	// Output-Parameter geschrieben h├Ątten.
    
        if (rc)
            error = 0;
        else
            error = ::GetLastError();
    
        if ((error == ERROR_IO_PENDING) || (error == ERROR_IO_INCOMPLETE))
        {
             // darf eigentlich nie vorkommen -> schwerer, unerwarteter fehler
             // -> programm beenden
             terminate();
    
    		// ACHTUNG: wenn man NICHT vor dem Aufruf von GetOverlappedResult sichergestellt hat dass der IO Request
    		//    wirklich abgeschlossen ist, und bWait=FALSE an GetOverlappedResult ├╝bergibt, dann ist ERROR_IO_INCOMPLETE
    		//    ein g├╝ltiger Returnwert der einfach nur angibt dass der IO Request eben noch nicht abgeschlossen ist.
    		//    (in unserem Fall IST aber sichergestellt dass der Request fertig ist, also d├╝rfen wir auch kein
    		//     ERROR_IO_INCOMPLETE zur├╝ckbekommen)
        }
    
    	// NOTE: wenn wir hier angekommen sind dann haben die Werte in "rc" und "error" die gleiche Bedeutung, wie
    	//    wenn der Request sofort (also ohne ERROR_IO_PENDING) abgeschlossen worden w├Ąre.
    	//    Das heisst wir k├Ânnen nun weitermachen als ob wir mit synchronem IO gearbeitet h├Ątten.
    }
    
    // Wenn wir hier angekommen sind ist der IO Request auf jeden Fall abgeschlossen.
    // "ov" ist wieder "frei" und kann z.B. freigegeben werden bzw. aus dem scope fallen,
    // der verwendete Event ist ebenso wieder frei und kann geschlossen oder wiederverwendet werden.
    
    if (rc)
    {
        // WaitCommEvent war erfolgreich
    }
    else
    {
        // WaitCommEvent war nicht erfolgreich, der Grund steht in "error"
    }
    

    Ich hoffe der Code ist f├╝r dich verst├Ąndlich.

    Nat├╝rlich muss man auch nicht in einer Schleife auf das Ergebnis warten, sondern kann das ganze in eine state machine packen. Oder eine kleine Hilfklasse f├╝r asynchrone IOs basteln (was vermutlich das schlaueste ist).



  • MsgWaitForMultipleObjects



  • Wenn ich nun overlapped verwende muss ich ja erst recht WaitForSingleObject oder ├Ąhnliches einsetzen um auf den Event zu warten, der ├╝ber die OVERLAPPED Struktur festgelegt wurde, womit wir wieder bei 2. w├Ąren, wo liegt also da der Vorteil?

    Du kannst z.B. mehrere IO Requests gleichzeitig lostreten ohne daf├╝r verschiedene Threads verwenden zu m├╝ssen. Was z.B. ein grosser Vorteil ist wenn man Daten von 2 Festplatten lesen und dann vergleichen will oder etwas ├Ąhnliches -- die 2 Platten k├Ânnen ja gleichzeitig arbeiten, und man w├╝rde die bloss ausbremsen wenn man die Zugriffe serialisiert.

    Oder du kannst, wenn du nur einen IO gleichzeitig brauchst, in einer Schleife pr├╝fen ob der IO schon fertig ist, und wenn nicht in der Zwischenzeit irgendwelche anderen Dinge erledigen. z.B. Messages dispatchen -- was auch immer.

    Solltest du allerdings einen Callback-Mechanismus wollen, dann musst du wohl oder ├╝bel IO Completion Ports verwenden. Siehe dazu CreateIoCompletionPort (MSDN).



  • Ich drehe kangsam durch:

    auf dem ATmega16 reicht:

    while(!(UCSRA & (1<<RXC)));
    

    um auf ein ankommendes Zeichen zu warten.

    value=UDR;
    

    enth├Ąlt dann das ankommende Zeichen.

    Ihr pr├Ąsentiert mir hier hunderte von Codezeilen um gerademal die obere Zeile zu erledigen. Mir ist klar, dass ein Computer schon ein wenig komplizierter ist als ein Microcontroller, aber langsam wird Sache ein wenig l├Ącherlich.
    Das erinnert mich an das hier:

    http://www.c-plusplus.net/evolution.htm



  • Die serielle Schnittstelle unter Windows anzusprechen ist *nicht* trivial! Es mag f├╝r viele zwar so scheinen, ist aber definitiv nicht so!
    Deshalb rate ich *jedem* davon ab dies selber zu machen! Verwende lieber eine Klasse die schon bew├Ąhrt und funktioniert:
    http://www.codeproject.com/system/serial.asp


Log in to reply