asynkron seriel portkommunikation i Windows i c

Indlæg af Hanne Mølgaard Plasc

Problem



Jeg får en fejl, når jeg forsøger at køre en c-fil, der gør nogle grundlæggende skrivninger til en seriel port. Jeg forsøger at køre det asynkront, fordi skrive undertiden tager lang tid at overføre. Min oprindelige version havde det kørende synkront med WriteFile () kommandoer, som fungerede fint. Jeg er ny til at bruge OVERLAPPED og vil sætte pris på og input om det.


Fejlen jeg får er:


Debug Assertion Failed! 
<path to dbgheap.c> 
Line: 1317 
Expression: \_CrtIsValidHeapPointer(pUserData)


når den anden skrivefunktion kaldes.


I hovedsagen:


    {
        //initialized port (with overlapped), DBC, and timeouts

        result = write\_port(outPortHandle, 128);
        result = write\_port(outPortHandle, 131);
    }




static void CALLBACK write\_compl(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped) {
        //write completed. check for errors? if so throw an exception maybe?
        printf("write completed--and made it to callback function
");
    }


int write\_port(HANDLE hComm,BYTE* lpBuf) {

   OVERLAPPED osWrite = {0};

   // Create this write operation's OVERLAPPED structure's hEvent.
   osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
   if (osWrite.hEvent == NULL)
      // error creating overlapped event handle
      return 0;

   // Issue write.
   if (!WriteFileEx(hComm, &lpBuf, 1, &osWrite, &write\_compl )) {
      if (GetLastError() != ERROR\_IO\_PENDING) { 
         // WriteFile failed, but isn't delayed. Report error and abort.
          printf("last error: \%ld",GetLastError());
          return 0; //failed, return false;
      }
      else {
         // Write is pending.
         WaitForSingleObjectEx(osWrite.hEvent, 50, TRUE);   //50 ms timeout

        return -1; //pending
      }
   }
   else {
        return 1; //finished
   }
}





Det var ikke den fulde kode, undskyld. Jeg brugte også en række BYTE'er, ikke konstanter. Men system ('pause') s forårsagede min fejlretningsfejl, fejlede fejl, og efter omhyggelig gennemgang af min kode, da WriteFileEx () var vellykket, satte det aldrig en alarm/timeout på begivenheden i den overlappede struktur, så Tilbagekaldelsesfunktionen ville aldrig blive kaldt. Jeg fik dog fastslået disse problemer.


Jeg har bare brug for hjælp til håndtering/adgang til en enkelt BYTE i en struktur, der tildeles når en ReadFileEx () -funktion kaldes (for at lagre BYTE'en, der læses, så den kan håndteres). Jeg har brug for at vide, hvordan man får adgang til denne BYTE-opbevaring ved hjælp af en forskydning og gør den overlappede struktur null. Ville den overlappede struktur null være lige så enkel som at sætte håndtaget i den til INVALID\_HANDLE\_VALUE?

Bedste reference


Jeg tror, ​​du har et par problemer:





Du sender et helt tal som en peger (din compiler skal advare mod dette eller helst nægte at kompilere koden):



  resultat=write\_port (outPortHandle, 128);



Sammenlign dette med definitionen af ​​write\_port:



  int write\_port (HANDLE hComm, BYTE * lpBuf) {



Ovennævnte udsagn stemmer ikke overens. Senere sender du derefter en peger til lpBuf-pegeren til WriteFileEx-funktionen ved at tage adressen på BYTE * -> '& lpBuf'. Dette vil ikke resultere i, hvad du tror det vil gøre .





Selvom du løser dette, vil du stadig have potentielle levetidsproblemer, når skrivningen er succesfuld, men vundet ikke inden for 50 ms timeout.


Når du bruger overlappede I/O, skal du sørge for, at læs/skriv-bufferen og den overlappede struktur forbliver gyldige, indtil I/O er afsluttet, annulleret eller den tilhørende enhed er lukket. I din kode ovenfor bruger du en pointer til en OVERLAPPED struct, der lever på stakken i dit opkald til WriteFileEx. Hvis WriteFileEx ikke afsluttes inden for 50 ms, vil den igangværende I/O referere til en ikke-eksisterende OVERLAPPED struct, og du vil (forhåbentlig) få adgangsfejl (eller værre, lydløst beskadiget stakdata et eller andet sted i din app).


Den kanoniske måde at håndtere disse livstidsproblemer på (hvis ydeevnen ikke er et stort problem), er at bruge en brugerdefineret struktur, der indeholder en OVERLAPPED-struktur og noget lager for de data, der skal læses/skrives. Allokér strukturen, når du skriver op og fordeler strukturen fra I/O-færdiggørelsesrutinen. Send adressen til den medfølgende OVERLAPPED struct til WriteFileEx, og brug f.eks. offsetof for at få adressen til den brugerdefinerede struktur fra OVERLAPPED adressen i færdiggørelsesrutinen.


Bemærk også, at WriteFileEx ikke rent faktisk bruger hEvent-medlemmet, IIRC.





EDIT: Tilføjet kodeeksempel, Bemærk venligst:



  1. Jeg har faktisk ikke forsøgt at kompilere koden, der kan være typografier eller andre problemer med koden.

  2. Det er ikke den mest effektive måde at sende data på (tildele/deallokere en hukommelsesblok for hver byte, der sendes). Det skal dog være nemt at forbedre.



    #include <stddef.h>
    #include <assert.h>
    #include <windows.h>

    // ...
    typedef struct \_MYOVERLAPPED
    {
        OVERLAPPED ol;
        BYTE buffer;
    } MYOVERLAPPED, *LPMYOVERLAPPED;
    // ...

    static void CALLBACK write\_compl(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
    {
        if (NULL == lpOverlapped)
        {
            assert(!"Should never happen");
            return;
        }

        LPBYTE pOlAsBytes = (LPBYTE)lpOverlapped;
        LPBYTE pMyOlAsBytes = pOlAsBytes - offsetof(MYOVERLAPPED, ol);
        LPMYOVERLAPPED pMyOl = (LPMYOVERLAPPED)pOlAsBytes;

        if ((ERROR\_SUCCESS == dwErrorCode) && 
            (sizeof(BYTE) == dwNumberOfBytesTransfered))
        {
            printf("written \%uc
", pMyOl->buffer);
        }
        else
        {
            // handle error
        }

        free(pMyOl);
    }


    int write\_port(HANDLE hComm, BYTE byte) {

       LPMYOVERLAPPED pMyOl = (LPMYOVERLAPPED)malloc(sizeof(MYOVERLAPPED));

       ZeroMemory(pMyOl, sizeof(MYOVERLAPPED));
       pMyOl->buffer = byte;

       // Issue write.
       if (!WriteFileEx(hComm, &pMyOl->buffer, sizeof(BYTE), pMyOl, &write\_compl )) {
          if (GetLastError() != ERROR\_IO\_PENDING) { 
             // WriteFile failed, but isn't delayed. Report error and abort.
              free(pMyOl);
              printf("last error: \%ld",GetLastError());
              return 0; //failed, return false;
          }
          else {
            return -1; //pending
          }
       }
       else {
            free(pMyOl);
            return 1; //finished
       }
    }

Andre referencer 1



    result = write\_port(outPortHandle, 128);
    result = write\_port(outPortHandle, 131);



LpBuf-argumentet skal være pointer til buffere, ikke konstanter.


f.eks.


char buffer;
buffer = 128;
result = write\_port(outPortHandle, &buffer);
buffer = 131;
result = write\_port(outPortHandle, &buffer);


Hvad du virkelig gerne vil gøre er også at passere en bufferlængde.


f.eks.


    char buffer[]  = { 128, 131 };
    result = write\_port(outPortHandle, &buffer, sizeof(buffer));

int write\_port(HANDLE hComm,BYTE* lpBuf, size\_t length) {

   ...

   // Issue write.
   if (!WriteFileEx(hComm, &lpBuf, length, &osWrite, &write\_compl )) {
   ...