ReadFile mit Timeout



  • Hallo,

    Ich versuche schon verzweifelt etwas zu finden, wie ich auf einer Comm-Schnittstelle ein "ReadFile" mit timeout machen kann. Dabei soll der timeout nicht erst aktiv werden, wenn ein Bit schon gelesen wurde(Wie es, so wie ich es verstehe, mit der "COMMTIMEOUTS" Struktur und "SetCommTimeouts" gemacht wird), sondern "ReadFile" soll nur dann aufgerufen werden, wenn es etwas zu lesen gibt.

    Bem. : unter Linux lässt sich das ganze mit "select" erreichen, doch bei Windows ist "select" wohl ausschließlich für "sockets" gedacht.

    Bim um eure Hilfe sehr dankbar

    Grüße

    lberger



  • SetCommMask mit EV_RXCHAR und WaitCommEvent verwenden.

    Blackbird



  • Guck dir mal Overlapped IOan:

    - file mit Flag_FILE_OVERLAPPED initialisieren
    - OVERLAPPED-struktur bei ReadFile mit angeben (dort gibst du auch ein Event mit an, das gesetzt wird wenn das read beendet ist)
    - mit WaitForSingle / Multiple Objects auf das Event warten
    - wenn dabei einen Timeout gibt, den Transfer mit CancelIO abbrechen
    - das Ergebnis mit GetOverlappedResult auswerten



  • 1. mit SetCommMask() ein Event festlegen
    1a. mit SetCommTimeouts() Timeout fuer ReadFile() festlegen
    2. mit WaitCommEvent() warten, bis was da ist
    3. mit ReadFile() lesen

    Am besten das Ganze in einen Minithread verpacken, dann kann das im Hintergrund werkeln.



  • SetCommTimeouts()

    *patsch* Ich wußte doch, daß es für die serielle was einfacheres gibt 🤡



  • Hi,
    Folgenden Code habe ich mir von der MSDN Seite kopiert und etwas abgeändert,
    so daß bei einem Read-event gelesen werden sollte. Funktioniert so aber nicht.
    Wenn ich mich mit dem Hyperteminal dranhänge, und über ein Nullmodemkabel Zeichen an COMM1 sende, werden weitehin nur "Punkte" gezeichnet!!!
    Und nicht wie erwartet, dass eingegebende Zeichen.
    Wie gesagt, einfach mit CommTimeouts geht das wohl auch nicht, da hierzu erst einmal ein bit gelesen werden muss, und wenn dann Innerhalb des "Timeouts" kein Zeichen mehr kommt, kehrt ReadFile zurück, natürlich mit weniger gelesenen Bits als angefordert.

    #include <windows.h>
    #include <assert.h>
    #include <stdio.h>
    
    void main( )
    {
        HANDLE hCom;
        OVERLAPPED o;
        BOOL fSuccess;
        DWORD dwEvtMask;
    	char c = '\0';
    	unsigned long X;
    
    	DCB dcb;
    	COMMTIMEOUTS ctout;
    
    	dcb.DCBlength = sizeof(dcb);
    	dcb.BaudRate = 9600;
    	dcb.Parity = NOPARITY;
    	dcb.ByteSize = 8;
    	dcb.StopBits = ONESTOPBIT;
    
    	ctout.ReadTotalTimeoutConstant = 50;
    
        hCom = CreateFile( "COM1",
            GENERIC_READ | GENERIC_WRITE,
            0,    // exclusive access 
            NULL, // default security attributes 
            OPEN_EXISTING,
    		FILE_FLAG_OVERLAPPED,
            NULL 
            );
    
        if (hCom == INVALID_HANDLE_VALUE) 
        {
            // Handle the error. 
            printf("CreateFile failed with error %d.\n", GetLastError());
            return;
        }
    	SetCommTimeouts(hCom, &ctout);
    	SetCommState(hCom, &dcb);
    
        // Set the event mask. 
    
       fSuccess = SetCommMask(hCom, EV_RXCHAR);
    
        // Create an event object for use by WaitCommEvent. 
    
        o.hEvent = CreateEvent(
            NULL,   // default security attributes 
            FALSE,  // auto reset event 
            FALSE,  // not signaled 
            NULL    // no name
    		);
    
        // Intialize the rest of the OVERLAPPED structure to zero.
        o.Internal = 0;
        o.InternalHigh = 0;
        o.Offset = 0;
        o.OffsetHigh = 0;
    
        assert(o.hEvent);
    
      while (1) {
        fSuccess = SetCommMask(hCom, EV_RXCHAR);
    
        if (!fSuccess) 
        {
            // Handle the error. 
            printf("SetCommMask failed with error %d.\n", GetLastError());
            return;
        }
    	WaitForSingleObject(hCom, 1000);
        if (WaitCommEvent(hCom, &dwEvtMask, &o)) 
        {
            if (dwEvtMask & EV_RXCHAR) 
            {
                 ReadFile(hCom, &c, 1, &X, &o);
    			 printf("%c", c);
            }
    
        }
        else
        {
            DWORD dwRet = GetLastError();
            if( ERROR_IO_PENDING == dwRet)
            {
                printf(".");
    
                // To do.
            }
            else 
                printf("Wait failed with error %d.\n", GetLastError());
        }
      }//while (1)
    }
    

    Was habe ich nicht richtig verstanden?



  • Möglicherweise WaitCommEvent und WaitForSingleObject in der Reihenfolge vertauscht?

    Hier ist ein Code-Schnipsel, mußt noch ReadFile einbauen:

    /**********************************************************************
    ** Testprogramm: Wartet auf eine Pegeländerung an DSR, CTS, ... an COM1
    **
    ** Getestet: OK ab 12/2002!
    **********************************************************************/
    
    #include <windows.h>
    #include <stdio.h>
    
    #define TIMELIMIT 100000   // Programm läuft 100 Sekunden
    
    // zusätzliche Events (DDK)
    #define SERIAL_EV_RXCHAR           0x0001  // Any Character received
    #define SERIAL_EV_RXFLAG           0x0002  // Received certain character
    #define SERIAL_EV_TXEMPTY          0x0004  // Transmitt Queue Empty
    #define SERIAL_EV_CTS              0x0008  // CTS changed state
    #define SERIAL_EV_DSR              0x0010  // DSR changed state
    #define SERIAL_EV_RLSD             0x0020  // RLSD changed state
    #define SERIAL_EV_BREAK            0x0040  // BREAK received
    #define SERIAL_EV_ERR              0x0080  // Line status error occurred
    #define SERIAL_EV_RING             0x0100  // Ring signal detected
    #define SERIAL_EV_PERR             0x0200  // Printer error occured
    #define SERIAL_EV_RX80FULL         0x0400  // Receive buffer is 80 percent full
    #define SERIAL_EV_EVENT1           0x0800  // Provider specific event 1
    #define SERIAL_EV_EVENT2           0x1000  // Provider specific event 2
    
    // Prototypes
    const char* decimalToBinary (const long value, 
    														 const unsigned length,
    														 char* pszBuffer);
    
    /********************************************************************
    ** DOS32-programm läuft TIMELIMIT / 1000 Sekunden und zeigt alle
    ** Pegeländerungen an COM 1 an
    ********************************************************************/
    int main (void)
    {
    	HANDLE hCom;
    	OVERLAPPED o;
    	DWORD dwStart;
    	DWORD dwEvtMaskIn = EV_CTS | EV_DSR | EV_BREAK | EV_RING | EV_RXCHAR | 
    	                    EV_RLSD | EV_ERR | EV_RXFLAG | EV_TXEMPTY |
    	                    SERIAL_EV_PERR | SERIAL_EV_RX80FULL |
    	                    SERIAL_EV_EVENT1 | SERIAL_EV_EVENT2;
    	DWORD dwEvtMask = 0;
    	char szString[] = ("\r\nWarte auf Events (Pegelaenderungen) an COM 1:\r\n\
    EV_RXCHAR  =0x%04x\r\nEV_RXFLAG  =0x%04x\r\nEV_TXEMPTY =0x%04x\r\n\
    EV_CTS     =0x%04x\r\nEV_DSR     =0x%04x\r\nEV_RLSD    =0x%04x\r\n\
    EV_BREAK   =0x%04x\r\nEV_ERR     =0x%04x\r\nEV_RING    =0x%04x\r\n\
    EV_PERR    =0x%04x\r\nEV_RX80FULL=0x%04x\r\nEV_EVENT1  =0x%04x\r\n\
    EV_EVENT2  =0x%04x");
    	char szBuf[sizeof (DWORD) * 8] = "";  // für decimalToBinary-Funktion
    	SYSTEMTIME    SystemTime;   // system time
    	TCHAR         lpszMsg[127] = "";
    
    	printf (szString, EV_RXCHAR, EV_RXFLAG, EV_TXEMPTY, 
    			EV_CTS, EV_DSR, EV_RLSD, EV_BREAK, EV_ERR, EV_RING, SERIAL_EV_PERR,
    			SERIAL_EV_RX80FULL, SERIAL_EV_EVENT1, SERIAL_EV_EVENT2);
    
    	hCom = CreateFile ("COM1",
        GENERIC_READ | GENERIC_WRITE,
        0,    // exclusive access 
        NULL, // no security attributes 
        OPEN_EXISTING,
        FILE_FLAG_OVERLAPPED,
        NULL);
    
    	if (hCom == INVALID_HANDLE_VALUE) 
        return printf ("\r\nCOM1 kann nicht geöffnet werden"); 
    
    	if (!SetCommMask (hCom, dwEvtMaskIn)) 
        return printf ("\r\nSetCommMask fehlgeschlagen"); 
    
    	// Create an event object for use in WaitCommEvent. 
    	o.hEvent = CreateEvent (
        NULL,   // no security attributes 
        FALSE,  // auto reset event 
        FALSE,  // not signaled 
        NULL);  // no name 
    
    	dwStart = GetTickCount ();
    
    	while (GetTickCount () - dwStart < TIMELIMIT)
    	{
    		WaitCommEvent (hCom, &dwEvtMask, &o);
    
    		if (WAIT_OBJECT_0 == WaitForSingleObject (o.hEvent, INFINITE))
    		{
    			GetLocalTime (&SystemTime);
    			printf ("\r\nEvents: %s - ", decimalToBinary (dwEvtMask, sizeof (DWORD) * 8, szBuf));
    
    			if (dwEvtMask & EV_DSR) 
    				printf ("DSR "); 
    
    			if (dwEvtMask & EV_CTS) 
    				printf ("CTS "); 
    
    			if (dwEvtMask & EV_BREAK) 
    				printf ("BREAK "); 
    
    			if (dwEvtMask & EV_RING) 
    				printf ("RING "); 
    
    			if (dwEvtMask & EV_RXCHAR) 
    				printf ("RXCHAR "); 
    
    			if (dwEvtMask & EV_RLSD) 
    				printf ("RLSD "); 
    
    			if (dwEvtMask & EV_ERR) 
    				printf ("ERR "); 
    
    			if (dwEvtMask & EV_RXFLAG) 
    				printf ("RXFLAG "); 
    
    			if (dwEvtMask & EV_TXEMPTY) 
    				printf ("TXEMPTY "); 
    
    			if (dwEvtMask & SERIAL_EV_PERR) 
    				printf ("PERR "); 
    
    			if (dwEvtMask & SERIAL_EV_RX80FULL) 
    				printf ("RX80FULL "); 
    
    			if (dwEvtMask & SERIAL_EV_EVENT1) 
    				printf ("EVENT1 "); 
    
    			if (dwEvtMask & SERIAL_EV_EVENT2) 
    				printf ("EVENT2 "); 
    
    			wsprintf (lpszMsg, "%s, den %02d.%02d.%04d um %d:%d Uhr %d,%d Sek.",
    				SystemTime.wDayOfWeek == 1 ? "Mo" : 
    				(SystemTime.wDayOfWeek == 2 ? "Di" : 
    				(SystemTime.wDayOfWeek == 3 ? "Mi" : 
    				(SystemTime.wDayOfWeek == 4 ? "Do" : 
    				(SystemTime.wDayOfWeek == 5 ? "Fr" : 
    				(SystemTime.wDayOfWeek == 6 ? "Sa" : 
    				(SystemTime.wDayOfWeek == 7 ? "So" : "Heute")))))), 
    				SystemTime.wDay, SystemTime.wMonth, SystemTime.wYear,
    				SystemTime.wHour, SystemTime.wMinute, SystemTime.wSecond, 
    				SystemTime.wMilliseconds);
    			printf (": %s", lpszMsg); 
    		}
    	}
    
    	CloseHandle (o.hEvent);
    	CloseHandle (hCom);
    
    	return printf ("\r\n!ENDE! %d Sekunden sind um!\r\n", TIMELIMIT / 1000);
    }
    
    /*****************************************************************************
    ** Umwandlung: Dezimal zu Binär
    **
    ** 1. Param: die Zahl
    ** 2. Param: Anzahl der Binärstellen (von 1..32)
    ** 3. Param: Zeiger auf ein char-Array, das den String aufnehmen kann
    *****************************************************************************/
    const char* decimalToBinary (const long value, 
    														 const unsigned length,
    														 char* pszBuffer)
    {
    	if ((length == 0) || (length > 32))
    	{
    		pszBuffer = '\0';
    		return pszBuffer;
    	}
    
    	unsigned digit = 1 << (length - 1);
    	char* pszCurrent = pszBuffer;
    
    	do
    	{
    		if (value & digit)
    			*pszCurrent = '1';
    		else
    			*pszCurrent = '0';
    		pszCurrent++;
    		digit /= 2;
    	} while (digit != 0);
    
    	*pszCurrent = '\0';
    
    	return pszBuffer;
    }
    

    Blackbird



  • Hey vielen Dank,
    Ich habe den Code jetzt foldendermaßen abgeändert, es besteht aber noch das Problem, daß die eingegebenen Bytes nicht richtig Empfangen werden. Manchnal kommt garnichts, und ein anderesmal immer ein "o" mit "Tilde" ("~"), unabhängig, welchen key ich drücke.

    Hier der abgeänderte Code, mit dem zumindest das Timeout funktioniert:

    /**********************************************************************
    ** Testprogramm: Wartet auf eine Pegeländerung an DSR, CTS, ... an COM1
    **
    ** Getestet LB: OK ab 05/2005!
    **********************************************************************/
    
    #include <windows.h>
    #include <stdio.h>
    
    #define TIMELIMIT 100000   // Programm läuft 100 Sekunden
    
    // zusätzliche Events (DDK)
    #define SERIAL_EV_RXCHAR           0x0001  // Any Character received
    #define SERIAL_EV_RXFLAG           0x0002  // Received certain character
    #define SERIAL_EV_TXEMPTY          0x0004  // Transmitt Queue Empty
    #define SERIAL_EV_CTS              0x0008  // CTS changed state
    #define SERIAL_EV_DSR              0x0010  // DSR changed state
    #define SERIAL_EV_RLSD             0x0020  // RLSD changed state
    #define SERIAL_EV_BREAK            0x0040  // BREAK received
    #define SERIAL_EV_ERR              0x0080  // Line status error occurred
    #define SERIAL_EV_RING             0x0100  // Ring signal detected
    #define SERIAL_EV_PERR             0x0200  // Printer error occured
    #define SERIAL_EV_RX80FULL         0x0400  // Receive buffer is 80 percent full
    #define SERIAL_EV_EVENT1           0x0800  // Provider specific event 1
    #define SERIAL_EV_EVENT2           0x1000  // Provider specific event 2
    
    // Prototypes
    const char* decimalToBinary (const long value,
                                                             const unsigned length,
                                                             char* pszBuffer);
    
    /********************************************************************
    ** DOS32-programm läuft TIMELIMIT / 1000 Sekunden und zeigt alle
    ** Pegeländerungen an COM 1 an
    ********************************************************************/
    int main (void)
    {
        HANDLE hCom;
        OVERLAPPED o;
        DWORD dwStart;
    	DWORD rv;
    	char c = '\0';
    	unsigned long X;
        DWORD dwEvtMaskIn = EV_CTS | EV_DSR | EV_BREAK | EV_RING | EV_RXCHAR |
                            EV_RLSD | EV_ERR | EV_RXFLAG | EV_TXEMPTY |
                            SERIAL_EV_PERR | SERIAL_EV_RX80FULL |
                            SERIAL_EV_EVENT1 | SERIAL_EV_EVENT2;
        DWORD dwEvtMask = 0;
        char szString[] = ("\r\nWarte auf Events (Pegelaenderungen) an COM 1:\r\n\
    EV_RXCHAR  =0x%04x\r\nEV_RXFLAG  =0x%04x\r\nEV_TXEMPTY =0x%04x\r\n\
    EV_CTS     =0x%04x\r\nEV_DSR     =0x%04x\r\nEV_RLSD    =0x%04x\r\n\
    EV_BREAK   =0x%04x\r\nEV_ERR     =0x%04x\r\nEV_RING    =0x%04x\r\n\
    EV_PERR    =0x%04x\r\nEV_RX80FULL=0x%04x\r\nEV_EVENT1  =0x%04x\r\n\
    EV_EVENT2  =0x%04x");
        char szBuf[sizeof (DWORD) * 8] = "";  // für decimalToBinary-Funktion
        SYSTEMTIME    SystemTime;   // system time
        TCHAR         lpszMsg[127] = "";
    
        printf (szString, EV_RXCHAR, EV_RXFLAG, EV_TXEMPTY,
                EV_CTS, EV_DSR, EV_RLSD, EV_BREAK, EV_ERR, EV_RING, SERIAL_EV_PERR,
                SERIAL_EV_RX80FULL, SERIAL_EV_EVENT1, SERIAL_EV_EVENT2);
    
        hCom = CreateFile ("COM1",
        GENERIC_READ | GENERIC_WRITE,
        0,    // exclusive access
        NULL, // no security attributes
        OPEN_EXISTING,
        FILE_FLAG_OVERLAPPED,
        NULL);
    
        if (hCom == INVALID_HANDLE_VALUE)
        return printf ("\r\nCOM1 kann nicht geöffnet werden");
    
        if (!SetCommMask (hCom, dwEvtMaskIn))
        return printf ("\r\nSetCommMask fehlgeschlagen");
    
        // Create an event object for use in WaitCommEvent.
        o.hEvent = CreateEvent (
        NULL,   // no security attributes
        FALSE,  // auto reset event
        FALSE,  // not signaled
        NULL);  // no name
    
        dwStart = GetTickCount ();
    
        while (GetTickCount () - dwStart < TIMELIMIT)
        {
            WaitCommEvent (hCom, &dwEvtMask, &o);
    	rv = WaitForSingleObject (o.hEvent, 1000); //geändert LB
    
            if (WAIT_OBJECT_0 == rv )
            {
                GetLocalTime (&SystemTime);
                printf ("\r\nEvents: %s - ", decimalToBinary (dwEvtMask, sizeof (DWORD) * 8, szBuf));
    
                if (dwEvtMask & EV_DSR)
                    printf ("DSR ");
    
                if (dwEvtMask & EV_CTS)
                    printf ("CTS ");
    
                if (dwEvtMask & EV_BREAK)
                    printf ("BREAK ");
    
                if (dwEvtMask & EV_RING)
                    printf ("RING ");
    
                if (dwEvtMask & EV_RXCHAR) { //geändert LB
    				printf("**********************************\n");
    				ReadFile(hCom, &c, 1, &X, &o);
    				printf("Read char: %c\n");
    				printf("**********************************\n");
    
                    printf ("RXCHAR ");
    			}
    
                if (dwEvtMask & EV_RLSD)
                    printf ("RLSD ");
    
                if (dwEvtMask & EV_ERR)
                    printf ("ERR ");
    
                if (dwEvtMask & EV_RXFLAG)
                    printf ("RXFLAG ");
    
                if (dwEvtMask & EV_TXEMPTY)
                    printf ("TXEMPTY ");
    
                if (dwEvtMask & SERIAL_EV_PERR)
                    printf ("PERR ");
    
                if (dwEvtMask & SERIAL_EV_RX80FULL)
                    printf ("RX80FULL ");
    
                if (dwEvtMask & SERIAL_EV_EVENT1)
                    printf ("EVENT1 ");
    
                if (dwEvtMask & SERIAL_EV_EVENT2)
                    printf ("EVENT2 ");
    
                wsprintf (lpszMsg, "%s, den %02d.%02d.%04d um %d:%d Uhr %d,%d Sek.",
                    SystemTime.wDayOfWeek == 1 ? "Mo" :
                    (SystemTime.wDayOfWeek == 2 ? "Di" :
                    (SystemTime.wDayOfWeek == 3 ? "Mi" :
                    (SystemTime.wDayOfWeek == 4 ? "Do" :
                    (SystemTime.wDayOfWeek == 5 ? "Fr" :
                    (SystemTime.wDayOfWeek == 6 ? "Sa" :
                    (SystemTime.wDayOfWeek == 7 ? "So" : "Heute")))))),
                    SystemTime.wDay, SystemTime.wMonth, SystemTime.wYear,
                    SystemTime.wHour, SystemTime.wMinute, SystemTime.wSecond,
                    SystemTime.wMilliseconds);
                printf (": %s", lpszMsg);
            }
    	if (rv == WAIT_TIMEOUT) { //geändert LB
    		printf(".");
    	}
        }
    
        CloseHandle (o.hEvent);
        CloseHandle (hCom);
    
        return printf ("\r\n!ENDE! %d Sekunden sind um!\r\n", TIMELIMIT / 1000);
    }
    
    /*****************************************************************************
    ** Umwandlung: Dezimal zu Binär
    **
    ** 1. Param: die Zahl
    ** 2. Param: Anzahl der Binärstellen (von 1..32)
    ** 3. Param: Zeiger auf ein char-Array, das den String aufnehmen kann
    *****************************************************************************/
    const char* decimalToBinary (const long value,
                                                             const unsigned length,
                                                             char* pszBuffer)
    {
        if ((length == 0) || (length > 32))
        {
            pszBuffer = '\0';
            return pszBuffer;
        }
    
        unsigned digit = 1 << (length - 1);
        char* pszCurrent = pszBuffer;
    
        do
        {
            if (value & digit)
                *pszCurrent = '1';
            else
                *pszCurrent = '0';
            pszCurrent++;
            digit /= 2;
        } while (digit != 0);
    
        *pszCurrent = '\0';
    
        return pszBuffer;
    }
    


  • Ich habe jetzt auch noch versucht die Baudrate neu zu setzen,
    daß Problem bleibt aber dasgleiche:

    DCB dcb;
               :
               :
    	GetCommState(hCom, &dcb);
    	dcb.DCBlength = sizeof(DCB);
    	dcb.BaudRate = 9600;
    	dcb.Parity = NOPARITY;
    	dcb.ByteSize = 8;
    	dcb.StopBits = ONESTOPBIT;
    
    	if (!SetCommState(hCom, &dcb) )
    		return printf("\r\nSetCommState fehlgeschlagen");
               :
               :
    


  • Ein Dummer Fehler, habe bei printf vergessen die char variable c anzugeben. Doch dennoch wurden die Zeichen nicht richtig interpretiert. Du hast noch vergessen, den Rest der OVERLAPPED struktur zu initialisieren:

    // Intialize the rest of the OVERLAPPED structure to zero.
        o.Internal = 0;
        o.InternalHigh = 0;
        o.Offset = 0;
        o.OffsetHigh = 0;
    

    Grüße

    lberger 😋



  • Hm, ja - hast recht, explizit initialisieren wäre besser.

    Hab' den code umgeschrieben, von allen Ballast befreit:

    // DOS32-Programm zum Senden/Empfangen von Bytes über COM (9600-8N1)
    // Alle empfangenen Bytes werden zurückgesendet.
    // OS: W95, W98, W98SE, WinME, WinNT, Win2000, WinXP
    // Note: Keine Fehlerbehandlung implementiert!
    #include <windows.h>
    #include <stdio.h>
    
    #define COM_BUFFER_SIZE 256       // Read- und Write-Buffer-Size
    #define BD_RATE         CBR_9600 // 9600 Baud
    #define HELP_STRING TEXT("Aufruf mit: progname <COM-Port-Nummer>\r\n")
    
    // Hauptprogramm: Aufruf mit: progname <COM-Port-Nummer>
    int main (int argc, char **argv)
    {
    	DCB           dcb;    
    	DWORD         iBytesWritten;
    	BOOL          bRet      = true;
    	DWORD         dwRead    = 0;
    	DWORD         dwSetMask = EV_RXCHAR | EV_ERR; 
    	DWORD         dwEvtMask; 
    	OVERLAPPED    o;
    	COMMTIMEOUTS  ct;
    	unsigned char InString[COM_BUFFER_SIZE + 1];
    	TCHAR         szCOM[6];
    
    	if (argc == 2 &&                              // progname + COM-Port-Nummer ?
    		  atoi (argv[1]) > 0 && atoi (argv[1]) < 5) // COM1 ... COM4?
    		wsprintf (szCOM, TEXT("COM%s"), argv[1]);   // String "basteln" ...
    	else
    	{ 
    		printf (TEXT("\r\nERROR:\t %s"), HELP_STRING);
    		return (1); // und tschüß ...
    	}
    
    	memset (&o, 0, sizeof (OVERLAPPED)); // Struktur mit 0en füllen
    	o.hEvent = CreateEvent (NULL, FALSE, FALSE, NULL); // einen Event setzten
    
    	HANDLE hCom = CreateFile (szCOM, GENERIC_WRITE | GENERIC_READ, 0, NULL, 
    		                        OPEN_EXISTING, 0, NULL);                     
    
    	if (hCom == INVALID_HANDLE_VALUE)
    	{ // Fehlerausgabe:
    		LPVOID lpMsgBuf;
    		FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | 
    										FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(),
    										MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL);
    		MessageBox (NULL, (LPCTSTR)lpMsgBuf, "Error: CreateFile", MB_OK | MB_ICONINFORMATION);
    		LocalFree (lpMsgBuf);
    		return (1); // und tschüß ...
    	}
    
    	dcb.DCBlength = sizeof(DCB);  // Laenge des Blockes MUSS gesetzt sein!
    	GetCommState (hCom, &dcb);    // COM-Einstellungen holen und aendern
    	dcb.BaudRate  = BD_RATE;      // Baudrate
    	dcb.ByteSize  = 8;            // Datenbits
    	dcb.Parity    = NOPARITY;     // Parität
    	dcb.StopBits  = ONESTOPBIT;   // Stopbits
    	SetCommState (hCom, &dcb);    // COM-Einstellungen speichern
    
    	GetCommTimeouts (hCom, &ct);
    	// Warte-Zeit [ms] vom Beginn eines Bytes bis zum Beginn des nächsten Bytes 
    	ct.ReadIntervalTimeout         = 1000 / BD_RATE * (dcb.ByteSize + 
    		                                                 (dcb.Parity == NOPARITY ? 0 : 1) + 
    																										 (dcb.StopBits == ONESTOPBIT ? 1 : 2)) * 2;
    	ct.ReadTotalTimeoutMultiplier  = 0;  // [ms] wird mit Read-Buffer-Size multipliziert
    	ct.ReadTotalTimeoutConstant    = 50; // wird an ReadTotalTimeoutMultiplier angehängt
    	ct.WriteTotalTimeoutMultiplier = 0;
    	ct.WriteTotalTimeoutConstant   = 0;
    	SetCommTimeouts (hCom, &ct);
    
    	// Zwischenspeicher des serial-Drivers einstellen (für read und write):
    	SetupComm (hCom, COM_BUFFER_SIZE, COM_BUFFER_SIZE);
    	SetCommMask (hCom, dwSetMask); // Empfangssignale definieren
    
    	do  // in Endlos-Schleife auf Empfangssignale warten:
    	{
    		WaitCommEvent (hCom, &dwEvtMask, &o); // Event mit Empfangssignalen verknüpfen
    
    		if (WAIT_OBJECT_0 == WaitForSingleObject (o.hEvent, INFINITE)) // warten bis Event
    		{
    			if (dwEvtMask & EV_RXCHAR) // Zeichen an RxD empfangen:
    			{
    				bRet = ReadFile (hCom, &InString, sizeof (InString), &dwRead, NULL);
    
    				if (!bRet)
    				{ // Fehlerausgabe:
    					LPVOID lpMsgBuf;
    					FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | 
    													FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(),
    													MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
    													(LPTSTR) &lpMsgBuf, 0, NULL);
    					MessageBox (NULL, (LPCTSTR)lpMsgBuf, "Error: ReadFile", 
    											MB_OK | MB_ICONINFORMATION);
    					LocalFree (lpMsgBuf);
    				}
    				else
    				{ // Ausgabe (oder Verarbeitung) der empfangenen Bytes:
    					InString[dwRead] = '\0'; // in "zero-ended"-String verwandeln
    					printf (TEXT("\r\n\tRxD (%d Byte(s)): %s"), dwRead, InString);
    					WriteFile (hCom, &InString, dwRead, &iBytesWritten, NULL); // Senden der Bytes
    				}
    			}
    
    			if (dwEvtMask & EV_ERR) 
    			{
    				MessageBox (NULL, "Error empfangen", "Error: ReadFile", MB_OK);
    				break; // Schleifen-Abbruch
    			}
    		}
    	}
    	while (1);
    
    	CloseHandle (hCom);     // COM schließen
    	CloseHandle (o.hEvent); // Event-Handle zurückgeben
    
    	return (0);
    }
    

    So geht's. Ist getestet.

    Blackbird
    PS: hab' schon wieder vergessen die OVERLAPPED struct explizit zu setzen - mußt Du noch einfügen? 😉



  • Ja, super, das ging ja schnell.
    Ich habe noch eine Verständnissfrage:
    Was soll das mit den Commtimeouts? Zuerst dachte ich ja, super, damit wird alles ganz einfach. Setze "ct.ReadTotalTimeoutConstant" auf einen Wert, wie z.B 50 ms, dann kehrt ReadFile schon automatisch nach 50 ms zurueck. Aber so einfach geht das scheinbar nicht.
    In deinem letzten Code wird ja eigentlich die Timeoutzeit mit "WaitForSingleObject" gesetzt, was nuetzt da "SetCommTimeOuts()"??? 😕 😕

    Vielen Dank fuer Klaerung.



  • Das sind die Warte-Zeiten zwischen einzelnen Frames, also wenn die Frames (StartBit,Daten,Stopbit(s)) nicht unmittelbar aneineandergereiht sind, sondern Pausen dazwischen sind. Die Zeiten in der Struct bestimmen, wann ein "Datenstrom" beginnt und endet und damit die Verarbeitung beginnen kann. Sonst wird nach jedem Zeichen im read buffer die "Verarbeitung" angestoßen.

    Blackbird



  • Also das ganze geht doch tatsächlich viel einfacher, die Timeouts haben wohl doch tatsächlich die Bedeutung, wie ich zuerst gedacht habe! Also mit folgender initialisierung des Comports:

    int init_com() {
    	DCB dcb;
    	COMMTIMEOUTS ct;
    
    	com = CreateFile(COM_PORT, GENERIC_READ | GENERIC_WRITE,0,NULL,
                                    OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
    	if (com == INVALID_HANDLE_VALUE) {
    		return -1;
    	}
    
    	GetCommState(com, &dcb);
    	dcb.DCBlength = sizeof(DCB);
    	dcb.BaudRate = 9600;
    	dcb.Parity = NOPARITY;
    	dcb.ByteSize = 8;
    	dcb.StopBits = ONESTOPBIT;
    	if (!SetCommState(com, &dcb) ) {
    		MessageBeep(1);
    	}
    	GetCommTimeouts(com, &ct);
       // Warte-Zeit [ms] vom Beginn eines Bytes bis zum Beginn des nächsten Bytes
        ct.ReadIntervalTimeout         = 1000 / 9600 * (dcb.ByteSize +
                                         (dcb.Parity == NOPARITY ? 0 : 1) +
                                         (dcb.StopBits == ONESTOPBIT ? 1 : 2)) * 2;
        ct.ReadTotalTimeoutMultiplier  = 0;  // [ms] wird mit Read-Buffer-Size multipliziert
        ct.ReadTotalTimeoutConstant    = 1000; // wird an ReadTotalTimeoutMultiplier angehängt
        ct.WriteTotalTimeoutMultiplier = 0;
        ct.WriteTotalTimeoutConstant   = 0;
    	SetCommTimeouts(com, &ct);
    	return 0;
    
    }
    

    und anschließendem Readaufruf in der MainfunktionÖ

    int main() {
    
    	unsigned int X;
    	unsigned char c;
    	char buff[256];
    	int i = 0;
    	char end_char;
    	FILE *stream;
    
    	init_com();
    
    	while (1) {
    		ReadFile(com, &c, 1, &X, NULL);
    		if (X == 0) {
    			printf(".");
    			continue;
    		} else if (c != ' ' && c != '\r') {
    			printf("%c", c);
    			buff[i++] = c;
    			continue;
    		}
            }
    

    erhalte ich das gewünschte timeout verhalten: ReadFile kehrt nach 1s zurück, ohne ein Zeichen gelesen zu haben, d.h X == 0, wenns nicht's zu lesen gibt, ließt den entprechenden char, wenns was zu lesen gibt.

    Na ja, oft muss man erst komplizierte Wege gehen, um die Einfachen zu verstehen.
    Also nochmals vielen Dank für deine Hilfe



  • hi,
    gibt es hier jemanden, der mir das mit dem overlapped erklären hat?
    ich verstehe die sache nicht wirklich.

    mein problem ist folgendes,
    ich habe ein gerät am comm, dass mir ständig daten schickt, ich muß aber trotzem ab und zu mal was hinschicken, um zB den status zu erfragen.

    CreateFile(	cPort,
    						GENERIC_READ|GENERIC_WRITE,
    						0,
    						0,
    						OPEN_EXISTING,
    						FILE_ATTRIBUTE_NORMAL|FILE_FLAG_WRITE_THROUGH,//|FILE_FLAG_OVERLAPPED,
    						NULL);
    
    dcb.DCBlength			= sizeof(dcb);
    	dcb.fBinary				= TRUE;
    	dcb.fParity				= FALSE;
    	dcb.fOutxCtsFlow		= FALSE;
    	dcb.fOutxDsrFlow		= FALSE;
    	dcb.fDtrControl			= DTR_CONTROL_DISABLE; 
    	dcb.fDsrSensitivity		= FALSE;
    	dcb.fTXContinueOnXoff	= FALSE;
    	dcb.fOutX				= FALSE;
    	dcb.fInX				= FALSE;
    	dcb.fErrorChar			= FALSE;
    	dcb.fNull				= FALSE;
    	dcb.fRtsControl			= RTS_CONTROL_DISABLE;
    	dcb.fAbortOnError		= FALSE;
    	dcb.wReserved			= 0;
    
    	dcb.BaudRate = iBaud;
    	dcb.ByteSize = (BYTE)iBits;
    	dcb.Parity	 = (BYTE)iParity;
    	dcb.StopBits = (BYTE)iStop;
    	dcb.fParity  = (dcb.Parity != NOPARITY);
    
    COMMTIMEOUTS timeouts;
    
    	timeouts.ReadIntervalTimeout = 10;
    	timeouts.ReadTotalTimeoutConstant = 0;
    	timeouts.ReadTotalTimeoutMultiplier = 10;
    	timeouts.WriteTotalTimeoutConstant = 50;
    	timeouts.WriteTotalTimeoutMultiplier = 50;
    

    wenn ich auf diese weise nur mit ReadFile und WriteFile arbeite dann funktioniert es, außer dass sich das programm manchmal nicht nachvollziehbar beim senden aufhängt, sieht so aus als würde ReadFile nach dem timeout nicht beendet... ?

    wenn ich overlapped arbeite, dann funktioniert es fast gar nicht?

    OVERLAPPED osStatus={0};
    osStatus.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    
    WORD WaitComm()
    {
    	DWORD	  dwCommEvent;	 // result from WaitCommEvent
    	DWORD	  dwOvRes;		 // result from GetOverlappedResult
    	DWORD	  dwRes;		   // result from WaitForSingleObject
    	BOOL	   fWaitingOnStat = FALSE;
    	DWORD StatusTimeout = 100;
    
    	DWORD dw_Tmp =  EV_RXCHAR;
    	if(!SetCommMask(GetHandle(), dw_Tmp)) 
    	{
    		MessageBox(0, "error", "error", MB_OK);
    		CloseHandle(osStatus.hEvent); // osStatus außerhalb angelegt
    		return 1;
    	}
    
    	if (!WaitCommEvent(GetHandle(), &dwCommEvent, &osStatus)) 
    	{
    	   if (GetLastError() != ERROR_IO_PENDING)	
    	   {  // Wait not delayed?
    		   ResetEvent(osStatus.hEvent);
    			return 1;
    	   } 
    	   else
    			fWaitingOnStat = TRUE;
    	}
    	else 
    	{
    
    		ResetEvent(osStatus.hEvent);
    		return 0;
    	}
    
    	switch(dwCommEvent)
    	{
    
    	case EV_RXCHAR:
    		//
    		// wait for pending operation to complete
    		//
    		if( fWaitingOnStat) 
    		{
    			dwRes = WaitForSingleObject(osStatus.hEvent ,StatusTimeout);
    		         switch(dwRes)
    			{
    					//
    					// status completed
    					//
    					case WAIT_OBJECT_0:
    						if (!GetOverlappedResult(GetHandle(), &osStatus, &dwOvRes, TRUE)) {
    								MessageBox(0, "error thickness sensor wait event", "error", MB_OK);
    						}
    						else   {	// status check completed successfully
    
    							fWaitingOnStat = FALSE;
    							//
    							// reset event handles
    							//
    
    							ResetEvent(osStatus.hEvent);
    														return 0;
    						}
    						break;
    
    					case WAIT_TIMEOUT:
    						//	timeout occured
    						//	no new characters are placed in input buffer
    						//
    						//
    
    						ResetEvent(osStatus.hEvent);
    
    					return 1;
    
    					default:
    						ResetEvent(osStatus.hEvent);
    						return 2;
    			}
    		}
    		break;
    		}
    
    return 3;
    

    read und write func:

    int SendData(char *buffer, int iBytesToWrite ) //Daten senden
    {
    
    	DWORD dwRes;
    	if(GetHandle() == INVALID_HANDLE_VALUE)
    	{
    		ResetEvent(this->osStatus.hEvent);
    		return(0);
    	}
    
    	DWORD dwBytesWritten=0, dwRet=0;
    
    	if(!PurgeComm(GetHandle(), PURGE_TXCLEAR | PURGE_RXCLEAR ))
    	{
    		ResetEvent(osStatus.hEvent);
    
    		return 0;
    	}
    
    	for(int i=0; i<iBytesToWrite; i++)
    	{
    		if(i == iBytesToWrite-1)
    			if(!PurgeComm(hCom, PURGE_RXCLEAR)) // um antwort besser filtern zu können
    			{
    				ResetEvent(osStatus.hEvent);
    
    				return 0;
    			} 
    
    		if(!WriteFile(GetHandle(), &buffer[i], 1, &dwBytesWritten, &osStatus)) // string an com senden
    		{
    		//	Sleep(1);
    			if (GetLastError() != ERROR_IO_PENDING) 
    			{
    				ResetEvent(osStatus.hEvent);
    
    				return 0;
    			}
    			else 
    			{
    				//
    				// write is delayed
    				//
    				dwRes = WaitForSingleObject(osStatus.hEvent, 100);
    				switch(dwRes)
    				{
    					//
    					// write event set
    					//
    					case WAIT_OBJECT_0:
    						 if (!GetOverlappedResult(GetHandle(), &osStatus, &dwBytesWritten, TRUE)) 
    						 {
    
    							// fRes = TRUE;
    						 }
    
    			/*			 if (dwWritten != dwToWrite) {
    							 fRes = TRUE;
    						 }
    						 else
    							 fRes = FALSE;
    
    */						 ResetEvent(osStatus.hEvent);
    						++dwRet;
    						 break;
    
    					//
    					// wait timed out
    					//
    						 break;
    					case WAIT_TIMEOUT:
    						ResetEvent(osStatus.hEvent);
    
    						return 0;
    
    					default://
    						ResetEvent(osStatus.hEvent);
    		//	
    																								return 0;
    				}
    			}
    		}
    		else 
    		{
    			//
    			// writefile returned immediately
    			//
    
    			ResetEvent(osStatus.hEvent);
    			++dwRet;
    		}
    
    	}
    
    	return((int)dwRet);
    }
    
    int ReadData(char *buffer, int iBytesToRead)
    {
    //	Sleep(1); // <-- ohne WaitForSingleObject bekomme ich an dieser stelle ohne das sleep fast nur "mist" ausgelesen
    	if(GetHandle()== INVALID_HANDLE_VALUE)
    	{
    		return(0);
    	}
    
    	DWORD dwRead =0;
    	char cRead[2]={0};
    	int i=0;
    	bool fWaitingOnRead;
    
    	if(ReadFile(GetHandle(), cRead, 1, &dwRead, &osStatus)) // jedes zeichen einzeln von der com abholen
    		{
    			if(dwRead !=1 )
    			{
    				ResetEvent(osStatus.hEvent);
    								return -1;
    			}
    			else
    			{
    				buffer[i++] = cRead[0];
    
    				if(i>=iBytesToRead-1)
    				{
    					ResetEvent(osStatus.hEvent);
    										return(dwRead);
    				}
    
    			}
    		}
    		else
    		{
    			if (GetLastError() != ERROR_IO_PENDING)	
    			{  // read not delayed?
    										ResetEvent(osStatus.hEvent);
    
    					return -1;
    			}
    			else
    			{
    				fWaitingOnRead = TRUE;
    		//		while (!HasOverlappedIoCompleted(&this->osStatus));// <-- funktioniert auch nicht besser
    			}
    
    			 // wait for pending operations to complete
    			 //
    
    			 if (fWaitingOnRead ) 
    			 {
    
    					dwRead = WaitForSingleObject(osStatus.hEvent, 100);
    
    					switch(dwRead)
    					{
    						//
    						// read completed
    						//
    
    						case WAIT_OBJECT_0:
    							if (!GetOverlappedResult(GetHandle(), &osStatus, &dwRead, TRUE)) 
    							{
    																ResetEvent(osStatus.hEvent);
    													return -1;
    							}
    							else {	  // read completed successfully
    
    								ResetEvent(osStatus.hEvent);
    															return dwRead;
    							}
    
    						case WAIT_TIMEOUT:
    							//
    							//	timeout, no character comes from comm
    							ResetEvent(osStatus.hEvent);
    													return -1;
    
    						default:
    
    							ResetEvent(osStatus.hEvent);
    													return -1;
    					}
    			 }
    
    	 	}
    
    		ResetEvent(osStatus.hEvent);
    		return -1;
    
    }
    

    wenn ich im main soowas mache wie:

    while(1)
    {
    Wait();
    while(ReadData()>0)
    // mach was mit den sachen
    }
    

    wenn ich mir die ereignisse mitlogge, dann habe ich das Wait, dann read und irgendwann kommt aber wait und dann wieder wait, aber mit INVALID_PARAMETER und beim overlapped nur das ReadFile mit dem timeout liest nicht das von der comm was ankommt???

    hat jemand ne idee?

    gruß
    Tommi



  • hmm...
    kann da keiner helfen?
    hab mal etwas an den timeouts rumgestellt, das hilft gar nich 😞

    laut msdn ist

    BOOL GetOverlappedResult(
      HANDLE hFile,                       // handle to file, pipe, or comm device
      LPOVERLAPPED lpOverlapped,          // pointer to overlapped structure
      LPDWORD lpNumberOfBytesTransferred, // pointer to actual bytes count
      BOOL bWait                          // wait flag
    );
    
    Parameters
    hFile 
    bWait 
    Specifies whether the function should wait for the pending overlapped operation to be completed. If TRUE, the function does not return until the operation has been completed. If FALSE and the operation is still pending, the function returns FALSE and the GetLastError function returns ERROR_IO_INCOMPLETE.
    

    wenn ich das richtig verstanden habe sollte die anwendung an dieser stelle "warten" bis sie fertig ist?

    wie kann es dann aber sein, dass WaitCommEvent mehrmals hintereinander (erst "normal" und dann mit INVALID_PARAMETER) aufgerufen wird?

    was mache ich falsch? 😕 😕 😕

    mfg


Anmelden zum Antworten