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


Anmelden zum Antworten